Skip to content

Commit

Permalink
Preserve special initial values in InRange.
Browse files Browse the repository at this point in the history
Currently, refining a `Domain` from `Arbitrary<T>` to `InRange<T>` causes it to "forget" any special values specified to `Init()` the domain. This change adds a `SpecialValues<T>::Get() -> std::array<T>` that provides these special values, defaulting to `{}`.

PiperOrigin-RevId: 653242068
  • Loading branch information
FuzzTest Team authored and copybara-github committed Jul 17, 2024
1 parent e57ec3d commit 0e7b1dc
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 20 deletions.
20 changes: 17 additions & 3 deletions domain_tests/numeric_domains_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,22 @@ TYPED_TEST(NumericTest, InRangeValidationRejectsInvalidRange) {
IsInvalid(testing::MatchesRegex(R"(The value .+ is not InRange\(.+\))")));
}

TYPED_TEST(NumericTest, InRangeGeneratesSpecialValues) {
using T = TypeParam;
auto domain = InRange<T>(T{0}, T{127});

absl::flat_hash_set<T> values;
absl::BitGen prng;
for (int i = 0;
!(values.contains(T{0}) && values.contains(T{1})) &&
i < IterationsToHitAll(/*num_cases=*/2, /*hit_probability=*/1.0 / 4);
++i) {
values.insert(domain.GetRandomValue(prng));
}

EXPECT_THAT(values, AllOf(Contains(T{0}), Contains(T{1})));
}

