diff options
| author | Tyge Løvset <[email protected]> | 2023-01-21 17:00:05 +0100 |
|---|---|---|
| committer | Tyge Løvset <[email protected]> | 2023-01-21 17:00:05 +0100 |
| commit | 798f3a637818147eaf231f438c7b104387de70e5 (patch) | |
| tree | 36beb7177621d2605a27d048b931fad4a8b62d5d /misc/tests | |
| parent | e97d06f95a65edd4fdee53555398f7b636a72ab5 (diff) | |
| download | STC-modified-798f3a637818147eaf231f438c7b104387de70e5.tar.gz STC-modified-798f3a637818147eaf231f438c7b104387de70e5.zip | |
Added modified ctest from https://github.com/bvdberg/ctest, and used on cregex_test.c. Will modify furter to make more like GoogleTest
Diffstat (limited to 'misc/tests')
| -rw-r--r-- | misc/tests/cregex_test.c | 267 | ||||
| -rw-r--r-- | misc/tests/ctest.h | 622 | ||||
| -rw-r--r-- | misc/tests/main.c | 31 |
3 files changed, 774 insertions, 146 deletions
diff --git a/misc/tests/cregex_test.c b/misc/tests/cregex_test.c index cd812aa2..8ac268f1 100644 --- a/misc/tests/cregex_test.c +++ b/misc/tests/cregex_test.c @@ -1,260 +1,235 @@ -#if 0 -#include <stdio.h> -#include <stdlib.h> -#include <assert.h> -#include <stc/csview.h> -#include <stc/stctest.h> +#define i_extern #include <stc/cregex.h> +#include <stc/csview.h> +#include "ctest.h" -#define START_TEST(f) void f(void) -#define END_TEST +#define M_START(m) ((m).str - inp) +#define M_END(m) (M_START(m) + (m).size) -#define M_START(re, m) ((m).str - (re).input) -#define M_END(re, m) (M_START(re, m) + (m).size) -START_TEST(compile_match_char) +CTEST(cregex, compile_match_char) { + const char* inp; cregex re = cregex_from("äsdf", 0); - EXPECT_EQ(re.error, 0); + ASSERT_EQ(re.error, 0); csview match; - EXPECT_TRUE(cregex_find("äsdf", &re, &match, 0)); - EXPECT_EQ(M_START(re, match), 0); - EXPECT_EQ(M_END(re, match), 5); // ä is two bytes wide + ASSERT_EQ(cregex_find(&re, inp="äsdf", &match, CREG_M_FULLMATCH), CREG_OK); + ASSERT_EQ(M_START(match), 0); + ASSERT_EQ(M_END(match), 5); // ä is two bytes wide - EXPECT_EQ(cregex_find("zäsdf", &re, &match, 0), 1); - EXPECT_EQ(M_START(re, match), 1); - EXPECT_EQ(M_END(re, match), 6); + ASSERT_EQ(cregex_find(&re, inp="zäsdf", &match, 0), CREG_OK); + ASSERT_EQ(M_START(match), 1); + ASSERT_EQ(M_END(match), 6); cregex_drop(&re); } -END_TEST -START_TEST(compile_match_anchors) +CTEST(cregex, compile_match_anchors) { - cregex re = cregex_from("^äs.f$", 0); - EXPECT_EQ(re.error, 0); + const char* inp; + cregex re = cregex_from(inp="^äs.f$", 0); + ASSERT_EQ(re.error, 0); csview match; - EXPECT_TRUE(cregex_find("äsdf", &re, &match, 0)); - EXPECT_EQ(M_START(re, match), 0); - EXPECT_EQ(M_END(re, match), 5); + ASSERT_EQ(cregex_find(&re, inp="äsdf", &match, 0), CREG_OK); + ASSERT_EQ(M_START(match), 0); + ASSERT_EQ(M_END(match), 5); - EXPECT_TRUE(cregex_is_match("äs♥f", &re)); - EXPECT_TRUE(cregex_is_match("äsöf", &re)); + ASSERT_TRUE(cregex_is_match(&re, "äs♥f")); + ASSERT_TRUE(cregex_is_match(&re, "äsöf")); - cregex_drop(re); + cregex_drop(&re); } -END_TEST -START_TEST(compile_match_quantifiers) +CTEST(cregex, compile_match_quantifiers1) { + const char* inp; c_AUTO (cregex, re) { re = cregex_from("ä+", 0); - EXPECT_EQ(re.error, 0); + ASSERT_EQ(re.error, 0); csview match; - EXPECT_EQ(cregex_find("ääb", &re, &match, 0), 1); - EXPECT_EQ(M_START(re, match), 0); - EXPECT_EQ(M_END(re, match), 4); + ASSERT_EQ(cregex_find(&re, inp="ääb", &match, 0), CREG_OK); + ASSERT_EQ(M_START(match), 0); + ASSERT_EQ(M_END(match), 4); - EXPECT_EQ(cregex_find("bäbb", &re, &match, 0), 1); - EXPECT_EQ(M_START(re, match), 1); - EXPECT_EQ(M_END(re, match), 3); + ASSERT_EQ(cregex_find(&re, inp="bäbb", &match, 0), CREG_OK); + ASSERT_EQ(M_START(match), 1); + ASSERT_EQ(M_END(match), 3); - EXPECT_FALSE(cregex_find("bbb", &re, &match, 0)); + ASSERT_EQ(cregex_find(&re, "bbb", &match, 0), CREG_NOMATCH); } +} + +CTEST(cregex, compile_match_quantifiers2) +{ + const char* inp; c_AUTO (cregex, re) { re = cregex_from("bä*", 0); - EXPECT_EQ(re.error, 0); + ASSERT_EQ(re.error, 0); csview match; - EXPECT_EQ(cregex_find("bääb", &re, &match, 0), 1); - EXPECT_EQ(M_START(re, match), 0); - EXPECT_EQ(M_END(re, match), 5); + ASSERT_EQ(cregex_find(&re, inp="bääb", &match, 0), CREG_OK); + ASSERT_EQ(M_START(match), 0); + ASSERT_EQ(M_END(match), 5); - EXPECT_EQ(cregex_find("bäbb", &re, &match, 0), 1); - EXPECT_EQ(M_START(re, match), 0); - EXPECT_EQ(M_END(re, match), 3); + ASSERT_EQ(cregex_find(&re, inp="bäbb", &match, 0), CREG_OK); + ASSERT_EQ(M_START(match), 0); + ASSERT_EQ(M_END(match), 3); - EXPECT_EQ(cregex_find("bbb", &re, &match, 0), 1); - EXPECT_EQ(M_START(re, match), 0); - EXPECT_EQ(M_END(re, match), 1); + ASSERT_EQ(cregex_find(&re, inp="bbb", &match, 0), CREG_OK); + ASSERT_EQ(M_START(match), 0); + ASSERT_EQ(M_END(match), 1); } } -END_TEST -START_TEST(compile_match_escaped_chars) +CTEST(cregex, compile_match_escaped_chars) { cregex re = cregex_from("\\n\\r\\t\\{", 0); - EXPECT_EQ(re.error, 0); + ASSERT_EQ(re.error, 0); csview match; - EXPECT_EQ(cregex_find("\n\r\t{", &re, &match, 0), 1); - EXPECT_EQ(cregex_find("\n\r\t", &re, &match, 0), 0); + ASSERT_EQ(cregex_find(&re, "\n\r\t{", &match, 0), CREG_OK); + ASSERT_EQ(cregex_find(&re, "\n\r\t", &match, 0), CREG_NOMATCH); cregex_drop(&re); } -END_TEST -START_TEST(compile_match_class_simple) +CTEST(cregex, compile_match_class_simple) { c_AUTO (cregex, re1, re2, re3) { re1 = cregex_from("\\s", 0); - EXPECT_EQ(re1.error, 0); + ASSERT_EQ(re1.error, 0); re2 = cregex_from("\\w", 0); - EXPECT_EQ(re2.error, 0); + ASSERT_EQ(re2.error, 0); re3 = cregex_from("\\D", 0); - EXPECT_EQ(re3.error, 0); + ASSERT_EQ(re3.error, 0); csview match; - EXPECT_EQ(cregex_find(" ", &re1, &match, 0), 1); - EXPECT_EQ(cregex_find("\r", &re1, &match, 0), 1); - EXPECT_EQ(cregex_find("\n", &re1, &match, 0), 1); + ASSERT_EQ(cregex_find(&re1, " " , &match, 0), CREG_OK); + ASSERT_EQ(cregex_find(&re1, "\r", &match, 0), CREG_OK); + ASSERT_EQ(cregex_find(&re1, "\n", &match, 0), CREG_OK); - EXPECT_EQ(cregex_find("a", &re2, &match, 0), 1); - EXPECT_EQ(cregex_find("0", &re2, &match, 0), 1); - EXPECT_EQ(cregex_find("_", &re2, &match, 0), 1); + ASSERT_EQ(cregex_find(&re2, "a", &match, 0), CREG_OK); + ASSERT_EQ(cregex_find(&re2, "0", &match, 0), CREG_OK); + ASSERT_EQ(cregex_find(&re2, "_", &match, 0), CREG_OK); - EXPECT_EQ( cregex_find("k", &re3, &match, 0), 1); - EXPECT_EQ(cregex_find("0", &re3, &match, 0), 0); + ASSERT_EQ(cregex_find(&re3, "k", &match, 0), CREG_OK); + ASSERT_EQ(cregex_find(&re3, "0", &match, 0), CREG_NOMATCH); } } -END_TEST -START_TEST(compile_match_or) +CTEST(cregex, compile_match_or) { c_AUTO (cregex, re, re2) { re = cregex_from("as|df", 0); - EXPECT_EQ(re.error, 0); + ASSERT_EQ(re.error, 0); - csview match; - EXPECT_EQ(cregex_find("as", &re, &match, 0), 1); - EXPECT_EQ(cregex_find("df", &re, &match, 0), 1); + csview match[4]; + ASSERT_EQ(cregex_find(&re, "as", match, 0), CREG_OK); + ASSERT_EQ(cregex_find(&re, "df", match, 0), CREG_OK); re2 = cregex_from("(as|df)", 0); - EXPECT_EQ(re2.error, 0); + ASSERT_EQ(re2.error, 0); - EXPECT_EQ(cregex_find(&re2, "as", &re, &match, 0), 1); - EXPECT_EQ(cregex_find(&re2, "df", &re, &match, 0), 1); + ASSERT_EQ(cregex_find(&re2, "as", match, 0), CREG_OK); + ASSERT_EQ(cregex_find(&re2, "df", match, 0), CREG_OK); } } -END_TEST -START_TEST(compile_match_class_complex_0) +CTEST(cregex, compile_match_class_complex_0) { cregex re = cregex_from("[asdf]", 0); - EXPECT_EQ(re.error, 0); + ASSERT_EQ(re.error, 0); csview match; - EXPECT_EQ(cregex_find("a", &re, &match, 0), 1); - EXPECT_EQ(cregex_find("s", &re, &match, 0), 1); - EXPECT_EQ(cregex_find("d", &re, &match, 0), 1); - EXPECT_EQ(cregex_find("f", &re, &match, 0), 1); + ASSERT_EQ(cregex_find(&re, "a", &match, 0), CREG_OK); + ASSERT_EQ(cregex_find(&re, "s", &match, 0), CREG_OK); + ASSERT_EQ(cregex_find(&re, "d", &match, 0), CREG_OK); + ASSERT_EQ(cregex_find(&re, "f", &match, 0), CREG_OK); cregex_drop(&re); } -END_TEST -START_TEST(compile_match_class_complex_1) +CTEST(cregex, compile_match_class_complex_1) { cregex re = cregex_from("[a-zä0-9öA-Z]", 0); - EXPECT_EQ(re.error, 0); + ASSERT_EQ(re.error, 0); csview match; - EXPECT_EQ(cregex_find("a", &re, &match, 0), 1); - EXPECT_EQ(cregex_find("5", &re, &match, 0), 1); - EXPECT_EQ(cregex_find("A", &re, &match, 0), 1); - EXPECT_EQ(cregex_find("ä", &re, &match, 0), 1); - EXPECT_EQ(cregex_find("ö", &re, &match, 0), 1); + ASSERT_EQ(cregex_find(&re, "a", &match, 0), CREG_OK); + ASSERT_EQ(cregex_find(&re, "5", &match, 0), CREG_OK); + ASSERT_EQ(cregex_find(&re, "A", &match, 0), CREG_OK); + ASSERT_EQ(cregex_find(&re, "ä", &match, 0), CREG_OK); + ASSERT_EQ(cregex_find(&re, "ö", &match, 0), CREG_OK); cregex_drop(&re); } -END_TEST -START_TEST(compile_match_cap) +CTEST(cregex, compile_match_cap) { cregex re = cregex_from("(abc)d", 0); - EXPECT_EQ(re.error, 0); + ASSERT_EQ(re.error, 0); - csview match; - EXPECT_EQ(cregex_find("abcd", &re, &match, 0)), 1; - EXPECT_EQ(cregex_find("llljabcdkk", &re, &match, 0), 1); - EXPECT_EQ(cregex_find("abc", &re, &match, 0), 0); + csview match[4]; + ASSERT_EQ(cregex_find(&re, "abcd", match, 0), CREG_OK); + ASSERT_EQ(cregex_find(&re, "llljabcdkk", match, 0), CREG_OK); + ASSERT_EQ(cregex_find(&re, "abc", match, 0), CREG_NOMATCH); cregex_drop(&re); } -END_TEST -START_TEST(search_all) +CTEST(cregex, search_all) { + const char* inp; c_AUTO (cregex, re) { re = cregex_from("ab", 0); csview m = {0}; int res; - - res = cregex_find("ab,ab,ab", &m, CREG_M_NEXT); - EXPECT_TRUE(res==1 && M_START(re, m) == 0); - res = cregex_find("ab,ab,ab", &m, CREG_M_NEXT); - EXPECT_TRUE(res==1 && M_START(re, m) == 3); - res = cregex_find("ab,ab,ab", &m, CREG_M_NEXT); - EXPECT_TRUE(res==1 && M_START(re, m) == 6); - res = cregex_find("ab,ab,ab", &m, CREG_M_NEXT); - EXPECT_NE(res, 1); + ASSERT_EQ(re.error, CREG_OK); + inp="ab,ab,ab"; + res = cregex_find(&re, inp, &m, CREG_M_NEXT); + ASSERT_EQ(M_START(m), 0); + res = cregex_find(&re, inp, &m, CREG_M_NEXT); + ASSERT_EQ(res, CREG_OK); + ASSERT_EQ(M_START(m), 3); + res = cregex_find(&re, inp, &m, CREG_M_NEXT); + ASSERT_EQ(M_START(m), 6); + res = cregex_find(&re, inp, &m, CREG_M_NEXT); + ASSERT_NE(res, CREG_OK); } } -END_TEST -START_TEST(captures_len) +CTEST(cregex, captures_len) { c_AUTO (cregex, re) { cregex re = cregex_from("(ab(cd))(ef)", 0); - EXPECT_EQ(cregex_captures(&re), 3); + ASSERT_EQ(cregex_captures(&re), 4); } } -END_TEST -START_TEST(captures_cap) +CTEST(cregex, captures_cap) { + const char* inp; c_AUTO (cregex, re) { re = cregex_from("(ab)((cd)+)", 0); - EXPECT_EQ(cregex_captures(&re), 3); + ASSERT_EQ(cregex_captures(&re), 4); csview cap[5]; - EXPECT_EQ(cregex_find("xxabcdcde", &re, cap, 0), 1); - EXPECT_TRUE(csview_equals(cap.match[0], c_SV("abcdcd"))); - /* - EXPECT_EQ(cap0.end, 8); - EXPECT_EQ(cap1.start, 2); - EXPECT_EQ(cap1.end, 4); - EXPECT_EQ(cap2.start, 4); - EXPECT_EQ(cap2.end, 8); - */ - EXPECT_FALSE(cregex_is_match("abcdcde", &re)); - EXPECT_TRUE(cregex_is_match("abcdcdcd", &re)); + ASSERT_EQ(cregex_find(&re, inp="xxabcdcde", cap, 0), CREG_OK); + ASSERT_TRUE(csview_equals(cap[0], "abcdcd")); + + ASSERT_EQ(M_END(cap[0]), 8); + ASSERT_EQ(M_START(cap[1]), 2); + ASSERT_EQ(M_END(cap[1]), 4); + ASSERT_EQ(M_START(cap[2]), 4); + ASSERT_EQ(M_END(cap[2]), 8); + + ASSERT_TRUE(cregex_is_match(&re, "abcdcde")); + ASSERT_TRUE(cregex_is_match(&re, "abcdcdcd")); } } -END_TEST - - -int main() -{ - RUN_TEST(compile_match_char); - RUN_TEST(compile_match_anchors); - RUN_TEST(compile_match_quantifiers); - RUN_TEST(compile_match_escaped_chars); - RUN_TEST(compile_match_class_simple); - RUN_TEST(compile_match_class_complex_0); - RUN_TEST(compile_match_class_complex_1); - RUN_TEST(compile_match_cap); - RUN_TEST(search_all); - RUN_TEST(captures_len); - RUN_TEST(captures_cap); - RUN_TEST(compile_match_or); - return REPORT_TESTS(); -} -#endif - diff --git a/misc/tests/ctest.h b/misc/tests/ctest.h new file mode 100644 index 00000000..13344428 --- /dev/null +++ b/misc/tests/ctest.h @@ -0,0 +1,622 @@ +/* Copyright 2011-2023 Bas van den Berg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CTEST_H +#define CTEST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __GNUC__ +#define CTEST_IMPL_FORMAT_PRINTF(a, b) __attribute__ ((format(printf, a, b))) +#else +#define CTEST_IMPL_FORMAT_PRINTF(a, b) +#endif + +#include <inttypes.h> /* intmax_t, uintmax_t, PRI* */ +#include <stdbool.h> /* bool, true, false */ +#include <stddef.h> /* size_t */ + +typedef void (*ctest_nullary_run_func)(void); +typedef void (*ctest_unary_run_func)(void*); +typedef void (*ctest_setup_func)(void*); +typedef void (*ctest_teardown_func)(void*); + +union ctest_run_func_union { + ctest_nullary_run_func nullary; + ctest_unary_run_func unary; +}; + +#define CTEST_IMPL_PRAGMA(x) _Pragma (#x) +#define CTEST_CONTAINER_OF(p, T, m) \ + ((T*)((char*)(p) + 0*sizeof((p) == &((T*)0)->m) - offsetof(T, m))) + +#if defined(__GNUC__) +#if defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +/* the GCC argument will work for both gcc and clang */ +#define CTEST_IMPL_DIAG_PUSH_IGNORED(w) \ + CTEST_IMPL_PRAGMA(GCC diagnostic push) \ + CTEST_IMPL_PRAGMA(GCC diagnostic ignored "-W" #w) +#define CTEST_IMPL_DIAG_POP() \ + CTEST_IMPL_PRAGMA(GCC diagnostic pop) +#else +/* the push/pop functionality wasn't in gcc until 4.6, fallback to "ignored" */ +#define CTEST_IMPL_DIAG_PUSH_IGNORED(w) \ + CTEST_IMPL_PRAGMA(GCC diagnostic ignored "-W" #w) +#define CTEST_IMPL_DIAG_POP() +#endif +#else +/* leave them out entirely for non-GNUC compilers */ +#define CTEST_IMPL_DIAG_PUSH_IGNORED(w) +#define CTEST_IMPL_DIAG_POP() +#endif + +struct ctest { + uint32_t magic0, padding; + + const char* ssname; // suite name + const char* ttname; // test name + union ctest_run_func_union run; + + void* data; + ctest_setup_func* setup; + ctest_teardown_func* teardown; + + int32_t skip; + uint32_t magic1; +}; + +#define CTEST_IMPL_NAME(name) ctest_##name +#define CTEST_IMPL_FNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_run) +#define CTEST_IMPL_TNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname) +#define CTEST_IMPL_DATA_SNAME(sname) CTEST_IMPL_NAME(sname##_data) +#define CTEST_IMPL_DATA_TNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_data) +#define CTEST_IMPL_SETUP_FNAME(sname) CTEST_IMPL_NAME(sname##_setup) +#define CTEST_IMPL_SETUP_FPNAME(sname) CTEST_IMPL_NAME(sname##_setup_ptr) +#define CTEST_IMPL_SETUP_TPNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_setup_ptr) +#define CTEST_IMPL_TEARDOWN_FNAME(sname) CTEST_IMPL_NAME(sname##_teardown) +#define CTEST_IMPL_TEARDOWN_FPNAME(sname) CTEST_IMPL_NAME(sname##_teardown_ptr) +#define CTEST_IMPL_TEARDOWN_TPNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_teardown_ptr) + +#ifdef __APPLE__ +#define CTEST_IMPL_SECTION __attribute__ ((used, section ("__DATA, .ctest"), aligned(1))) +#else +#define CTEST_IMPL_SECTION __attribute__ ((used, section (".ctest"), aligned(1))) +#endif + +#define CTEST_IMPL_STRUCT(sname, tname, tskip, tdata, tsetup, tteardown) \ + static struct ctest CTEST_IMPL_TNAME(sname, tname) CTEST_IMPL_SECTION = { \ + 0xBADCAFE0, 0, \ + #sname, \ + #tname, \ + { (ctest_nullary_run_func) CTEST_IMPL_FNAME(sname, tname) }, \ + tdata, \ + (ctest_setup_func*) tsetup, \ + (ctest_teardown_func*) tteardown, \ + tskip, \ + 0xBADCAFE1, \ + } + +#ifdef __cplusplus + +#define CTEST_SETUP(sname) \ + template <> void CTEST_IMPL_SETUP_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* data) + +#define CTEST_TEARDOWN(sname) \ + template <> void CTEST_IMPL_TEARDOWN_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* data) + +#define CTEST_DATA(sname) \ + template <typename T> void CTEST_IMPL_SETUP_FNAME(sname)(T* data) { } \ + template <typename T> void CTEST_IMPL_TEARDOWN_FNAME(sname)(T* data) { } \ + struct CTEST_IMPL_DATA_SNAME(sname) + +#define CTEST_IMPL_CTEST(sname, tname, tskip) \ + static void CTEST_IMPL_FNAME(sname, tname)(void); \ + CTEST_IMPL_STRUCT(sname, tname, tskip, NULL, NULL, NULL); \ + static void CTEST_IMPL_FNAME(sname, tname)(void) + +#define CTEST_IMPL_CTEST2(sname, tname, tskip) \ + static struct CTEST_IMPL_DATA_SNAME(sname) CTEST_IMPL_DATA_TNAME(sname, tname); \ + static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* data); \ + static void (*CTEST_IMPL_SETUP_TPNAME(sname, tname))(struct CTEST_IMPL_DATA_SNAME(sname)*) = &CTEST_IMPL_SETUP_FNAME(sname)<struct CTEST_IMPL_DATA_SNAME(sname)>; \ + static void (*CTEST_IMPL_TEARDOWN_TPNAME(sname, tname))(struct CTEST_IMPL_DATA_SNAME(sname)*) = &CTEST_IMPL_TEARDOWN_FNAME(sname)<struct CTEST_IMPL_DATA_SNAME(sname)>; \ + CTEST_IMPL_STRUCT(sname, tname, tskip, &CTEST_IMPL_DATA_TNAME(sname, tname), &CTEST_IMPL_SETUP_TPNAME(sname, tname), &CTEST_IMPL_TEARDOWN_TPNAME(sname, tname)); \ + static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* data) + +#else + +#define CTEST_SETUP(sname) \ + static void CTEST_IMPL_SETUP_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* data); \ + static void (*CTEST_IMPL_SETUP_FPNAME(sname))(struct CTEST_IMPL_DATA_SNAME(sname)*) = &CTEST_IMPL_SETUP_FNAME(sname); \ + static void CTEST_IMPL_SETUP_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* data) + +#define CTEST_TEARDOWN(sname) \ + static void CTEST_IMPL_TEARDOWN_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* data); \ + static void (*CTEST_IMPL_TEARDOWN_FPNAME(sname))(struct CTEST_IMPL_DATA_SNAME(sname)*) = &CTEST_IMPL_TEARDOWN_FNAME(sname); \ + static void CTEST_IMPL_TEARDOWN_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* data) + +#define CTEST_DATA(sname) \ + struct CTEST_IMPL_DATA_SNAME(sname); \ + static void (*CTEST_IMPL_SETUP_FPNAME(sname))(struct CTEST_IMPL_DATA_SNAME(sname)*); \ + static void (*CTEST_IMPL_TEARDOWN_FPNAME(sname))(struct CTEST_IMPL_DATA_SNAME(sname)*); \ + struct CTEST_IMPL_DATA_SNAME(sname) + +#define CTEST_IMPL_CTEST(sname, tname, tskip) \ + static void CTEST_IMPL_FNAME(sname, tname)(void); \ + CTEST_IMPL_STRUCT(sname, tname, tskip, NULL, NULL, NULL); \ + static void CTEST_IMPL_FNAME(sname, tname)(void) + +#define CTEST_IMPL_CTEST2(sname, tname, tskip) \ + static struct CTEST_IMPL_DATA_SNAME(sname) CTEST_IMPL_DATA_TNAME(sname, tname); \ + static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* data); \ + CTEST_IMPL_STRUCT(sname, tname, tskip, &CTEST_IMPL_DATA_TNAME(sname, tname), &CTEST_IMPL_SETUP_FPNAME(sname), &CTEST_IMPL_TEARDOWN_FPNAME(sname)); \ + static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* data) + +#endif + +void CTEST_LOG(const char* fmt, ...) CTEST_IMPL_FORMAT_PRINTF(1, 2); +void CTEST_ERR(const char* fmt, ...) CTEST_IMPL_FORMAT_PRINTF(1, 2); // doesn't return + +#define CTEST(sname, tname) CTEST_IMPL_CTEST(sname, tname, 0) +#define CTEST_SKIP(sname, tname) CTEST_IMPL_CTEST(sname, tname, 1) + +#define CTEST2(sname, tname) CTEST_IMPL_CTEST2(sname, tname, 0) +#define CTEST2_SKIP(sname, tname) CTEST_IMPL_CTEST2(sname, tname, 1) + + +void assert_str(const char* cmp, const char* exp, const char* real, const char* caller, int line); +#define ASSERT_STR(exp, real) assert_str("==", exp, real, __FILE__, __LINE__) +#define ASSERT_NOT_STR(exp, real) assert_str("!=", exp, real, __FILE__, __LINE__) +#define ASSERT_STRSTR(str, substr) assert_str("=~", str, substr, __FILE__, __LINE__) +#define ASSERT_NOT_STRSTR(str, substr) assert_str("!~", str, substr, __FILE__, __LINE__) + +void assert_wstr(const char* cmp, const wchar_t *exp, const wchar_t *real, const char* caller, int line); +#define ASSERT_WSTR(exp, real) assert_wstr("==", exp, real, __FILE__, __LINE__) +#define ASSERT_NOT_WSTR(exp, real) assert_wstr("!=", exp, real, __FILE__, __LINE__) +#define ASSERT_WSTRSTR(str, substr) assert_wstr("=~", str, substr, __FILE__, __LINE__) +#define ASSERT_NOT_WSTRSTR(str, substr) assert_wstr("!~", str, substr, __FILE__, __LINE__) + +void assert_data(const unsigned char* exp, size_t expsize, + const unsigned char* real, size_t realsize, + const char* caller, int line); +#define ASSERT_DATA(exp, expsize, real, realsize) \ + assert_data(exp, expsize, real, realsize, __FILE__, __LINE__) + +#define CTEST_FLT_EPSILON 1e-5 +#define CTEST_DBL_EPSILON 1e-12 + +void assert_compare(const char* cmp, intmax_t exp, intmax_t real, const char* caller, int line); +#define ASSERT_EQUAL(exp, real) assert_compare("==", exp, real, __FILE__, __LINE__) +#define ASSERT_NOT_EQUAL(exp, real) assert_compare("!=", exp, real, __FILE__, __LINE__) + +#define ASSERT_EQ ASSERT_EQUAL +#define ASSERT_NE ASSERT_NOT_EQUAL +#define ASSERT_LT(v1, v2) assert_compare("<", v1, v2, __FILE__, __LINE__) +#define ASSERT_LE(v1, v2) assert_compare("<=", v1, v2, __FILE__, __LINE__) +#define ASSERT_GT(v1, v2) assert_compare(">", v1, v2, __FILE__, __LINE__) +#define ASSERT_GE(v1, v2) assert_compare(">=", v1, v2, __FILE__, __LINE__) + +void assert_compare_u(const char* cmp, uintmax_t exp, uintmax_t real, const char* caller, int line); +#define ASSERT_EQUAL_U(exp, real) assert_compare_u("==", exp, real, __FILE__, __LINE__) +#define ASSERT_NOT_EQUAL_U(exp, real) assert_compare_u("!=", exp, real, __FILE__, __LINE__) + +void assert_interval(intmax_t exp1, intmax_t exp2, intmax_t real, const char* caller, int line); +#define ASSERT_INTERVAL(exp1, exp2, real) assert_interval(exp1, exp2, real, __FILE__, __LINE__) + +void assert_null(void* real, const char* caller, int line); +#define ASSERT_NULL(real) assert_null((void*)real, __FILE__, __LINE__) + +void assert_not_null(const void* real, const char* caller, int line); +#define ASSERT_NOT_NULL(real) assert_not_null(real, __FILE__, __LINE__) + +void assert_true(int real, const char* caller, int line); +#define ASSERT_TRUE(real) assert_true(real, __FILE__, __LINE__) + +void assert_false(int real, const char* caller, int line); +#define ASSERT_FALSE(real) assert_false(real, __FILE__, __LINE__) + +void assert_fail(const char* caller, int line); +#define ASSERT_FAIL() assert_fail(__FILE__, __LINE__) + +void assert_dbl_compare(const char* cmp, double exp, double real, double tol, const char* caller, int line); +#define ASSERT_DBL_NEAR(exp, real) assert_dbl_compare("==", exp, real, -CTEST_DBL_EPSILON, __FILE__, __LINE__) +#define ASSERT_DBL_NEAR_TOL(exp, real, tol) assert_dbl_compare("==", exp, real, tol, __FILE__, __LINE__) +#define ASSERT_DBL_FAR(exp, real) assert_dbl_compare("!=", exp, real, -CTEST_DBL_EPSILON, __FILE__, __LINE__) +#define ASSERT_DBL_FAR_TOL(exp, real, tol) assert_dbl_compare("!=", exp, real, tol, __FILE__, __LINE__) + +#define ASSERT_FLT_NEAR(v1, v2) assert_dbl_compare("==", v1, v2, -CTEST_FLT_EPSILON, __FILE__, __LINE__) +#define ASSERT_FLT_FAR(v1, v2) assert_dbl_compare("!=", v1, v2, -CTEST_FLT_EPSILON, __FILE__, __LINE__) +#define ASSERT_DBL_LT(v1, v2) assert_dbl_compare("<", v1, v2, 0.0, __FILE__, __LINE__) +#define ASSERT_DBL_GT(v1, v2) assert_dbl_compare(">", v1, v2, 0.0, __FILE__, __LINE__) + +#ifdef CTEST_MAIN + +#include <setjmp.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#if !defined(_WIN32) || defined(__GNUC__) +#include <unistd.h> +#elif defined(_WIN32) +#include <io.h> +#endif +#include <stdint.h> +#include <stdlib.h> +#include <wchar.h> + +static size_t ctest_errorsize; +static char* ctest_errormsg; +#define MSG_SIZE 4096 +static char ctest_errorbuffer[MSG_SIZE]; +static jmp_buf ctest_err; +static int color_output = 1; +static const char* suite_name; + +typedef int (*ctest_filter_func)(struct ctest*); + +#define ANSI_BLACK "\033[0;30m" +#define ANSI_RED "\033[0;31m" +#define ANSI_GREEN "\033[0;32m" +#define ANSI_YELLOW "\033[0;33m" +#define ANSI_BLUE "\033[0;34m" +#define ANSI_MAGENTA "\033[0;35m" +#define ANSI_CYAN "\033[0;36m" +#define ANSI_GREY "\033[0;37m" +#define ANSI_DARKGREY "\033[01;30m" +#define ANSI_BRED "\033[01;31m" +#define ANSI_BGREEN "\033[01;32m" +#define ANSI_BYELLOW "\033[01;33m" +#define ANSI_BBLUE "\033[01;34m" +#define ANSI_BMAGENTA "\033[01;35m" +#define ANSI_BCYAN "\033[01;36m" +#define ANSI_WHITE "\033[01;37m" +#define ANSI_NORMAL "\033[0m" + +CTEST(suite, test) { } + +static void vprint_errormsg(const char* const fmt, va_list ap) CTEST_IMPL_FORMAT_PRINTF(1, 0); +static void print_errormsg(const char* const fmt, ...) CTEST_IMPL_FORMAT_PRINTF(1, 2); + +static void vprint_errormsg(const char* const fmt, va_list ap) { + // (v)snprintf returns the number that would have been written + const int ret = vsnprintf(ctest_errormsg, ctest_errorsize, fmt, ap); + if (ret < 0) { + ctest_errormsg[0] = 0x00; + } else { + const size_t size = (size_t) ret; + const size_t s = (ctest_errorsize <= size ? size -ctest_errorsize : size); + // ctest_errorsize may overflow at this point + ctest_errorsize -= s; + ctest_errormsg += s; + } +} + +static void print_errormsg(const char* const fmt, ...) { + va_list argp; + va_start(argp, fmt); + vprint_errormsg(fmt, argp); + va_end(argp); +} + +static void msg_start(const char* color, const char* title) { + if (color_output) { + print_errormsg("%s", color); + } + print_errormsg(" %s: ", title); +} + +static void msg_end(void) { + if (color_output) { + print_errormsg(ANSI_NORMAL); + } + print_errormsg("\n"); +} + +void CTEST_LOG(const char* fmt, ...) +{ + va_list argp; + msg_start(ANSI_BLUE, "LOG"); + + va_start(argp, fmt); + vprint_errormsg(fmt, argp); + va_end(argp); + + msg_end(); +} + +CTEST_IMPL_DIAG_PUSH_IGNORED(missing-noreturn) + +void CTEST_ERR(const char* fmt, ...) +{ + va_list argp; + msg_start(ANSI_YELLOW, "ERR"); + + va_start(argp, fmt); + vprint_errormsg(fmt, argp); + va_end(argp); + + msg_end(); + longjmp(ctest_err, 1); +} + +CTEST_IMPL_DIAG_POP() + +void assert_str(const char* cmp, const char* exp, const char* real, const char* caller, int line) { + if ((!exp ^ !real) || (exp && ( + (cmp[1] == '=' && ((cmp[0] == '=') ^ (strcmp(exp, real) == 0))) || + (cmp[1] == '~' && ((cmp[0] == '=') ^ (strstr(exp, real) != NULL))) + ))) { + CTEST_ERR("%s:%d assertion failed, '%s' %s '%s'", caller, line, exp, cmp, real); + } +} + +void assert_wstr(const char* cmp, const wchar_t *exp, const wchar_t *real, const char* caller, int line) { + if ((!exp ^ !real) || (exp && ( + (cmp[1] == '=' && ((cmp[0] == '=') ^ (wcscmp(exp, real) == 0))) || + (cmp[1] == '~' && ((cmp[0] == '=') ^ (wcsstr(exp, real) != NULL))) + ))) { + CTEST_ERR("%s:%d assertion failed, '%ls' %s '%ls'", caller, line, exp, cmp, real); + } +} + +void assert_data(const unsigned char* exp, size_t expsize, + const unsigned char* real, size_t realsize, + const char* caller, int line) { + size_t i; + if (expsize != realsize) { + CTEST_ERR("%s:%d expected %" PRIuMAX " bytes, got %" PRIuMAX, caller, line, (uintmax_t) expsize, (uintmax_t) realsize); + } + for (i=0; i<expsize; i++) { + if (exp[i] != real[i]) { + CTEST_ERR("%s:%d expected 0x%02x at offset %" PRIuMAX " got 0x%02x", + caller, line, exp[i], (uintmax_t) i, real[i]); + } + } +} + +static bool get_compare_result(const char* cmp, int c3, bool eq) { + if (cmp[0] == '<') + return c3 < 0 || ((cmp[1] == '=') & eq); + if (cmp[0] == '>') + return c3 > 0 || ((cmp[1] == '=') & eq); + return (cmp[0] == '=') == eq; +} + +void assert_compare(const char* cmp, intmax_t exp, intmax_t real, const char* caller, int line) { + int c3 = (real < exp) - (exp < real); + + if (!get_compare_result(cmp, c3, c3 == 0)) { + CTEST_ERR("%s:%d assertion failed, %" PRIdMAX " %s %" PRIdMAX "", caller, line, exp, cmp, real); + } +} + +void assert_compare_u(const char* cmp, uintmax_t exp, uintmax_t real, const char* caller, int line) { + int c3 = (real < exp) - (exp < real); + + if (!get_compare_result(cmp, c3, c3 == 0)) { + CTEST_ERR("%s:%d assertion failed, %" PRIuMAX " %s %" PRIuMAX, caller, line, exp, cmp, real); + } +} + +void assert_interval(intmax_t exp1, intmax_t exp2, intmax_t real, const char* caller, int line) { + if (real < exp1 || real > exp2) { + CTEST_ERR("%s:%d expected %" PRIdMAX "-%" PRIdMAX ", got %" PRIdMAX, caller, line, exp1, exp2, real); + } +} + +static bool approximately_equal(double a, double b, double epsilon) { + double d = a - b; + if (d < 0) d = -d; + if (a < 0) a = -a; + if (b < 0) b = -b; + return d <= (a > b ? a : b)*epsilon; /* D.Knuth */ +} + +/* tol < 0 means it is an epsilon, else absolute error */ +void assert_dbl_compare(const char* cmp, double exp, double real, double tol, const char* caller, int line) { + double diff = exp - real; + double absdiff = diff < 0 ? -diff : diff; + int c3 = (real < exp) - (exp < real); + bool eq = tol < 0 ? approximately_equal(exp, real, -tol) : absdiff <= tol; + + if (!get_compare_result(cmp, c3, eq)) { + const char* tolstr = "tol"; + if (tol < 0) { + tolstr = "eps"; + tol = -tol; + } + CTEST_ERR("%s:%d assertion failed, %.8g %s %.8g (diff %.4g, %s %.4g)", caller, line, exp, cmp, real, diff, tolstr, tol); + } +} + +void assert_null(void* real, const char* caller, int line) { + if ((real) != NULL) { + CTEST_ERR("%s:%d should be NULL", caller, line); + } +} + +void assert_not_null(const void* real, const char* caller, int line) { + if (real == NULL) { + CTEST_ERR("%s:%d should not be NULL", caller, line); + } +} + +void assert_true(int real, const char* caller, int line) { + if ((real) == 0) { + CTEST_ERR("%s:%d should be true", caller, line); + } +} + +void assert_false(int real, const char* caller, int line) { + if ((real) != 0) { + CTEST_ERR("%s:%d should be false", caller, line); + } +} + +void assert_fail(const char* caller, int line) { + CTEST_ERR("%s:%d shouldn't come here", caller, line); +} + + +static int suite_all(struct ctest* t) { + (void) t; // fix unused parameter warning + return 1; +} + +static int suite_filter(struct ctest* t) { + return strncmp(suite_name, t->ssname, strlen(suite_name)) == 0; +} + +static void color_print(const char* color, const char* text) { + if (color_output) + printf("%s%s" ANSI_NORMAL "\n", color, text); + else + printf("%s\n", text); +} + +#ifndef CTEST_NO_SEGFAULT +#include <signal.h> +static void sighandler(int signum) +{ + const char msg_color[] = ANSI_BRED "[SIGSEGV: Segmentation fault]" ANSI_NORMAL "\n"; + const char msg_nocolor[] = "[SIGSEGV: Segmentation fault]\n"; + + const char* msg = color_output ? msg_color : msg_nocolor; + write(STDOUT_FILENO, msg, (unsigned int)strlen(msg)); + + /* "Unregister" the signal handler and send the signal back to the process + * so it can terminate as expected */ + signal(signum, SIG_DFL); +#if !defined(_WIN32) || defined(__CYGWIN__) + kill(getpid(), signum); +#endif +} +#endif + +int ctest_main(int argc, const char *argv[]); + +__attribute__((no_sanitize_address)) int ctest_main(int argc, const char *argv[]) +{ + static int total = 0; + static int num_ok = 0; + static int num_fail = 0; + static int num_skip = 0; + static int idx = 1; + static ctest_filter_func filter = suite_all; + +#ifndef CTEST_NO_SEGFAULT + signal(SIGSEGV, sighandler); +#endif + + if (argc == 2) { + suite_name = argv[1]; + filter = suite_filter; + } +#ifdef CTEST_NO_COLORS + color_output = 0; +#else + color_output = isatty(1); +#endif + clock_t t1 = clock(); + + uint32_t* magic_begin = &CTEST_IMPL_TNAME(suite, test).magic1; + uint32_t* magic_end = &CTEST_IMPL_TNAME(suite, test).magic0, *m; + size_t skipints = sizeof(struct ctest)/sizeof *m; + +#if (defined __TINYC__ && defined __unix__) + #define CTEST_PEEK 10 /* search 4*(1+10) bytes outside ctest entry bounds */ +#else + #define CTEST_PEEK 0 /* access only 4 bytes outside outer ctest entry bounds */ +#endif + for (m = magic_begin; magic_begin - m <= skipints + CTEST_PEEK; --m) { + if (*m == 0xBADCAFE1) { + magic_begin = m; + m -= skipints - 1; + } + } + for (m = magic_end; m - magic_end <= skipints; ++m) { + if (*m == 0xBADCAFE0) { + magic_end = m; + m += skipints - 1; + } + } + magic_begin = &CTEST_CONTAINER_OF(magic_begin, struct ctest, magic1)->magic0; + + static struct ctest* test; + for (m = magic_begin; m <= magic_end; m += skipints) { + while (*m != 0xBADCAFE0) ++m; + test = CTEST_CONTAINER_OF(m, struct ctest, magic0); + if (test == &CTEST_IMPL_TNAME(suite, test)) continue; + if (filter(test)) total++; + } + + for (m = magic_begin; m <= magic_end; m += skipints) { + while (*m != 0xBADCAFE0) ++m; + test = CTEST_CONTAINER_OF(m, struct ctest, magic0); + if (test == &CTEST_IMPL_TNAME(suite, test)) continue; + if (filter(test)) { + ctest_errorbuffer[0] = 0; + ctest_errorsize = MSG_SIZE-1; + ctest_errormsg = ctest_errorbuffer; + printf("TEST %d/%d %s:%s ", idx, total, test->ssname, test->ttname); + fflush(stdout); + if (test->skip) { + color_print(ANSI_BYELLOW, "[SKIPPED]"); + num_skip++; + } else { + int result = setjmp(ctest_err); + if (result == 0) { + if (test->setup && *test->setup) (*test->setup)(test->data); + if (test->data) + test->run.unary(test->data); + else + test->run.nullary(); + if (test->teardown && *test->teardown) (*test->teardown)(test->data); + // if we got here it's ok +#ifdef CTEST_NO_COLOR_OK + printf("[OK]\n"); +#else + color_print(ANSI_BGREEN, "[OK]"); +#endif + num_ok++; + } else { + color_print(ANSI_BRED, "[FAIL]"); + num_fail++; + } + if (ctest_errorsize != MSG_SIZE-1) printf("%s", ctest_errorbuffer); + } + idx++; + } + } + clock_t t2 = clock(); + + const char* color = (num_fail) ? ANSI_BRED : ANSI_GREEN; + char results[80]; + snprintf(results, sizeof(results), "RESULTS: %d tests (%d ok, %d failed, %d skipped) ran in %.1f ms", + total, num_ok, num_fail, num_skip, (double)(t2 - t1)*1000.0/CLOCKS_PER_SEC); + color_print(color, results); + return num_fail; +} + +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/misc/tests/main.c b/misc/tests/main.c new file mode 100644 index 00000000..2b8931c4 --- /dev/null +++ b/misc/tests/main.c @@ -0,0 +1,31 @@ +/* Copyright 2011-2023 Bas van den Berg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> + +#define CTEST_MAIN + +// uncomment lines below to enable/disable features. See README.md for details +//#define CTEST_NO_SEGFAULT +//#define CTEST_NO_COLORS +//#define CTEST_NO_COLOR_OK + +#include "ctest.h" + +int main(int argc, const char *argv[]) +{ + return ctest_main(argc, argv); +} + |
