diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..95fb71feefa7ce306c15a7ae1a444ca39daa6ba2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1,10 @@ +## Future releases +- More `ASSERT_*` macros +- Deprecate `END_TEST` and get rid of `add_test` and change `TEST` impl +## 0.1.0 +- `TEST` +- `ASSERT_EQ` +- `ASSERT_TRUE` +- `litefprocut::add_test` +- `litefprocut::init` +- `litefprocut::run_all_tests` \ No newline at end of file diff --git a/README.md b/README.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..bb81f863af5b07d4c5d50bc3e2ef133beb4fc05d 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,3 @@ +# LiteFProcUT - alightweight & simple file-processing functionality UT framework + +Documentation can be found in `docs/` folder \ No newline at end of file diff --git a/bin/lib/.gitkeep b/bin/lib/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/bin/tests/.gitkeep b/bin/tests/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/bin/tests/some_test b/bin/tests/some_test new file mode 100644 index 0000000000000000000000000000000000000000..453cc91a255fa27e00d9a87a06bccae56620a68c Binary files /dev/null and b/bin/tests/some_test differ diff --git a/dist/.gitkeep b/dist/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/dist/litefprocut.tar.gz b/dist/litefprocut.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..8aa91cb41f85dcd886218b0e762b6c9645d37b41 Binary files /dev/null and b/dist/litefprocut.tar.gz differ diff --git a/dist/litefprocut/litefprocut.hpp b/dist/litefprocut/litefprocut.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3ffad80820a50bbf3fb497a7f05f868fb6c9779a --- /dev/null +++ b/dist/litefprocut/litefprocut.hpp @@ -0,0 +1,110 @@ +#ifndef LITEFPROCUT_ADD_TEST_HPP +#define LITEFPROCUT_ADD_TEST_HPP + +#include<vector> +#include<functional> +#include<string> +#include<tuple> +#include<fstream> +#include<iostream> +#include<cstring> + +namespace litefprocut +{ + struct test + { + std::size_t passed{}; + std::size_t failed{}; + std::tuple<std::string /*name*/, std::string /*fname*/, std::function<void(const std::string&)>/*test*/> set{}; + explicit test(const std::size_t& p, const std::size_t& f, const decltype(set)& s) : passed{p}, failed{f}, set{s} {} + explicit test(const decltype(set)& s) : set{s} {} + explicit test() = default; + ~test() = default; + }; + struct suite + { + std::vector<test> tests{}; + std::size_t passed{}; + std::size_t failed{}; + std::size_t skipped{}; + std::vector<std::string> skipped_tests{}; + int result{}; + explicit suite() = default; + }; + suite this_suite{}; + decltype(this_suite.tests)::value_type this_test{}; + void add_test(const std::string& fname, const std::string& name, std::function<void(const std::string&)> test_func) + { + this_suite.tests.push_back(test{std::make_tuple(name, fname, test_func)}); + } + void run_all_tests() + { + for(auto& test : this_suite.tests) + { + this_test = test; + std::ifstream ifs{std::get<1>(this_test.set)}; + std::string content{}; + std::string aux{}; + while(std::getline(ifs, aux)) content += aux; + std::cerr << "\e[36mRunning test " << std::get<0>(this_test.set) << " [" << (this_suite.passed + this_suite.failed + this_suite.skipped + 1) << "/" << this_suite.tests.size() << "]\n\e[0m"; + + if(std::find(this_suite.skipped_tests.begin(), this_suite.skipped_tests.end(), std::get<0>(this_test.set)) != this_suite.skipped_tests.end()) + { + ++this_suite.skipped; + std::cerr << "\e[33mTest " << std::get<0>(this_test.set) << " skipped\n\e[0m"; + } + else + { + std::get<2>(this_test.set)(content); + if(this_test.failed == 0) + { + std::cerr << "\e[32mTest " << std::get<0>(this_test.set) << " passed\n\e[0m"; + ++this_suite.passed; + } + else + { + std::cerr << "\e[31mTest " << std::get<0>(this_test.set) << " failed\n\e[0m"; + std::cerr << "\e[31m\t" << this_test.failed << " assertion" << (this_test.failed == 1 ? "" : "s") << " failed, \e[0m\e[32m" << this_test.passed << " passed\n\e[0m"; + ++this_suite.failed; + --this_suite.result; + } + } + } + std::cerr << "\e[3\n2mPassed: " << this_suite.passed + << " (" << ((static_cast<long double>(this_suite.passed) / static_cast<long double>(this_suite.tests.size())) * 100.0) << "%)\n\e[0m"; + std::cerr << "\e[31mFailed: " << this_suite.failed + << " (" << ((static_cast<long double>(this_suite.failed) / static_cast<long double>(this_suite.tests.size())) * 100.0) << "%)\n\e[0m"; + std::cerr << "\e[33mSkipped: " << this_suite.skipped + << " (" << ((static_cast<long double>(this_suite.skipped) / static_cast<long double>(this_suite.tests.size())) * 100.0) << "%)\n\e[0m\n"; + } + void init(const int& argc, char** argv, const std::size_t& n) + { + if(argc < n + 1) + { + std::cerr << "Please provide at least " << n << " arguments!\n"; + std::cerr << "After that " << n << " arguments, you could append:\n"; + std::cerr << "--skip <test name>...\n"; + std::exit(1); + } + else + { + if(std::strcmp(argv[n + 1], "--skip") == 0) + { + for(std::size_t i = n + 2; i < argc; i++) + { + this_suite.skipped_tests.push_back(argv[i]); + // std::cerr << "Adding test " << argv[i] << " to this_suite.skipped_tests...\n"; + } + } + } + } + // FIXME: Does it make sense? + // void next_suite(const std::string& suite_name) + // { + // system(suite_name); + // } +} + +#include "macros.hpp" + +#endif \ No newline at end of file diff --git a/dist/litefprocut/macros.hpp b/dist/litefprocut/macros.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6336d985e14bdc3587a56a636588c26c5b903660 --- /dev/null +++ b/dist/litefprocut/macros.hpp @@ -0,0 +1,46 @@ +#ifndef LITEFPROC_MACROS_HPP +#define LITEFPROC_MACROS_HPP + +#include<iostream> + +#define TEST(name, fname_var)\ +litefprocut::add_test(fname_var, #name, [argv](const std::string& content) +#define END_TEST \ +); +#define ASSERT_EQ(lhs, rhs)\ +if((lhs) != (rhs))\ +{\ + std::cerr << "\e[31mAssertion failed:\n\e[0m";\ + std::cerr << "\e[31m\tExpected: " << rhs << "\n\e[0m";\ + std::cerr << "\e[31m\tActual: " << lhs << "\n\e[0m";\ + ++litefprocut::this_test.failed;\ +}\ +else ++litefprocut::this_test.passed; +#define ASSERT_TRUE(expr)\ +if(!(expr))\ +{\ + std::cerr << "\e[31mExpectation of true failed:\n\e[0m";\ + ++litefprocut::this_test.failed;\ +}\ +else ++litefprocut::this_test.passed; + +// std::ifstream& litefprocut_##name##_ifs{#fname};\ +// std::string litefprocut_##name##_content{};\ +// std::string litefprocut_##name##_aux{}\ +// while(std::getline(litefprocut_##name##_ifs, litefprocut_##name##_aux)) litefprocut_##name##_content += litefprocut_##name##_aux;\ + +// Example: +// int main(int argc, char** argv) +// { +// litefprocut::init(argc, argv, 2); +// TEST(is-version-loaded-correctly, argv[1]) +// { +// std::string fname = argv[1]; +// ASSERT_EQ(load_version(content), fname[fname.find("ver") + 3]) +// } +// litefprocut::run_all_tests(); +// litefprocut::next_suite(argv[2]); +// return litefprocut::this_suite::result; +// } + +#endif \ No newline at end of file diff --git a/docs/assets/index.md b/docs/assets/index.md new file mode 100644 index 0000000000000000000000000000000000000000..bf58542ab73247900347c5407374208f1db28a6c --- /dev/null +++ b/docs/assets/index.md @@ -0,0 +1,14 @@ +# TEST(name, fname_var) +`name` - name of the test (literally, not within `"`) +`fname_var` - variable holding processed file name (i.e. `argv[<something>]`) +# ASSERT_EQ(lhs, rhs) +Asserts if `lhs == rhs`. +# ASSERT_TRUE(expr) +Asserts if `expr` is true. + +# `void litefprocut::init(const int& argc, char** argv, const std::size_t& n)` +Checks if `argc < n + 1` and marks given tests as skipped. +# `void litefprocut::run_all_tests()` +Runs all tests in suite +# `void litefprocut::add_test(const std::string& fname, const std::string& name, std::function<void(const std::string&)> test_func)` +Adds test to the `this_suite` object. Can be used directly, but without `TEST` macro in such a case. \ No newline at end of file diff --git a/include/.gitkeep b/include/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/include/litefprocut.hpp b/include/litefprocut.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3ffad80820a50bbf3fb497a7f05f868fb6c9779a --- /dev/null +++ b/include/litefprocut.hpp @@ -0,0 +1,110 @@ +#ifndef LITEFPROCUT_ADD_TEST_HPP +#define LITEFPROCUT_ADD_TEST_HPP + +#include<vector> +#include<functional> +#include<string> +#include<tuple> +#include<fstream> +#include<iostream> +#include<cstring> + +namespace litefprocut +{ + struct test + { + std::size_t passed{}; + std::size_t failed{}; + std::tuple<std::string /*name*/, std::string /*fname*/, std::function<void(const std::string&)>/*test*/> set{}; + explicit test(const std::size_t& p, const std::size_t& f, const decltype(set)& s) : passed{p}, failed{f}, set{s} {} + explicit test(const decltype(set)& s) : set{s} {} + explicit test() = default; + ~test() = default; + }; + struct suite + { + std::vector<test> tests{}; + std::size_t passed{}; + std::size_t failed{}; + std::size_t skipped{}; + std::vector<std::string> skipped_tests{}; + int result{}; + explicit suite() = default; + }; + suite this_suite{}; + decltype(this_suite.tests)::value_type this_test{}; + void add_test(const std::string& fname, const std::string& name, std::function<void(const std::string&)> test_func) + { + this_suite.tests.push_back(test{std::make_tuple(name, fname, test_func)}); + } + void run_all_tests() + { + for(auto& test : this_suite.tests) + { + this_test = test; + std::ifstream ifs{std::get<1>(this_test.set)}; + std::string content{}; + std::string aux{}; + while(std::getline(ifs, aux)) content += aux; + std::cerr << "\e[36mRunning test " << std::get<0>(this_test.set) << " [" << (this_suite.passed + this_suite.failed + this_suite.skipped + 1) << "/" << this_suite.tests.size() << "]\n\e[0m"; + + if(std::find(this_suite.skipped_tests.begin(), this_suite.skipped_tests.end(), std::get<0>(this_test.set)) != this_suite.skipped_tests.end()) + { + ++this_suite.skipped; + std::cerr << "\e[33mTest " << std::get<0>(this_test.set) << " skipped\n\e[0m"; + } + else + { + std::get<2>(this_test.set)(content); + if(this_test.failed == 0) + { + std::cerr << "\e[32mTest " << std::get<0>(this_test.set) << " passed\n\e[0m"; + ++this_suite.passed; + } + else + { + std::cerr << "\e[31mTest " << std::get<0>(this_test.set) << " failed\n\e[0m"; + std::cerr << "\e[31m\t" << this_test.failed << " assertion" << (this_test.failed == 1 ? "" : "s") << " failed, \e[0m\e[32m" << this_test.passed << " passed\n\e[0m"; + ++this_suite.failed; + --this_suite.result; + } + } + } + std::cerr << "\e[3\n2mPassed: " << this_suite.passed + << " (" << ((static_cast<long double>(this_suite.passed) / static_cast<long double>(this_suite.tests.size())) * 100.0) << "%)\n\e[0m"; + std::cerr << "\e[31mFailed: " << this_suite.failed + << " (" << ((static_cast<long double>(this_suite.failed) / static_cast<long double>(this_suite.tests.size())) * 100.0) << "%)\n\e[0m"; + std::cerr << "\e[33mSkipped: " << this_suite.skipped + << " (" << ((static_cast<long double>(this_suite.skipped) / static_cast<long double>(this_suite.tests.size())) * 100.0) << "%)\n\e[0m\n"; + } + void init(const int& argc, char** argv, const std::size_t& n) + { + if(argc < n + 1) + { + std::cerr << "Please provide at least " << n << " arguments!\n"; + std::cerr << "After that " << n << " arguments, you could append:\n"; + std::cerr << "--skip <test name>...\n"; + std::exit(1); + } + else + { + if(std::strcmp(argv[n + 1], "--skip") == 0) + { + for(std::size_t i = n + 2; i < argc; i++) + { + this_suite.skipped_tests.push_back(argv[i]); + // std::cerr << "Adding test " << argv[i] << " to this_suite.skipped_tests...\n"; + } + } + } + } + // FIXME: Does it make sense? + // void next_suite(const std::string& suite_name) + // { + // system(suite_name); + // } +} + +#include "macros.hpp" + +#endif \ No newline at end of file diff --git a/include/macros.hpp b/include/macros.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6336d985e14bdc3587a56a636588c26c5b903660 --- /dev/null +++ b/include/macros.hpp @@ -0,0 +1,46 @@ +#ifndef LITEFPROC_MACROS_HPP +#define LITEFPROC_MACROS_HPP + +#include<iostream> + +#define TEST(name, fname_var)\ +litefprocut::add_test(fname_var, #name, [argv](const std::string& content) +#define END_TEST \ +); +#define ASSERT_EQ(lhs, rhs)\ +if((lhs) != (rhs))\ +{\ + std::cerr << "\e[31mAssertion failed:\n\e[0m";\ + std::cerr << "\e[31m\tExpected: " << rhs << "\n\e[0m";\ + std::cerr << "\e[31m\tActual: " << lhs << "\n\e[0m";\ + ++litefprocut::this_test.failed;\ +}\ +else ++litefprocut::this_test.passed; +#define ASSERT_TRUE(expr)\ +if(!(expr))\ +{\ + std::cerr << "\e[31mExpectation of true failed:\n\e[0m";\ + ++litefprocut::this_test.failed;\ +}\ +else ++litefprocut::this_test.passed; + +// std::ifstream& litefprocut_##name##_ifs{#fname};\ +// std::string litefprocut_##name##_content{};\ +// std::string litefprocut_##name##_aux{}\ +// while(std::getline(litefprocut_##name##_ifs, litefprocut_##name##_aux)) litefprocut_##name##_content += litefprocut_##name##_aux;\ + +// Example: +// int main(int argc, char** argv) +// { +// litefprocut::init(argc, argv, 2); +// TEST(is-version-loaded-correctly, argv[1]) +// { +// std::string fname = argv[1]; +// ASSERT_EQ(load_version(content), fname[fname.find("ver") + 3]) +// } +// litefprocut::run_all_tests(); +// litefprocut::next_suite(argv[2]); +// return litefprocut::this_suite::result; +// } + +#endif \ No newline at end of file diff --git a/scripts/archive.bash b/scripts/archive.bash new file mode 100644 index 0000000000000000000000000000000000000000..ac202251c8839fa9779f11d7282990b45c0ce0a9 --- /dev/null +++ b/scripts/archive.bash @@ -0,0 +1,8 @@ +if [[ ! -d dist/litefprocut ]]; then +mkdir dist/litefprocut +fi +cp include/litefprocut.hpp dist/litefprocut/litefprocut.hpp +cp include/macros.hpp dist/litefprocut/macros.hpp + +tar zcvf litefprocut.tar.gz dist/litefprocut/*.hpp +mv litefprocut.tar.gz dist/litefprocut.tar.gz \ No newline at end of file diff --git a/test.cpp b/test.cpp index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..22a7425e19c7e1f99ea079f6e3481128515c84e6 100644 --- a/test.cpp +++ b/test.cpp @@ -0,0 +1,14 @@ +#include<cstdlib> + +#if defined(_WIN32) || defined(_WIN64) +#define PATHSEP "\\" +#define EXEC_EXT ".exe" +#else +#define PATHSEP "/" +#define EXEC_EXT "" +#endif + +int main() +{ + return system("bin" PATHSEP "tests" PATHSEP "some_test" EXEC_EXT); +} \ No newline at end of file diff --git a/tests/.gitkeep b/tests/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/tests/some_test.cpp b/tests/some_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d61888882c504b14b63d20a0d2b65829a9d81de1 --- /dev/null +++ b/tests/some_test.cpp @@ -0,0 +1,26 @@ +#include "litefprocut.hpp" +#include "macros.hpp" + +int main(int argc, char** argv) +{ + litefprocut::init(argc, argv, 1); + TEST(is-true, argv[1]) + { + std::string fname = argv[1]; + ASSERT_TRUE(!false) + } + END_TEST + TEST(is-false, argv[1]) + { + ASSERT_EQ(false, false) + } + END_TEST + TEST(does-sum-of-2-and-5-eq-10-or-7?, argv[1]) + { + ASSERT_EQ(2 + 5, 10) + ASSERT_EQ(2 + 5, 7) + } + END_TEST + litefprocut::run_all_tests(); + return litefprocut::this_suite.result; +} \ No newline at end of file