blob: 436c09b8341100e3c69919fecc8e964b41ea99b0 [file] [log] [blame]
// Copyright 2023 The Centipede 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 "./centipede/fuzztest_mutator.h"
#include <cstddef>
#include <limits>
#include <string>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/container/flat_hash_set.h"
#include "absl/strings/str_join.h"
#include "./centipede/defs.h"
namespace centipede {
namespace {
using ::testing::AllOf;
using ::testing::Each;
using ::testing::Le;
using ::testing::SizeIs;
using ::testing::Values;
TEST(FuzzTestMutator, DifferentRngSeedsLeadToDifferentMutantSequences) {
FuzzTestMutator mutator[2]{FuzzTestMutator(/*seed=*/1),
FuzzTestMutator(/*seed=*/2)};
std::vector<ByteArray> res[2];
for (size_t i = 0; i < 2; i++) {
ByteArray data = {0};
std::vector<MutationInputRef> mutation_inputs = {{.data = data}};
std::vector<ByteArray> mutants;
constexpr size_t kMutantSequenceLength = 100;
for (size_t iter = 0; iter < kMutantSequenceLength; iter++) {
mutator[i].MutateMany(mutation_inputs, 1, mutants);
ASSERT_EQ(mutants.size(), 1);
res[i].push_back(mutants[0]);
}
}
EXPECT_NE(res[0], res[1]);
}
TEST(FuzzTestMutator, MutateManyWorksWithInputsLargerThanMaxLen) {
constexpr size_t kMaxLen = 4;
FuzzTestMutator mutator(/*seed=*/1);
EXPECT_TRUE(mutator.set_max_len(kMaxLen));
constexpr size_t kNumMutantsToGenerate = 10000;
std::vector<ByteArray> mutants;
mutator.MutateMany(
{
{.data = {0, 1, 2, 3, 4, 5, 6, 7}},
{.data = {0}},
{.data = {0, 1}},
{.data = {0, 1, 2}},
{.data = {0, 1, 2, 3}},
},
kNumMutantsToGenerate, mutants);
EXPECT_THAT(mutants,
AllOf(SizeIs(kNumMutantsToGenerate), Each(SizeIs(Le(kMaxLen)))));
}
// Test parameter containing the mutation settings and the expectations of a
// single mutation step.
struct MutationStepTestParameter {
// The input to be mutated.
ByteArray seed_input;
// The set of mutants to be expected by mutating `seed_input`.
absl::flat_hash_set<ByteArray> expected_mutants;
// The set of mutants not supposed to be seen by mutating `seed_input`.
absl::flat_hash_set<ByteArray> unexpected_mutants;
// The max length of the mutants. If unset, will not set the limit.
std::optional<size_t> max_len;
// The mutation dictionary.
std::vector<ByteArray> dictionary;
// The comparison data following the format of ExecutionMetadata::cmp_data.
ByteArray cmp_data;
// The minimum number of iterations regardless of whether all mutants in
// `expected_mutants` are found or not.
size_t min_num_iterations = 1000;
// The maximum number of iterations to try before all mutants in
// `expected_mutants` are found.
size_t max_num_iterations = 100000000;
};
class MutationStepTest
: public testing::TestWithParam<MutationStepTestParameter> {};
TEST_P(MutationStepTest, GeneratesExpectedMutantsAndAvoidsUnexpectedMutants) {
FuzzTestMutator mutator(/*seed=*/1);
ASSERT_LE(GetParam().min_num_iterations, GetParam().max_num_iterations);
if (GetParam().max_len.has_value())
EXPECT_TRUE(mutator.set_max_len(*GetParam().max_len));
mutator.AddToDictionary(GetParam().dictionary);
absl::flat_hash_set<ByteArray> unmatched_expected_mutants =
GetParam().expected_mutants;
const auto& unexpected_mutants = GetParam().unexpected_mutants;
ExecutionMetadata metadata = {.cmp_data = GetParam().cmp_data};
const std::vector<MutationInputRef> inputs = {
{.data = GetParam().seed_input, .metadata = &metadata}};
std::vector<ByteArray> mutants;
for (size_t i = 0; i < GetParam().max_num_iterations; i++) {
mutator.MutateMany(inputs, 1, mutants);
ASSERT_EQ(mutants.size(), 1);
const auto& mutant = mutants[0];
EXPECT_FALSE(unexpected_mutants.contains(mutant))
<< "Unexpected mutant: {" << absl::StrJoin(mutant, ",") << "}";
unmatched_expected_mutants.erase(mutant);
if (unmatched_expected_mutants.empty() &&
i >= GetParam().min_num_iterations)
break;
}
EXPECT_TRUE(unmatched_expected_mutants.empty());
}
INSTANTIATE_TEST_SUITE_P(InsertByteUpToMaxLen, MutationStepTest,
Values(MutationStepTestParameter{
.seed_input = {0, 1, 2},
.expected_mutants =
{
{0, 1, 2, 3},
{0, 3, 1, 2},
{3, 0, 1, 2},
},
.unexpected_mutants =
{
{0, 1, 2, 3, 4},
{0, 3, 4, 1, 2},
{3, 4, 0, 1, 2},
},
.max_len = 4,
}));
INSTANTIATE_TEST_SUITE_P(OverwriteFromDictionary, MutationStepTest,
Values(MutationStepTestParameter{
.seed_input = {1, 2, 3, 4, 5},
.expected_mutants =
{
{1, 2, 7, 8, 9},
{1, 7, 8, 9, 5},
{7, 8, 9, 4, 5},
{1, 2, 3, 0, 6},
{1, 2, 0, 6, 5},
{1, 0, 6, 4, 5},
{0, 6, 3, 4, 5},
{42, 2, 3, 4, 5},
{1, 42, 3, 4, 5},
{1, 2, 42, 4, 5},
{1, 2, 3, 42, 5},
{1, 2, 3, 4, 42},
},
.dictionary =
{
{7, 8, 9},
{0, 6},
{42},
},
}));
INSTANTIATE_TEST_SUITE_P(
OverwriteFromCmpDictionary, MutationStepTest,
Values(MutationStepTestParameter{
.seed_input = {1, 2, 40, 50, 60},
.expected_mutants =
{
{3, 4, 40, 50, 60},
{1, 2, 10, 20, 30},
},
.cmp_data = {/*size*/ 2, /*lhs*/ 1, 2, /*rhs*/ 3, 4, /*size*/ 3,
/*lhs*/ 10, 20, 30, /*rhs*/ 40, 50, 60},
}));
INSTANTIATE_TEST_SUITE_P(InsertFromDictionary, MutationStepTest,
Values(MutationStepTestParameter{
.seed_input = {1, 2, 3},
.expected_mutants =
{
{1, 2, 3, 4, 5},
{1, 2, 4, 5, 3},
{1, 4, 5, 2, 3},
{4, 5, 1, 2, 3},
{1, 2, 3, 6, 7, 8},
{1, 2, 6, 7, 8, 3},
{1, 6, 7, 8, 2, 3},
{6, 7, 8, 1, 2, 3},
},
.dictionary =
{
{4, 5},
{6, 7, 8},
},
}));
INSTANTIATE_TEST_SUITE_P(InsertFromCmpDictionary, MutationStepTest,
Values(MutationStepTestParameter{
.seed_input = {1, 2, 3},
.expected_mutants =
{
{1, 2, 3, 4, 5},
{1, 2, 4, 5, 3},
{1, 4, 5, 2, 3},
{4, 5, 1, 2, 3},
{1, 2, 3, 6, 7, 8},
{1, 2, 6, 7, 8, 3},
{1, 6, 7, 8, 2, 3},
{6, 7, 8, 1, 2, 3},
},
.cmp_data = {/*size*/ 2, /*lhs*/ 4, 5, /*rhs*/ 4,
5, /*size*/ 3,
/*lhs*/ 6, 7, 8, /*rhs*/ 6, 7, 8},
}));
INSTANTIATE_TEST_SUITE_P(
SkipsLongCmpEntry, MutationStepTest,
Values(MutationStepTestParameter{
.seed_input = {0},
.expected_mutants =
{
{0, 1, 2, 3, 4},
},
.unexpected_mutants =
{
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
},
.cmp_data = {/*size*/ 20, /*lhs*/ 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13,
14, 15, 16, 17, 18, 19, 20,
/*rhs*/ 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20,
/*size*/ 4, /*lhs*/ 1, 2, 3, 4, /*rhs*/ 1, 2,
3, 4}}));
} // namespace
} // namespace centipede