Make sure WithFields[AlwaysSet/Unset] overwrite a With[Min/Max]RepeatedFieldsSize. Currently, `.WithFieldsUnset().WithMinRepeatedFieldsSize(1)` yields an error because the first method sets the maximum size to 0 and the later sets the minimum size to 1. Similarly, `WithMaxRepeatedFieldsSize(0).WithFieldsAlwaysSet()` yields an error. To address the issue, make sure `WithFields[AlwaysSet/Unset]` methods will overwrite `With[Min/Max]RepeatedFieldsSize` methods. One of the consequences which is a little unexpected is that in the following, the maximum size will set to default and not to the user-provided value: .WithMaxRepeatedFieldsSize(7).WithFieldsAlwaysSet() PiperOrigin-RevId: 561642709
diff --git a/doc/domains-reference.md b/doc/domains-reference.md index 0d75c71..88885f0 100644 --- a/doc/domains-reference.md +++ b/doc/domains-reference.md
@@ -510,6 +510,11 @@ ); ``` +Warning: `With[Repeated]Fields[Unset|AlwaysSet]` will overwrite previous +`WithRepeatedFields[Min|Max]?Size` for a given field. For example, in the +following code, the repeated fields can have any size other than zero: +`Arbitrary<MyProto>().WithRepeatedFieldsMinSize(2).WithRepeatedFieldsSize(2).WithFieldsAlwaysSet()` + Notice that `With[Optional|Repeated]Fields[Unset|AlwaysSet]` and `WithRepeatedFields[Min|Max]?Size` work recursively and apply to subprotos as well, unless subproto domains are explicitly defined. Also, calling
diff --git a/domain_tests/arbitrary_domains_test.cc b/domain_tests/arbitrary_domains_test.cc index d78f2bf..a165abb 100644 --- a/domain_tests/arbitrary_domains_test.cc +++ b/domain_tests/arbitrary_domains_test.cc
@@ -770,6 +770,27 @@ R"(.* field rep_ext .* Invalid size: .+. Max size: 0)"))); } +TEST(ProtocolBuffer, WithFieldsAlwaysSetResetsWithMaxRepeatedFieldsSize) { + absl::BitGen bitgen; + + Domain<TestProtobuf> domain = + Arbitrary<TestProtobuf>() + .WithFieldsUnset() + .WithRepeatedFieldsMaxSize( + [](const google::protobuf::FieldDescriptor* field) { + return field->name() == "rep_i32"; + }, + 1) + .WithFieldsAlwaysSet(); + + EXPECT_THAT(GenerateInitialValues(domain, 1000), + Contains(ResultOf( + [](const Value<Domain<TestProtobuf>>& val) { + return val.user_value.rep_i32_size(); + }, + Gt(1)))); +} + TEST(ProtocolBufferEnum, Arbitrary) { auto domain = Arbitrary<TestProtobuf_Enum>(); absl::BitGen bitgen;
diff --git a/fuzztest/internal/domains/container_of_impl.h b/fuzztest/internal/domains/container_of_impl.h index 8e94f50..8b1a2a3 100644 --- a/fuzztest/internal/domains/container_of_impl.h +++ b/fuzztest/internal/domains/container_of_impl.h
@@ -44,6 +44,9 @@ // Used for ChoosePosition(); enum class IncludeEnd { kYes, kNo }; +// Default maximum size for FuzzTest containers/lists. +inline constexpr size_t kDefaultContainerMaxSize = 1000; + // For cases where the type is a container, choose one of the elements in the // container. template <typename Container> @@ -359,8 +362,7 @@ size_t min_size() const { return min_size_; } size_t max_size() const { - static constexpr size_t kDefaultMaxSize = 1000; - return max_size_.value_or(std::max(min_size_, kDefaultMaxSize)); + return max_size_.value_or(std::max(min_size_, kDefaultContainerMaxSize)); } private:
diff --git a/fuzztest/internal/domains/protobuf_domain_impl.h b/fuzztest/internal/domains/protobuf_domain_impl.h index 89c22ac..6ce5dff 100644 --- a/fuzztest/internal/domains/protobuf_domain_impl.h +++ b/fuzztest/internal/domains/protobuf_domain_impl.h
@@ -233,9 +233,16 @@ if (optional_policy == OptionalPolicy::kAlwaysNull) { max_repeated_fields_sizes_.push_back( {.filter = And(IsRepeated<FieldDescriptor>(), filter), .value = 0}); + min_repeated_fields_sizes_.push_back( + {.filter = And(IsRepeated<FieldDescriptor>(), filter), .value = 0}); } else if (optional_policy == OptionalPolicy::kWithoutNull) { + // TODO(b/298168054): Ideally, min/max should get updated only if the + // fields are not already always set. min_repeated_fields_sizes_.push_back( {.filter = And(IsRepeated<FieldDescriptor>(), filter), .value = 1}); + max_repeated_fields_sizes_.push_back( + {.filter = And(IsRepeated<FieldDescriptor>(), filter), + .value = fuzztest::internal::kDefaultContainerMaxSize}); } optional_policies_.push_back( {.filter = std::move(filter), .value = optional_policy});