blob: 1d746d34644b493daf490f5780984c6a4f30b3e3 [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.
#ifndef SRC_LIB_FUZZING_FIDL_TEST_UTILS_H_
#define SRC_LIB_FUZZING_FIDL_TEST_UTILS_H_
#include <lib/async-loop/cpp/loop.h>
#include <lib/fidl/cpp/interface_request.h>
#include <lib/fxl/macros.h>
#include <lib/fxl/synchronization/thread_annotations.h>
#include <lib/sync/completion.h>
#include <lib/sys/cpp/testing/component_context_provider.h>
#include <lib/zx/eventpair.h>
#include <stddef.h>
#include <stdint.h>
#include <atomic>
#include <limits>
#include <mutex>
#include <random>
#include <string>
#include <unordered_map>
#include <vector>
#include <gtest/gtest.h>
namespace fuzzing {
// Helper function to create a deterministically pseudorandom integer type.
template <typename T>
T Pick() {
static std::mt19937_64 prng;
return static_cast<T>(prng() & std::numeric_limits<T>::max());
}
// Helper function to create an array of deterministically pseudorandom integer types.
template <typename T>
void PickArray(T* out, size_t out_len) {
for (size_t i = 0; i < out_len; ++i) {
out[i] = Pick<T>();
}
}
// Helper function to create a vector of deterministically pseudorandom integer types.
template <typename T = uint8_t>
std::vector<T> PickVector(size_t size) {
std::vector<T> v;
v.reserve(size);
for (size_t i = 0; i < size; ++i) {
v.push_back(Pick<T>());
}
return v;
}
// Common base class for unit tests. This class provides methods for synchrnoizing different threads
// and FIDL services and for recording and retrieving information from the global, exported
// interface functions.
class TestBase : public ::testing::Test {
public:
~TestBase() override = default;
// These methods can be called from the library interface functions outside the test instance.
// If |Record| is called multiple times, or |Set*| is called multiple times with the same |key|,
// only the data from the last call will be saved.
// See also the |FUZZER_TEST_*| macros below.
static TestBase* Record(const std::string& func);
void Signal();
void SetU64(const std::string& key, uint64_t val) FXL_LOCKS_EXCLUDED(mutex_);
void SetBytes(const std::string& key, const uint8_t* buf, size_t buf_len)
FXL_LOCKS_EXCLUDED(mutex_);
void SetPaired(zx::eventpair ep) { ep_ = std::move(ep); }
protected:
TestBase();
// gTest methods.
void SetUp() override;
void TearDown() override;
// FIDL methods.
async_dispatcher_t* dispatcher() const { return loop_.dispatcher(); }
std::unique_ptr<sys::ComponentContext> TakeContext() { return std::move(context_); }
template <typename Interface>
void AddPublicService(fidl::InterfaceRequestHandler<Interface> handler) {
ASSERT_NE(context_.get(), nullptr);
context_->outgoing()->AddPublicService(std::move(handler));
}
template <typename Interface>
void ConnectToPublicService(fidl::InterfaceRequest<Interface> request) {
provider_.ConnectToPublicService(std::move(request));
}
// These methods correspond to those public methods called by the library interface functions.
// See also the |FUZZER_TEST_*| macros below.
const char* GetRecorded() FXL_LOCKS_EXCLUDED(mutex_);
void Wait();
uint64_t GetU64(const std::string& key) FXL_LOCKS_EXCLUDED(mutex_);
std::vector<uint8_t> GetBytes(const std::string& key) FXL_LOCKS_EXCLUDED(mutex_);
void MatchBytes(const std::string& key, const std::vector<uint8_t>& bytes)
FXL_LOCKS_EXCLUDED(mutex_);
void SignalPeer(zx_signals_t signals);
zx_signals_t WaitOne();
private:
// Macro variables.
static std::atomic<TestBase*> current_;
// FIDL variables.
async::Loop loop_;
sys::testing::ComponentContextProvider provider_;
std::unique_ptr<sys::ComponentContext> context_;
// Sync variables.
sync_completion_t sync_;
// Eventpair variables.
zx::eventpair ep_;
// Recording variables.
std::mutex mutex_;
std::string recorded_ FXL_GUARDED_BY(mutex_);
std::unordered_map<std::string, uint64_t> u64s_ FXL_GUARDED_BY(mutex_);
std::unordered_map<std::string, std::vector<uint8_t>> bytes_ FXL_GUARDED_BY(mutex_);
FXL_DISALLOW_COPY_ASSIGN_AND_MOVE(TestBase);
};
// These macros allow the exported, global interface functions to interact with the unit tests.
#define FUZZER_TEST_RECORD_U64(x) \
TestBase::Record(__FUNCTION__)->SetU64(#x, static_cast<uint64_t>(x))
#define FUZZER_TEST_RECORD_BYTES(o, o_len) TestBase::Record(__FUNCTION__)->SetBytes(#o, o, o_len)
#define FUZZER_TEST_SIGNAL() TestBase::Record(__FUNCTION__)->Signal()
} // namespace fuzzing
#endif // SRC_LIB_FUZZING_FIDL_TEST_UTILS_H_