// Copyright 2023 The Pigweed Authors // // 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 // // https://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. #include #include "gtest/gtest.h" #include "pw_fuzzer/fuzztest.h" #include "pw_result/result.h" // Most of the tests in this file only validate that the domains provided by // Pigweed build, both with and without FuzzTest. Each domain comprises one or // more FuzzTest domains, so the validation of the distribution of values // produced by domains is left to and assumed from FuzzTest's own domain tests. namespace pw::fuzzer { namespace { //////////////////////////////////////////////////////////////// // Constants, macros, and types used by the domain tests below. constexpr size_t kSize = 8; constexpr char kMin = 4; constexpr char kMax = 16; /// Generates a target function and fuzz test for a specific type. /// /// The generated test simply checks that the provided domain can produce values /// of the appropriate type. Generally, explicitly providing a target function /// is preferred for readability, but this macro can be useful for templated /// domains that are tested for many repetitive numerical types. /// /// This macro should not be used directly. Instead, use /// `FUZZ_TEST_FOR_INTEGRAL`, `FUZZ_TEST_FOR_FLOATING_POINT`, or /// `FUZZ_TEST_FOR_ARITHMETIC`. #define FUZZ_TEST_FOR_TYPE(Suite, Target, Domain, Type, ...) \ void Target(Type t) { Take##Domain(t); } \ FUZZ_TEST(Suite, Target).WithDomains(Domain(__VA_ARGS__)) /// Generates target functions and fuzz tests for a integral types. #define FUZZ_TEST_FOR_INTEGRAL(Suite, Target, Domain, ...) \ FUZZ_TEST_FOR_TYPE(Suite, Target##_Char, Domain, char, __VA_ARGS__); \ FUZZ_TEST_FOR_TYPE( \ Suite, Target##_UChar, Domain, unsigned char, __VA_ARGS__); \ FUZZ_TEST_FOR_TYPE(Suite, Target##_Short, Domain, short, __VA_ARGS__); \ FUZZ_TEST_FOR_TYPE( \ Suite, Target##_UShort, Domain, unsigned short, __VA_ARGS__); \ FUZZ_TEST_FOR_TYPE(Suite, Target##_Int, Domain, int, __VA_ARGS__); \ FUZZ_TEST_FOR_TYPE(Suite, Target##_UInt, Domain, unsigned int, __VA_ARGS__); \ FUZZ_TEST_FOR_TYPE(Suite, Target##_Long, Domain, long, __VA_ARGS__); \ FUZZ_TEST_FOR_TYPE(Suite, Target##_ULong, Domain, unsigned long, __VA_ARGS__) /// Generates target functions and fuzz tests for a floating point types. #define FUZZ_TEST_FOR_FLOATING_POINT(Suite, Target, Domain, ...) \ FUZZ_TEST_FOR_TYPE(Suite, Target##_Float, Domain, float, __VA_ARGS__); \ FUZZ_TEST_FOR_TYPE(Suite, Target##_Double, Domain, double, __VA_ARGS__) /// Generates target functions and fuzz tests for a all arithmetic types. #define FUZZ_TEST_FOR_ARITHMETIC(Suite, Target, Domain, ...) \ FUZZ_TEST_FOR_INTEGRAL(Suite, Target, Domain, __VA_ARGS__); \ FUZZ_TEST_FOR_FLOATING_POINT(Suite, Target, Domain, __VA_ARGS__) // Test struct that can be produced by FuzzTest. struct StructForTesting { int a; long b; }; // Test class that can be produced by FuzzTest. class ClassForTesting { public: ClassForTesting(unsigned char c, short d) : c_(c), d_(d) {} unsigned char c() const { return c_; } short d() const { return d_; } private: unsigned char c_; short d_; }; //////////////////////////////////////////////////////////////// // Arbitrary domains forwarded or stubbed from FuzzTest template void TakeArbitrary(T) {} void TakeArbitraryBool(bool b) { TakeArbitrary(b); } FUZZ_TEST(ArbitraryTest, TakeArbitraryBool).WithDomains(Arbitrary()); FUZZ_TEST_FOR_ARITHMETIC(ArbitraryTest, TakeArbitrary, Arbitrary); void TakeArbitraryStruct(const StructForTesting& s) { TakeArbitrary(s); } FUZZ_TEST(ArbitraryTest, TakeArbitraryStruct) .WithDomains(Arbitrary()); void TakeArbitraryTuple(const std::tuple& t) { TakeArbitrary>(t); } FUZZ_TEST(ArbitraryTest, TakeArbitraryTuple) .WithDomains(Arbitrary>()); void TakeArbitraryOptional(const std::optional& o) { TakeArbitrary>(o); } FUZZ_TEST(ArbitraryTest, TakeArbitraryOptional) .WithDomains(Arbitrary>()); //////////////////////////////////////////////////////////////// // Numerical domains forwarded or stubbed from FuzzTest template void TakeInRange(Arithmetic x) { EXPECT_GE(x, Arithmetic(kMin)); EXPECT_LE(x, Arithmetic(kMax)); } FUZZ_TEST_FOR_ARITHMETIC(DomainTest, TakeInRange, InRange, kMin, kMax); template void TakeNonZero(Arithmetic x) { EXPECT_NE(x, Arithmetic(0)); } FUZZ_TEST_FOR_ARITHMETIC(DomainTest, TakeNonZero, NonZero); template void TakePositive(Arithmetic x) { EXPECT_GT(x, Arithmetic(0)); } FUZZ_TEST_FOR_ARITHMETIC(DomainTest, TakePositive, Positive); template void TakeNonNegative(Arithmetic x) { EXPECT_GE(x, Arithmetic(0)); } FUZZ_TEST_FOR_ARITHMETIC(DomainTest, TakeNonNegative, NonNegative); template void TakeNegative(Arithmetic x) { EXPECT_LT(x, Arithmetic(0)); } FUZZ_TEST_FOR_ARITHMETIC(DomainTest, TakeNegative, Positive); template void TakeNonPositive(Arithmetic x) { EXPECT_LE(x, Arithmetic(0)); } FUZZ_TEST_FOR_ARITHMETIC(DomainTest, TakeNonPositive, NonNegative); template void TakeFinite(FloatingPoint f) { EXPECT_TRUE(std::isfinite(f)); } FUZZ_TEST_FOR_FLOATING_POINT(DomainTest, TakeFinite, Finite); //////////////////////////////////////////////////////////////// // Character domains forwarded or stubbed from FuzzTest void TakeNonZeroChar(char c) { EXPECT_NE(c, 0); } FUZZ_TEST(DomainTest, TakeNonZeroChar).WithDomains(NonZeroChar()); void TakeNumericChar(char c) { EXPECT_TRUE(std::isdigit(c)); } FUZZ_TEST(DomainTest, TakeNumericChar).WithDomains(NumericChar()); void TakeLowerChar(char c) { EXPECT_TRUE(std::islower(c)); } FUZZ_TEST(DomainTest, TakeLowerChar).WithDomains(LowerChar()); void TakeUpperChar(char c) { EXPECT_TRUE(std::isupper(c)); } FUZZ_TEST(DomainTest, TakeUpperChar).WithDomains(UpperChar()); void TakeAlphaChar(char c) { EXPECT_TRUE(std::isalpha(c)); } FUZZ_TEST(DomainTest, TakeAlphaChar).WithDomains(AlphaChar()); void TakeAlphaNumericChar(char c) { EXPECT_TRUE(std::isalnum(c)); } FUZZ_TEST(DomainTest, TakeAlphaNumericChar).WithDomains(AlphaNumericChar()); void TakePrintableAsciiChar(char c) { EXPECT_TRUE(std::isprint(c)); } FUZZ_TEST(DomainTest, TakePrintableAsciiChar).WithDomains(PrintableAsciiChar()); void TakeAsciiChar(char c) { EXPECT_GE(c, 0); EXPECT_LE(c, 127); } FUZZ_TEST(DomainTest, TakeAsciiChar).WithDomains(AsciiChar()); //////////////////////////////////////////////////////////////// // Regular expression domains forwarded or stubbed from FuzzTest // TODO: b/285775246 - Add support for `fuzztest::InRegexp`. // void TakeMatch(std::string_view sv) { // ASSERT_EQ(sv.size(), 3U); // EXPECT_EQ(sv[0], 'a'); // EXPECT_EQ(sv[2], 'c'); // } // FUZZ_TEST(DomainTest, TakeMatch).WithDomains(InRegexp("a.c")); //////////////////////////////////////////////////////////////// // Enumerated domains forwarded or stubbed from FuzzTest void TakeSingleDigitEvenNumber(int n) { EXPECT_LT(n, 10); EXPECT_EQ(n % 2, 0); } FUZZ_TEST(DomainTest, TakeSingleDigitEvenNumber) .WithDomains(ElementOf({0, 2, 4, 6, 8})); enum Flags : uint8_t { kFlag1 = 1 << 0, kFlag2 = 1 << 1, kFlag3 = 1 << 2, }; void TakeFlagCombination(uint8_t flags) { EXPECT_FALSE(flags & Flags::kFlag2); } FUZZ_TEST(DomainTest, TakeFlagCombination) .WithDomains(BitFlagCombinationOf({Flags::kFlag1, Flags::kFlag3})); //////////////////////////////////////////////////////////////// // Aggregate domains forwarded or stubbed from FuzzTest void TakeStructForTesting(const StructForTesting& obj) { EXPECT_NE(obj.a, 0); EXPECT_LT(obj.b, 0); } FUZZ_TEST(DomainTest, TakeStructForTesting) .WithDomains(StructOf(NonZero(), Negative())); void TakeClassForTesting(const ClassForTesting& obj) { EXPECT_GE(obj.c(), kMin); EXPECT_LE(obj.c(), kMax); EXPECT_GE(obj.d(), 0); } FUZZ_TEST(DomainTest, TakeClassForTesting) .WithDomains(ConstructorOf(InRange(kMin, kMax), NonNegative())); void TakePair(const std::pair& p) { EXPECT_TRUE(std::islower(p.first)); EXPECT_TRUE(std::isfinite(p.second)); } FUZZ_TEST(DomainTest, TakePair) .WithDomains(PairOf(LowerChar(), Finite())); void TakeTuple(std::tuple a, long b) { EXPECT_NE(std::get(a), 0); EXPECT_NE(std::get(a), 0); EXPECT_NE(b, 0); } FUZZ_TEST(DomainTest, TakeTuple) .WithDomains(TupleOf(NonZero(), NonZero()), NonZero()); void TakeVariant(const std::variant&) {} FUZZ_TEST(DomainTest, TakeVariant) .WithDomains(VariantOf(Arbitrary(), Arbitrary())); void TakeOptional(const std::optional&) {} FUZZ_TEST(DomainTest, TakeOptional).WithDomains(OptionalOf(Arbitrary())); void TakeNullOpt(const std::optional& option) { EXPECT_FALSE(option); } FUZZ_TEST(DomainTest, TakeNullOpt).WithDomains(NullOpt()); void TakeNonNull(const std::optional& option) { EXPECT_TRUE(option); } FUZZ_TEST(DomainTest, TakeNonNull) .WithDomains(NonNull(OptionalOf(Arbitrary()))); //////////////////////////////////////////////////////////////// // Other miscellaneous domains forwarded or stubbed from FuzzTest void TakePositiveOrMinusOne(int n) { if (n != -1) { EXPECT_GT(n, 0); } } FUZZ_TEST(DomainTest, TakePositiveOrMinusOne) .WithDomains(OneOf(Just(-1), Positive())); void TakePackedValue(uint32_t value) { EXPECT_GE(value & 0xFFFF, 1000U); EXPECT_LT(value >> 16, 2048U); } FUZZ_TEST(DomainTest, TakePackedValue) .WithDomains( Map([](uint16_t lower, uint16_t upper) { return (uint32_t(upper) << 16) | lower; }, InRange(1000U, std::numeric_limits::max()), InRange(0U, 2047U))); void TakeOrdered(size_t x, size_t y) { EXPECT_LT(x, y); } void FlatMapAdapter(const std::pair& p) { TakeOrdered(p.first, p.second); } FUZZ_TEST(DomainTest, FlatMapAdapter) .WithDomains(FlatMap( [](size_t x) { return PairOf( Just(x), InRange(x + 1, std::numeric_limits::max())); }, InRange(0, std::numeric_limits::max() - 1))); void TakeEven(unsigned int n) { EXPECT_EQ(n % 2, 0U); } FUZZ_TEST(DomainTest, TakeEven) .WithDomains(Filter([](unsigned int n) { return n % 2 == 0; }, Arbitrary())); //////////////////////////////////////////////////////////////// // pw_status-related types void TakeStatus(const Status&) {} FUZZ_TEST(ArbitraryTest, TakeStatus).WithDomains(Arbitrary()); void TakeStatusWithSize(const StatusWithSize&) {} FUZZ_TEST(ArbitraryTest, TakeStatusWithSize) .WithDomains(Arbitrary()); void TakeNonOkStatus(const Status& status) { EXPECT_FALSE(status.ok()); } FUZZ_TEST(FilterTest, TakeNonOkStatus).WithDomains(NonOkStatus()); //////////////////////////////////////////////////////////////// // pw_result-related types void TakeResult(const Result&) {} FUZZ_TEST(DomainTest, TakeResult).WithDomains(ResultOf(Arbitrary())); FUZZ_TEST(ArbitraryTest, TakeResult).WithDomains(Arbitrary>()); //////////////////////////////////////////////////////////////// // pw_containers-related types void TakeVector(const Vector& vector) { EXPECT_EQ(vector.max_size(), kSize); } FUZZ_TEST(DomainTest, TakeVector) .WithDomains(VectorOf(Arbitrary())); FUZZ_TEST(ArbitraryTest, TakeVector) .WithDomains(Arbitrary>()); void TakeVectorAsContainer(const Vector&) {} FUZZ_TEST(ContainerTest, TakeVectorAsContainer) .WithDomains(ContainerOf>(Arbitrary())); void TakeVectorNonEmpty(const Vector& vector) { EXPECT_FALSE(vector.empty()); } FUZZ_TEST(ContainerTest, TakeVectorNonEmpty) .WithDomains(NonEmpty(ContainerOf>(Arbitrary()))); void TakeVectorLessThan3(const Vector& vector) { EXPECT_LT(vector.size(), 3U); } FUZZ_TEST(ContainerTest, TakeVectorLessThan3) .WithDomains( ContainerOf>(Arbitrary()).WithMaxSize(2)); void TakeVectorAtLeast3(const Vector& vector) { EXPECT_GE(vector.size(), 3U); } FUZZ_TEST(ContainerTest, TakeVectorAtLeast3) .WithDomains( ContainerOf>(Arbitrary()).WithMinSize(3)); void TakeVectorExactly3(const Vector& vector) { EXPECT_EQ(vector.size(), 3U); } FUZZ_TEST(ContainerTest, TakeVectorExactly3) .WithDomains(ContainerOf>(Arbitrary()).WithSize(3)); void TakeVectorUnique(const Vector& vector) { for (auto i = vector.begin(); i != vector.end(); ++i) { for (auto j = i + 1; j != vector.end(); ++j) { EXPECT_NE(*i, *j); } } } FUZZ_TEST(ContainerTest, TakeVectorUnique) .WithDomains( UniqueElementsContainerOf>(Arbitrary())); FUZZ_TEST(DomainTest, TakeVectorUnique) .WithDomains(UniqueElementsVectorOf(Arbitrary())); void TakeFlatMap(const containers::FlatMap&) {} FUZZ_TEST(DomainTest, TakeFlatMap) .WithDomains(FlatMapOf(Arbitrary(), Arbitrary())); FUZZ_TEST(ArbitraryTest, TakeFlatMap) .WithDomains(Arbitrary>()); FUZZ_TEST(ContainerTest, TakeFlatMap) .WithDomains(ContainerOf>( FlatMapPairOf(Arbitrary(), Arbitrary()))); FUZZ_TEST(MapToTest, TakeFlatMap) .WithDomains(MapToFlatMap( UniqueElementsVectorOf(Arbitrary()).WithSize(kSize), ArrayOf(Arbitrary()))); void TakeDeque(const InlineDeque& deque) { EXPECT_EQ(deque.max_size(), kSize); } FUZZ_TEST(DomainTest, TakeDeque).WithDomains(DequeOf(Arbitrary())); FUZZ_TEST(ArbitraryTest, TakeDeque) .WithDomains(Arbitrary>()); void TakeBasicDeque(const BasicInlineDeque& deque) { EXPECT_EQ(deque.max_size(), kSize); } FUZZ_TEST(DomainTest, TakeBasicDeque) .WithDomains(BasicDequeOf(Arbitrary())); FUZZ_TEST(ArbitraryTest, TakeBasicDeque) .WithDomains(Arbitrary>()); void TakeQueue(const InlineQueue& queue) { EXPECT_EQ(queue.max_size(), kSize); } FUZZ_TEST(DomainTest, TakeQueue).WithDomains(QueueOf(Arbitrary())); FUZZ_TEST(ArbitraryTest, TakeQueue) .WithDomains(Arbitrary>()); void TakeBasicQueue(const BasicInlineQueue& queue) { EXPECT_EQ(queue.max_size(), kSize); } FUZZ_TEST(DomainTest, TakeBasicQueue) .WithDomains(BasicQueueOf(Arbitrary())); FUZZ_TEST(ArbitraryTest, TakeBasicQueue) .WithDomains(Arbitrary>()); // Test item that can be added to an intrusive list. class TestItem : public IntrusiveList::Item { public: constexpr explicit TestItem(long value) : value_(value) {} long value() const { return value_; } private: long value_; }; // IntrusiveLists cannot be generated directly, but ScopedLists can. void TakeIntrusiveList(const IntrusiveList& list) { EXPECT_LE(list.size(), kSize); } void ScopedListAdapter(const ScopedList& scoped) { TakeIntrusiveList(scoped.list()); } FUZZ_TEST(DomainTest, ScopedListAdapter) .WithDomains(ScopedListOf(Arbitrary())); //////////////////////////////////////////////////////////////// // pw_string-related types void TakeString(const InlineString<>& string) { EXPECT_EQ(string.max_size(), kSize); } FUZZ_TEST(DomainTest, TakeString) .WithDomains(StringOf(Arbitrary())); FUZZ_TEST(ArbitraryTest, TakeString) .WithDomains(Arbitrary>()); FUZZ_TEST(FilterTest, TakeString).WithDomains(String()); void TakeStringAsContainer(const InlineString<>&) {} FUZZ_TEST(ContainerTest, TakeStringAsContainer) .WithDomains(ContainerOf>(Arbitrary())); void TakeStringNonEmpty(const InlineString<>& string) { EXPECT_FALSE(string.empty()); } FUZZ_TEST(ContainerTest, TakeStringNonEmpty) .WithDomains(NonEmpty(ContainerOf>(Arbitrary()))); void TakeStringLessThan3(const InlineString<>& string) { EXPECT_LT(string.size(), 3U); } FUZZ_TEST(ContainerTest, TakeStringLessThan3) .WithDomains( ContainerOf>(Arbitrary()).WithMaxSize(2)); void TakeStringAtLeast3(const InlineString<>& string) { EXPECT_GE(string.size(), 3U); } FUZZ_TEST(ContainerTest, TakeStringAtLeast3) .WithDomains( ContainerOf>(Arbitrary()).WithMinSize(3)); void TakeStringExactly3(const InlineString<>& string) { EXPECT_EQ(string.size(), 3U); } FUZZ_TEST(ContainerTest, TakeStringExactly3) .WithDomains( ContainerOf>(Arbitrary()).WithSize(3)); void TakeAsciiString(const InlineString<>& string) { EXPECT_TRUE(std::all_of( string.begin(), string.end(), [](int c) { return c < 0x80; })); } FUZZ_TEST(FilterTest, TakeAsciiString).WithDomains(AsciiString()); void TakePrintableAsciiString(const InlineString<>& string) { EXPECT_TRUE(std::all_of(string.begin(), string.end(), isprint)); } FUZZ_TEST(FilterTest, TakePrintableAsciiString) .WithDomains(PrintableAsciiString()); } // namespace } // namespace pw::fuzzer