mirror of
				https://github.com/HandmadeMath/HandmadeMath.git
				synced 2025-11-03 17:24:23 +00:00 
			
		
		
		
	Add test coverage macros (#104)
* Add coverage features and add it, laboriously, to everything * Fix easy tests * Add tests for != operators * Clean up test framework a little * Add documentation of coverage macros * Fix tests for mat4 to quaternion * Slightly improve formatting of coverage output * Trailing whitespace must die
This commit is contained in:
		
							
								
								
									
										9
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
root = true
 | 
			
		||||
 | 
			
		||||
[*.{c,cpp,h}]
 | 
			
		||||
charset = utf-8
 | 
			
		||||
indent_style = space
 | 
			
		||||
indent_size = 4
 | 
			
		||||
end_of_line = lf
 | 
			
		||||
insert_final_newline = true
 | 
			
		||||
trim_trailing_whitespace = true
 | 
			
		||||
@@ -5,4 +5,7 @@ compiler:
 | 
			
		||||
install:
 | 
			
		||||
  - cd test
 | 
			
		||||
script:
 | 
			
		||||
  - make all
 | 
			
		||||
  - make c
 | 
			
		||||
  - make c_no_sse
 | 
			
		||||
  - make cpp
 | 
			
		||||
  - make cpp_no_sse
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										681
									
								
								HandmadeMath.h
									
									
									
									
									
								
							
							
						
						
									
										681
									
								
								HandmadeMath.h
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,3 +1,7 @@
 | 
			
		||||
#ifndef WITHOUT_COVERAGE
 | 
			
		||||
#include "HandmadeTest.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define HANDMADE_MATH_IMPLEMENTATION
 | 
			
		||||
#define HANDMADE_MATH_NO_INLINE
 | 
			
		||||
#include "../HandmadeMath.h"
 | 
			
		||||
 
 | 
			
		||||
@@ -4,19 +4,46 @@
 | 
			
		||||
  This is Handmade Math's test framework. It is fully compatible with both C
 | 
			
		||||
  and C++, although it requires some compiler-specific features.
 | 
			
		||||
 | 
			
		||||
  To use Handmade Test, you must #define HANDMADE_TEST_IMPLEMENTATION in
 | 
			
		||||
  exactly one C or C++ file that includes the header, like this:
 | 
			
		||||
 | 
			
		||||
    #define HANDMADE_TEST_IMPLEMENTATION
 | 
			
		||||
    #include "HandmadeTest.h"
 | 
			
		||||
 | 
			
		||||
  The basic way of creating a test is using the TEST macro, which registers a
 | 
			
		||||
  single test to be run:
 | 
			
		||||
 | 
			
		||||
     TEST(MyCategory, MyTestName) {
 | 
			
		||||
    TEST(MyCategory, MyTestName) {
 | 
			
		||||
        // test code, including asserts/expects
 | 
			
		||||
     }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  The main function of your test code should then call hmt_run_all_tests and
 | 
			
		||||
  return the result:
 | 
			
		||||
  Handmade Test also provides macros you can use to check the coverage of
 | 
			
		||||
  important parts of your code. Define a coverage case by using the COVERAGE
 | 
			
		||||
  macro outside the function you wish to test, providing both a name and the
 | 
			
		||||
  number of asserts you expect to see covered over the course of your test.
 | 
			
		||||
  Then use the ASSERT_COVERED macro in every part of the function you wish to
 | 
			
		||||
  check coverage on. For example:
 | 
			
		||||
 | 
			
		||||
     int main() {
 | 
			
		||||
        return hmt_run_all_tests();
 | 
			
		||||
     }
 | 
			
		||||
    COVERAGE(MyCoverageCase, 3)
 | 
			
		||||
    void MyFunction(int a, int b) {
 | 
			
		||||
        if (a > b) {
 | 
			
		||||
            ASSERT_COVERED(MyCoverageCase);
 | 
			
		||||
            return 10;
 | 
			
		||||
        } else if (a < b) {
 | 
			
		||||
            ASSERT_COVERED(MyCoverageCase);
 | 
			
		||||
            return -10;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ASSERT_COVERED(MyCoverageCase);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  The main function of your test code should then call hmt_run_all_tests (and
 | 
			
		||||
  optionally hmt_check_all_coverage) and return the result:
 | 
			
		||||
 | 
			
		||||
    int main() {
 | 
			
		||||
        return hmt_run_all_tests() || hmt_check_all_coverage();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  =============================================================================
 | 
			
		||||
 | 
			
		||||
@@ -40,7 +67,7 @@
 | 
			
		||||
#define HMT_RED   "\033[31m"
 | 
			
		||||
#define HMT_GREEN "\033[32m"
 | 
			
		||||
 | 
			
		||||
#define HMT_INITIAL_ARRAY_SIZE 1024
 | 
			
		||||
#define HMT_ARRAY_SIZE 1024
 | 
			
		||||
 | 
			
		||||
typedef struct hmt_testresult_struct {
 | 
			
		||||
    int count_cases;
 | 
			
		||||
@@ -57,20 +84,137 @@ typedef struct hmt_test_struct {
 | 
			
		||||
typedef struct hmt_category_struct {
 | 
			
		||||
    const char* name;
 | 
			
		||||
    int num_tests;
 | 
			
		||||
    int tests_capacity;
 | 
			
		||||
    hmt_test* tests;
 | 
			
		||||
} hmt_category;
 | 
			
		||||
 | 
			
		||||
int hmt_num_categories = 0;
 | 
			
		||||
int hmt_category_capacity = HMT_INITIAL_ARRAY_SIZE;
 | 
			
		||||
hmt_category* categories = 0;
 | 
			
		||||
typedef struct hmt_covercase_struct {
 | 
			
		||||
    const char* name;
 | 
			
		||||
    int expected_asserts;
 | 
			
		||||
    int actual_asserts;
 | 
			
		||||
    int* asserted_lines;
 | 
			
		||||
} hmt_covercase;
 | 
			
		||||
 | 
			
		||||
hmt_category _hmt_new_category(const char* name);
 | 
			
		||||
hmt_test _hmt_new_test(const char* name, hmt_test_func func);
 | 
			
		||||
hmt_covercase _hmt_new_covercase(const char* name, int expected);
 | 
			
		||||
void _hmt_register_test(const char* category, const char* name, hmt_test_func func);
 | 
			
		||||
void _hmt_register_covercase(const char* name, const char* expected_asserts);
 | 
			
		||||
void _hmt_count_cover(const char* name, int line);
 | 
			
		||||
 | 
			
		||||
#define _HMT_TEST_FUNCNAME(category, name) _hmt_test_ ## category ## _ ## name
 | 
			
		||||
#define _HMT_TEST_FUNCNAME_INIT(category, name) _hmt_test_ ## category ## _ ## name ## _init
 | 
			
		||||
#define _HMT_COVERCASE_FUNCNAME_INIT(name) _hmt_covercase_ ## name ## _init
 | 
			
		||||
 | 
			
		||||
#define HMT_TEST(category, name) \
 | 
			
		||||
void _HMT_TEST_FUNCNAME(category, name)(hmt_testresult* _result); \
 | 
			
		||||
INITIALIZER(_HMT_TEST_FUNCNAME_INIT(category, name)) { \
 | 
			
		||||
    _hmt_register_test(#category, #name, _HMT_TEST_FUNCNAME(category, name)); \
 | 
			
		||||
} \
 | 
			
		||||
void _HMT_TEST_FUNCNAME(category, name)(hmt_testresult* _result)
 | 
			
		||||
 | 
			
		||||
#define _HMT_CASE_START() \
 | 
			
		||||
    _result->count_cases++;
 | 
			
		||||
 | 
			
		||||
#define _HMT_CASE_FAIL() \
 | 
			
		||||
    _result->count_failures++; \
 | 
			
		||||
    printf("\n      - " HMT_RED "[FAIL] (line %d) " HMT_RESET, __LINE__);
 | 
			
		||||
 | 
			
		||||
#define HMT_COVERAGE(name, num_asserts) \
 | 
			
		||||
INITIALIZER(_HMT_COVERCASE_FUNCNAME_INIT(name)) { \
 | 
			
		||||
    _hmt_register_covercase(#name, #num_asserts); \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
#define HMT_ASSERT_COVERED(name) \
 | 
			
		||||
{ \
 | 
			
		||||
    _hmt_count_cover(#name, __LINE__); \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Asserts and expects
 | 
			
		||||
 */
 | 
			
		||||
#define HMT_EXPECT_TRUE(_actual) { \
 | 
			
		||||
    _HMT_CASE_START(); \
 | 
			
		||||
    if (!(_actual)) { \
 | 
			
		||||
        _HMT_CASE_FAIL(); \
 | 
			
		||||
        printf("Expected true but got something false"); \
 | 
			
		||||
    } \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
#define HMT_EXPECT_FALSE(_actual) { \
 | 
			
		||||
    _HMT_CASE_START(); \
 | 
			
		||||
    if (_actual) { \
 | 
			
		||||
        _HMT_CASE_FAIL(); \
 | 
			
		||||
        printf("Expected false but got something true"); \
 | 
			
		||||
    } \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
#define HMT_EXPECT_FLOAT_EQ(_actual, _expected) { \
 | 
			
		||||
    _HMT_CASE_START(); \
 | 
			
		||||
    float actual = (_actual); \
 | 
			
		||||
    float diff = actual - (_expected); \
 | 
			
		||||
    if (diff < -FLT_EPSILON || FLT_EPSILON < diff) { \
 | 
			
		||||
        _HMT_CASE_FAIL(); \
 | 
			
		||||
        printf("Expected %f, got %f", (_expected), actual); \
 | 
			
		||||
    } \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
#define HMT_EXPECT_NEAR(_actual, _expected, _epsilon) { \
 | 
			
		||||
    _HMT_CASE_START(); \
 | 
			
		||||
    float actual = (_actual); \
 | 
			
		||||
    float diff = actual - (_expected); \
 | 
			
		||||
    if (diff < -(_epsilon) || (_epsilon) < diff) { \
 | 
			
		||||
        _HMT_CASE_FAIL(); \
 | 
			
		||||
        printf("Expected %f, got %f", (_expected), actual); \
 | 
			
		||||
    } \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
#define HMT_EXPECT_LT(_actual, _expected) { \
 | 
			
		||||
    _HMT_CASE_START(); \
 | 
			
		||||
    if ((_actual) >= (_expected)) { \
 | 
			
		||||
        _HMT_CASE_FAIL(); \
 | 
			
		||||
        printf("Expected %f to be less than %f", (_actual), (_expected)); \
 | 
			
		||||
    } \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
#define HMT_EXPECT_GT(_actual, _expected) { \
 | 
			
		||||
    _HMT_CASE_START(); \
 | 
			
		||||
    if ((_actual) <= (_expected)) { \
 | 
			
		||||
        _HMT_CASE_FAIL(); \
 | 
			
		||||
        printf("Expected %f to be greater than %f", (_actual), (_expected)); \
 | 
			
		||||
    } \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
#ifndef HMT_SAFE_MACROS
 | 
			
		||||
// Friendly defines
 | 
			
		||||
#define TEST(category, name) HMT_TEST(category, name)
 | 
			
		||||
#define COVERAGE(name, expected_asserts) HMT_COVERAGE(name, expected_asserts)
 | 
			
		||||
#define ASSERT_COVERED(name) HMT_ASSERT_COVERED(name)
 | 
			
		||||
#define EXPECT_TRUE(_actual) HMT_EXPECT_TRUE(_actual)
 | 
			
		||||
#define EXPECT_FALSE(_actual) HMT_EXPECT_FALSE(_actual)
 | 
			
		||||
#define EXPECT_FLOAT_EQ(_actual, _expected) HMT_EXPECT_FLOAT_EQ(_actual, _expected)
 | 
			
		||||
#define EXPECT_NEAR(_actual, _expected, _epsilon) HMT_EXPECT_NEAR(_actual, _expected, _epsilon)
 | 
			
		||||
#define EXPECT_LT(_actual, _expected) HMT_EXPECT_LT(_actual, _expected)
 | 
			
		||||
#define EXPECT_GT(_actual, _expected) HMT_EXPECT_GT(_actual, _expected)
 | 
			
		||||
#endif // HMT_SAFE_MACROS
 | 
			
		||||
 | 
			
		||||
#endif // HANDMADETEST_H
 | 
			
		||||
 | 
			
		||||
#ifdef HANDMADE_TEST_IMPLEMENTATION
 | 
			
		||||
 | 
			
		||||
#ifndef HANDMADE_TEST_IMPLEMENTATION_GUARD
 | 
			
		||||
#define HANDMADE_TEST_IMPLEMENTATION_GUARD
 | 
			
		||||
 | 
			
		||||
int _hmt_num_categories = 0;
 | 
			
		||||
hmt_category* _hmt_categories = 0;
 | 
			
		||||
 | 
			
		||||
int _hmt_num_covercases = 0;
 | 
			
		||||
hmt_covercase* _hmt_covercases = 0;
 | 
			
		||||
 | 
			
		||||
hmt_category _hmt_new_category(const char* name) {
 | 
			
		||||
    hmt_category cat = {
 | 
			
		||||
        .name = name,
 | 
			
		||||
        .num_tests = 0,
 | 
			
		||||
        .tests_capacity = HMT_INITIAL_ARRAY_SIZE,
 | 
			
		||||
        .tests = (hmt_test*) malloc(HMT_INITIAL_ARRAY_SIZE * sizeof(hmt_test))
 | 
			
		||||
        .tests = (hmt_test*) malloc(HMT_ARRAY_SIZE * sizeof(hmt_test))
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return cat;
 | 
			
		||||
@@ -85,52 +229,97 @@ hmt_test _hmt_new_test(const char* name, hmt_test_func func) {
 | 
			
		||||
    return test;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int hmt_register_test(const char* category, const char* name, hmt_test_func func) {
 | 
			
		||||
hmt_covercase _hmt_new_covercase(const char* name, int expected) {
 | 
			
		||||
    hmt_covercase covercase = {
 | 
			
		||||
        .name = name,
 | 
			
		||||
        .expected_asserts = expected,
 | 
			
		||||
        .actual_asserts = 0,
 | 
			
		||||
        .asserted_lines = (int*) malloc(HMT_ARRAY_SIZE * sizeof(int)),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return covercase;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void _hmt_register_test(const char* category, const char* name, hmt_test_func func) {
 | 
			
		||||
    // initialize categories array if not initialized
 | 
			
		||||
    if (!categories) {
 | 
			
		||||
        categories = (hmt_category*) malloc(hmt_category_capacity * sizeof(hmt_category));
 | 
			
		||||
    if (!_hmt_categories) {
 | 
			
		||||
        _hmt_categories = (hmt_category*) malloc(HMT_ARRAY_SIZE * sizeof(hmt_category));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Find the matching category, if possible
 | 
			
		||||
    int cat_index;
 | 
			
		||||
    for (cat_index = 0; cat_index < hmt_num_categories; cat_index++) {
 | 
			
		||||
        if (strcmp(categories[cat_index].name, category) == 0) {
 | 
			
		||||
    for (cat_index = 0; cat_index < _hmt_num_categories; cat_index++) {
 | 
			
		||||
        if (strcmp(_hmt_categories[cat_index].name, category) == 0) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Expand the array of categories if necessary
 | 
			
		||||
    if (cat_index >= hmt_category_capacity) {
 | 
			
		||||
        // TODO: If/when we ever split HandmadeTest off into its own package,
 | 
			
		||||
        // we should start with a smaller initial capacity and dynamically expand.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Add a new category if necessary
 | 
			
		||||
    if (cat_index >= hmt_num_categories) {
 | 
			
		||||
        categories[cat_index] = _hmt_new_category(category);
 | 
			
		||||
        hmt_num_categories++;
 | 
			
		||||
    if (cat_index >= _hmt_num_categories) {
 | 
			
		||||
        _hmt_categories[cat_index] = _hmt_new_category(category);
 | 
			
		||||
        _hmt_num_categories++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hmt_category* cat = &categories[cat_index];
 | 
			
		||||
    hmt_category* cat = &_hmt_categories[cat_index];
 | 
			
		||||
 | 
			
		||||
    // Add the test to the category
 | 
			
		||||
    if (cat->num_tests >= cat->tests_capacity) {
 | 
			
		||||
        // TODO: If/when we ever split HandmadeTest off into its own package,
 | 
			
		||||
        // we should start with a smaller initial capacity and dynamically expand.
 | 
			
		||||
    }
 | 
			
		||||
    cat->tests[cat->num_tests] = _hmt_new_test(name, func);
 | 
			
		||||
    cat->num_tests++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void _hmt_register_covercase(const char* name, const char* expected_asserts) {
 | 
			
		||||
    // initialize cases array if not initialized
 | 
			
		||||
    if (!_hmt_covercases) {
 | 
			
		||||
        _hmt_covercases = (hmt_covercase*) malloc(HMT_ARRAY_SIZE * sizeof(hmt_covercase));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check for existing case with that name, because the macro can run multiple
 | 
			
		||||
    // times in different translation units
 | 
			
		||||
    for (int i = 0; i < _hmt_num_covercases; i++) {
 | 
			
		||||
        if (strcmp(_hmt_covercases[i].name, name) == 0) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _hmt_covercases[_hmt_num_covercases] = _hmt_new_covercase(name, atoi(expected_asserts));
 | 
			
		||||
    _hmt_num_covercases++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
hmt_covercase* _hmt_find_covercase(const char* name) {
 | 
			
		||||
    for (int i = 0; i < _hmt_num_covercases; i++) {
 | 
			
		||||
        if (strcmp(_hmt_covercases[i].name, name) == 0) {
 | 
			
		||||
            return &_hmt_covercases[i];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void _hmt_count_cover(const char* name, int line) {
 | 
			
		||||
    hmt_covercase* covercase = _hmt_find_covercase(name);
 | 
			
		||||
    if (covercase == 0) {
 | 
			
		||||
        printf(HMT_RED "ERROR (line %d): Could not find coverage case with name \"%s\".\n" HMT_RESET, line, name);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // see if this line has already been covered
 | 
			
		||||
    for (int i = 0; i < covercase->actual_asserts; i++) {
 | 
			
		||||
        if (covercase->asserted_lines[i] == line) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    covercase->asserted_lines[covercase->actual_asserts] = line;
 | 
			
		||||
    covercase->actual_asserts++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int hmt_run_all_tests() {
 | 
			
		||||
    int count_alltests = 0;
 | 
			
		||||
    int count_allfailedtests = 0; // failed test cases
 | 
			
		||||
    int count_allfailures = 0; // failed asserts
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < hmt_num_categories; i++) {
 | 
			
		||||
        hmt_category cat = categories[i];
 | 
			
		||||
    for (int i = 0; i < _hmt_num_categories; i++) {
 | 
			
		||||
        hmt_category cat = _hmt_categories[i];
 | 
			
		||||
        int count_catfailedtests = 0;
 | 
			
		||||
        int count_catfailures = 0;
 | 
			
		||||
 | 
			
		||||
@@ -177,87 +366,33 @@ int hmt_run_all_tests() {
 | 
			
		||||
    return (count_allfailedtests > 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define _HMT_TEST_FUNCNAME(category, name) category ## _ ## name
 | 
			
		||||
#define _HMT_TEST_FUNCNAME_INIT(category, name) category ## _ ## name ## _init
 | 
			
		||||
int hmt_check_all_coverage() {
 | 
			
		||||
    printf("Coverage:\n");
 | 
			
		||||
 | 
			
		||||
#define HMT_TEST(category, name) \
 | 
			
		||||
void _HMT_TEST_FUNCNAME(category, name)(hmt_testresult* _result); \
 | 
			
		||||
INITIALIZER(_HMT_TEST_FUNCNAME_INIT(category, name)) { \
 | 
			
		||||
    hmt_register_test(#category, #name, _HMT_TEST_FUNCNAME(category, name)); \
 | 
			
		||||
} \
 | 
			
		||||
void _HMT_TEST_FUNCNAME(category, name)(hmt_testresult* _result)
 | 
			
		||||
    int count_failures = 0;
 | 
			
		||||
 | 
			
		||||
#define _HMT_CASE_START() \
 | 
			
		||||
    _result->count_cases++;
 | 
			
		||||
    for (int i = 0; i < _hmt_num_covercases; i++) {
 | 
			
		||||
        hmt_covercase covercase = _hmt_covercases[i];
 | 
			
		||||
 | 
			
		||||
#define _HMT_CASE_FAIL() \
 | 
			
		||||
    _result->count_failures++; \
 | 
			
		||||
    printf("\n      - " HMT_RED "[FAIL] (%d) " HMT_RESET, __LINE__);
 | 
			
		||||
        if (covercase.expected_asserts != covercase.actual_asserts) {
 | 
			
		||||
            count_failures++;
 | 
			
		||||
            printf("%s: " HMT_RED "FAIL (expected %d asserts, got %d)\n" HMT_RESET, covercase.name, covercase.expected_asserts, covercase.actual_asserts);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Asserts and expects
 | 
			
		||||
 */
 | 
			
		||||
#define HMT_EXPECT_TRUE(_actual) do { \
 | 
			
		||||
    _HMT_CASE_START(); \
 | 
			
		||||
    if (!(_actual)) { \
 | 
			
		||||
        _HMT_CASE_FAIL(); \
 | 
			
		||||
        printf("Expected true but got something false"); \
 | 
			
		||||
    } \
 | 
			
		||||
} while (0)
 | 
			
		||||
    if (count_failures > 0) {
 | 
			
		||||
        printf("\n");
 | 
			
		||||
        printf(HMT_RED);
 | 
			
		||||
    } else {
 | 
			
		||||
        printf(HMT_GREEN);
 | 
			
		||||
    }
 | 
			
		||||
    printf("%d coverage cases tested, %d failures\n", _hmt_num_covercases, count_failures);
 | 
			
		||||
    printf(HMT_RESET);
 | 
			
		||||
 | 
			
		||||
#define HMT_EXPECT_FALSE(_actual) do { \
 | 
			
		||||
    _HMT_CASE_START(); \
 | 
			
		||||
    if (_actual) { \
 | 
			
		||||
        _HMT_CASE_FAIL(); \
 | 
			
		||||
        printf("Expected false but got something true"); \
 | 
			
		||||
    } \
 | 
			
		||||
} while (0)
 | 
			
		||||
    printf("\n");
 | 
			
		||||
 | 
			
		||||
#define HMT_EXPECT_FLOAT_EQ(_actual, _expected) do { \
 | 
			
		||||
    _HMT_CASE_START(); \
 | 
			
		||||
    float actual = (_actual); \
 | 
			
		||||
    float diff = actual - (_expected); \
 | 
			
		||||
    if (diff < -FLT_EPSILON || FLT_EPSILON < diff) { \
 | 
			
		||||
        _HMT_CASE_FAIL(); \
 | 
			
		||||
        printf("Expected %f, got %f", (_expected), actual); \
 | 
			
		||||
    } \
 | 
			
		||||
} while (0)
 | 
			
		||||
    return (count_failures > 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define HMT_EXPECT_NEAR(_actual, _expected, _epsilon) do { \
 | 
			
		||||
    _HMT_CASE_START(); \
 | 
			
		||||
    float actual = (_actual); \
 | 
			
		||||
    float diff = actual - (_expected); \
 | 
			
		||||
    if (diff < -(_epsilon) || (_epsilon) < diff) { \
 | 
			
		||||
        _HMT_CASE_FAIL(); \
 | 
			
		||||
        printf("Expected %f, got %f", (_expected), actual); \
 | 
			
		||||
    } \
 | 
			
		||||
} while (0)
 | 
			
		||||
 | 
			
		||||
#define HMT_EXPECT_LT(_actual, _expected) do { \
 | 
			
		||||
    _HMT_CASE_START(); \
 | 
			
		||||
    if ((_actual) >= (_expected)) { \
 | 
			
		||||
        _HMT_CASE_FAIL(); \
 | 
			
		||||
        printf("Expected %f to be less than %f", (_actual), (_expected)); \
 | 
			
		||||
    } \
 | 
			
		||||
} while (0)
 | 
			
		||||
 | 
			
		||||
#define HMT_EXPECT_GT(_actual, _expected) do { \
 | 
			
		||||
    _HMT_CASE_START(); \
 | 
			
		||||
    if ((_actual) <= (_expected)) { \
 | 
			
		||||
        _HMT_CASE_FAIL(); \
 | 
			
		||||
        printf("Expected %f to be greater than %f", (_actual), (_expected)); \
 | 
			
		||||
    } \
 | 
			
		||||
} while (0)
 | 
			
		||||
 | 
			
		||||
#ifndef HMT_SAFE_MACROS
 | 
			
		||||
// Friendly defines
 | 
			
		||||
#define TEST(category, name) HMT_TEST(category, name)
 | 
			
		||||
#define EXPECT_TRUE(_actual) HMT_EXPECT_TRUE(_actual)
 | 
			
		||||
#define EXPECT_FALSE(_actual) HMT_EXPECT_FALSE(_actual)
 | 
			
		||||
#define EXPECT_FLOAT_EQ(_actual, _expected) HMT_EXPECT_FLOAT_EQ(_actual, _expected)
 | 
			
		||||
#define EXPECT_NEAR(_actual, _expected, _epsilon) HMT_EXPECT_NEAR(_actual, _expected, _epsilon)
 | 
			
		||||
#define EXPECT_LT(_actual, _expected) HMT_EXPECT_LT(_actual, _expected)
 | 
			
		||||
#define EXPECT_GT(_actual, _expected) HMT_EXPECT_GT(_actual, _expected)
 | 
			
		||||
#endif // HMT_SAFE_MACROS
 | 
			
		||||
 | 
			
		||||
#endif // HANDMADETEST_H
 | 
			
		||||
#endif // HANDMADE_TEST_IMPLEMENTATION_GUARD
 | 
			
		||||
#endif // HANDMADE_TEST_IMPLEMENTATION
 | 
			
		||||
 
 | 
			
		||||
@@ -2,18 +2,17 @@ BUILD_DIR=./build
 | 
			
		||||
 | 
			
		||||
CXXFLAGS+=-g -Wall -Wextra -pthread -Wno-missing-braces -Wno-missing-field-initializers
 | 
			
		||||
 | 
			
		||||
all: build_all
 | 
			
		||||
	$(BUILD_DIR)/hmm_test_c
 | 
			
		||||
	$(BUILD_DIR)/hmm_test_c_no_sse
 | 
			
		||||
	$(BUILD_DIR)/hmm_test_cpp
 | 
			
		||||
	$(BUILD_DIR)/hmm_test_cpp_no_sse
 | 
			
		||||
all: c c_no_sse cpp cpp_no_sse build_c_without_coverage build_cpp_without_coverage
 | 
			
		||||
 | 
			
		||||
build_all: c c_no_sse cpp cpp_no_sse
 | 
			
		||||
build_all: build_c build_c_no_sse build_cpp build_cpp_no_sse
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm -rf $(BUILD_DIR)
 | 
			
		||||
 | 
			
		||||
c: HandmadeMath.c test_impl
 | 
			
		||||
c: build_c
 | 
			
		||||
	$(BUILD_DIR)/hmm_test_c
 | 
			
		||||
 | 
			
		||||
build_c: HandmadeMath.c test_impl
 | 
			
		||||
	@echo "\nCompiling in C mode"
 | 
			
		||||
	mkdir -p $(BUILD_DIR)
 | 
			
		||||
	cd $(BUILD_DIR)\
 | 
			
		||||
@@ -22,7 +21,10 @@ c: HandmadeMath.c test_impl
 | 
			
		||||
			-lm \
 | 
			
		||||
		&& $(CC) -ohmm_test_c HandmadeMath.o hmm_test.o -lm
 | 
			
		||||
 | 
			
		||||
c_no_sse: HandmadeMath.c test_impl
 | 
			
		||||
c_no_sse: build_c_no_sse
 | 
			
		||||
	$(BUILD_DIR)/hmm_test_c_no_sse
 | 
			
		||||
 | 
			
		||||
build_c_no_sse: HandmadeMath.c test_impl
 | 
			
		||||
	@echo "\nCompiling in C mode (no SSE)"
 | 
			
		||||
	mkdir -p $(BUILD_DIR)
 | 
			
		||||
	cd $(BUILD_DIR) \
 | 
			
		||||
@@ -32,7 +34,10 @@ c_no_sse: HandmadeMath.c test_impl
 | 
			
		||||
			-lm \
 | 
			
		||||
		&& $(CC) -ohmm_test_c_no_sse HandmadeMath.o hmm_test.o -lm
 | 
			
		||||
 | 
			
		||||
cpp: HandmadeMath.cpp test_impl
 | 
			
		||||
cpp: build_cpp
 | 
			
		||||
	$(BUILD_DIR)/hmm_test_cpp
 | 
			
		||||
 | 
			
		||||
build_cpp: HandmadeMath.cpp test_impl
 | 
			
		||||
	@echo "\nCompiling in C++ mode"
 | 
			
		||||
	mkdir -p $(BUILD_DIR)
 | 
			
		||||
	cd $(BUILD_DIR) \
 | 
			
		||||
@@ -40,7 +45,10 @@ cpp: HandmadeMath.cpp test_impl
 | 
			
		||||
			-DHANDMADE_MATH_CPP_MODE \
 | 
			
		||||
			../HandmadeMath.cpp ../hmm_test.cpp
 | 
			
		||||
 | 
			
		||||
cpp_no_sse: HandmadeMath.cpp test_impl
 | 
			
		||||
cpp_no_sse: build_cpp_no_sse
 | 
			
		||||
	$(BUILD_DIR)/hmm_test_cpp_no_sse
 | 
			
		||||
 | 
			
		||||
build_cpp_no_sse: HandmadeMath.cpp test_impl
 | 
			
		||||
	@echo "\nCompiling in C++ mode (no SSE)"
 | 
			
		||||
	mkdir -p $(BUILD_DIR)
 | 
			
		||||
	cd $(BUILD_DIR) \
 | 
			
		||||
@@ -49,3 +57,21 @@ cpp_no_sse: HandmadeMath.cpp test_impl
 | 
			
		||||
			../HandmadeMath.cpp ../hmm_test.cpp
 | 
			
		||||
 | 
			
		||||
test_impl: hmm_test.cpp hmm_test.c
 | 
			
		||||
 | 
			
		||||
build_c_without_coverage: HandmadeMath.c
 | 
			
		||||
	@echo "\nCompiling in C mode"
 | 
			
		||||
	mkdir -p $(BUILD_DIR)
 | 
			
		||||
	cd $(BUILD_DIR)\
 | 
			
		||||
		&& $(CC) $(CPPFLAGS) $(CXXFLAGS) -std=c99 \
 | 
			
		||||
			-DWITHOUT_COVERAGE \
 | 
			
		||||
			-c ../HandmadeMath.c ../hmm_test.c \
 | 
			
		||||
			-lm \
 | 
			
		||||
		&& $(CC) -ohmm_test_c HandmadeMath.o hmm_test.o -lm
 | 
			
		||||
 | 
			
		||||
build_cpp_without_coverage: HandmadeMath.cpp test_impl
 | 
			
		||||
	@echo "\nCompiling in C++ mode (no SSE)"
 | 
			
		||||
	mkdir -p $(BUILD_DIR)
 | 
			
		||||
	cd $(BUILD_DIR) \
 | 
			
		||||
		&& $(CXX) $(CPPFLAGS) $(CXXFLAGS) -ohmm_test_cpp_no_sse \
 | 
			
		||||
			-DHANDMADE_MATH_CPP_MODE -DWITHOUT_COVERAGE \
 | 
			
		||||
			../HandmadeMath.cpp ../hmm_test.cpp
 | 
			
		||||
 
 | 
			
		||||
@@ -4,8 +4,13 @@ You can compile and run the tests yourself by running:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
make
 | 
			
		||||
build/hmm_test_c
 | 
			
		||||
build/hmm_test_c_no_sse
 | 
			
		||||
build/hmm_test_cpp
 | 
			
		||||
build/hmm_test_cpp_no_sse
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
To run a specific test configuration, run one of:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
make c
 | 
			
		||||
make c_no_sse
 | 
			
		||||
make cpp
 | 
			
		||||
make cpp_no_sse
 | 
			
		||||
```
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,9 @@ TEST(Equality, Vec2)
 | 
			
		||||
 | 
			
		||||
    EXPECT_TRUE(a == b);
 | 
			
		||||
    EXPECT_FALSE(a == c);
 | 
			
		||||
 | 
			
		||||
    EXPECT_FALSE(a != b);
 | 
			
		||||
    EXPECT_TRUE(a != c);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -33,6 +36,9 @@ TEST(Equality, Vec3)
 | 
			
		||||
 | 
			
		||||
    EXPECT_TRUE(a == b);
 | 
			
		||||
    EXPECT_FALSE(a == c);
 | 
			
		||||
 | 
			
		||||
    EXPECT_FALSE(a != b);
 | 
			
		||||
    EXPECT_TRUE(a != c);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -51,5 +57,8 @@ TEST(Equality, Vec4)
 | 
			
		||||
 | 
			
		||||
    EXPECT_TRUE(a == b);
 | 
			
		||||
    EXPECT_FALSE(a == c);
 | 
			
		||||
 | 
			
		||||
    EXPECT_FALSE(a != b);
 | 
			
		||||
    EXPECT_TRUE(a != c);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ TEST(Initialization, Vectors)
 | 
			
		||||
    // Test vec2
 | 
			
		||||
    //
 | 
			
		||||
    hmm_vec2 v2 = HMM_Vec2(1.0f, 2.0f);
 | 
			
		||||
    hmm_vec2 v2i = HMM_Vec2(1, 2);
 | 
			
		||||
    hmm_vec2 v2i = HMM_Vec2i(1, 2);
 | 
			
		||||
 | 
			
		||||
    EXPECT_FLOAT_EQ(v2.X, 1.0f);
 | 
			
		||||
    EXPECT_FLOAT_EQ(v2.Y, 2.0f);
 | 
			
		||||
 
 | 
			
		||||
@@ -151,16 +151,16 @@ TEST(QuaternionOps, Mat4ToQuat)
 | 
			
		||||
        EXPECT_NEAR(result.W, cosf, abs_error);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Rotate 135 degrees on the Y axis (this hits case 4)
 | 
			
		||||
    // Rotate 45 degrees on the X axis (this hits case 4)
 | 
			
		||||
    {
 | 
			
		||||
        hmm_mat4 m = HMM_Rotate(135, HMM_Vec3(0, 1, 0));
 | 
			
		||||
        hmm_mat4 m = HMM_Rotate(45, HMM_Vec3(1, 0, 0));
 | 
			
		||||
        hmm_quaternion result = HMM_Mat4ToQuaternion(m);
 | 
			
		||||
 | 
			
		||||
        float cosf = 0.3826834324f; // cos(135/2 degrees)
 | 
			
		||||
        float sinf = 0.9238795325f; // sin(135/2 degrees)
 | 
			
		||||
        float cosf = 0.9238795325f; // cos(90/2 degrees)
 | 
			
		||||
        float sinf = 0.3826834324f; // sin(90/2 degrees)
 | 
			
		||||
 | 
			
		||||
        EXPECT_NEAR(result.X, 0.0f, abs_error);
 | 
			
		||||
        EXPECT_NEAR(result.Y, sinf, abs_error);
 | 
			
		||||
        EXPECT_NEAR(result.X, sinf, abs_error);
 | 
			
		||||
        EXPECT_NEAR(result.Y, 0.0f, abs_error);
 | 
			
		||||
        EXPECT_NEAR(result.Z, 0.0f, abs_error);
 | 
			
		||||
        EXPECT_NEAR(result.W, cosf, abs_error);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,14 @@ TEST(ScalarMath, Trigonometry)
 | 
			
		||||
    EXPECT_NEAR(HMM_TanF(HMM_PI32), 0.0f, trigAbsError);
 | 
			
		||||
    EXPECT_NEAR(HMM_TanF(-HMM_PI32 / 4), -1.0f, trigAbsError);
 | 
			
		||||
 | 
			
		||||
    EXPECT_NEAR(HMM_ATanF(0.0f), 0.0f, trigAbsError);
 | 
			
		||||
    EXPECT_NEAR(HMM_ATanF(HMM_PI32), 1.2626272557f, trigAbsError);
 | 
			
		||||
    EXPECT_NEAR(HMM_ATanF(-HMM_PI32), -1.2626272557f, trigAbsError);
 | 
			
		||||
 | 
			
		||||
    EXPECT_NEAR(HMM_ATan2F(0.0f, 1.0f), 0.0f, trigAbsError);
 | 
			
		||||
    EXPECT_NEAR(HMM_ATan2F(1.0f, 1.0f), HMM_PI32 / 4.0f, trigAbsError);
 | 
			
		||||
    EXPECT_NEAR(HMM_ATan2F(1.0f, 0.0f), HMM_PI32 / 2.0f, trigAbsError);
 | 
			
		||||
 | 
			
		||||
    // This isn't the most rigorous because we're really just sanity-
 | 
			
		||||
    // checking that things work by default.
 | 
			
		||||
}
 | 
			
		||||
@@ -35,6 +43,18 @@ TEST(ScalarMath, ToRadians)
 | 
			
		||||
    EXPECT_FLOAT_EQ(HMM_ToRadians(-180.0f), -HMM_PI32);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(ScalarMath, ExpF)
 | 
			
		||||
{
 | 
			
		||||
    EXPECT_NEAR(HMM_ExpF(0.0f), 1.0f, 0.0001f);
 | 
			
		||||
    EXPECT_NEAR(HMM_ExpF(1.0f), 2.7182818285f, 0.0001f);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(ScalarMath, LogF)
 | 
			
		||||
{
 | 
			
		||||
    EXPECT_NEAR(HMM_LogF(1.0f), 0.0f, 0.0001f);
 | 
			
		||||
    EXPECT_NEAR(HMM_LogF(2.7182818285f), 1.0f, 0.0001f);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(ScalarMath, SquareRoot)
 | 
			
		||||
{
 | 
			
		||||
    EXPECT_FLOAT_EQ(HMM_SquareRootF(16.0f), 4.0f);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,9 @@
 | 
			
		||||
#include "HandmadeTest.h"
 | 
			
		||||
#include "hmm_test.h"
 | 
			
		||||
 | 
			
		||||
int main()
 | 
			
		||||
{
 | 
			
		||||
    return hmt_run_all_tests();
 | 
			
		||||
    int tests_failed = hmt_run_all_tests();
 | 
			
		||||
    int coverage_failed = hmt_check_all_coverage();
 | 
			
		||||
 | 
			
		||||
    return tests_failed || coverage_failed;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
#include <float.h>
 | 
			
		||||
 | 
			
		||||
#define HANDMADE_TEST_IMPLEMENTATION
 | 
			
		||||
#include "HandmadeTest.h"
 | 
			
		||||
 | 
			
		||||
#include "../HandmadeMath.h"
 | 
			
		||||
 | 
			
		||||
#include "categories/ScalarMath.h"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user