TYPED_TEST(NumericTest, InRangeValueIsParsedCorrectly) {
using T = TypeParam;
const T min = std::numeric_limits<T>::is_signed ? -100 : 10;
Expand Down Expand Up @@ -301,9 +317,7 @@ TEST(InRange, InitGeneratesSeeds) {
TEST(InRange, InitGeneratesSeedsFromSeedProvider) {
auto domain =
InRange(std::numeric_limits<int>::min(), std::numeric_limits<int>::max())
.WithSeeds([]() -> std::vector<int> {
return {7, 42};
});
.WithSeeds([]() -> std::vector<int> { return {7, 42}; });

EXPECT_THAT(GenerateInitialValues(domain, 1000),
AllOf(Contains(Value(domain, 7)), Contains(Value(domain, 42))));
Expand Down
1 change: 1 addition & 0 deletions fuzztest/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ cc_library(
"internal/domains/optional_of_impl.h",
"internal/domains/serialization_helpers.h",
"internal/domains/smart_pointer_of_impl.h",
"internal/domains/special_values.h",
"internal/domains/unique_elements_container_of_impl.h",
"internal/domains/value_mutation_helpers.h",
"internal/domains/variant_of_impl.h",
Expand Down
1 change: 1 addition & 0 deletions fuzztest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ fuzztest_cc_library(
"internal/domains/optional_of_impl.h"
"internal/domains/serialization_helpers.h"
"internal/domains/smart_pointer_of_impl.h"
"internal/domains/special_values.h"
"internal/domains/unique_elements_container_of_impl.h"
"internal/domains/value_mutation_helpers.h"
"internal/domains/variant_of_impl.h"
Expand Down
18 changes: 4 additions & 14 deletions fuzztest/internal/domains/arbitrary_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include "./fuzztest/internal/domains/one_of_impl.h"
#include "./fuzztest/internal/domains/optional_of_impl.h"
#include "./fuzztest/internal/domains/smart_pointer_of_impl.h"
#include "./fuzztest/internal/domains/special_values.h"
#include "./fuzztest/internal/domains/value_mutation_helpers.h"
#include "./fuzztest/internal/domains/variant_of_impl.h"
#include "./fuzztest/internal/meta.h"
Expand Down Expand Up @@ -131,14 +132,8 @@ class ArbitraryImpl<T, std::enable_if_t<!std::is_const_v<T> &&
if constexpr (sizeof(T) == 1) {
return ChooseFromAll(prng);
} else {
static constexpr T special[] = {
T{0}, T{1},
// For some types, ~T{} is promoted to int. Convert back to T.
static_cast<T>(~T{}),
std::numeric_limits<T>::is_signed
? std::numeric_limits<T>::max()
: std::numeric_limits<T>::max() >> 1};
return ChooseOneOr(special, prng, [&] { return ChooseFromAll(prng); });
return ChooseOneOr(SpecialValues<T>::Get(), prng,
[&] { return ChooseFromAll(prng); });
}
}

Expand Down Expand Up @@ -253,12 +248,7 @@ class ArbitraryImpl<T, std::enable_if_t<std::is_floating_point_v<T>>>

value_type Init(absl::BitGenRef prng) {
if (auto seed = this->MaybeGetRandomSeed(prng)) return *seed;
const T special[] = {
T{0.0}, T{-0.0}, T{1.0}, T{-1.0}, std::numeric_limits<T>::max(),
std::numeric_limits<T>::infinity(), -std::numeric_limits<T>::infinity(),
// std::nan is double. Cast to T explicitly.
static_cast<T>(std::nan(""))};
return ChooseOneOr(special, prng,
return ChooseOneOr(SpecialValues<T>::Get(), prng,
[&] { return absl::Uniform(prng, T{0}, T{1}); });
}

Expand Down
15 changes: 13 additions & 2 deletions fuzztest/internal/domains/in_range_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include <optional>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>

#include "absl/random/bit_gen_ref.h"
#include "absl/random/distributions.h"
Expand All @@ -29,6 +31,7 @@
#include "absl/strings/str_format.h"
#include "./fuzztest/internal/coverage.h"
#include "./fuzztest/internal/domains/domain_base.h"
#include "./fuzztest/internal/domains/special_values.h"
#include "./fuzztest/internal/domains/value_mutation_helpers.h"
#include "./fuzztest/internal/logging.h"
#include "./fuzztest/internal/printer.h"
Expand Down Expand Up @@ -88,8 +91,16 @@ class InRangeImpl : public domain_implementor::DomainBase<InRangeImpl<T>> {

value_type Init(absl::BitGenRef prng) {
if (auto seed = this->MaybeGetRandomSeed(prng)) return *seed;
// TODO(sbenzaquen): Add more interesting points in the range.
const T special[] = {min_, max_};

const auto default_specials = SpecialValues<T>::Get();

std::vector<value_type> special = {min_, max_};
std::copy_if(std::begin(default_specials), std::end(default_specials),
std::back_inserter(special), [&](const value_type& val) {
// Strict inequality so we don't double-count the endpoints.
return min_ < val && val < max_;
});

return ChooseOneOr(special, prng, [&] {
return absl::Uniform(absl::IntervalClosedClosed, prng, min_, max_);
});
Expand Down
60 changes: 60 additions & 0 deletions fuzztest/internal/domains/special_values.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2024 Google LLC
//
// 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.

#ifndef FUZZTEST_FUZZTEST_INTERNAL_DOMAINS_SPECIAL_VALUES_H_
#define FUZZTEST_FUZZTEST_INTERNAL_DOMAINS_SPECIAL_VALUES_H_

#include <array>
#include <cmath>
#include <limits>
#include <type_traits>

namespace fuzztest::internal {

template <typename T, typename = void>
class SpecialValues {
public:
static constexpr std::array<T, 0> Get() { return {}; }
};

template <typename T>
class SpecialValues<T, std::enable_if_t<std::numeric_limits<T>::is_integer>> {
public:
static constexpr auto Get() {
// NB: std::to_array() is not available until C++20.
return std::array{
T{0}, T{1},
// For some types, ~T{} is promoted to int. Convert back to T.
static_cast<T>(~T{}),
std::numeric_limits<T>::is_signed
? std::numeric_limits<T>::max()
: static_cast<T>(std::numeric_limits<T>::max() >> 1)};
}
};

template <typename T>
class SpecialValues<T, std::enable_if_t<std::is_floating_point_v<T>>> {
public:
static auto Get() {
return std::array{
T{0.0}, T{-0.0}, T{1.0}, T{-1.0}, std::numeric_limits<T>::max(),
std::numeric_limits<T>::infinity(), -std::numeric_limits<T>::infinity(),
// std::nan is double. Cast to T explicitly.
static_cast<T>(std::nan(""))};
}
};

} // namespace fuzztest::internal

#endif // FUZZTEST_FUZZTEST_INTERNAL_DOMAINS_SPECIAL_VALUES_H_
9 changes: 8 additions & 1 deletion fuzztest/internal/domains/value_mutation_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,17 @@ void RunOne(absl::BitGenRef prng, F... f) {

template <typename T, size_t N, typename F>
T ChooseOneOr(const T (&values)[N], absl::BitGenRef prng, F f) {
int i = absl::Uniform<int>(absl::IntervalClosedClosed, prng, 0, N);
const int i = absl::Uniform<int>(absl::IntervalClosedClosed, prng, 0, N);
return i == N ? f() : values[i];
}

template <typename C, typename F>
auto ChooseOneOr(const C& values, absl::BitGenRef prng, F f) {
const int i =
absl::Uniform<int>(absl::IntervalClosedClosed, prng, 0, values.size());
return i < values.size() ? values[i] : f();
}

template <typename T>
T SampleFromUniformRange(absl::BitGenRef prng, T min, T max) {
return absl::Uniform(absl::IntervalClosedClosed, prng, min, max);
Expand Down

0 comments on commit 0e7b1dc

Please sign in to comment.