blob: 71f69bd6b78d8e18f042c2af39b8730f5aca8792 [file] [log] [blame]
// Copyright 2022 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.
// Tests of domains Map, FlatMap, and Filter.
#include <bitset>
#include <cctype>
#include <deque>
#include <iterator>
#include <list>
#include <optional>
#include <set>
#include <string>
#include <unordered_set>
#include <utility>
#include <variant>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/algorithm/container.h"
#include "absl/random/random.h"
#include "./fuzztest/domain.h"
#include "./domain_tests/domain_testing.h"
#include "./fuzztest/internal/type_support.h"
namespace fuzztest {
namespace {
using ::testing::Each;
using ::testing::Eq;
using ::testing::IsEmpty;
using ::testing::UnorderedElementsAre;
TEST(Map, WorksWithSameCorpusType) {
auto domain = Map([](int a) { return ~a; }, Arbitrary<int>());
absl::BitGen bitgen;
Value value(domain, bitgen);
EXPECT_EQ(value.user_value, ~std::get<0>(value.corpus_value));
}
enum class Color : int { Red, Green, Blue, Yellow };
TEST(Map, WorksWithDifferentCorpusType) {
auto colors = ElementOf({Color::Blue});
auto domain = Map(
[](Color a) -> std::string { return a == Color::Blue ? "Blue" : "None"; },
colors);
absl::BitGen bitgen;
Value value(domain, bitgen);
// `0` is the index in the ElementOf
EXPECT_EQ(typename decltype(colors)::corpus_type{0},
std::get<0>(value.corpus_value));
EXPECT_EQ("Blue", value.user_value);
}
TEST(Map, AcceptsMultipleInnerDomains) {
auto domain = Map(
[](int a, std::string_view b) {
std::string s;
for (; a > 0; --a) s += b;
return s;
},
InRange(2, 4), ElementOf<std::string_view>({"A", "B"}));
absl::BitGen bitgen;
Set<std::string> values;
while (values.size() < 6) {
values.insert(Value(domain, bitgen).user_value);
}
EXPECT_THAT(values,
UnorderedElementsAre("AA", "AAA", "AAAA", "BB", "BBB", "BBBB"));
}
TEST(FlatMap, WorksWithSameCorpusType) {
auto domain = FlatMap([](int a) { return Just(~a); }, Arbitrary<int>());
absl::BitGen bitgen;
Value value(domain, bitgen);
EXPECT_EQ(value.user_value, ~std::get<1>(value.corpus_value));
}
TEST(FlatMap, WorksWithDifferentCorpusType) {
auto colors = Just(Color::Blue);
auto domain = FlatMap(
[](Color a) {
std::string s = a == Color::Blue ? "Blue" : "None";
return Just(s);
},
colors);
absl::BitGen bitgen;
Value value(domain, bitgen);
// `0` is the index in the ElementOf
EXPECT_EQ(typename decltype(colors)::corpus_type{0},
std::get<1>(value.corpus_value));
EXPECT_EQ("Blue", value.user_value);
}
TEST(FlatMap, AcceptsMultipleInnerDomains) {
auto domain =
FlatMap([](int len, char c) { return StringOf(Just(c)).WithSize(len); },
InRange(2, 4), ElementOf({'A', 'B'}));
absl::BitGen bitgen;
Set<std::string> values;
while (values.size() < 6) {
values.insert(Value(domain, bitgen).user_value);
}
EXPECT_THAT(values,
UnorderedElementsAre("AA", "AAA", "AAAA", "BB", "BBB", "BBBB"));
}
TEST(FlatMap, SerializationRoundTrip) {
auto domain = FlatMap([](int len) { return AsciiString().WithSize(len); },
InRange(0, 10));
absl::BitGen bitgen;
Value value(domain, bitgen);
auto serialized = domain.SerializeCorpus(value.corpus_value);
EXPECT_EQ(domain.ParseCorpus(serialized), value.corpus_value);
}
TEST(FlatMap, MutationAcceptsChangingDomains) {
auto domain = FlatMap([](int len) { return AsciiString().WithSize(len); },
InRange(0, 10));
absl::BitGen bitgen;
Value value(domain, bitgen);
auto mutated = value.corpus_value;
while (std::get<1>(value.corpus_value) == std::get<1>(mutated)) {
// We demand that our output domain has size `len` above. This will check
// fail in ContainerOfImpl if we try to generate a string of the wrong
// length.
domain.Mutate(mutated, bitgen, false);
}
EXPECT_EQ(domain.GetValue(mutated).size(), std::get<1>(mutated));
}
TEST(FlatMap, MutationAcceptsShrinkingOutputDomains) {
auto domain = FlatMap([](int len) { return AsciiString().WithMaxSize(len); },
InRange(0, 10));
absl::BitGen bitgen;
std::optional<Value<decltype(domain)>> value;
// Generate something shrinkable
while (!value.has_value() || value->user_value.empty()) {
value = Value(domain, bitgen);
}
auto mutated = value->corpus_value;
while (!domain.GetValue(mutated).empty()) {
domain.Mutate(mutated, bitgen, true);
}
EXPECT_THAT(domain.GetValue(mutated), IsEmpty());
}
TEST(FlatMap, MutationDoesNotAlterInputDomains) {
auto domain =
FlatMap([](int len) { return VectorOf(Arbitrary<int>()).WithSize(len); },
InRange(5, 10));
absl::BitGen bitgen;
std::optional<Value<decltype(domain)>> value;
// Generate something shrinkable
auto all_zeros = [](const std::vector<int>& v) {
return absl::c_all_of(v, [](int x) { return x == 0; });
};
while (!value.has_value() || all_zeros(value->user_value)) {
value = Value(domain, bitgen);
}
auto mutated = value->corpus_value;
const size_t original_size = value->user_value.size();
while (!all_zeros(domain.GetValue(mutated))) {
domain.Mutate(mutated, bitgen, true);
EXPECT_THAT(domain.GetValue(mutated).size(), Eq(original_size));
}
EXPECT_THAT(domain.GetValue(mutated), Each(Eq(0)));
}
TEST(Filter, CanFilterInitCalls) {
Domain<int> domain = Filter([](int i) { return i % 2 == 0; }, InRange(1, 10));
absl::BitGen bitgen;
Set<int> seen;
while (seen.size() < 5) {
seen.insert(Value(domain, bitgen).user_value);
}
EXPECT_THAT(seen, UnorderedElementsAre(2, 4, 6, 8, 10));
}
TEST(Filter, CanFilterMutateCalls) {
Domain<int> domain = Filter([](int i) { return i % 2 == 0; }, InRange(1, 10));
absl::BitGen bitgen;
Value value(domain, bitgen);
Set<int> seen;
while (seen.size() < 5) {
value.Mutate(domain, bitgen, false);
seen.insert(value.user_value);
}
EXPECT_THAT(seen, UnorderedElementsAre(2, 4, 6, 8, 10));
}
TEST(Filter, CanRoundTripConversions) {
Domain<int> domain =
Filter([](int i) { return i % 2 == 0; }, ElementOf({1, 2, 3, 4}));
absl::BitGen bitgen;
Value value(domain, bitgen);
VerifyRoundTripThroughConversion(value, domain);
}
} // namespace
} // namespace fuzztest