blob: 432b75fdbdf0a30c840716f15c135b65e64be5b9 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/sys/fuzzing/framework/engine/module-proxy.h"
#include <random>
#include <gtest/gtest.h>
#include "src/sys/fuzzing/common/shared-memory.h"
#include "src/sys/fuzzing/framework/target/module.h"
#include "src/sys/fuzzing/framework/testing/module.h"
namespace fuzzing {
namespace {
// Test fixture.
TEST(ModuleProxyTest, Measure) {
ModuleProxy proxy({0, 0}, FakeModule::kNumPCs);
// No modules added.
EXPECT_EQ(proxy.Measure(), 0U);
// Add a module.
FakeModule module0;
proxy.Add(module0.counters(), module0.num_pcs());
module0[0] = 1;
module0[1] = 1;
module0[2] = 1;
EXPECT_EQ(proxy.Measure(), 3U);
// Idempotent.
EXPECT_EQ(proxy.Measure(), 3U);
// Same counters, different features.
FakeModule module1;
proxy.Add(module1.counters(), module1.num_pcs());
module1[0] = 2;
module1[1] = 2;
module1[2] = 2;
EXPECT_EQ(proxy.Measure(), 3U);
// Different counters, different features.
FakeModule module2;
proxy.Add(module2.counters(), module2.num_pcs());
module2[3] = 4;
module2[4] = 4;
module2[5] = 4;
EXPECT_EQ(proxy.Measure(), 6U);
// All the bits.
memset(module0.counters(), 0xff, module0.num_pcs());
memset(module1.counters(), 0xff, module1.num_pcs());
memset(module2.counters(), 0xff, module2.num_pcs());
EXPECT_EQ(proxy.Measure(), FakeModule::kNumPCs);
}
TEST(ModuleProxyTest, Accumulate) {
ModuleProxy proxy({0, 0}, FakeModule::kNumPCs);
// No modules added.
EXPECT_EQ(proxy.Accumulate(), 0U);
// Add a module.
FakeModule module0;
proxy.Add(module0.counters(), module0.num_pcs());
module0[0] = 1;
module0[1] = 1;
module0[2] = 1;
EXPECT_EQ(proxy.Accumulate(), 3U);
// Features are no longer "new".
EXPECT_EQ(proxy.Accumulate(), 0U);
// Same counters, but different features.
FakeModule module1;
proxy.Add(module1.counters(), module1.num_pcs());
module1[0] = 2;
module1[1] = 2;
module1[2] = 2;
EXPECT_EQ(proxy.Accumulate(), 3U);
// Different counters and different features.
FakeModule module2;
proxy.Add(module2.counters(), module2.num_pcs());
module2[3] = 4;
module2[4] = 4;
module2[5] = 4;
EXPECT_EQ(proxy.Accumulate(), 3U);
// Clear accumulated.
proxy.Clear();
EXPECT_EQ(proxy.Accumulate(), 6U);
// All the bits.
memset(module0.counters(), 0xff, module0.num_pcs());
memset(module1.counters(), 0xff, module1.num_pcs());
memset(module2.counters(), 0xff, module2.num_pcs());
EXPECT_EQ(proxy.Accumulate(), FakeModule::kNumPCs);
}
TEST(ModuleProxyTest, GetCoverage) {
ModuleProxy proxy({0, 0}, FakeModule::kNumPCs);
// No coverage until a call to |Accumulate|.
FakeModule module0;
proxy.Add(module0.counters(), module0.num_pcs());
module0[0] = 1;
module0[1] = 1;
module0[2] = 1;
size_t num_features;
EXPECT_EQ(proxy.GetCoverage(&num_features), 0U);
EXPECT_EQ(num_features, 0U);
EXPECT_EQ(proxy.Accumulate(), 3U);
EXPECT_EQ(proxy.GetCoverage(&num_features), 3U);
EXPECT_EQ(num_features, 3U);
// Idempotent.
EXPECT_EQ(proxy.GetCoverage(&num_features), 3U);
EXPECT_EQ(num_features, 3U);
// More features, but same number of PCs.
while (module0[0]) {
module0[0] += 1;
module0[1] += 1;
module0[2] += 1;
EXPECT_LE(proxy.Accumulate(), 3U);
}
EXPECT_EQ(proxy.GetCoverage(&num_features), 3U);
EXPECT_EQ(num_features, 24U);
}
TEST(ModuleProxyTest, Remove) {
ModuleProxy proxy({0, 0}, FakeModule::kNumPCs);
// No modules added.
EXPECT_EQ(proxy.Accumulate(), 0U);
// Add a module.
FakeModule module0;
proxy.Add(module0.counters(), module0.num_pcs());
module0[0] = 1;
module0[1] = 1;
module0[2] = 1;
EXPECT_EQ(proxy.Accumulate(), 3U);
FakeModule module1;
proxy.Add(module1.counters(), module1.num_pcs());
module1[0] = 2;
module1[1] = 2;
module1[2] = 2;
EXPECT_EQ(proxy.Accumulate(), 3U);
// Remove counters. The counter sums reduce, leading to new features.
proxy.Remove(module0.counters());
EXPECT_EQ(proxy.Accumulate(), 3U);
// Removed counters have no effect.
module0[1] = 10;
EXPECT_EQ(proxy.Accumulate(), 0U);
// Removal doesn't affect accumulated.
module1[1] = 1;
EXPECT_EQ(proxy.Accumulate(), 0U);
}
TEST(ModuleProxyTest, Features) {
uint8_t counters[sizeof(uint64_t)];
memset(counters, 0, sizeof(counters));
ModuleProxy proxy({0, 0}, sizeof(counters));
proxy.Add(counters, sizeof(counters));
// Every (non-zero) counter value maps to one feature.
EXPECT_EQ(proxy.Measure(), 0U);
for (size_t i = 1; i < 256; ++i) {
counters[0] = static_cast<uint8_t>(i);
EXPECT_EQ(proxy.Measure(), 1U);
}
// Measure and Accumulate detect exactly the same new features.
for (size_t i = 0; i < 256; ++i) {
counters[0] = static_cast<uint8_t>(i);
EXPECT_EQ(proxy.Measure(), proxy.Accumulate());
}
// The inline 8-bit counter can map to 8 possible features.
proxy.Clear();
size_t total = 0;
for (size_t i = 0; i < 256; ++i) {
counters[0] = static_cast<uint8_t>(i);
total += proxy.Accumulate();
}
EXPECT_EQ(total, 8U);
}
TEST(ModuleProxyTest, FromModule) {
FakeModule fake;
auto module = std::make_unique<Module>(fake.counters(), fake.pcs(), fake.num_pcs());
// Share it.
zx::vmo vmo;
EXPECT_EQ(module->Share(&vmo), ZX_OK);
auto shmem = std::make_unique<SharedMemory>();
EXPECT_EQ(shmem->Link(std::move(vmo)), ZX_OK);
// Add module to a proxy.
ModuleProxy proxy(module->id(), shmem->size());
proxy.Add(shmem->data(), shmem->size());
EXPECT_EQ(proxy.Measure(), 0U);
// Update a counter and propagate.
fake[fake.num_pcs() - 1] = 255;
module->Update();
EXPECT_EQ(proxy.Measure(), 1U);
// Remove the module from the proxy.
proxy.Remove(shmem->data());
// Can discard objects once removed.
module.reset();
shmem.reset();
EXPECT_EQ(proxy.Measure(), 0U);
}
} // namespace
} // namespace fuzzing