From 1a4a19b9382387685971265b35daccf2cc668aac Mon Sep 17 00:00:00 2001 From: Tom Hicks Date: Fri, 7 Apr 2023 12:05:11 -0700 Subject: [PATCH] Started implementing execute/describe/it and expect. --- examples/JTest.cpp | 61 ++++++++++++++++------ examples/JTest.h | 1 + examples/example.cpp | 121 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 164 insertions(+), 19 deletions(-) diff --git a/examples/JTest.cpp b/examples/JTest.cpp index 23c53a3..08c178c 100644 --- a/examples/JTest.cpp +++ b/examples/JTest.cpp @@ -97,27 +97,57 @@ namespace JTest { return make_testbundle("", {test}); } - testresults_t execute(testbundle_t bundle) { -/* - Try to do this in parallel where possible + testbundle_t xit(const string& label, const test_fn& test_method, optional options) { + // TODO: replace this with a call to it and setting _disabled to true after it and xit are made to return test_t. + test_t test; + test._disabled = true; + test._label = label; + test._test_method = test_method; + return make_testbundle("", {test}); + } - If the bundle is marked as disabled (xdescribe) report all the tests in it and child bundles as SKIPPED. + testresults_t execute(testbundle_t bundle) { testresults_t results; - try { - bundle.beforeAll(); - for each child of bundle.children - results += execute(child) - for each test of bundle.tests - results += execute(test) - bundle.afterAll(); - } catch (...) { - Report as much info as possible. This likely means something happened in beforeAll() or afterAll(). + + if (bundle._disabled) { + // TODO: recursively report all tests as skipped. + } else { + try { + // TODO: Try to do this in parallel where possible + if (bundle._beforeAll.has_value()) { + bundle._beforeAll.value()(); + } + for_each(bundle._children.begin(), bundle._children.end(), [&results](testbundle_t bundle) { + // TODO: Find a way to make child tests get our beforeEach and afterEach callbacks. + results += execute(bundle); + }); + // TODO: Consider capturing these callbacks differently. Without the bundle? By value? + for_each(bundle._tests.begin(), bundle._tests.end(), [&results, &bundle](test_t test) { + if (bundle._beforeEach.has_value()) { + bundle._beforeEach.value()(); + } + results += execute(test); + if (bundle._afterEach.has_value()) { + bundle._afterEach.value()(); + } + }); + if (bundle._afterAll.has_value()) { + bundle._afterAll.value()(); + } + } catch(...) { + // TODO: Log this and mark the tests as failed. + // Report as much info as possible. This likely means something happened in beforeAll() or afterAll(). + } } -*/ throw unimplemented_function_error("execute(testbundle_t)"); } testresults_t execute(test_t test) { + int status = 0; + if (test._disabled) { + + } + /* status = PASSED (PASSED, FAILED, SKIPPED (PENDING | DISABLED), QUEUED, RUNNING) QUEUED and RUNNING might not make sense. If the test is marked as disabled (xit) then record it as a skipped test and return. @@ -164,9 +194,10 @@ namespace JTest { } } - testbundle_t describe(const wstring& label, const make_testbundle_fn& make_tests, optional options) { + testbundle_t describe(const string& label, const make_testbundle_fn& make_tests, optional options) { testbundle_t bundle = make_tests(); bundle._label = label; + bundle._disabled = false; if (options.has_value()) { describeoptions_t options_v = options.value(); bundle._afterAll = combine(options_v.getAfterAll(), bundle._afterAll); diff --git a/examples/JTest.h b/examples/JTest.h index a8c5ffc..6fe5630 100644 --- a/examples/JTest.h +++ b/examples/JTest.h @@ -20,6 +20,7 @@ namespace JTest { // vector skipped; }; + struct test_t { std::string _label; test_fn _test_method; bool _disabled; diff --git a/examples/example.cpp b/examples/example.cpp index 5f322bf..0997613 100644 --- a/examples/example.cpp +++ b/examples/example.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include using namespace JTest; using namespace MyNS; @@ -23,10 +25,6 @@ testresults_t test_ClassToTest_main(const vector& argv) { { it("should do the thing", [](){ // Throw exception if somethings goes wrong - - - // TODO: This unnecessary, by throwing exceptions. - return (test_t){}; }), it("should do the other thing", [](){ @@ -95,6 +93,120 @@ testresults_t test_ClassToTest_2(const vector& argv) { ); } +template +class Expectable { + public: + Expectable(const T& actual); + virtual ~Expectable(); + virtual void toEqual(const T& value); + virtual void toNotEqual(const T& value); + // Maybe these funcs should return tuple instead. + virtual void toBe(std::function>(const T& actual)> evaluator); + //virtual void toBeNull(); + //virtual void toNotBeNull(); + //virtual void toThrow(...); + private: + const T& actual_; +}; + +template +Expectable::Expectable(const T& actual) +: actual_(actual) {} + +template +Expectable::~Expectable() {} + +class FailedExpectation: std::runtime_error { + public: + FailedExpectation(const std::string& message); + virtual ~FailedExpectation(); +}; + +FailedExpectation::FailedExpectation(const std::string& message) +: runtime_error(message) { +} + +FailedExpectation::~FailedExpectation() {} + +template +void Expectable::toEqual(const T& value) { + if (actual_ != value) { + std::ostringstream os; + os << "Expected: " << actual_ << " to be " << value; + throw(FailedExpectation(os.str())); + } +} + +template +void Expectable::toNotEqual(const T& value) { + if (actual_ == value) { + std::ostringstream os; + os << "Expected: " << actual_ << " to not be " << value; + throw(FailedExpectation(os.str())); + } +} + +template +void Expectable::toBe(std::function>(const T& actual)> evaluator) { + auto result = evaluator(actual_); + if (!std::get<0>(result)) { + std::ostringstream os; + // std::optional message = std::get<1>; + std::optional message = std::get<1>(result); + if (message.has_value()) { + os << "Expected: " << actual_ << " to pass validation, but " << message.value(); + } else { + os << "Expected: " << actual_ << " to pass validation."; + } + throw(FailedExpectation(os.str())); + } +} + +// template +// void Expectable::toBeNull() { +// if (actual_ != nullptr) { +// std::ostringstream os; +// os << "Expected null, but got " << actual_; +// throw(FailedExpectation(os.str())); +// } +// } + +// template +// void Expectable::toNotBeNull() { +// if (actual_ == nullptr) { +// throw(FailedExpectation("Expected non-null, but got null")); +// } +// } + +// template +// void Expectable::toThrow(...) {} + +template +Expectable expect(const T& actual); + +template +Expectable expect(const T& actual) { + return Expectable(actual); +} + +int MyAddFunction(int a, int b) { + return 0; +} + +testresults_t test_BasicExpectable_main(const vector& argv) { + return execute( + describe("MyAddFunction", [](){ + return make_testbundle({ + it("should add 2 and 2 to get 4", [](){ + expect(MyAddFunction(2, 2)).toEqual(4); + }), + it("should ", [](){ + // Throw exception if somethings goes wrong + }), + }, make_describeoptions()); + })); +} + // Dummy test harness int main(int argc, char* argv[]) { try { @@ -102,6 +214,7 @@ int main(int argc, char* argv[]) { testresults_t results; results = add(results, test_ClassToTest_main(args)); + results = add(results, test_BasicExpectable_main(args)); print_test_results(results, cout); }