2020import static com .code_intelligence .jazzer .mutation .support .InputStreamSupport .extendWithZeros ;
2121import static com .code_intelligence .jazzer .mutation .support .Preconditions .require ;
2222import static com .code_intelligence .jazzer .mutation .support .TestSupport .anyPseudoRandom ;
23+ import static com .code_intelligence .jazzer .mutation .support .TestSupport .asMap ;
2324import static com .code_intelligence .jazzer .mutation .support .TypeSupport .asAnnotatedType ;
2425import static com .google .common .truth .Truth .assertThat ;
2526import static com .google .common .truth .Truth .assertWithMessage ;
2627import static java .lang .Math .floor ;
2728import static java .lang .Math .pow ;
2829import static java .lang .Math .sqrt ;
30+ import static java .util .Arrays .asList ;
31+ import static java .util .Arrays .stream ;
2932import static java .util .Collections .emptyList ;
3033import static java .util .Collections .singletonList ;
34+ import static java .util .stream .Collectors .toList ;
35+ import static java .util .stream .Collectors .toMap ;
3136import static java .util .stream .IntStream .rangeClosed ;
3237import static org .junit .jupiter .params .provider .Arguments .arguments ;
3338
7782import java .io .IOException ;
7883import java .lang .reflect .AnnotatedType ;
7984import java .util .ArrayList ;
85+ import java .util .HashMap ;
8086import java .util .HashSet ;
8187import java .util .List ;
8288import java .util .Map ;
@@ -179,10 +185,22 @@ null, emptyList(), singletonList(null), singletonList(false), singletonList(true
179185 new TypeHolder <@ NotNull Map <@ NotNull Boolean , @ NotNull Boolean >>() {}.annotatedType (),
180186 "Map<Boolean,Boolean>" ,
181187 false ,
182- // 1 0-element map, 4 1-element maps
183- distinctElements (1 + 4 ),
184- // 1 0-element map, 4 1-element maps, 4 2-element maps
185- distinctElements (1 + 4 + 4 )),
188+ exactly (
189+ asMap (),
190+ asMap (false , false ),
191+ asMap (false , true ),
192+ asMap (true , false ),
193+ asMap (true , true )),
194+ exactly (
195+ asMap (),
196+ asMap (false , false ),
197+ asMap (false , true ),
198+ asMap (true , false ),
199+ asMap (true , true ),
200+ asMap (false , false , true , false ),
201+ asMap (false , false , true , true ),
202+ asMap (false , true , true , false ),
203+ asMap (false , true , true , true ))),
186204 arguments (
187205 asAnnotatedType (byte .class ),
188206 "Byte" ,
@@ -567,20 +585,25 @@ public static Stream<Arguments> protoStressTestCases() {
567585 SingleOptionOneOfField3 .newBuilder ().setBoolField (true ).build ())));
568586 }
569587
570- @ SafeVarargs
571- private static Consumer <List <Object >> all (Consumer <List <Object >>... checks ) {
572- return list -> {
573- for (Consumer <List <Object >> check : checks ) {
574- check .accept (list );
588+ private static CloseableConsumer all (CloseableConsumer ... checks ) {
589+ return new CloseableConsumer () {
590+ @ Override
591+ public void close () throws Exception {
592+ for (CloseableConsumer check : checks ) {
593+ check .close ();
594+ }
575595 }
576- };
577- }
578596
579- private static Consumer <List <Object >> distinctElements (int num ) {
580- return list -> assertThat (new HashSet <>(list ).size ()).isAtLeast (num );
597+ @ Override
598+ public void accept (Object value ) {
599+ for (CloseableConsumer check : checks ) {
600+ check .accept (value );
601+ }
602+ }
603+ };
581604 }
582605
583- private static Consumer < List < Object >> manyDistinctElements () {
606+ private static CloseableConsumer manyDistinctElements () {
584607 return distinctElementsRatio (MANY_DISTINCT_ELEMENTS_RATIO );
585608 }
586609
@@ -603,8 +626,7 @@ private static int boundHits(long domainSize, double probability) {
603626 * Asserts that a given list contains at least as many distinct elements as can be expected when
604627 * picking {@code picks} out of {@code domainSize} elements uniformly at random.
605628 */
606- private static Consumer <List <Object >> expectedNumberOfDistinctElements (
607- long domainSize , int picks ) {
629+ private static CloseableConsumer expectedNumberOfDistinctElements (long domainSize , int picks ) {
608630 // https://www.randomservices.org/random/urn/Birthday.html#mom2
609631 double expectedValue = domainSize * (1 - pow (1 - 1.0 / domainSize , picks ));
610632 double variance =
@@ -615,56 +637,118 @@ private static Consumer<List<Object>> expectedNumberOfDistinctElements(
615637 // Allow missing the expected value by two standard deviations. For a normal distribution,
616638 // this would correspond to 95% of all cases.
617639 int almostCertainLowerBound = (int ) floor (expectedValue - 2 * standardDeviation );
618- return list ->
640+ HashSet <Integer > hashes = new HashSet <>();
641+ return new CloseableConsumer () {
642+ @ Override
643+ public void accept (Object value ) {
644+ hashes .add (Objects .hashCode (value ));
645+ }
646+
647+ @ Override
648+ public void close () {
619649 assertWithMessage (
620650 "V=distinct elements among %s picked out of %s\n E[V]=%s\n σ[V]=%s" ,
621651 picks , domainSize , expectedValue , standardDeviation )
622- .that (new HashSet <>( list ) .size ())
652+ .that (hashes .size ())
623653 .isAtLeast (almostCertainLowerBound );
654+ }
655+ };
624656 }
625657
626- private static Consumer < List < Object >> distinctElementsRatio (double ratio ) {
658+ private static CloseableConsumer distinctElementsRatio (double ratio ) {
627659 require (ratio > 0 );
628660 require (ratio <= 1 );
629- return list -> assertThat (new HashSet <>(list ).size () / (double ) list .size ()).isAtLeast (ratio );
661+ List <Integer > hashes = new ArrayList <>();
662+ return new CloseableConsumer () {
663+ @ Override
664+ public void accept (Object value ) {
665+ hashes .add (Objects .hashCode (value ));
666+ }
667+
668+ @ Override
669+ public void close () {
670+ assertThat (new HashSet <>(hashes ).size () / (double ) hashes .size ()).isAtLeast (ratio );
671+ }
672+ };
630673 }
631674
632- private static Consumer < List < Object >> exactly (Object ... expected ) {
633- return list -> assertThat ( new HashSet <>( list )). containsExactly ( expected );
675+ private static CloseableConsumer exactly (Object ... expected ) {
676+ return containsInternal ( true , expected );
634677 }
635678
636- private static Consumer < List < Object >> contains (Object ... expected ) {
637- return list -> assertThat ( new HashSet <>( list )). containsAtLeastElementsIn ( expected );
679+ private static CloseableConsumer contains (Object ... expected ) {
680+ return containsInternal ( false , expected );
638681 }
639682
640- private static Consumer <List <Object >> doesNotContain (Object ... expected ) {
641- return list -> assertThat (new HashSet <>(list )).containsNoneIn (expected );
683+ private static CloseableConsumer containsInternal (boolean exactly , Object ... expected ) {
684+ Map <Object , Boolean > sawValue =
685+ stream (expected )
686+ .collect (
687+ toMap (
688+ value -> value ,
689+ value -> false ,
690+ (a , b ) -> {
691+ throw new IllegalStateException ("Duplicate value " + a );
692+ },
693+ HashMap ::new ));
694+ return new CloseableConsumer () {
695+ @ Override
696+ public void accept (Object value ) {
697+ if (exactly ) {
698+ assertThat (value ).isIn (sawValue .keySet ());
699+ }
700+ sawValue .put (value , true );
701+ }
702+
703+ @ Override
704+ public void close () {
705+ assertThat (sawValue .entrySet ().stream ().filter (e -> !e .getValue ()).collect (toList ()))
706+ .isEmpty ();
707+ }
708+ };
642709 }
643710
644- private static Consumer <List <Object >> mapSizeInClosedRange (int min , int max ) {
645- return list -> {
646- list .forEach (
647- map -> {
648- if (map instanceof Map ) {
649- assertThat (((Map ) map ).size ()).isAtLeast (min );
650- assertThat (((Map ) map ).size ()).isAtMost (max );
651- } else {
652- throw new IllegalArgumentException (
653- "Expected a list of maps, got list of" + map .getClass ().getName ());
654- }
655- });
711+ private static CloseableConsumer doesNotContain (Object ... expected ) {
712+ return new CloseableConsumer () {
713+ @ Override
714+ public void accept (Object value ) {
715+ assertThat (value ).isNotIn (asList (expected ));
716+ }
717+
718+ @ Override
719+ public void close () {}
656720 };
657721 }
658722
723+ private static CloseableConsumer mapSizeInClosedRange (int min , int max ) {
724+ return new CloseableConsumer () {
725+ @ Override
726+ public void accept (Object map ) {
727+ if (map instanceof Map ) {
728+ assertThat (((Map ) map ).size ()).isAtLeast (min );
729+ assertThat (((Map ) map ).size ()).isAtMost (max );
730+ } else {
731+ throw new IllegalArgumentException (
732+ "Expected a list of maps, got list of" + map .getClass ().getName ());
733+ }
734+ }
735+
736+ @ Override
737+ public void close () {}
738+ };
739+ }
740+
741+ interface CloseableConsumer extends AutoCloseable , Consumer <Object > {}
742+
659743 @ ParameterizedTest (name = "{index} {0}, {1}" )
660744 @ MethodSource ({"stressTestCases" , "protoStressTestCases" })
661745 void genericMutatorStressTest (
662746 AnnotatedType type ,
663747 String mutatorTree ,
664748 boolean hasFixedSize ,
665- Consumer < List < Object >> expectedInitValues ,
666- Consumer < List < Object >> expectedMutatedValues )
667- throws IOException {
749+ CloseableConsumer checkInitValues ,
750+ CloseableConsumer checkMutatedValues )
751+ throws Exception {
668752 validateAnnotationUsage (type );
669753 SerializingMutator mutator = Mutators .newFactory ().createOrThrow (type );
670754 assertThat (mutator .toString ()).isEqualTo (mutatorTree );
@@ -678,8 +762,6 @@ void genericMutatorStressTest(
678762
679763 PseudoRandom rng = anyPseudoRandom ();
680764
681- List <Object > initValues = new ArrayList <>();
682- List <Object > mutatedValues = new ArrayList <>();
683765 for (int i = 0 ; i < NUM_INITS ; i ++) {
684766 Object value = mutator .init (rng );
685767
@@ -689,7 +771,7 @@ void genericMutatorStressTest(
689771 testReadWriteRoundtrip (mutator , fixedValue );
690772 testReadWriteExclusiveRoundtrip (mutator , fixedValue );
691773
692- initValues . add ( mutator . detach ( value ) );
774+ checkInitValues . accept ( value );
693775 value = fixFloatingPointsForProtos (value );
694776
695777 for (int mutation = 0 ; mutation < NUM_MUTATE_PER_INIT ; mutation ++) {
@@ -705,7 +787,7 @@ void genericMutatorStressTest(
705787 }
706788 }
707789
708- mutatedValues . add ( mutator . detach ( value ) );
790+ checkMutatedValues . accept ( value );
709791
710792 // For proto messages, each float field with value -0.0f, and double field with value -0.0
711793 // will be converted to 0.0f and 0.0, respectively. This is because the values -0f and 0f
@@ -720,8 +802,8 @@ void genericMutatorStressTest(
720802 }
721803 }
722804
723- expectedInitValues . accept ( initValues );
724- expectedMutatedValues . accept ( mutatedValues );
805+ checkInitValues . close ( );
806+ checkMutatedValues . close ( );
725807 }
726808
727809 private static <T > void testReadWriteExclusiveRoundtrip (Serializer <T > serializer , T value )
0 commit comments