Internal change
PiperOrigin-RevId: 478049530
diff --git a/doc/domains-reference.md b/doc/domains-reference.md
index b36a039..edc3b4b 100644
--- a/doc/domains-reference.md
+++ b/doc/domains-reference.md
@@ -318,26 +318,8 @@
You can customize the domain for a subset of fields, for example all fields with
message type `Date`, or all fields with "amount" in the field's name.
-IMPORTANT: Note that customization options can conflict each other. For any
-proto field, when multiple customization calls apply to that field, only the
-first call will have an effect on the field.
-
-Consider the following for example:
-
-```c++
-.WithInt32Fields(Just(0)).WithInt32Filed("zipcode", Just(11111))
-```
-
-Here, all int32 fields will be set to zero for the former customization, because
-the first customization applies to all int32 fields. To set "zipcode" field, its
-customization should appear first:
-
-```c++
-.WithInt32Filed("zipcode", Just(11111)).WithInt32Fields(Just(0))
-```
-
-When called in the opposite order, "zipcode" field will be set to 11111 and all
-other int32 fields will be zero.
+IMPORTANT: Note that customization options can conflict each other. In case of
+conflicts the latter customization always overrides the former.
**Customizing Multiple Fields With Same Type:** You can set the domain for a
subset of fields with the same type using `With<Type>Fields`. By default this
@@ -380,12 +362,12 @@
}
FUZZ_TEST(MySuite, DoingStuffDoesNotCrashWithCustomProto)
.WithDomains(Arbitrary<Moving>()
- // Balance field can be negative.
- .WihtInt32Field("balance", Arbitrary<int>())
- // All zipcode fields which should have 5 digits.
- .WithInt32Fields(IsZipcode, InRange(10000, 99999))
- // All other int fields should be positive.
+ // All int fields should be positive
.WithInt32Fields(Positive<int>())
+ // except balance field which can be negative
+ .WihtInt32Field("balance", Arbitrary<int>())
+ // and except all zipcode fields which should have 5 digits
+ .WithInt32Fields(IsZipcode, InRange(10000, 99999))
// All Timestamp fields should have "nanos" field unset.
.WithProtobufFields(IsTimestamp, Arbitrary<Timestamp>().WithInt32FieldUnset("nanos")));
```
@@ -402,11 +384,14 @@
}
FUZZ_TEST(MySuite, DoingStuffDoesNotCrashWithCustomProto).
WithDomains(Arbitrary<MyProto>()
- .WithInt32Field("foo", InRange(0, 10))
- .WithOptionalFieldsUnset(IsProtoType)
- // Other optional fields should be set. We don't override the nullness for
- // "foo" (which can be unset or unset), and all proto fields.
+ // Always set optional fields
.WithOptionalFieldsAlwaysSet()
+ // except fields that contain nested protos.
+ .WithOptionalFieldsUnset(IsProtoType)
+ // and except "foo" field. We override the nullness by using the
+ // WithInt32Filed (instead of WithInt32FieldAlwaysSet()), which will
+ // enable the fuzzer make this field both set and unset.
+ .WithInt32Field("foo", Arbitrary<int>())
);
```
@@ -420,14 +405,13 @@
}
FUZZ_TEST(MySuite, DoingStuffDoesNotCrashWithCustomProto).
WithDomains(Arbitrary<MyProto>()
- // "additional_info" can be empty or arbitrary large.
- .WithInt32Field("additional_info", VectorOf(String()))
- // Citizenship fields which can size at most 2.
- .WithRepeatedFieldsMaxSize(IsCitizenship, 2)
- // Other Repeated fields should have size in range 1-10. We don't override
- // the size for "additional_info", and all citizenship fields.
+ // Repeated fields should have size in range 1-10
.WithRepeatedFieldsMinSize(1)
.WithRepeatedFieldsMaxSize(10)
+ // except citizenship fields which can size at most 2.
+ .WithRepeatedFieldsMaxSize(IsCitizenship, 2)
+ // and except "additional_info" field which can be empty or arbitrary large
+ .WithInt32Field("additional_info", VectorOf(String()).WithMinSize(0))
);
```
diff --git a/e2e_tests/functional_test.cc b/e2e_tests/functional_test.cc
index d3247a6..dfe5a2e 100644
--- a/e2e_tests/functional_test.cc
+++ b/e2e_tests/functional_test.cc
@@ -436,18 +436,18 @@
}
TEST(UnitTestModeTest,
- DefaultOptionalPolicyAppliesToAllOptionalFieldsWithoutExplicitDomain) {
- auto [status, std_out, std_err] = RunWith(
- GetGTestFilterFlag("MySuite."
- "FieldBIsAlwaysSetAndAllOtherOptionalFieldsAreUnset"));
+ DefaultOptionalPolicyAppliesToAllOptionalFieldsWithoutOverwrittenDomain) {
+ auto [status, std_out, std_err] = RunWith(GetGTestFilterFlag(
+ "MySuite."
+ "FailsWhenAnyOptionalFieldsHaveValueButNotFieldsWithOverwrittenDomain"));
EXPECT_THAT(status.ExitCode(), Eq(0));
}
TEST(UnitTestModeTest,
- DefaultOptionalPolicyAppliesToAllFieldsWithoutAnAlreadyDefinedPolicy) {
+ DefaultOptionalPolicyAppliesToAllOptionalFieldsWithoutOverwrittenPolicy) {
auto [status, std_out, std_err] = RunWith(GetGTestFilterFlag(
"MySuite."
- "BoolFieldsAreAlwaysSetAndAllOtherOptionalFieldsAreUnset"));
+ "FailsWhenAnyOptionalFieldsHaveValueButNotFieldsWithOverwrittenPolicy"));
EXPECT_THAT(status.ExitCode(), Eq(0));
}
@@ -459,10 +459,10 @@
}
TEST(UnitTestModeTest,
- AvoidsFailureIfByDefaultPolicyNotPropagatedOnRecursiveStructures) {
+ AvoidsFailureIfSetByDefaultPolicyIsOverwrittenOnRecursiveStructures) {
auto [status, std_out, std_err] = RunWith(GetGTestFilterFlag(
"MySuite."
- "InitializesRecursiveProtoIfInfiniteRecursivePolicyStopsPropagating"));
+ "InitializesRecursiveProtoIfInfiniteRecursivePolicyIsOverwritten"));
EXPECT_THAT(status.ExitCode(), Eq(0));
}
@@ -492,8 +492,8 @@
}
TEST(UnitTestModeTest, PoliciesApplyToFieldsInOrder) {
- auto [status, std_out, std_err] =
- RunWith(GetGTestFilterFlag("MySuite.Int32FieldsRespectCustomizations"));
+ auto [status, std_out, std_err] = RunWith(GetGTestFilterFlag(
+ "MySuite.FailsWhenI32FieldValuesDontRespectAllPolicies"));
EXPECT_THAT(status.ExitCode(), Eq(0));
}
diff --git a/e2e_tests/testdata/fuzz_tests_for_functional_testing.cc b/e2e_tests/testdata/fuzz_tests_for_functional_testing.cc
index 255a03a..298bd19 100644
--- a/e2e_tests/testdata/fuzz_tests_for_functional_testing.cc
+++ b/e2e_tests/testdata/fuzz_tests_for_functional_testing.cc
@@ -218,34 +218,36 @@
FUZZ_TEST(MySuite, FailsWhenAnyOptionalFieldsHaveValue)
.WithDomains(Arbitrary<TestProtobuf>().WithOptionalFieldsUnset());
-void FieldBIsAlwaysSetAndAllOtherOptionalFieldsAreUnset(
+void FailsWhenAnyOptionalFieldsHaveValueButNotFieldsWithOverwrittenDomain(
const TestProtobuf& proto) {
if (!proto.has_b() || AnyNonBooleanOptionalFieldIsSet(proto)) {
std::abort();
}
}
-FUZZ_TEST(MySuite, FieldBIsAlwaysSetAndAllOtherOptionalFieldsAreUnset)
+FUZZ_TEST(MySuite,
+ FailsWhenAnyOptionalFieldsHaveValueButNotFieldsWithOverwrittenDomain)
.WithDomains(Arbitrary<TestProtobuf>()
- .WithBoolFieldAlwaysSet("b", Arbitrary<bool>())
- .WithOptionalFieldsUnset());
+ .WithOptionalFieldsUnset()
+ .WithBoolFieldAlwaysSet("b", Arbitrary<bool>()));
-void BoolFieldsAreAlwaysSetAndAllOtherOptionalFieldsAreUnset(
+void FailsWhenAnyOptionalFieldsHaveValueButNotFieldsWithOverwrittenPolicy(
const TestProtobuf& proto) {
if (!proto.has_b() || AnyNonBooleanOptionalFieldIsSet(proto)) {
std::abort();
}
}
-FUZZ_TEST(MySuite, BoolFieldsAreAlwaysSetAndAllOtherOptionalFieldsAreUnset)
+FUZZ_TEST(MySuite,
+ FailsWhenAnyOptionalFieldsHaveValueButNotFieldsWithOverwrittenPolicy)
.WithDomains(
Arbitrary<TestProtobuf>()
- .WithOptionalFieldsUnset([](const FieldDescriptor* field) {
- return field->type() == FieldDescriptor::TYPE_INT32;
- })
+ .WithOptionalFieldsUnset()
.WithOptionalFieldsAlwaysSet([](const FieldDescriptor* field) {
return field->type() == FieldDescriptor::TYPE_BOOL ||
field->type() == FieldDescriptor::TYPE_INT32;
})
- .WithOptionalFieldsUnset());
+ .WithOptionalFieldsUnset([](const FieldDescriptor* field) {
+ return field->type() == FieldDescriptor::TYPE_INT32;
+ }));
bool IsTestSubProtobuf(const FieldDescriptor* field) {
return field->message_type()->full_name() ==
@@ -275,7 +277,7 @@
.WithDomains(Arbitrary<TestProtobuf>().WithProtobufFields(
IsTestSubProtobuf, Arbitrary<TestProtobufWithRecursion>()));
-void Int32FieldsRespectCustomizations(const TestProtobuf& proto) {
+void FailsWhenI32FieldValuesDontRespectAllPolicies(const TestProtobuf& proto) {
if (!proto.has_i32() || proto.i32() != 1) {
std::abort();
}
@@ -302,15 +304,15 @@
}
bool IsRepeated(const FieldDescriptor* field) { return field->is_repeated(); }
-FUZZ_TEST(MySuite, Int32FieldsRespectCustomizations)
+FUZZ_TEST(MySuite, FailsWhenI32FieldValuesDontRespectAllPolicies)
.WithDomains(Arbitrary<TestProtobuf>()
- .WithInt32FieldAlwaysSet("i32", fuzztest::Just(1))
- .WithInt32Fields(IsRepeated, fuzztest::Just(2))
- .WithInt32Fields(IsNotRequired, fuzztest::Just(3))
+ .WithOptionalFieldsAlwaysSet(IsInt32)
+ .WithRepeatedFieldsMinSize(IsInt32, 1)
.WithRepeatedInt32Field(
"rep_i32", VectorOf(fuzztest::Just(4)).WithMinSize(1))
- .WithOptionalFieldsAlwaysSet(IsInt32)
- .WithRepeatedFieldsMinSize(IsInt32, 1));
+ .WithInt32Fields(IsNotRequired, fuzztest::Just(3))
+ .WithInt32Fields(IsRepeated, fuzztest::Just(2))
+ .WithInt32FieldAlwaysSet("i32", fuzztest::Just(1)));
void FailsIfCantInitializeProto(const TestProtobufWithRecursion& proto) {}
FUZZ_TEST(MySuite, FailsIfCantInitializeProto)
@@ -318,16 +320,16 @@
.WithOptionalFieldsAlwaysSet()
.WithStringField("id", Arbitrary<std::string>()));
-void InitializesRecursiveProtoIfInfiniteRecursivePolicyStopsPropagating(
+void InitializesRecursiveProtoIfInfiniteRecursivePolicyIsOverwritten(
const TestProtobufWithRecursion& proto) {}
FUZZ_TEST(MySuite,
- InitializesRecursiveProtoIfInfiniteRecursivePolicyStopsPropagating)
+ InitializesRecursiveProtoIfInfiniteRecursivePolicyIsOverwritten)
.WithDomains(Arbitrary<TestProtobufWithRecursion>()
+ .WithOptionalFieldsAlwaysSet()
.WithProtobufField(
"child",
Arbitrary<TestProtobufWithRecursion::ChildProto>()
- .WithStringField("id", Arbitrary<std::string>()))
- .WithOptionalFieldsAlwaysSet());
+ .WithStringField("id", Arbitrary<std::string>())));
bool AreRepeatedFieldsSizesCorrect(absl::FunctionRef<bool(int)> is_size_correct,
const TestProtobuf& proto) {
@@ -384,9 +386,9 @@
FUZZ_TEST(MySuite,
FailsIfRepeatedEnumsHaveZeroValueAndOptionalEnumHasNonZeroValue)
.WithDomains(Arbitrary<TestProtobuf>()
+ .WithEnumFieldsTransformed(IgnoreZero)
.WithEnumField("e",
- fuzztest::Just<int>(TestProtobuf::Label1))
- .WithEnumFieldsTransformed(IgnoreZero));
+ fuzztest::Just<int>(TestProtobuf::Label1)));
void FailsIfProtobufEnumEqualsLabel4(TestProtobuf::Enum e) {
if (e == TestProtobuf::Enum::TestProtobuf_Enum_Label4) {
diff --git a/fuzztest/internal/protobuf_domain.h b/fuzztest/internal/protobuf_domain.h
index b895ba6..e8fa640 100644
--- a/fuzztest/internal/protobuf_domain.h
+++ b/fuzztest/internal/protobuf_domain.h
@@ -199,7 +199,9 @@
using Filter = std::function<bool(const FieldDescriptor*)>;
public:
- ProtoPolicy() {}
+ ProtoPolicy()
+ : optional_policies_({{.filter = IncludeAll<FieldDescriptor>(),
+ .value = OptionalPolicy::kWithNull}}) {}
ProtoPolicy GetChildrenPolicy() const {
ProtoPolicy children_policy;
@@ -296,7 +298,7 @@
"GetOptionalPolicy should apply to optional fields only!");
std::optional<OptionalPolicy> result =
GetPolicyValue(optional_policies_, field);
- if (!result.has_value()) return OptionalPolicy::kWithNull;
+ FUZZTEST_INTERNAL_CHECK(result.has_value(), "optional policy is not set!");
return *result;
}
@@ -340,8 +342,8 @@
std::optional<T> GetPolicyValue(
const std::vector<FilterToValue<T>>& filter_to_values,
const FieldDescriptor* field) const {
- // Return the first policy that applies.
- for (int i = 0; i < filter_to_values.size(); ++i) {
+ // Return the policy that is not overwritten.
+ for (int i = filter_to_values.size() - 1; i >= 0; --i) {
if (!filter_to_values[i].filter(field)) continue;
if constexpr (std::is_same_v<T, Domain<std::unique_ptr<Message>>>) {
absl::BitGen gen;