Renames tinytest files again.
This commit is contained in:
315
tinytest.cpp
Normal file
315
tinytest.cpp
Normal file
@@ -0,0 +1,315 @@
|
||||
#define _XOPEN_SOURCE_EXTENDED
|
||||
#include "tinytest.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace TinyTest {
|
||||
namespace {
|
||||
using std::endl;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
} // End namespace
|
||||
|
||||
// Test lifecycle
|
||||
// suiteSetupFn(); - This is called to allocate any suite level resources. This
|
||||
// is called once when the suite begins. These functions may be called in
|
||||
// parallel but execution will not proceed past this block until they have all
|
||||
// finished.
|
||||
// testSetupFn(); - This is called once for every test in tests. You may use
|
||||
// it to allocate resources or setup mocks, stubs, and spies. testFn(...); -
|
||||
// This is called once for every test to execute the test. Only one of these
|
||||
// test functions will actually be run for each test in tests. They should
|
||||
// return true if the test passed, return false if the test failed or there
|
||||
// was an error, and be nullptr if they should be skipped. The executed
|
||||
// function will be called with expectedOutput and the result of testFn(...).
|
||||
// They can be used to test functions with side effects, especially void
|
||||
// functions. maybe_compare_function; - This is the highest priority compare
|
||||
// function. If it is not nullptr then it will be called.
|
||||
// suite_compare_function; - This is the second highest priority compare
|
||||
// function. If maybe_compare_function is nullptr and this is not nullptr then
|
||||
// it will be called.
|
||||
// [](TResult expected, TResult actual) { return expected, actual; } - This is
|
||||
// the lowest priority compare function. If all other compare functions are
|
||||
// nullptr then this will be called to evaluate the test. testTeardownFn(); -
|
||||
// This is called once for every test in tests. You must free/release any
|
||||
// resources allocated by testSetupFn.
|
||||
// This ends the parallel test functions section all tests will have completed
|
||||
// before execution proceeds. Collect reports - Ths step is not visible to the
|
||||
// user at this point, but data returned by all of the test functions is
|
||||
// collected here. This is where you will eventually be able to format/log data
|
||||
// for reports. suiteTeardownFn(); - This is called after all test calls have
|
||||
// completed, all testTeardownFn calls have completed, and all test reports/logs
|
||||
// have been written. You should free any resources allocated in suiteSetupFn.
|
||||
|
||||
// TODO: Add TShared(*)(string /*test_name*/, UUID /*testRunId*/)
|
||||
// allocateSharedData to the test tuple to make some shared data that can be
|
||||
// used in a thread safe way by setup, teardown, and evaluate steps of the test.
|
||||
// TODO: Add TShared to be returned by the setup functions, and consumed by the
|
||||
// evaluate and teardown functions.
|
||||
// Suite setup/teardown functions should allocate/free.
|
||||
// Test setup/teardown functions should consume the data allocated by Suite
|
||||
// setup. Test setup functions may allocate additional resources. If they do
|
||||
// then the allocated resources they should be freed by test teardown
|
||||
// function. Suite and/or Test compare functions may consume this shared data,
|
||||
// but it will not be shared with the execution of testFn.
|
||||
|
||||
// This function is called to execute a test suite. You provide it with some
|
||||
// configuration info, optional utility callback functions, and test data (input
|
||||
// parameters for each call to testFn and the expected result). It returns a
|
||||
// TestResults that should be treated as an opaque data type. Not all parameters
|
||||
// are named in code, but they are named and explained in the comments and will
|
||||
// be described by those names below.
|
||||
// string suite_label - This is the name of this test suite. It is used for
|
||||
// reporting messages. FnToTest testFn - This is the function to test. This
|
||||
// may be replaced if necessary by function. It may not currently support
|
||||
// class methods, but that is planned. vector<tuple<...>> tests - This is the
|
||||
// test run data. Each tuple in the vector is a single test run. It's members
|
||||
// are explained below.
|
||||
// string test_name - This is the name of this test. It is used for
|
||||
// reporting messages. TResult expectedOutput - This is the expected result
|
||||
// of executing this test. bool(*)(const TResult expected, const TResult
|
||||
// actual) maybe_compare_function - This is optional. If unset or set to
|
||||
// nullptr it is skipped. If set to a function it is called to evaluate the
|
||||
// test results. It takes the expected and actual results as parameters and
|
||||
// should return true if the test passed and false otherwise. This may be
|
||||
// changed to return a TestResults at some point. void(*)(TInputParams...)
|
||||
// testSetupFn - This is optional. If unset or set to nullptr it is skipped.
|
||||
// If set to a function it is called before each test to setup the
|
||||
// environment for the test. You may use it to allocate resources and setup
|
||||
// mocks, stubs, and spies. void(*)(TInputParams...) testTeardownFn - This
|
||||
// is optiona. If unset or set to nullptr it is skipped. If set to a
|
||||
// function it is called after each test to cleanup the environment after
|
||||
// the test. You should free resources allocated by testSetupFn. bool
|
||||
// isEnabled - This is optional. If unset or set to true the test is run. If
|
||||
// set to false this test is skipped. If skipped it will be reported as a
|
||||
// skipped/disabled test.
|
||||
// bool(*)(const TResult expected, const TResult actual)
|
||||
// suite_compare_function - This is optional. If unset or set to nullptr it is
|
||||
// skipped. If set to a function and maybe_compare_function is not called for
|
||||
// a test run then this function is called to evaluate the test results. It
|
||||
// takes the expected and actual results as parameters and should return true
|
||||
// if the test passed and false otherwise. This may be changed to return a
|
||||
// TestResults at some point. void(*)() suiteSetupFn - This is optional. If
|
||||
// unset or set to nullptr it is skipped. If set to a function it is called
|
||||
// before starting this test suite to setup the environment. You may use it to
|
||||
// allocate resources and setup mocks, stubs, and spies. void(*)()
|
||||
// suiteTeardownFn - This is optional. If unset or set to nullptr it is
|
||||
// skipped. If set to a function it is called after all tests in this suite
|
||||
// have finished and all reporting has finished. You should free resources
|
||||
// allocated by suiteSetupFn.
|
||||
// This method should be called like so. This is the minimal call and omits all
|
||||
// of the optional params. This is the most common usage. You should put one
|
||||
// tuple of inputs and expected output for each test case.
|
||||
// results = collect_and_report_TestResultstest_fn(
|
||||
// "Test: functionUnderTest",
|
||||
// functionUnderTest,
|
||||
// vector({
|
||||
// make_tuple(
|
||||
// "ShouldReturnAppleForGroupId_1_and_ItemId_2",
|
||||
// string("Apple"),
|
||||
// make_tuple(1,2),
|
||||
// ),
|
||||
// }),
|
||||
// );
|
||||
// The suites can be run from one file as such. From a file called
|
||||
// ThingDoer_test.cpp to test the class/methods ThingDoer declared in
|
||||
// ThingDoer.cpp. This isn't mandatory but is a best practice. You can use
|
||||
// testFn without calling collect_and_report_TestResults() and also could call
|
||||
// it from a normal int main(int argc, char* argv[]) or other function.
|
||||
// TestResults test_main_ThingDoer(int argc, char* argv[]) {
|
||||
// TestResults results;
|
||||
// results = collect_and_report_TestResults(results, testFn("doThing1",
|
||||
// ...), argc, argv); results = collect_and_report_TestResults(results,
|
||||
// testFn("doThing2", ...), argc, argv); return results;
|
||||
// }
|
||||
// Then some test harness either generated or explicit can call
|
||||
// test_main_ThingDoer(...) and optionally reported there. Reporting granularity
|
||||
// is controlled by how frequently you call collect_and_report_TestResults(...).
|
||||
// You can combine test results with results = results + testFn(..); and then
|
||||
// collect_and_report_TestResults on the aggregate TestResults value.
|
||||
|
||||
// _Step_9 - if T2 is a single value then make_tuple<T2>(T2) and call longer
|
||||
// version auto testFunction = [](int id){return id==0?"":"";}; auto
|
||||
// compareFunction = [](const string a, const string b){return a==b;};
|
||||
// template<typename TResult, typename FnToTest, typename... TInputParams>
|
||||
|
||||
// _Step_10 -
|
||||
// test_fn(string, _FnToTest, vector<tuple<string, _T1, _CompareFn,
|
||||
// <tuple<_T2...>>>)
|
||||
// Default to (string, _FnToTest, vector<tuple<"", _T1, [](a,b){return a==b;},
|
||||
// make_tuple()) Also allow make_tuple(T2) if the last param is not a tuple.
|
||||
|
||||
TestResults::TestResults() : errors_(0), failed_(0), passed_(0), skipped_(0), total_(0) {}
|
||||
|
||||
TestResults::TestResults(const TestResults& other)
|
||||
: error_messages_(other.error_messages_),
|
||||
errors_(other.errors_),
|
||||
failed_(other.failed_),
|
||||
failure_messages_(other.failure_messages_),
|
||||
passed_(other.passed_),
|
||||
skip_messages_(other.skip_messages_),
|
||||
skipped_(other.skipped_),
|
||||
total_(other.total_) {}
|
||||
|
||||
TestResults::TestResults(uint32_t errors,
|
||||
uint32_t failed,
|
||||
uint32_t passed,
|
||||
uint32_t skipped,
|
||||
uint32_t total,
|
||||
vector<string> error_messages,
|
||||
vector<string> failure_messages,
|
||||
vector<string> skip_messages)
|
||||
: error_messages_(error_messages),
|
||||
errors_(errors),
|
||||
failed_(failed),
|
||||
failure_messages_(failure_messages),
|
||||
passed_(passed),
|
||||
skip_messages_(skip_messages),
|
||||
skipped_(skipped),
|
||||
total_(total) {}
|
||||
|
||||
TestResults& TestResults::error() {
|
||||
errors_++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
TestResults& TestResults::error(string message) {
|
||||
errors_++;
|
||||
error_messages_.push_back(message);
|
||||
return *this;
|
||||
}
|
||||
|
||||
TestResults& TestResults::fail() {
|
||||
total_++;
|
||||
failed_++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
TestResults& TestResults::fail(const string& message) {
|
||||
total_++;
|
||||
failed_++;
|
||||
failure_messages_.push_back(message);
|
||||
return *this;
|
||||
}
|
||||
|
||||
vector<string> TestResults::failure_messages() {
|
||||
return failure_messages_;
|
||||
}
|
||||
|
||||
TestResults& TestResults::pass() {
|
||||
total_++;
|
||||
passed_++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
TestResults& TestResults::skip() {
|
||||
total_++;
|
||||
skipped_++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
TestResults& TestResults::skip(const string& message) {
|
||||
total_++;
|
||||
skipped_++;
|
||||
skip_messages_.push_back(message);
|
||||
return *this;
|
||||
}
|
||||
|
||||
vector<string> TestResults::skip_messages() {
|
||||
return skip_messages_;
|
||||
}
|
||||
|
||||
vector<string> TestResults::error_messages() {
|
||||
return error_messages_;
|
||||
}
|
||||
|
||||
uint32_t TestResults::errors() {
|
||||
return errors_;
|
||||
}
|
||||
|
||||
uint32_t TestResults::failed() {
|
||||
return failed_;
|
||||
}
|
||||
|
||||
uint32_t TestResults::passed() {
|
||||
return passed_;
|
||||
}
|
||||
|
||||
uint32_t TestResults::skipped() {
|
||||
return skipped_;
|
||||
}
|
||||
|
||||
uint32_t TestResults::total() {
|
||||
return total_;
|
||||
}
|
||||
|
||||
TestResults TestResults::operator+(const TestResults& other) const {
|
||||
vector<string> error_messages;
|
||||
error_messages.insert(error_messages.end(), error_messages_.begin(), error_messages_.end());
|
||||
error_messages.insert(error_messages.end(), other.error_messages_.begin(), other.error_messages_.end());
|
||||
vector<string> failure_messages;
|
||||
failure_messages.insert(failure_messages.end(), failure_messages_.begin(), failure_messages_.end());
|
||||
failure_messages.insert(failure_messages.end(), other.failure_messages_.begin(), other.failure_messages_.end());
|
||||
vector<string> skip_messages;
|
||||
skip_messages.insert(skip_messages.end(), skip_messages_.begin(), skip_messages_.end());
|
||||
skip_messages.insert(skip_messages.end(), other.skip_messages_.begin(), other.skip_messages_.end());
|
||||
|
||||
return TestResults(errors_ + other.errors_,
|
||||
failed_ + other.failed_,
|
||||
passed_ + other.passed_,
|
||||
skipped_ + other.skipped_,
|
||||
total_ + other.total_,
|
||||
error_messages,
|
||||
failure_messages,
|
||||
skip_messages);
|
||||
}
|
||||
|
||||
TestResults& TestResults::operator+=(const TestResults& other) {
|
||||
error_messages_.insert(error_messages_.end(), other.error_messages_.begin(), other.error_messages_.end());
|
||||
errors_ += other.errors_;
|
||||
failed_ += other.failed_;
|
||||
failure_messages_.insert(failure_messages_.end(), other.failure_messages_.begin(), other.failure_messages_.end());
|
||||
passed_ += other.passed_;
|
||||
skip_messages_.insert(skip_messages_.end(), other.skip_messages_.begin(), other.skip_messages_.end());
|
||||
skipped_ += other.skipped_;
|
||||
total_ += other.total_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void PrintResults(std::ostream& os, TestResults results) {
|
||||
auto skip_messages = results.skip_messages();
|
||||
if (skip_messages.size() > 0) {
|
||||
os << "Skipped:" << endl;
|
||||
for_each(skip_messages.begin(), skip_messages.end(), [&os](const string& message) {
|
||||
os << "🚧Skipped: " << message << endl;
|
||||
});
|
||||
}
|
||||
auto failure_messages = results.failure_messages();
|
||||
if (failure_messages.size() > 0) {
|
||||
os << "Failures:" << endl;
|
||||
for_each(failure_messages.begin(), failure_messages.end(), [&os](const string& message) {
|
||||
os << "❌FAILED: " << message << endl;
|
||||
});
|
||||
}
|
||||
auto error_messages = results.error_messages();
|
||||
if (error_messages.size() > 0) {
|
||||
os << "Errors:" << endl;
|
||||
for_each(error_messages.begin(), error_messages.end(), [&os](const string& message) {
|
||||
os << "🔥ERROR: " << message << endl;
|
||||
});
|
||||
}
|
||||
os << "Total tests: " << results.total() << endl;
|
||||
os << "Passed: " << results.passed() << " ✅" << endl;
|
||||
os << "Failed: " << results.failed() << " ❌" << endl;
|
||||
os << "Skipped: " << results.skipped() << " 🚧" << endl;
|
||||
os << "Errors: " << results.errors() << " 🔥" << endl;
|
||||
}
|
||||
|
||||
MaybeTestConfigureFunction DefaultTestConfigureFunction() {
|
||||
return std::nullopt;
|
||||
}
|
||||
} // namespace TinyTest
|
||||
Reference in New Issue
Block a user