diff --git a/.github/workflows/fuzzing.yml b/.github/workflows/fuzzing.yml new file mode 100644 index 000000000..ea8d17cd1 --- /dev/null +++ b/.github/workflows/fuzzing.yml @@ -0,0 +1,73 @@ +name: Fuzzing +on: + schedule: + # At 03:00 UTC (01:00 CEST) every day + - cron: '0 3 * * *' + + merge_group: + + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + + build_and_test: + runs-on: ${{ matrix.os }} + name: Fuzzing + strategy: + matrix: + os: [ubuntu-22.04, macos-14] + jdk: [21, 8] + include: + - jdk: 21 + # Workaround for https://github.com/bazelbuild/bazel/issues/14502 + extra_bazel_args: "--jvmopt=-Djava.security.manager=allow" + - os: ubuntu-22.04 + arch: "linux" + - os: macos-14 + arch: "macos-arm64" + bazel_args: "--xcode_version_config=//.github:host_xcodes" + + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: zulu + java-version: 21 + + - name: Set Build Buddy config + run: .github/scripts/echoBuildBuddyConfig.sh ${{ secrets.BUILDBUDDY_API_KEY }} >> $GITHUB_ENV + shell: bash + + - name: Cache Fuzzing Corpus + uses: actions/cache@v4 + with: + path: | + selffuzz/src/test/resources/.corpus + + key: fuzzing-corpus-${{ matrix.os }}-${{ matrix.jdk }}-${{ github.run_id }}-${{ github.run_attempt }} + restore-keys: | + fuzzing-corpus- + + - name: Build & Fuzz + run: | + bazelisk run ${{env.BUILD_BUDDY_CONFIG}} --java_runtime_version=remotejdk_${{ matrix.jdk }} ${{ matrix.bazel_args }} ${{ matrix.extra_bazel_args }} //selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation:ArgumentsMutatorFuzzTest --jvmopt=-Xmx10000m -- -runs=1000000 + + # Notification job that runs after all matrix jobs complete + notification: + needs: build_and_test + runs-on: ubuntu-24.04 + if: failure() # Run regardless of build_and_test outcome + steps: + - name: Slack notification on failure + run: | + curl -X POST -H 'Content-type: application/json' \ + --data '{ + "workflow_url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + }' \ + ${{ secrets.SLACK_WEBHOOK_URL }} diff --git a/.github/workflows/run-all-tests-pr.yml b/.github/workflows/run-all-tests-pr.yml index 8efd5e238..68a17a067 100644 --- a/.github/workflows/run-all-tests-pr.yml +++ b/.github/workflows/run-all-tests-pr.yml @@ -72,6 +72,16 @@ jobs: choco install llvm --version=19.1.0 --force echo "C:\Program Files\LLVM\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + - name: Load fuzzing corpus cache + uses: actions/cache/restore@v4 + with: + path: | + selffuzz/src/test/resources/.corpus + + key: fuzzing-corpus-${{ matrix.os }}-${{ matrix.jdk }}-${{ github.run_id }}-${{ github.run_attempt }} + restore-keys: | + fuzzing-corpus- + - name: Build & Test run: bazelisk test ${{env.BUILD_BUDDY_CONFIG}} --java_runtime_version=remotejdk_${{ matrix.jdk }} ${{ matrix.bazel_args }} ${{ matrix.extra_bazel_args }} --build_tag_filters="-no-${{ matrix.arch }},-no-${{ matrix.arch }}-jdk${{ matrix.jdk }},-no-jdk${{ matrix.jdk }}" --test_tag_filters="-no-${{ matrix.arch }},-no-${{ matrix.arch }}-jdk${{ matrix.jdk }},-no-jdk${{ matrix.jdk }}" //... diff --git a/selffuzz/.gitignore b/selffuzz/.gitignore index 20978dd86..0e0c3777a 100644 --- a/selffuzz/.gitignore +++ b/selffuzz/.gitignore @@ -2,4 +2,3 @@ .cifuzz-corpus/ .cifuzz-findings/ target/ -src/test/resources/ \ No newline at end of file diff --git a/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/ArgumentsMutatorFuzzTest.java b/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/ArgumentsMutatorFuzzTest.java new file mode 100644 index 000000000..d95e3f60a --- /dev/null +++ b/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/ArgumentsMutatorFuzzTest.java @@ -0,0 +1,272 @@ +/* + * Copyright 2025 Code Intelligence GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.code_intelligence.selffuzz.mutation; + +import static com.google.common.truth.Truth.assertThat; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.junit.FuzzTest; +import com.code_intelligence.jazzer.mutation.annotation.WithSize; +import com.code_intelligence.jazzer.mutation.annotation.WithUtf8Length; +import com.code_intelligence.jazzer.protobuf.Proto3; +import com.code_intelligence.selffuzz.jazzer.mutation.ArgumentsMutator; +import com.code_intelligence.selffuzz.jazzer.mutation.annotation.NotNull; +import com.code_intelligence.selffuzz.jazzer.mutation.annotation.WithLength; +import com.code_intelligence.selffuzz.jazzer.mutation.mutator.Mutators; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.InputStream; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Method; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class ArgumentsMutatorFuzzTest { + static List methods = getSelfFuzzTestMethods(); + static List mutators = + methods.stream() + .map( + m -> + ArgumentsMutator.forMethod(Mutators.newFactory(), m) + .orElseThrow(() -> new IllegalArgumentException("Invalid method: " + m))) + .collect(Collectors.toList()); + + static { + System.out.println("Found " + methods.size() + " @SelfFuzzTest methods."); + for (Method method : methods) { + System.out.println(" - " + method); + } + assertThat(methods).isNotEmpty(); + } + + /** + * Second-order fuzzing of the mutation framework. Runs all fuzz tests marked by @SelfFuzzTest. We + * use FuzzedDataProvider to force the top-level fuzzer to not use the mutation framework, for + * easier debugging. + */ + @FuzzTest + void allTests(FuzzedDataProvider data) throws Throwable { + int index = data.consumeInt(0, methods.size() - 1); + Method method = methods.get(index); + ArgumentsMutator mutator = mutators.get(index); + + long seed = data.consumeLong(); + byte[] input = data.consumeRemainingAsBytes(); + + try { + mutator.init(seed); + ByteArrayOutputStream initedOut = new ByteArrayOutputStream(); + mutator.write(new DataOutputStream(initedOut)); + InputStream inited = new ByteArrayInputStream(initedOut.toByteArray()); + + mutator.read(new ByteArrayInputStream(input)); + mutator.invoke(this, true); + + mutator.mutate(seed); + ByteArrayOutputStream mutatedOut = new ByteArrayOutputStream(); + mutator.write(new DataOutputStream(mutatedOut)); + InputStream mutated = new ByteArrayInputStream(mutatedOut.toByteArray()); + + mutator.crossOver(mutated, inited, seed); + } catch (Exception e) { + throw new RuntimeException("In method: " + method, e); + } + } + + @SelfFuzzTest + void fuzzStrings( + @NotNull @WithUtf8Length(min = 5, max = 7) String s0, + @NotNull String s1, + @NotNull @WithUtf8Length(min = 10, max = 20) String s2) {} + + @SelfFuzzTest // BUG: null pointer exception + void fuzzListOfMaps(Map nullableMap) {} + + @SelfFuzzTest + void fuzzListOfLists(List<@NotNull List> nullableMap, List> nullableList) {} + + @SelfFuzzTest + void fuzzPPrimitiveArrays( + int @WithLength(max = 10) [] a0, boolean[] a2, int @WithLength(max = 8193) [] a3) {} + + @SelfFuzzTest + void fuzzBean(@NotNull ConstructorPropertiesAnnotatedBean bean, BeanWithParent beanWithParent) {} + + @SelfFuzzTest + void fuzzListOfBeans(@WithSize(max = 4) List beanWithParent) {} + + @SelfFuzzTest + void fuzzListOfListOfBeans( + @WithSize(max = 4) List<@WithSize(max = 4) List> beanWithParent) {} + + @SelfFuzzTest + void fuzzTime(LocalDate date, LocalTime time, LocalDateTime dateTime) {} + + @SelfFuzzTest + void fuzz_Arrays( + List listOfIntArrays, + byte[] @WithLength(max = 11) [] byteArrays) {} + + @SelfFuzzTest + void fuzz_Builder( + // @NotNull // BUG: @NotNull causes "Index -1 out of bounds for length 0" + // in InPlaceProductMutator.writeExclusive + ImmutableBuilder b) {} + + @SelfFuzzTest + void fuzzPrimitives( + Integer i0, + int i1, + Boolean b0, + boolean b1, + Double d0, + double d1, + Float f0, + float f1, + Long l0, + long l1, + Byte by0, + byte by1, + Short s0, + short s1) {} + + @SelfFuzzTest + void fuzzPrimitivesNotNull( + @NotNull Integer i0, + int i1, + @NotNull Boolean b0, + boolean b1, + @NotNull Double d0, + double d1, + @NotNull Float f0, + float f1, + @NotNull Long l0, + long l1, + @NotNull Byte by0, + byte by1, + @NotNull Short s0, + short s1) {} + + @SelfFuzzTest + void fuzzPrimitiveArrays( + Integer @WithLength(max = 3) [] i0, + int[] i1, + Boolean @WithLength(max = 3) [] b0, + boolean[] b1, + Double @WithLength(max = 3) [] d0, + double[] d1, + Float @WithLength(max = 3) [] f0, + float[] f1, + Long @WithLength(max = 3) [] l0, + long[] l1, + Byte @WithLength(max = 3) [] by0, + byte[] by1, + Short @WithLength(max = 3) [] s0, + short[] s1) {} + + enum MyEnum { + A, + B, + C, + D, + E, + F, + G + } + + @SelfFuzzTest + void fuzz_Enums(MyEnum e0, MyEnum e1, MyEnum e2) {} + + @SelfFuzzTest + void fuzz_ProtoBufs( + // com.google.protobuf.StringValue v0, // BUG: makes maxIncreaseSize negative in + // LibProtobufMutator.mutate + com.google.protobuf.Int32Value v1, + com.google.protobuf.BoolValue v2, + com.google.protobuf.UInt64Value v3, + com.google.protobuf.FloatValue v4, + com.google.protobuf.DoubleValue v5, + // com.google.protobuf.BytesValue v6, // BUG: makes maxIncreaseSize negative in + // LibProtobufMutator.mutate + com.google.protobuf.Int64Value v7) { + if (v7 != null) { + assertThat(v7.getValue()).isAtLeast(Long.MIN_VALUE); + assertThat(v7.getValue()).isAtMost(Long.MAX_VALUE); + } + } + + @SelfFuzzTest + void fuzz_ProtoBufsNotNull( + // @NotNull com.google.protobuf.StringValue v0, // BUG: makes maxIncreaseSize negative in + // LibProtobufMutator.mutate + @NotNull com.google.protobuf.Int32Value v1, + @NotNull com.google.protobuf.BoolValue v2, + @NotNull com.google.protobuf.UInt64Value v3, + @NotNull com.google.protobuf.FloatValue v4, + @NotNull com.google.protobuf.DoubleValue v5, + // @NotNull com.google.protobuf.BytesValue v6, // BUG: makes maxIncreaseSize negative in + // LibProtobufMutator.mutate + @NotNull com.google.protobuf.Int64Value v7) { + if (v7 != null) { + assertThat(v7.getValue()).isAtLeast(Long.MIN_VALUE); + assertThat(v7.getValue()).isAtMost(Long.MAX_VALUE); + } + } + + // BUG: makes maxIncreaseSize negative in LibProtobufMutator.mutate + // @SelfFuzzTest + // public static void fuzz_TestProtobuf(TestProtobuf o1) {} + + @SelfFuzzTest + void fuzz_MapField3(Proto3.MapField3 o1) {} + + // BUG: causes java.lang.IllegalArgumentException: argument type mismatch + // no problem when testing the two types separately + // @SelfFuzzTest + // public static void fuzz_MutuallyReferringProtobufs( + // Proto2.TestProtobuf o1, Proto2.TestSubProtobuf o2) {} + + /** + * @return all methods in this class annotated by @SelfFuzzTest. If any of those methods are + * annotated by @Solo, only those are returned. + */ + private static List getSelfFuzzTestMethods() { + return Arrays.stream(MethodHandles.lookup().lookupClass().getDeclaredMethods()) + .filter(m -> m.isAnnotationPresent(SelfFuzzTest.class)) + .collect( + Collectors.collectingAndThen( + Collectors.partitioningBy(m -> m.isAnnotationPresent(Solo.class)), + // Return @Solo methods if any, otherwise all @SelfFuzzTest methods. + map -> map.get(true).isEmpty() ? map.get(false) : map.get(true))); + } + + /** Every method (public or private) annotated by @SelfFuzzTest will be fuzzed. */ + @Retention(RetentionPolicy.RUNTIME) + public @interface SelfFuzzTest {} + + /** When debugging, annotate @SelfFuzzTest fuzz tests by @Solo to only run those. */ + @Retention(RetentionPolicy.RUNTIME) + public @interface Solo {} +} diff --git a/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/BUILD.bazel b/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/BUILD.bazel new file mode 100644 index 000000000..9824d1592 --- /dev/null +++ b/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/BUILD.bazel @@ -0,0 +1,32 @@ +load("//bazel:fuzz_target.bzl", "java_fuzz_target_test") + +java_fuzz_target_test( + name = "ArgumentsMutatorFuzzTest", + srcs = [ + "ArgumentsMutatorFuzzTest.java", + "BeanWithParent.java", + "ConstructorPropertiesAnnotatedBean.java", + "ImmutableBuilder.java", + ], + data = ["//selffuzz/src/test/resources:ArgumentsMutatorFuzzTest-corpus"], + fuzzer_args = [ + # Make sure that the fuzzer can run. Longer fuzzing runs will be done in a separate GH action. + "-runs=10000", + "$(rlocationpaths //selffuzz/src/test/resources:ArgumentsMutatorFuzzTest-corpus)", + ], + target_class = "com.code_intelligence.selffuzz.mutation.ArgumentsMutatorFuzzTest", + deps = [ + "//deploy:jazzer-project", + "//selffuzz:jazzer_api_selffuzz", + "//selffuzz:jazzer_selffuzz", + "//src/main/java/com/code_intelligence/jazzer/junit:fuzz_test", + "//src/main/java/com/code_intelligence/jazzer/mutation/annotation/proto:protobuf_runtime_compile_only", + "//src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto:proto2_java_proto", + "//src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto:proto3_java_proto", + "@maven//:com_google_truth_truth", + "@maven//:org_junit_jupiter_junit_jupiter_api", + "@maven//:org_junit_jupiter_junit_jupiter_engine", + "@maven//:org_junit_platform_junit_platform_launcher", + "@protobuf//java/core", + ], +) diff --git a/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/BeanWithParent.java b/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/BeanWithParent.java new file mode 100644 index 000000000..6e996ef4f --- /dev/null +++ b/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/BeanWithParent.java @@ -0,0 +1,63 @@ +/* + * Copyright 2025 Code Intelligence GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.code_intelligence.selffuzz.mutation; + +import java.beans.ConstructorProperties; +import java.util.Objects; + +public class BeanWithParent extends ConstructorPropertiesAnnotatedBean { + protected int quz; + + @ConstructorProperties({"foo", "BAR", "baz", "quz"}) + BeanWithParent(boolean a, String b, int c, int q) { + super(a, b, c); + this.quz = q; + } + + public int getQuz() { + return quz; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + BeanWithParent that = (BeanWithParent) o; + return quz == that.quz; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), quz); + } + + @Override + public String toString() { + return "BeanWithParent{" + + "quz=" + + quz + + ", foo=" + + isFoo() + + ", bar='" + + getBAR() + + '\'' + + ", baz=" + + getBaz() + + '}'; + } +} diff --git a/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/ConstructorPropertiesAnnotatedBean.java b/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/ConstructorPropertiesAnnotatedBean.java new file mode 100644 index 000000000..c9a1faf31 --- /dev/null +++ b/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/ConstructorPropertiesAnnotatedBean.java @@ -0,0 +1,63 @@ +/* + * Copyright 2025 Code Intelligence GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.code_intelligence.selffuzz.mutation; + +import java.beans.ConstructorProperties; +import java.util.Objects; + +public class ConstructorPropertiesAnnotatedBean { + private final boolean foo; + private final String bar; + private final int baz; + + @ConstructorProperties({"foo", "BAR", "baz"}) + ConstructorPropertiesAnnotatedBean(boolean a, String b, int c) { + this.foo = a; + this.bar = b; + this.baz = c; + } + + public boolean isFoo() { + return foo; + } + + public String getBAR() { + return bar; + } + + int getBaz() { + return baz; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ConstructorPropertiesAnnotatedBean that = (ConstructorPropertiesAnnotatedBean) o; + return foo == that.foo && baz == that.baz && Objects.equals(bar, that.bar); + } + + @Override + public int hashCode() { + return Objects.hash(foo, bar, baz); + } + + @Override + public String toString() { + return "SimpleTypeBean{" + "foo=" + foo + ", bar='" + bar + '\'' + ", baz=" + baz + '}'; + } +} diff --git a/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/ImmutableBuilder.java b/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/ImmutableBuilder.java new file mode 100644 index 000000000..afdf894df --- /dev/null +++ b/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/ImmutableBuilder.java @@ -0,0 +1,77 @@ +/* + * Copyright 2025 Code Intelligence GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.code_intelligence.selffuzz.mutation; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +public class ImmutableBuilder { + private final int i; + private final boolean b; + private final List list; + + public ImmutableBuilder() { + this(0, false, Collections.emptyList()); + } + + private ImmutableBuilder(int i, boolean b, List list) { + this.i = i; + this.b = b; + this.list = list; + } + + public int getI() { + return i; + } + + public boolean isB() { + return b; + } + + public ImmutableBuilder withI(int i) { + return new ImmutableBuilder(i, b, list); + } + + // Both withX and setX are supported on immutable builders. + public ImmutableBuilder setB(boolean b) { + return new ImmutableBuilder(i, b, list); + } + + public ImmutableBuilder setList(List list) { + return new ImmutableBuilder(i, b, list); + } + + @Override + @SuppressWarnings("PatternVariableCanBeUsed") + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ImmutableBuilder)) return false; + ImmutableBuilder that = (ImmutableBuilder) o; + return i == that.i && b == that.b; + } + + @Override + public int hashCode() { + return Objects.hash(i, b); + } + + @Override + public String toString() { + return "ImmutableBuilder{" + "i=" + i + ", b=" + b + '}'; + } +} diff --git a/selffuzz/src/test/resources/.corpus/ArgumentsMutatorFuzzTest/seed b/selffuzz/src/test/resources/.corpus/ArgumentsMutatorFuzzTest/seed new file mode 100644 index 000000000..74e0f12e3 --- /dev/null +++ b/selffuzz/src/test/resources/.corpus/ArgumentsMutatorFuzzTest/seed @@ -0,0 +1 @@ +! \ No newline at end of file diff --git a/selffuzz/src/test/resources/.gitignore b/selffuzz/src/test/resources/.gitignore new file mode 100644 index 000000000..075ae33ee --- /dev/null +++ b/selffuzz/src/test/resources/.gitignore @@ -0,0 +1 @@ +.corpus/ diff --git a/selffuzz/src/test/resources/BUILD.bazel b/selffuzz/src/test/resources/BUILD.bazel new file mode 100644 index 000000000..aee3fdbdb --- /dev/null +++ b/selffuzz/src/test/resources/BUILD.bazel @@ -0,0 +1,5 @@ +filegroup( + name = "ArgumentsMutatorFuzzTest-corpus", + srcs = [".corpus/ArgumentsMutatorFuzzTest"], + visibility = ["//selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation:__pkg__"], +) diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/annotation/proto/BUILD.bazel b/src/main/java/com/code_intelligence/jazzer/mutation/annotation/proto/BUILD.bazel index 38b469267..0a68b420c 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/annotation/proto/BUILD.bazel +++ b/src/main/java/com/code_intelligence/jazzer/mutation/annotation/proto/BUILD.bazel @@ -16,6 +16,7 @@ java_library( # supporting proto mutations. neverlink = True, visibility = [ + "//selffuzz:__pkg__", "//selffuzz:__subpackages__", "//src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto:__pkg__", ], diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/MapMutatorFactory.java b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/MapMutatorFactory.java index 6e2e4f171..6754d514b 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/MapMutatorFactory.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/MapMutatorFactory.java @@ -31,7 +31,6 @@ import static com.code_intelligence.jazzer.mutation.support.TypeSupport.parameterTypesIfParameterized; import static java.lang.Math.min; import static java.lang.String.format; -import static java.util.stream.Collectors.toMap; import com.code_intelligence.jazzer.mutation.annotation.WithSize; import com.code_intelligence.jazzer.mutation.api.Debuggable; @@ -207,11 +206,11 @@ public boolean hasFixedSize() { @Override public Map detach(Map value) { - return value.entrySet().stream() - .collect( - toMap( - entry -> keyMutator.detach(entry.getKey()), - entry -> valueMutator.detach(entry.getValue()))); + Map detached = new LinkedHashMap<>(value.size(), 1.0f); + for (Map.Entry entry : value.entrySet()) { + detached.put(keyMutator.detach(entry.getKey()), valueMutator.detach(entry.getValue())); + } + return detached; } @Override diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/BUILD.bazel b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/BUILD.bazel index 5c8545753..17081d854 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/BUILD.bazel +++ b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/BUILD.bazel @@ -11,7 +11,10 @@ proto_library( java_proto_library( name = "proto3_java_proto", testonly = True, - visibility = ["//src/test/java/com/code_intelligence/jazzer/mutation/mutator:__pkg__"], + visibility = [ + "//selffuzz:__subpackages__", + "//src/test/java/com/code_intelligence/jazzer/mutation/mutator:__pkg__", + ], deps = [":proto3_proto"], ) @@ -24,6 +27,7 @@ java_proto_library( name = "proto2_java_proto", testonly = True, visibility = [ + "//selffuzz:__subpackages__", "//src/test/java/com/code_intelligence/jazzer/mutation/mutator:__pkg__", "//tests:__pkg__", ],