From 011953cd2ccecdc5fe0b8fe83b8d0dde10fa81ab Mon Sep 17 00:00:00 2001 From: Tom Hicks Date: Wed, 10 May 2023 01:08:14 -0700 Subject: [PATCH] Adds cpp-utils as a dependency. Uses PrettyPrint from cpp-utils instead of the local copies. --- .gitignore | 3 +- BUILD | 1 + WORKSPACE | 15 +- tinytest.h | 625 +++++++++++++++++++++------------------------- tinytest_test.cpp | 87 +------ 5 files changed, 300 insertions(+), 431 deletions(-) diff --git a/.gitignore b/.gitignore index 4f7eab6..b320697 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,9 @@ /.vscode/ /bazel-* /build/ -/tmp/ /docs/ +/external +/tmp/ # Prerequisites *.d diff --git a/BUILD b/BUILD index 02ed995..4e280c7 100644 --- a/BUILD +++ b/BUILD @@ -35,6 +35,7 @@ cc_library( hdrs = ["tinytest.h"], includes = ["*.h"], visibility = ["//visibility:public"], + deps = ["@cpputils//:pretty_print"], ) cc_test( diff --git a/WORKSPACE b/WORKSPACE index 5c93534..e79b256 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -52,20 +52,9 @@ http_archive( urls = ["https://github.com/google/googletest/archive/ccdeec888ebb740a7ea4e07d3e84a1b7ee32b315.zip"], ) -http_archive( - name = "com_google_absl", - strip_prefix = "abseil-cpp-b971ac5250ea8de900eae9f95e06548d14cd95fe", - urls = ["https://github.com/abseil/abseil-cpp/archive/b971ac5250ea8de900eae9f95e06548d14cd95fe.zip"], -) - -http_archive( - name = "bazel_skylib", - sha256 = "f7be3474d42aae265405a592bb7da8e171919d74c16f082a5457840f06054728", - urls = ["https://github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz"], -) - http_archive( name = "cpputils", + sha256 = "2d1f0904640706ad9b88bfc2acf03225af6d9cf9aca0863b6cff8ceeb976aca6", strip_prefix = "cpp-utils-52de4b8bc2f6d8118dba94ade47f70522ba87b56", - urls = ["https://github.com/headhunter45/TinyTest/archive/460c9492d927689b9db7f28d8742705dc0bbee62.zip"], + urls = ["https://github.com/headhunter45/cpp-utils/archive/52de4b8bc2f6d8118dba94ade47f70522ba87b56.tar.gz"], ) diff --git a/tinytest.h b/tinytest.h index a1b5ae3..2fed249 100644 --- a/tinytest.h +++ b/tinytest.h @@ -1,5 +1,5 @@ -#ifndef TEST_H__ -#define TEST_H__ +#ifndef TinyTest__tinytest_h__ +#define TinyTest__tinytest_h__ /*************************************************************************************** * @file tinytest.h * * * @@ -21,144 +21,208 @@ #include #include +#include "pretty_print.h" + namespace TinyTest { -// Begin EscapeForPrinting -template -std::basic_string_view EscapeForPrinting(const std::basic_string_view& text) { - return std::regex_replace(text, std::regex("\033"), "\\033"); -} -template -std::basic_string EscapeForPrinting(const std::basic_string& text) { - return std::regex_replace(text, std::regex("\033"), "\\033"); -} +/// @defgroup tests Tests +/// @defgroup test_suites Test Suites +/// @defgroup test_results Test Results +/// @defgroup test_execution Test Execution +/// @defgroup configure_functions Configure Functions +/// @defgroup compare_functions Compare Functions +/// @defgroup helpers Helpers -template -std::basic_string EscapeForPrinting(const TChar* text) { - return std::regex_replace(text, std::regex("\033"), "\\033"); -} +/// @addtogroup configure_functions +/// @{ -// End EscapeForPrinting +/// @brief This is a type that represents a setup or teardown function for tests. +using TestConfigureFunction = std::function; -// Begin PrettyPrint -// std::string_view. -template -auto& PrettyPrint(std::basic_ostream& os, const std::basic_string_view& item) { - os << "\"" << EscapeForPrinting(item) << "\""; - return os; -} +/// @brief This is a type that represents an optional setup or teardown function for tests. +using MaybeTestConfigureFunction = std::optional; -// std::string. -template -auto& PrettyPrint(std::basic_ostream& os, const std::basic_string& item) { - os << "\"" << EscapeForPrinting(item) << "\""; - return os; -} +/// @brief This is the default configure function. +/// @return The default configure function. This is currently std::nullopt. +MaybeTestConfigureFunction DefaultTestConfigureFunction(); -// const char*. -template -auto& PrettyPrint(std::basic_ostream& os, const TChar* item) { - os << "\"" << EscapeForPrinting(item) << "\""; - return os; -} +/// @brief Combines multiple test configure functions into a single one. +/// @param first The first setup function if this is nullopt it is ignored. +/// @param second The second setup function if this is nullopt it is ignored. +/// @return The resulting setup function or nullopt if both first and second are nullopt. +MaybeTestConfigureFunction Coalesce(MaybeTestConfigureFunction first, MaybeTestConfigureFunction second); +/// @} -// tuple<...> -template -auto& PrettyPrint(std::basic_ostream os, const std::tuple& tuple) { - std::apply( - [&os](auto&&... args) { - if (sizeof...(TArgs) == 0) { - os << "[]"; - return; - } - size_t n = 0; - os << "[ "; - ((PrettyPrint(os, args) << (++n != sizeof...(TArgs) ? ", " : "")), ...); - os << " ]"; - }, - tuple); - return os; -} +/// @addtogroup compare_functions +/// @{ -// containers -template ().begin()), decltype(std::declval().end())>>, - typename = std::enable_if_t().begin())>::iterator_category>>> -auto& PrettyPrint(std::basic_ostream& os, TContainer container) { - os << "[ "; - for (auto it = container.begin(); it != container.end(); it++) { - if (it != container.begin()) { - os << ", "; - } - PrettyPrint(os, *it); - } - os << " ]"; - return os; -} +/// @brief This is a type that represents a compare function for types of TResult. +/// +/// It should return true if the parameters are equal and false otherwise. +/// @tparam TResult The type of the parameters. This is the return type of the function being tested. +template +using TestCompareFunction = std::function; -// Catch-all for everything else. -template -auto& PrettyPrint(std::basic_ostream& os, TItem item) { - os << item; - return os; -} +/// @brief This is a type that represents an optional compare function for types of TResult. +/// @tparam TResult The type of the parameters. This is the return type of the function being tested. +template +using MaybeTestCompareFunction = std::optional>; -// End PrettyPrint +/// @brief This is the default compare function. +/// @tparam TResult The return type of the test function. +/// @return The default compare function. Currently this is std::nullopt. +template +MaybeTestCompareFunction DefaultTestCompareFunction(); +/// @} -// Begin PrettyPrintWithSeparator -// Prints args with separator between them. std::string_view separator. -template -auto& PrettyPrintWithSeparator(std::basic_ostream os, - std::basic_string_view separator, - TArgs&&... args) { - (((PrettyPrint(os, args), os), EscapeForPrinting(separator)), ...); - return os; -} +// TODO: For some reason all hell breaks loose if test_name or expected output +// are const&. Figure out why. Probably need to use decay and make const& where we want it explicitly. +/// @addtogroup tests +/// @{ -// Prints args with separator between them. std::string separator. -template -auto& PrettyPrintWithSeparator(std::basic_ostream os, - std::basic_string separator, - TArgs&&... args) { - (((PrettyPrint_item(os, args), os), EscapeForPrinting(separator)), ...); - return os; -} +/// @brief This is a type that represents an individual test. +/// @tparam TResult The return type of the test function. +/// @tparam ...TInputParams The parameters to pass to the test function +template +using TestTuple = std::tuple< + /// test_name + std::string, + /// expected_output + TResult, + /// input_params - The input parameters for this test. These will be used when calling std::apply with + /// function_to_test to execute the test. + std::tuple, + /// test_compare_function - If this is not nullptr then this function will be called instead of + /// suite_compare_function to determine if the test passes. Use this to check for side effects of the test. Return + /// true if the test passes and false otherwise. + MaybeTestCompareFunction, + /// test_setup_function - If this is not nullptr this function is called before each test to setup the environment. + /// It is called with std::apply and input_params so you can use them to mock records with specific IDs or calculate + /// an expected result. + MaybeTestConfigureFunction, + /// test_teardown_function - If this is not nullptr this function is called after each test to cleanup any + /// allocated/shared resources. + MaybeTestConfigureFunction, + /// is_enabled - If this is false the test, setup, and teardown functions are not run. + bool>; -// Prints args with separator between them. const char* separator. -template -auto& PrettyPrintWithSeparator(std::basic_ostream os, const TChar* separator, Args&&... args) { - ((os << args << EscapeForPrinting(separator)), ...); - return os; -} +/// @brief Executes a TestSuite. +/// @tparam TResult The result type of the test. +/// @tparam ...TInputParams The types of parameters sent to the test function. +/// @param test_name The label for this test. For example "should calculate the +/// interest". +/// @param expected The expected output of calling the test function with these +/// input parameters. +/// @param input_params The input parameters to use when calling the test +/// function. +/// @param test_compare_function An optional function that can be used to Compare the +/// expected and actual return values. This is good for when you only care about +/// certain fields being equal. +/// @param before_each This is called to setup the environment before running +/// the test. This is where you should build mocks, setup spies, and set any +/// other values you need before calling the test function. +/// @param after_each This is called after each test run to cleanup anything +/// allocated in before_each. +/// @param is_enabled If false this test run is not executed and considered +/// skipped for reporting purposes. +/// @return A TestTuple suitable for use as a test run when calling test_fn. +template +TestTuple MakeTest(const std::string& test_name, + const TResult& expected, + std::tuple input_params, + MaybeTestCompareFunction test_compare_function = std::nullopt, + MaybeTestConfigureFunction before_each = std::nullopt, + MaybeTestConfigureFunction after_each = std::nullopt, + bool is_enabled = true); +/// @} -// End PrettyPrintWithSeparator +/// @addtogroup test_suites +/// @{ -//////////////////// +/// @brief This type represents a test suite. +/// @tparam TResult The return type of the function to test. +/// @tparam TFunctionToTest The type of the function to test. +/// @tparam ...TInputParams The types of the input parameters to the function to test. +template +using TestSuite = std::tuple< + /// test_name - The name of the test. + std::string, + /// function_to_test - The function to test. It will be executed once for each item in the tests initializer_list. + std::function, + /// tests - This is an initializer list of @link TestTuple @endlink that represent the test runs to execute. + std::initializer_list>, + /// test_compare_function - This is an optional function that overrides how test results are compared. + MaybeTestCompareFunction, + /// before_each - This is an optional function that is executed before each test. + MaybeTestConfigureFunction, + /// after_each - This is an optional function that is executed after each test. + MaybeTestConfigureFunction, + // is_enabled - If true the suite is executed. If false all test runs are reported as skipped and none are run. + bool>; -// TODO: Document this. +/// @brief Makes a TestSuite tuple from the given parameters. +/// @tparam TResult The return type of function_to_test. +/// @tparam TFunctionToTest The type of function_to_test. +/// @tparam ...TInputParams The parameter types of function_to_test. +/// @param suite_name The label for this test suite. +/// @param function_to_test The function to test. +/// @param test_data The configuration for the test runs. +/// @param compare An optional compare function to use when evaluating test results. +/// @param before_each An optional function to run before each test. +/// @param after_each An optional function to run after each test. +/// @param is_enabled If false the test suite is skipped. All tests in the suite will be reported as skipped. +/// @return The results of the test suite. +template +TestSuite MakeTestSuite(const std::string& suite_name, + TFunctionToTest function_to_test, + std::initializer_list> test_data, + MaybeTestCompareFunction compare = std::nullopt, + MaybeTestConfigureFunction before_each = std::nullopt, + MaybeTestConfigureFunction after_each = std::nullopt, + bool is_enabled = true); +/// @} + +/// @addtogroup helpers +/// @{ + +/// @brief Intercepts cout, executes the function and returns a string with all tesxt sent to cout while running the +/// function. +/// @tparam TResult The return type of function_to_execute. +/// @tparam ...TParameters The parameter types of function_to_execute. +/// @param function_to_execute The function to execute. +/// @param maybe_args The args to call function_to_execute with or nullopt if it takes no parameters. +/// @return A string containing all text written to cout by function_to_execute. template -std::string InterceptCout(std::function fnToExecute, - std::optional> maybe_args = std::nullopt) { - std::ostringstream os; - auto saved_buffer = std::cout.rdbuf(); - std::cout.rdbuf(os.rdbuf()); - // TODO: run the function +std::string InterceptCout(std::function function_to_execute, + std::optional> maybe_args = std::nullopt); - if (maybe_args.has_value()) { - std::apply(fnToExecute, maybe_args.value()); - } else { - std::invoke(fnToExecute); - } - std::cout.rdbuf(saved_buffer); - return os.str(); -} +/// @brief This function compares two vectors. +/// @tparam TChar The character type of the stream to write to. +/// @tparam TTraits The character_traits type of the stream to write to. +/// @tparam TItem The type of item in the vectors. +/// @param error_message The stream to write error messages to. +/// @param expected The expected vector. +/// @param actual The actual vector. +/// @return The error_message stream. +template +auto& Compare(std::basic_ostream& error_message, + std::vector expected, + std::vector actual); -/// @brief +/// @} + +/// @addtogroup test_results + +/// @brief Represents the results of running some number of tests. +/// +/// This type may evolve over time, but currently it tracks: +/// * The total number of tests run. +/// * The number of failures and any messages sent with the failures. +/// * The number of tests skipped and any messages sent with the skips. +/// * The number of tests with errors and any error messages sent with the errors. +/// * __Note:__ Errors do not count as test runs. Errored tests will be recorded as a passed/failed/skipped test +/// separately. +/// * The number of passed tests. class TestResults { public: /// @brief Creates an empty TestResults instance representing no tests run. @@ -173,8 +237,7 @@ class TestResults { /// @param failed The number of failed tests. /// @param passed The number of passed tests. /// @param skipped The number of skipped tests. - /// @param total The total number of tests run. This should equal the sum of - /// failed, passed, and skipped tests. + /// @param total The total number of tests run. This should equal the sum of failed, passed, and skipped tests. /// @param error_messages The list of error messages. /// @param failure_messages The list of failure messages. /// @param skip_messages The list of skip messages. @@ -275,82 +338,33 @@ class TestResults { uint32_t total_; }; -/// @brief -/// @tparam TResult -template -using TestCompareFunction = std::function; +/// @brief Writes a friendly version of results to the provided stream. +/// @param os The stream to write to. +/// @param results The TestResults to write. +void PrintResults(std::ostream& os, TestResults results); -/// @brief -/// @tparam TResult -template -using MaybeTestCompareFunction = std::optional>; - -// TODO: Document this. -template -MaybeTestCompareFunction DefaultTestCompareFunction() { - return std::nullopt; -} - -// TODO: Document this. -using TestConfigureFunction = std::function; -// TODO: Document this. -using MaybeTestConfigureFunction = std::optional; -// TOD: Document this. -MaybeTestConfigureFunction DefaultTestConfigureFunction(); - -// TODO: For some reason all hell breaks loose if test_name or expected output -// are const&. Figure out why. Probably need to use decay and make const& where we want it explicitly. -/// @brief -/// @tparam TResult -/// @tparam ...TInputParams -template -using TestTuple = - std::tuple /* input_params - The input parameters for this test. These will be used when - calling std::apply with function_to_test to execute the test. */ - , - MaybeTestCompareFunction /* test_Compare_function - If this is not nullprt then this function - will be called instead of suite_Compare_function to determine if the - test passes. Use this to check for side effects of the test. Return - true if the test passes and false otherwise. */ - , - MaybeTestConfigureFunction /* test_setup_function - If this is not nullptr this function is called before - each test to setup the environment. It is called with std::apply and - input_params so you can use them to mock records with specific IDs or - calculate an expected result. */ - , - MaybeTestConfigureFunction /* test_teardown_function If this is not nullptr this function is called after - each test to cleanup any allocated/shared resources. */ - , - bool /* is_enabled If this is false the test, setup, and teardown functions are not run. */>; - -/// @brief -/// @tparam TResult -/// @tparam TFunctionToTest -/// @tparam ...TInputParams -template -using TestSuite = std::tuple, - std::initializer_list>, - MaybeTestCompareFunction, - MaybeTestConfigureFunction, - MaybeTestConfigureFunction, - bool>; +/// @addtogroup test_execution +/// @{ +/// @brief This function marks a test as skipped with an optional reason. +/// @param results The TestResults to update. +/// @param suite_label The label for the test suite. +/// @param test_label The label for the test. +/// @param reason The optional reason the test is being skipped. +/// @return The TestResults for chaining. TestResults& SkipTest(TestResults& results, const std::string& suite_label, const std::string& test_label, std::optional reason = std::nullopt); -/// @brief +/// @brief Executes a TestSuite. /// @tparam TResult The result type of the test. /// @tparam TInputParams... The types of parameters sent to the test function. /// @param suite_label The label for this test suite. For example a class name /// such as "MortgageCalculator". /// @param function_to_test The function to be tested. It will be called with /// std::apply and a std::tuple made from each item in tests. -/// @param tests A std::vector of test runs. +/// @param tests An std::initializer_list of test runs. /// @param suite_Compare A function used to Compare the expected and actual test /// results. This can be overridden per test by setting test_Compare. /// @param after_all This is called before each suite is started to setup the @@ -367,7 +381,90 @@ TestResults ExecuteSuite(std::string suite_label, MaybeTestCompareFunction suite_Compare = std::nullopt, MaybeTestConfigureFunction before_all = std::nullopt, MaybeTestConfigureFunction after_all = std::nullopt, - bool is_enabled = true) { + bool is_enabled = true); + +/// @brief +/// @tparam TResult The result type of the test. +/// @tparam TInputParams... The types of parameters sent to the test function. +/// @param test_suite A tuple representing the test suite configuration. +template +TestResults ExecuteSuite(const TestSuite& test_suite); + +/// @} + +template +MaybeTestCompareFunction DefaultTestCompareFunction() { + return std::nullopt; +} + +template +TestTuple MakeTest(const std::string& test_name, + const TResult& expected, + std::tuple input_params, + MaybeTestCompareFunction test_compare_function, + MaybeTestConfigureFunction before_each, + MaybeTestConfigureFunction after_each, + bool is_enabled) { + return make_tuple(test_name, expected, input_params, test_compare_function, before_each, after_each, is_enabled); +} + +template +TestSuite MakeTestSuite(const std::string& suite_name, + TFunctionToTest function_to_test, + std::initializer_list> test_data, + MaybeTestCompareFunction compare, + MaybeTestConfigureFunction before_each, + MaybeTestConfigureFunction after_each, + bool is_enabled) { + return make_tuple(suite_name, function_to_test, test_data, compare, before_each, after_each, is_enabled); +} + +template +std::string InterceptCout(std::function function_to_execute, + std::optional> maybe_args) { + std::ostringstream os; + auto saved_buffer = std::cout.rdbuf(); + std::cout.rdbuf(os.rdbuf()); + + if (maybe_args.has_value()) { + std::apply(function_to_execute, maybe_args.value()); + } else { + std::invoke(function_to_execute); + } + std::cout.rdbuf(saved_buffer); + return os.str(); +} + +template +auto& Compare(std::basic_ostream& error_message, + std::vector expected, + std::vector actual) { + if (expected.size() != actual.size()) { + error_message << "size mismatch expected: " << expected.size() << ", actual: " << actual.size(); + return error_message; + } + + for (size_t index = 0; index < expected.size(); index++) { + if (expected[index] != actual[index]) { + error_message << "vectors differ at index " << index << ", "; + CPPUtils::PrettyPrint(error_message, expected[index]) << " != "; + CPPUtils::PrettyPrint(error_message, actual[index]) << ", expected: "; + CPPUtils::PrettyPrint(error_message, expected) << ", actual: "; + CPPUtils::PrettyPrint(error_message, actual); + return error_message; + } + } + return error_message; +} + +template +TestResults ExecuteSuite(std::string suite_label, + std::function function_to_test, + std::initializer_list> tests, + MaybeTestCompareFunction suite_Compare, + MaybeTestConfigureFunction before_all, + MaybeTestConfigureFunction after_all, + bool is_enabled) { TestResults results; if (!is_enabled) { std::cout << "🚧Skipping suite: " << suite_label << " because it is disabled." << std::endl; @@ -451,7 +548,9 @@ TestResults ExecuteSuite(std::string suite_label, std::cout << " āœ…PASSED" << std::endl; } else { std::ostringstream os; - os << "expected: \"" << expected_output << "\", actual: \"" << actual << "\""; + os << "expected: "; + CPPUtils::PrettyPrint(os, expected_output) << ", actual: "; + CPPUtils::PrettyPrint(os, actual); results.Fail(qualified_test_label + " " + os.str()); std::cout << " āŒFAILED: " << os.str() << std::endl; } @@ -471,10 +570,6 @@ TestResults ExecuteSuite(std::string suite_label, return results; } -/// @brief -/// @tparam TResult The result type of the test. -/// @tparam TInputParams... The types of parameters sent to the test function. -/// @param test_suite A tuple representing the test suite configuration. template TestResults ExecuteSuite(const TestSuite& test_suite) { std::string suite_label = std::get<0>(test_suite); @@ -487,152 +582,6 @@ TestResults ExecuteSuite(const TestSuite& test_suite) return ExecuteSuite(suite_label, function_to_test, tests, suite_Compare, before_all, after_all, is_enabled); } -/// @brief -/// @tparam TResult The result type of the test. -/// @tparam ...TInputParams The types of parameters sent to the test function. -/// @param test_name The label for this test. For example "should calculate the -/// interest". -/// @param expected The expected output of calling the test function with these -/// input parameters. -/// @param input_params The input parameters to use when calling the test -/// function. -/// @param test_Compare_fn An optional function that can be used to Compare the -/// expected and actual return values. This is good for when you only care about -/// certain fields being equal. -/// @param before_each This is called to setup the environment before running -/// the test. This is where you should build mocks, setup spies, and set any -/// other values you need before calling the test function. -/// @param after_each This is called after each test run to cleanup anything -/// allocated in before_each. -/// @param is_enabled If false this test run is not executed and considered -/// skipped for reporting purposes. -/// @return A TestTuple suitable for use as a test run when calling test_fn. -template -TestTuple MakeTest(const std::string& test_name, - const TResult& expected, - std::tuple input_params, - MaybeTestCompareFunction test_Compare_fn = std::nullopt, - MaybeTestConfigureFunction before_each = std::nullopt, - MaybeTestConfigureFunction after_each = std::nullopt, - bool is_enabled = true) { - return make_tuple(test_name, expected, input_params, test_Compare_fn, before_each, after_each, is_enabled); -} - -/// @brief -/// @tparam TResult -/// @tparam TFunctionToTest -/// @tparam ...TInputParams -/// @param suite_name -/// @param function_to_test -/// @param test_data -/// @param Compare -/// @param before_each -/// @param after_each -/// @param is_enabled -/// @return -template -TestSuite MakeTestSuite(const std::string& suite_name, - TFunctionToTest function_to_test, - std::initializer_list> test_data, - MaybeTestCompareFunction Compare = std::nullopt, - MaybeTestConfigureFunction before_each = std::nullopt, - MaybeTestConfigureFunction after_each = std::nullopt, - bool is_enabled = true) { - return make_tuple(suite_name, function_to_test, test_data, Compare, before_each, after_each, is_enabled); -} - -/// @brief Writes a friendly version of results to the provided stream. -/// @param os The stream to write to. -/// @param results The TestResults to write. -void PrintResults(std::ostream& os, TestResults results); - -/// @brief -/// @param first -/// @param second -/// @return -MaybeTestConfigureFunction Coalesce(MaybeTestConfigureFunction first, MaybeTestConfigureFunction second); - -// TODO: Document this. -// Tuple printer based on code from: -// https://stackoverflow.com/questions/6245735/pretty-print-stdtuple/31116392#58417285 -template -auto& operator<<(std::basic_ostream& os, std::tuple const& t) { - std::apply( - [&os](auto&&... args) { - if (sizeof...(TArgs) == 0) { - os << "[]"; - return; - } - size_t n = 0; - os << "[ "; - ((TinyTest::PrettyPrint(os, args) << (++n != sizeof...(TArgs) ? ", " : "")), ...); - os << " ]"; - }, - t); - return os; -} - -// TODO: Document this. -template -auto& operator<<(std::basic_ostream& os, const void* pointer) { - os << pointer; -} - -// TODO: Simplify this. -template ().begin()), decltype(std::declval().end())>>, - typename = std::enable_if_t().begin())>::iterator_category>>> -auto& operator<<(std::basic_ostream& os, TContainer container) { - os << "[ "; - for (auto it = container.begin(); it != container.end(); it++) { - if (it != container.begin()) { - os << ", "; - } - TinyTest::PrettyPrint(os, *it); - } - os << " ]"; - return os; -} - -// TODO: Document this. -template -auto& operator<<(std::basic_ostream& os, std::initializer_list container) { - os << "[ "; - for (auto it = container.begin(); it != container.end(); it++) { - if (it != container.begin()) { - os << ", "; - } - TinyTest::PrettyPrint(os, *it); - } - os << " ]"; - return os; -} - -// TODO: Document this. -template -auto& Compare(std::basic_ostream& error_message, - std::vector expected, - std::vector actual) { - if (expected.size() != actual.size()) { - error_message << "size mismatch expected: " << expected.size() << ", actual: " << actual.size(); - return error_message; - } - - for (size_t index = 0; index < expected.size(); index++) { - if (expected[index] != actual[index]) { - error_message << "vectors differ at index " << index << ", \"" << expected[index] << "\" != \"" << actual[index] - << "\", expected: \"" << expected << "\", actual: \"" << actual << "\""; - return error_message; - } - } - return error_message; -} - } // End namespace TinyTest -#endif // End !defined TEST_H__ +#endif // End !defined(TinyTest__tinytest_h__) diff --git a/tinytest_test.cpp b/tinytest_test.cpp index 5395dd5..0046216 100644 --- a/tinytest_test.cpp +++ b/tinytest_test.cpp @@ -42,74 +42,6 @@ using TinyTest::PrintResults; using TinyTest::TestResults; using TinyTest::TestSuite; using TinyTest::TestTuple; -using TinyTest::operator<<; - -TEST(TuplePrinter, ShouldPrintAnEmptyTuple) { - ostringstream os; - auto tuple = make_tuple(); - os << tuple; - // Ideally something like "[]" or "Tuple: []" - EXPECT_THAT(os.str(), Eq("[]")); -} - -TEST(TuplePrinter, ShouldPrintATupleOfOneString) { - ostringstream os; - auto tuple = make_tuple("asdf"); - os << tuple; - // Ideally this wouldn't have a space at the end. - EXPECT_THAT(os.str(), Eq("[ \"asdf\" ]")); -} - -TEST(TuplePrinter, ShouldPrintATupleOfTwoIntegers) { - ostringstream os; - auto tuple = make_tuple(69, 420); - os << tuple; - EXPECT_THAT(os.str(), Eq("[ 69, 420 ]")); -} - -TEST(TuplePrinter, ShouldPrintATupleOfTwoStrings) { - ostringstream os; - auto tuple = make_tuple("first", "second"); - os << tuple; - EXPECT_THAT(os.str(), Eq("[ \"first\", \"second\" ]")); -} - -TEST(TuplePrinter, ShouldPrintATupleOfOneStringAndOneInteger) { - ostringstream os; - auto tuple = make_tuple("this is a string that ends with 69", 420); - os << tuple; - EXPECT_THAT(os.str(), Eq("[ \"this is a string that ends with 69\", 420 ]")); -} - -TEST(TuplePrinter, ShouldPringATupleOfStringsContainingSpaces) { - ostringstream os; - auto tuple = make_tuple(" ", " ", " ", " "); - os << tuple; - EXPECT_THAT(os.str(), Eq("[ \" \", \" \", \" \", \" \" ]")); -} - -TEST(VectorPrinter, ShouldPrintAnEmptyVector) { - ostringstream os; - vector value = vector({1, 2, 3, 4}); - os << value; - EXPECT_THAT(os.str(), Eq("[ 1, 2, 3, 4 ]")); -} - -TEST(VectorPrinter, ShouldPrintAVectorOfCStrings) { - ostringstream os; - vector value = vector({"asdf", "fdsa", "lemon", "cherry"}); - os << value; - // Ideally "[ \"asdf\", \"fdsa\", \"lemon\", \"cherry\" ]" - EXPECT_THAT(os.str(), Eq("[ \"asdf\", \"fdsa\", \"lemon\", \"cherry\" ]")); -} - -TEST(VectorPrinter, ShouldPrintAVectorOfStrings) { - ostringstream os; - vector value = vector({"asdf", "fdsa", "lemon", "cherry"}); - os << value; - // Ideally "[ \"asdf\", \"fdsa\", \"lemon\", \"cherry\" ]" - EXPECT_THAT(os.str(), Eq("[ \"asdf\", \"fdsa\", \"lemon\", \"cherry\" ]")); -} TEST(VectorCompare, ShouldPrintSizeMismatch) { ostringstream os; @@ -124,9 +56,8 @@ TEST(VectorCompare, ShouldPrintVectorsDifferAtIndexZero) { vector first = vector({1, 2, 3, 4}); vector second = vector({0, 1, 2, 3}); Compare(os, first, second); - EXPECT_THAT( - os.str(), - Eq("vectors differ at index 0, \"1\" != \"0\", expected: \"[ 1, 2, 3, 4 ]\", actual: \"[ 0, 1, 2, 3 ]\"")); + EXPECT_THAT(os.str(), + Eq((string) "vectors differ at index 0, 1 != 0, expected: [ 1, 2, 3, 4 ], actual: [ 0, 1, 2, 3 ]")); } TEST(VectorCompare, ShouldPrintVectorsDifferAtEnd) { @@ -134,9 +65,7 @@ TEST(VectorCompare, ShouldPrintVectorsDifferAtEnd) { vector first = vector({1, 2, 3, 4}); vector second = vector({1, 2, 3, 0}); Compare(os, first, second); - EXPECT_THAT( - os.str(), - Eq("vectors differ at index 3, \"4\" != \"0\", expected: \"[ 1, 2, 3, 4 ]\", actual: \"[ 1, 2, 3, 0 ]\"")); + EXPECT_THAT(os.str(), Eq("vectors differ at index 3, 4 != 0, expected: [ 1, 2, 3, 4 ], actual: [ 1, 2, 3, 0 ]")); } TEST(VectorCompare, ShouldPrintNothingWhenVectorsAreEqual) { @@ -881,7 +810,7 @@ TEST(ExecuteSuiteWithParams, ShouldExecuteASuiteWithASingleFailure) { Eq( R"test(šŸš€Beginning Suite: My Suite Beginning Test: Test Name - āŒFAILED: expected: "0", actual: "1" + āŒFAILED: expected: 0, actual: 1 Ending Test: Test Name Ending Suite: My Suite )test")); @@ -1053,7 +982,7 @@ TEST(ExecuteSuiteWithParams, ShouldCatchAnExceptionThrownByATest) { R"test(šŸš€Beginning Suite: My Suite Beginning Test: Test Name šŸ”„ERROR: Caught exception "std::exception". - āŒFAILED: expected: "1", actual: "0" + āŒFAILED: expected: 1, actual: 0 Ending Test: Test Name Ending Suite: My Suite )test")); @@ -1113,7 +1042,7 @@ TEST(ExecuteSuiteWithParams, ShouldCatchAStringThrownByATest) { R"test(šŸš€Beginning Suite: My Suite Beginning Test: Test Name šŸ”„ERROR: Caught string "burp". - āŒFAILED: expected: "1", actual: "0" + āŒFAILED: expected: 1, actual: 0 Ending Test: Test Name Ending Suite: My Suite )test")); @@ -1173,7 +1102,7 @@ TEST(ExecuteSuiteWithParams, ShouldCatchACStringThrownByATest) { R"test(šŸš€Beginning Suite: My Suite Beginning Test: Test Name šŸ”„ERROR: Caught c-string "burp". - āŒFAILED: expected: "1", actual: "0" + āŒFAILED: expected: 1, actual: 0 Ending Test: Test Name Ending Suite: My Suite )test")); @@ -1233,7 +1162,7 @@ TEST(ExecuteSuiteWithParams, ShouldCatchSomethingElseThrownByATest) { R"test(šŸš€Beginning Suite: My Suite Beginning Test: Test Name šŸ”„ERROR: Caught something that is neither an std::exception nor an std::string. - āŒFAILED: expected: "1", actual: "0" + āŒFAILED: expected: 1, actual: 0 Ending Test: Test Name Ending Suite: My Suite )test"));