blob: 4ddb712af738fcb8f87d19ebb46919a19adad8b2 [file] [log] [blame]
// Copyright 2018 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 ZXTEST_BASE_RUNNER_H_
#define ZXTEST_BASE_RUNNER_H_
#include <lib/stdcompat/span.h>
#include <lib/stdcompat/string_view.h>
#include <atomic>
#include <cstdio>
#include <fbl/string.h>
#include <fbl/vector.h>
#include <zxtest/base/assertion.h>
#include <zxtest/base/environment.h>
#include <zxtest/base/event-broadcaster.h>
#include <zxtest/base/log-sink.h>
#include <zxtest/base/observer.h>
#include <zxtest/base/parameterized-value.h>
#include <zxtest/base/reporter.h>
#include <zxtest/base/test-case.h>
#include <zxtest/base/test-driver.h>
#include <zxtest/base/test-info.h>
namespace zxtest {
// Prefix used to prevent a test from executing, without explicitly requesting disabled test to run.
static constexpr cpp17::string_view kDisabledTestPrefix = "DISABLED_";
namespace internal {
// Test Driver implementation for the runner. Observes lifecycle events to
// reset the test state correctly.
class TestDriverImpl final : public TestDriver, public LifecycleObserver {
public:
TestDriverImpl();
~TestDriverImpl() final;
// Called when a test is skipped.
void Skip() final;
// Return true if the test has been skipped.
bool IsSkipped() const { return status_ == TestStatus::kSkipped; }
// Return true if the is allowed to continue execution.
bool Continue() const final;
// Returns the status of the current test.
TestStatus Status() const final { return status_; }
// Reports before every test starts.
void OnTestStart(const TestCase& test_case, const TestInfo& test) final;
// Reports when current test assert condition fails. May be called
// concurrently from multiple threads.
void OnAssertion(const Assertion& assertion) final;
// Reports after a test execution was skipped.
void OnTestSkip(const TestCase& test_case, const TestInfo& test) final;
// Reports after test execution completed with failures.
void OnTestFailure(const TestCase& test_case, const TestInfo& test) final;
// Reports after test execution completed with no failures.
void OnTestSuccess(const TestCase& test_case, const TestInfo& test) final;
// Resets the states for running new tests.
void Reset();
// Returns whether the current test has any failures so far. May be
// called concurrently from multiple threads.
bool CurrentTestHasAnyFailures() const { return current_test_has_any_failures_; }
// Returns whether any test driven by this instance had any test failure.
// This is not cleared on |TestDriverImpl::Reset|.
bool HadAnyFailures() const { return had_any_failures_; }
// Control whether calls to OnAssertion do anything.
void EnableAsserts() { asserts_enabled_ = true; }
void DisableAsserts() { asserts_enabled_ = false; }
private:
std::atomic<TestStatus> status_ = TestStatus::kFailed;
std::atomic<bool> current_test_has_any_failures_ = false;
std::atomic<bool> current_test_has_fatal_failures_ = false;
std::atomic<bool> had_any_failures_ = false;
bool asserts_enabled_ = true;
};
} // namespace internal
// Struct used to safely reference a registered test. This is not affected
// by vector growth.
struct TestRef {
size_t test_case_index = 0;
size_t test_index = 0;
};
// Returns the amount of registered and active test and testcases.
struct RunnerSummary {
// Number of iterations to run.
size_t total_iterations = 1;
// Number of registered tests that match a filter.
size_t active_test_count = 0;
// Number of registered test cases that match a filter.
size_t active_test_case_count = 0;
// Number of registered tests.
size_t registered_test_count = 0;
// Number of registered test cases.
size_t registered_test_case_count = 0;
};
// Holds the pattern used for filtering.
struct FilterOp {
// Returns true if the test_case and test matches |pattern|.
bool operator()(const fbl::String& test_case, const fbl::String& test) const;
fbl::String pattern;
bool run_disabled = false;
};
// This class is the entry point for test and constructs registration.
class Runner {
public:
struct Options {
// Parses the contents of argv into |Options|.
static Options FromArgs(int argc, char** argv, fbl::Vector<fbl::String>* errors);
// Prints the usage message into the |stream|.
static void Usage(char* bin, LogSink* sink);
// Pattern for filtering tests. Empty pattern matches all.
fbl::String filter;
// Seed used for random decisions.
int seed = 0;
// Number of iterations to run.
int repeat = 1;
// When set test order within a test case are randomized.
bool shuffle = true;
// When set prints the help message.
bool help = false;
// When set list all registered tests.
bool list = false;
// When set, disabled tests will be executed.
bool run_disabled = false;
// Whether the test suite should stop running upon encountering the first fatal failure.
bool break_on_failure = false;
};
// Default Runner options.
static const Options kDefaultOptions;
// Returns process shared |Runner| instance.
static Runner* GetInstance();
Runner() = delete;
explicit Runner(Reporter&& reporter);
Runner(const Runner&) = delete;
Runner(Runner&&) = delete;
virtual ~Runner();
Runner& operator=(const Runner&) = delete;
Runner& operator=(Runner&&) = delete;
// Register a test for execution with the default factory.
template <typename TestBase, typename TestImpl>
TestRef RegisterTest(const fbl::String& test_case_name, const fbl::String& test_name,
const char* filename, int line) {
return RegisterTest<TestBase, TestImpl>(test_case_name, test_name, filename, line,
&Test::Create<TestImpl>);
}
// Register a test for execution with a customized factory.
template <typename TestBase, typename TestImpl>
TestRef RegisterTest(const fbl::String& test_case_name, const fbl::String& test_name,
const char* filename, int line, internal::TestFactory factory) {
static_assert(std::is_base_of<Test, TestImpl>::value, "Must inherit from Test");
SourceLocation location = {.filename = filename, .line_number = line};
return RegisterTest(test_case_name, test_name, location, std::move(factory),
internal::Accessor<TestBase>::SetUpTestSuite(),
internal::Accessor<TestBase>::TearDownTestSuite());
}
template <typename SuiteClass>
bool AddParameterizedTest(std::unique_ptr<internal::AddTestDelegate> delegate,
const fbl::String& suite_name, const fbl::String& test_name,
const SourceLocation& location) {
std::unique_ptr<internal::ParameterizedTestCaseInfo> new_suite =
delegate->CreateSuite(suite_name);
auto target_suite = new_suite.get();
auto fixture_id = internal::TypeIdProvider<SuiteClass>::Get();
for (auto& test_info : parameterized_test_info_) {
if (test_info->GetFixtureId() == fixture_id) {
// found existing entry
target_suite = test_info.get();
}
}
ZX_ASSERT_MSG(delegate->AddTest(target_suite, test_name, location), "Failed to add a test");
if (target_suite == new_suite.get()) {
parameterized_test_info_.push_back(std::move(new_suite));
}
return true;
}
template <typename SuiteClass, typename ParamType>
bool AddInstantiation(std::unique_ptr<internal::AddInstantiationDelegate<ParamType>> delegate,
const fbl::String& instantiation_name, const SourceLocation& location,
zxtest::internal::ValueProvider<ParamType>& provider,
std::function<std::string(zxtest::TestParamInfo<ParamType>)> name_fn) {
auto fixture_id = internal::TypeIdProvider<SuiteClass>::Get();
bool found_match = false;
for (auto& test_info : parameterized_test_info_) {
if (test_info->GetFixtureId() == fixture_id) {
// found existing entry
auto* suite = test_info.get();
found_match = true;
ZX_ASSERT_MSG(
delegate->AddInstantiation(suite, instantiation_name, location, provider, name_fn),
"Failed to add instantiation.");
}
}
ZX_ASSERT_MSG(found_match, "Failed to find a test suite to add an instantiation.");
return true;
}
// Runs the registered tests with the specified |options|.
int Run(const Options& options);
// List tests according to options.
void List(const Options& options);
const RunnerSummary& summary() const { return summary_; }
const TestInfo& GetTestInfo(const TestRef& test_ref) {
return test_cases_[test_ref.test_case_index].GetTestInfo(test_ref.test_index);
}
// Adds an environment to be set up and tear down for each iteration.
void AddGlobalTestEnvironment(std::unique_ptr<Environment> environment) {
environments_.push_back(std::move(environment));
}
// Provides an entry point for assertions. The runner will propagate the assertion to the
// interested parties. This is needed in a global scope, because helper methods do not have
// access to a |Test| instance and legacy tests are not part of a Fixture, but wrapped by one.
// If this is called without any test running, it will have no effect.
void NotifyAssertion(const Assertion& assertion);
// Tells the runner to skip the current test. The runner will propagate the message to
// interested parties. See NotifyAssertion above for more explanation as to why this is in
// global scope.
void SkipCurrent(const Message& message);
// Returns true if the current test is skipped.
bool IsSkipped() const { return test_driver_.IsSkipped(); }
// Returns true if the current test should be aborted. This happens as a result of a fatal
// failure.
bool CurrentTestHasFatalFailures() const { return !test_driver_.Continue(); }
// Returns whether the current test has experienced any type of failure.
bool CurrentTestHasFailures() const { return test_driver_.CurrentTestHasAnyFailures(); }
int random_seed() const { return options_ ? options_->seed : kDefaultOptions.seed; }
void AddObserver(LifecycleObserver* observer) { event_broadcaster_.Subscribe(observer); }
// Set of options currently in use. By default |Runner::kDefaultOptions| will be returned.
const Options& options() const { return options_ ? *options_ : kDefaultOptions; }
// Notify the runner that the test is in a bad state, and should attempt to exit. This means
// end test execution.
void NotifyFatalError() { fatal_error_ = true; }
// Returns a pointer to the |LogSink| where the |reporter_| is running to.
Reporter* mutable_reporter() { return &reporter_; }
// Returns true if the runner is currently executing tests.
bool IsRunning() const { return is_running_; }
void EnableAsserts() { return test_driver_.EnableAsserts(); }
void DisableAsserts() { return test_driver_.DisableAsserts(); }
void PushTrace(zxtest::Message* trace) { scoped_traces_.push_back(trace); }
void PopTrace() { scoped_traces_.pop_back(); }
cpp20::span<zxtest::Message*> GetScopedTraces() { return scoped_traces_; }
private:
friend class RunnerTestPeer;
TestRef RegisterTest(const fbl::String& test_case_name, const fbl::String& test_name,
const SourceLocation& location, internal::TestFactory factory,
internal::SetUpTestCaseFn set_up, internal::TearDownTestCaseFn tear_down);
virtual void RegisterParameterizedTests() final {
if (should_register_parameterized_tests) {
for (auto& test : parameterized_test_info_) {
test->RegisterTest(this);
}
should_register_parameterized_tests = false;
}
}
void EnforceOptions(const Runner::Options& options);
// List of registered environments.
fbl::Vector<std::unique_ptr<Environment>> environments_;
// List of registered test cases.
fbl::Vector<TestCase> test_cases_;
// Store the traces coming from the SCOPED_TRACE() macro.
std::vector<zxtest::Message*> scoped_traces_;
// Serves as a |LifecycleObserver| list where events are sent to all subscribed observers.
internal::EventBroadcaster event_broadcaster_;
// Driver owned by the |Runner| instance, which drives tests registered for execution
// with the given instance. We need at the |Runner| level, to reduce the amount of piping
// and exposure of the internal classes, so we can propagate errors in Helper methods
// or those that are not within a Fixture scope.
internal::TestDriverImpl test_driver_;
// Provides human readable output.
Reporter reporter_;
// Runner information.
RunnerSummary summary_;
// Sets of options to use for |Runner::Run| or |Runner::List|.
const Options* options_ = nullptr;
bool fatal_error_ = false;
fbl::Vector<std::unique_ptr<zxtest::internal::ParameterizedTestCaseInfo>>
parameterized_test_info_;
bool should_register_parameterized_tests = true;
bool is_running_ = false;
};
// Entry point for C++
int RunAllTests(int argc, char** argv);
} // namespace zxtest
#endif // ZXTEST_BASE_RUNNER_H_