-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit d56a755
Showing
5 changed files
with
294 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
* | ||
!/**/ | ||
!*.* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2020 Mateusz Chudyk | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# TinyTest | ||
|
||
## Overview | ||
|
||
Simply header-only unit test framework written in C++. | ||
|
||
License: [MIT]. | ||
|
||
[MIT]: LICENSE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
#include "tinytest.h" | ||
|
||
TINY_TEST(passed_test) { | ||
TINY_CHECK(1, 1); | ||
TINY_CHECK_EPS(1.0f, 1.1f, 0.5f); | ||
|
||
int mem_expected1[] = {1, 2, 3}; | ||
int mem_actual1[] = {1, 2, 3}; | ||
TINY_CHECK_MEM(mem_expected1, mem_actual1, sizeof(mem_expected1) / sizeof(mem_expected1[0])); | ||
|
||
float mem_expected2[] = {1.1f, 2.2f, 3.3f}; | ||
float mem_actual2[] = {1.2f, 2.0f, 3.5f}; | ||
TINY_CHECK_MEM_EPS(mem_expected2, mem_actual2, sizeof(mem_expected2) / sizeof(mem_expected2[0]), 0.5f); | ||
} | ||
|
||
TINY_TEST(failed_test) { | ||
TINY_CHECK(1, 2); | ||
TINY_CHECK_EPS(1.0f, 1.1f, 0.001f); | ||
|
||
int mem_expected1[] = {1, 2, 3}; | ||
int mem_actual1[] = {1, 3, 3}; | ||
TINY_CHECK_MEM(mem_expected1, mem_actual1, sizeof(mem_expected1) / sizeof(mem_expected1[0])); | ||
|
||
float mem_expected2[] = {1.1f, 2.2f, 3.3f}; | ||
float mem_actual2[] = {1.1f, 2.0f, 3.3f}; | ||
TINY_CHECK_MEM_EPS(mem_expected2, mem_actual2, sizeof(mem_expected2) / sizeof(mem_expected2[0]), 0.001f); | ||
} | ||
|
||
TINY_TEST(tiny_fail) { | ||
TINY_FAIL("This test always fails"); | ||
} | ||
|
||
TINY_TEST(tiny_log) { | ||
TINY_LOG(TINY_DEFAULT, "Hello world!"); | ||
TINY_LOG(TINY_DEFAULT, "This is the %d. log.", 2); | ||
TINY_LOG(TINY_YELLOW, "You can specify a color for a whole log..."); | ||
TINY_LOG(TINY_DEFAULT, "...or only for a " TINY_COLOR(TINY_YELLOW, "part") " of it using TINY_COLOR macro."); | ||
TINY_LOG(TINY_DEFAULT, "Built-in colors:"); | ||
TINY_LOG(TINY_GRAY, "TINY_GRAY"); | ||
TINY_LOG(TINY_RED, "TINY_RED"); | ||
TINY_LOG(TINY_GREEN, "TINY_GREEN"); | ||
TINY_LOG(TINY_YELLOW, "TINY_YELLOW"); | ||
TINY_LOG(TINY_BLUE, "TINY_BLUE"); | ||
TINY_LOG(TINY_MAGENTA, "TINY_MAGENTA"); | ||
TINY_LOG(TINY_CYAN, "TINY_CYAN"); | ||
} | ||
|
||
int main() { | ||
return tinytest::run_all_tests(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
#ifndef TINY_TEST_H | ||
#define TINY_TEST_H | ||
|
||
//------------------------------------------------------------------------------ | ||
// TinyTest information | ||
//------------------------------------------------------------------------------ | ||
|
||
#define TINY_TEST_NAME "TinyTest" | ||
#define TINY_TEST_VERSION "0.1.0" | ||
|
||
//------------------------------------------------------------------------------ | ||
// You can define following macros by your own to customize TinyTest: | ||
// - TINY_TEST_MAX_TESTS - maximum number of tests | ||
// - TINY_TEST_PRINTF(format, ...) - printing functions | ||
//------------------------------------------------------------------------------ | ||
|
||
#ifndef TINY_TEST_MAX_TESTS | ||
#define TINY_TEST_MAX_TESTS 1000 | ||
#endif | ||
|
||
#ifndef TINY_TEST_PRINTF | ||
#include <stdio.h> | ||
#define TINY_TEST_PRINTF(format, ...) printf(format, __VA_ARGS__) | ||
#endif | ||
|
||
//------------------------------------------------------------------------------ | ||
// Macros you can use to create tests, checks, logs, etc. | ||
//------------------------------------------------------------------------------ | ||
|
||
// Colors | ||
#define TINY_DEFAULT "\x1b[0m" | ||
#define TINY_GRAY "\x1b[90m" | ||
#define TINY_RED "\x1b[91m" | ||
#define TINY_GREEN "\x1b[92m" | ||
#define TINY_YELLOW "\x1b[93m" | ||
#define TINY_BLUE "\x1b[94m" | ||
#define TINY_MAGENTA "\x1b[95m" | ||
#define TINY_CYAN "\x1b[96m" | ||
#define TINY_COLOR(color, text) color text "\x1b[0m" | ||
|
||
// Logs | ||
#define TINY_LOG(color, ...) __CHOOSE_WRAPPER(__TINY_LOG, __AT_LEAST_1_ARG(__VA_ARGS__), color, __VA_ARGS__) | ||
|
||
// Test | ||
#define TINY_TEST(test_name) \ | ||
void test_name(tinytest::TestResult&); \ | ||
static tinytest::TestAppender _tiny_wrapper_##test_name(#test_name " (" __FILE__ ")", test_name); \ | ||
void test_name(tinytest::TestResult& _result_) | ||
|
||
#define TINY_FAIL(...) \ | ||
do { \ | ||
TINY_LOG(TINY_RED, __VA_ARGS__); \ | ||
_result_.passed = false; \ | ||
} while (false) | ||
|
||
#define TINY_CHECK(expected, actual) \ | ||
do { \ | ||
++_result_.checks; \ | ||
if ((expected) != (actual)) { \ | ||
TINY_FAIL("values are different (expected = %d, actual = %d)", (expected), (actual)); \ | ||
++_result_.failed_checks; \ | ||
} \ | ||
} while (false) | ||
|
||
#define TINY_CHECK_EPS(expected, actual, epsilon) \ | ||
do { \ | ||
++_result_.checks; \ | ||
if (__FABS((expected) - (actual)) > (epsilon)) { \ | ||
TINY_FAIL("values differ by more then %f (expected = %f, actual = %f)", (epsilon), (expected), (actual)); \ | ||
++_result_.failed_checks; \ | ||
} \ | ||
} while (false) | ||
|
||
#define TINY_CHECK_MEM(expected, actual, elements) \ | ||
do { \ | ||
++_result_.checks; \ | ||
bool failed = false; \ | ||
for (unsigned i = 0; i < (elements); ++i) \ | ||
if ((expected)[i] != (actual)[i]) { \ | ||
TINY_FAIL("memories differ at %u-th position (expected = %d, actual = %d)", i, (expected)[i], (actual)[i]); \ | ||
failed = true; \ | ||
} \ | ||
if (failed) \ | ||
++_result_.failed_checks; \ | ||
} while (false) | ||
|
||
#define TINY_CHECK_MEM_EPS(expected, actual, elements, epsilon) \ | ||
do { \ | ||
++_result_.checks; \ | ||
bool failed = false; \ | ||
for (unsigned i = 0; i < (elements); ++i) \ | ||
if (__FABS((expected)[i] - (actual)[i]) > (epsilon)) { \ | ||
TINY_FAIL("memories differ at %u-th position by more then %f (expected = %f, actual = %f)", i, (epsilon), (expected)[i], (actual)[i]); \ | ||
failed = true; \ | ||
} \ | ||
if (failed) \ | ||
++_result_.failed_checks; \ | ||
} while (false) | ||
|
||
//------------------------------------------------------------------------------ | ||
// TinyTest implementation detail. It's not important for you if you only | ||
// want to use TinyTest. | ||
//------------------------------------------------------------------------------ | ||
|
||
// Floating point absolute value | ||
#define __FABS(a) ((a) < 0 ? -(a) : (a)) | ||
|
||
// Workaround for unsupported optional arguments in variadic macros. Works if | ||
// number of arguments passed in __VA_ARGS__ is not greater then 100. | ||
#define __AT_LEAST_1_ARG_INNER(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ | ||
_11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, \ | ||
_26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \ | ||
_41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, \ | ||
_56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, \ | ||
_71, _72, _73, _74, _75, _76, _77, _78, _79, _80, _81, _82, _83, _84, _85, \ | ||
_86, _87, _88, _89, _90, _91, _92, _93, _94, _95, _96, _97, _98, _99, _100, \ | ||
ARG, ...) ARG | ||
|
||
#define __AT_LEAST_1_ARG(...) \ | ||
__AT_LEAST_1_ARG_INNER(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ | ||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ | ||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ | ||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ | ||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0) | ||
|
||
#define __CHOOSE_WRAPPER_INNER(name, more_then_one, ...) \ | ||
name##_##more_then_one(__VA_ARGS__) | ||
|
||
#define __CHOOSE_WRAPPER(name, version, ...) \ | ||
__CHOOSE_WRAPPER_INNER(name, version, __VA_ARGS__) | ||
|
||
#define __TINY_LOG_0(color, format) TINY_TEST_PRINTF("[ ] " TINY_COLOR(color, "Line #%d: " format "\n"), __LINE__) | ||
#define __TINY_LOG_1(color, format, ...) TINY_TEST_PRINTF("[ ] " TINY_COLOR(color, "Line #%d: " format "\n"), __LINE__, __VA_ARGS__) | ||
|
||
#define __PRINTLN_0(format) TINY_TEST_PRINTF(format "%c", '\n') | ||
#define __PRINTLN_1(format, ...) TINY_TEST_PRINTF(format "\n", __VA_ARGS__) | ||
#define __PRINTLN(...) __CHOOSE_WRAPPER(__PRINTLN, __AT_LEAST_1_ARG(__VA_ARGS__), __VA_ARGS__) | ||
|
||
struct tinytest { | ||
struct TestResult { | ||
bool passed; | ||
unsigned checks; | ||
unsigned failed_checks; | ||
}; | ||
|
||
struct TestAppender { | ||
TestAppender(const char* name, void(*body)(TestResult&)) { | ||
*tinytest::all_tests_it++ = (Test){name, body}; | ||
} | ||
}; | ||
|
||
static bool run_all_tests() { | ||
__PRINTLN( | ||
"================================================================================\n" | ||
TINY_TEST_NAME " v" TINY_TEST_VERSION "\n" | ||
"================================================================================" | ||
); | ||
|
||
unsigned passed = 0; | ||
unsigned failed = 0; | ||
unsigned total_checks = 0; | ||
unsigned total_failed_checks = 0; | ||
for (const Test* it = all_tests; it != all_tests_it; ++it) { | ||
__PRINTLN("%c[ TEST ] %s", (it != all_tests ? '\n' : '\0'), it->name); | ||
|
||
TestResult result = {true, 0, 0}; | ||
it->body(result); | ||
|
||
total_checks += result.checks; | ||
total_failed_checks += result.failed_checks; | ||
|
||
if (result.passed) { | ||
__PRINTLN("[------] " TINY_COLOR(TINY_GREEN, "Passed (%u/%u)"), result.checks, result.checks); | ||
++passed; | ||
} | ||
else { | ||
__PRINTLN("[------] " TINY_COLOR(TINY_RED, "Failed (%u/%u)"), result.failed_checks, result.checks); | ||
++failed; | ||
} | ||
} | ||
|
||
__PRINTLN( | ||
"================================================================================\n" | ||
TINY_COLOR(TINY_GREEN, "Passed %u (%u/%u)\n") | ||
TINY_COLOR(TINY_RED, "Failed %u (%u/%u)\n") | ||
"================================================================================", | ||
passed, total_checks - total_failed_checks, total_checks, | ||
failed, total_failed_checks, total_checks); | ||
|
||
if (failed == 0) | ||
__PRINTLN(TINY_COLOR(TINY_GREEN, "All tests passed!\n")); | ||
else | ||
__PRINTLN(TINY_COLOR(TINY_RED, "%d %s failed!\n"), failed, (failed == 1 ? "test" : "tests")); | ||
|
||
return failed == 0; | ||
} | ||
|
||
private: | ||
struct Test { | ||
const char* name; | ||
void (*body)(TestResult&); | ||
}; | ||
|
||
static Test all_tests[TINY_TEST_MAX_TESTS]; | ||
static Test* all_tests_it; | ||
}; | ||
|
||
tinytest::Test tinytest::all_tests[TINY_TEST_MAX_TESTS] = {}; | ||
tinytest::Test* tinytest::all_tests_it = tinytest::all_tests; | ||
|
||
#endif |