blob: 0f31b2ccd076db504ee15753347247f18edd971b [file] [log] [blame]
// Copyright 2022 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/knobs.h"
#include <cstddef>
#include "gtest/gtest.h"
#include "absl/container/flat_hash_map.h"
namespace centipede {
static const KnobId knob0 = Knobs::NewId("kn0");
static const KnobId knob1 = Knobs::NewId("kn1");
static const KnobId knob2 = Knobs::NewId("kn2");
static const KnobId knob3 = Knobs::NewId("kn3");
TEST(Knobs, Name) {
EXPECT_EQ(Knobs::Name(knob0), "kn0");
EXPECT_EQ(Knobs::Name(knob1), "kn1");
EXPECT_EQ(Knobs::Name(knob2), "kn2");
EXPECT_EQ(Knobs::Name(knob3), "kn3");
}
// Depends on FRIEND_TEST in KnobId - don't rename.
TEST(Knobs, Choose) {
Knobs knobs;
EXPECT_EQ(knob0.id(), 0);
EXPECT_EQ(knob1.id(), 1);
EXPECT_EQ(knobs.Choose({knob3, knob2, knob1}, 0), knob3);
EXPECT_EQ(knobs.Choose({knob3, knob2, knob1}, 1), knob2);
EXPECT_EQ(knobs.Choose({knob3, knob2, knob1}, 2), knob1);
constexpr size_t kNumIter = 1000000;
knobs.Set(16);
absl::flat_hash_map<size_t, size_t> id_to_freq;
for (size_t iter = 0; iter < kNumIter; ++iter) {
++id_to_freq[knobs.Choose({knob3, knob2, knob1}, iter).id()];
}
EXPECT_EQ(id_to_freq[knob0.id()], 0);
EXPECT_GE(id_to_freq[knob1.id()], kNumIter / 4);
EXPECT_GE(id_to_freq[knob2.id()], kNumIter / 4);
EXPECT_GE(id_to_freq[knob3.id()], kNumIter / 4);
knobs.Set({100, 0, 10, 1});
id_to_freq.clear();
for (size_t iter = 0; iter < kNumIter; ++iter) {
++id_to_freq[knobs.Choose({knob0, knob1, knob2, knob3}, iter).id()];
}
EXPECT_EQ(id_to_freq[knob1.id()], 0);
EXPECT_GT(id_to_freq[knob0.id()], 9 * id_to_freq[knob2.id()]);
EXPECT_GT(id_to_freq[knob2.id()], 9 * id_to_freq[knob3.id()]);
EXPECT_GT(id_to_freq[knob3.id()], kNumIter / 200);
absl::flat_hash_map<std::string, size_t> str_to_freq;
for (size_t iter = 0; iter < kNumIter; ++iter) {
++str_to_freq[knobs.Choose<std::string>({knob0, knob2}, {"AAA", "BBB"},
iter)];
}
EXPECT_GT(str_to_freq["AAA"], 9 * str_to_freq["BBB"]);
EXPECT_GT(str_to_freq["BBB"], kNumIter / 200);
}
TEST(Knobs, GenerateBool) {
Knobs knobs;
constexpr size_t kNumIter = 255;
// Checks the GenerateBool on kNumIter different (fake) random values,
// verifies the expected number of "true" results.
auto check = [&](Knobs::value_type knob_value,
size_t expected_num_true_results) {
knobs.Set(knob_value);
size_t num_true = 0;
for (size_t fake_random = 0; fake_random < kNumIter; ++fake_random) {
if (knobs.GenerateBool(knob0, fake_random)) ++num_true;
}
EXPECT_EQ(num_true, expected_num_true_results);
};
check(0, kNumIter / 2 + 1); // true half the time
check(-128, 0); // Never true
check(127, kNumIter); // Always true.
for (int8_t i = -127; i < 127; i++) {
// The greater the knob value, the more frequently we see true.
check(i, 128 + i);
}
}
TEST(KnobsDeathTest, NewId) {
auto allocate_too_many_knob_ids = []() {
for (size_t i = 0; i < Knobs::kNumKnobs; ++i) {
Knobs::NewId(absl::StrCat("kn", i));
}
};
EXPECT_DEATH(allocate_too_many_knob_ids(),
"Knobs::NewId: no more IDs left, aborting");
}
TEST(KnobsDeathTest, Choose) {
Knobs knobs;
EXPECT_DEATH(knobs.Choose<int>({}, {}, 0), "");
EXPECT_DEATH(knobs.Choose<int>({knob1, knob2}, {1}, 0), "");
}
} // namespace centipede