From 9e187b60938d3cd46413bcb63b84faa48f990658 Mon Sep 17 00:00:00 2001 From: butterkeks Date: Sat, 4 May 2024 18:24:20 +0200 Subject: [PATCH] Add some tests + change s8_stopper --- .github/workflows/ci.yml | 2 + Makefile | 11 +++-- include/util.h | 21 ++++----- src/deinflector.c | 44 +++++++++--------- src/test.c | 31 ------------- src/tests.c | 97 ++++++++++++++++++++++++++++++++++++++++ src/util.c | 12 ++--- 7 files changed, 144 insertions(+), 74 deletions(-) delete mode 100644 src/test.c create mode 100644 src/tests.c diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e5776a6..e5ef83f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,8 @@ jobs: - run: clang-format --version - run: make clean analyse + - run: make tests + - run: ./tests on: push: diff --git a/Makefile b/Makefile index 8ab0c72..aa6d3eb 100644 --- a/Makefile +++ b/Makefile @@ -23,10 +23,9 @@ DEBUG_CFLAGS=-DDEBUG \ -Wall -Wextra -Wpedantic -Wstrict-prototypes -Wdouble-promotion -Wshadow \ -Wno-unused-parameter -Wno-sign-conversion -Wno-unused-function -Wpointer-arith \ -Wmissing-prototypes -Wstrict-prototypes -Wstrict-overflow -Wcast-align \ - -Wno-maybe-uninitialized \ -fsanitize=leak,address,undefined -fsanitize-undefined-trap-on-error -fstack-protector-strong \ - -Og -ggdb -RELEASE_CFLAGS=-O3 -flto -march=native + -Og -ggdb +RELEASE_CFLAGS=-O3 -flto -march=native -Wmaybe-uninitialized FILES=dictpopup.c util.c platformdep.c deinflector.c settings.c db.c ankiconnectc.c database.c jppron.c pdjson.c FILES_H=ankiconnectc.h db.h deinflector.h settings.h util.h platformdep.h database.h jppron.h pdjson.h @@ -53,10 +52,10 @@ debug: $(bins) debug: CFLAGS+=$(DEBUG_CFLAGS) dictpopup: $(SRC) $(SRC_H) - $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ src/gtk3popup.c $(SRC) $(LDLIBS) + $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(SDIR)/gtk3popup.c $(SRC) $(LDLIBS) dictpopup-create: $(SRC_CREATE) $(SRC_H_CREATE) - $(CC) $(CFLAGS_CREATE) -o $@ src/dictpopup_create.c $(SRC_CREATE) $(LDLIBS_CREATE) + $(CC) $(CFLAGS_CREATE) -o $@ $(SDIR)/dictpopup_create.c $(SRC_CREATE) $(LDLIBS_CREATE) release: version=$$(git describe); prefix=dictpopup-$${version#v}; \ @@ -90,7 +89,7 @@ clean: rm -f dictpopup dictpopup-create tests tests: $(SRC) $(SRC_H) - $(CC) $(CFLAGS) $(DEBUG_CFLAGS) -o $@ $(SDIR)/test.c $(SRC) $(LDLIBS) + $(CC) $(CFLAGS) $(DEBUG_CFLAGS) $(CPPFLAGS) -o $@ $(SDIR)/tests.c $(SRC) $(LDLIBS) check test: tests ./tests diff --git a/include/util.h b/include/util.h index c6a33c0..02d0c39 100644 --- a/include/util.h +++ b/include/util.h @@ -37,6 +37,12 @@ void *xrealloc(void *ptr, size_t size); // clang-format on /* ------------------- Start s8 utils ---------------- */ +// This encodes an invalid UTF-8 char to be used as a stopper +// for variable argument length macros +#define S8_STOPPER \ + (s8) { \ + .s = (u8[]){0xF8}, .len = 1 \ + } #define lengthof(s) (arrlen("" s "") - 1) #define s8(s) \ { (u8 *)s, arrlen(s) - 1 } @@ -114,16 +120,11 @@ void _nonnull_ frees8buffer(s8 **buf); * * Returns: A newly allocated s8 containing the concatenated string */ -#define concat(...) \ - concat_((s8[]){__VA_ARGS__, S("CONCAT_STOPPER")}, S("CONCAT_STOPPER")) // Might be better to use - // a stopper that - // consists of a single - // invalid char -s8 _nonnull_ concat_(s8 *strings, const s8 stopper); - -#define buildpath(...) \ - buildpath_((s8[]){__VA_ARGS__, S("BUILD_PATH_STOPPER")}, S("BUILD_PATH_STOPPER")) -s8 _nonnull_ buildpath_(s8 *pathcomps, const s8 stopper); +#define concat(...) concat_((s8[]){__VA_ARGS__, S8_STOPPER}) +s8 _nonnull_ concat_(s8 *strings); + +#define buildpath(...) buildpath_((s8[]){__VA_ARGS__, S8_STOPPER}) +s8 _nonnull_ buildpath_(s8 *pathcomps); /* ------------------- End s8 utils ---------------- */ /* --------------------------- string builder -----------------------_ */ diff --git a/src/deinflector.c b/src/deinflector.c index 6223bd6..c02f2f1 100644 --- a/src/deinflector.c +++ b/src/deinflector.c @@ -142,19 +142,10 @@ s8 kanji2hira(s8 input) { * Replaces the last @suffix_len bytes of @word with @replacement. */ static void add_replace_suffix(s8 word, s8 replacement, size suffix_len) { - assert(word.s); assert(word.len >= suffix_len); - assert(replacement.len >= 0); - - // TODO: Rewrite this using s8 functions - s8 replstr = {0}; - replstr.len = word.len - suffix_len + replacement.len; - replstr.s = new (u8, replstr.len); - - memcpy(replstr.s, word.s, (size_t)(word.len - suffix_len)); - if (replacement.len) - memcpy(replstr.s + word.len - suffix_len, replacement.s, (size_t)replacement.len); + word.len -= suffix_len; + s8 replstr = concat(word, replacement); buf_push(deinfs, replstr); } @@ -162,19 +153,13 @@ static void add_replace_suffix(s8 word, s8 replacement, size suffix_len) { * Same as add_replace_suffix but with prefix */ static void add_replace_prefix(s8 word, s8 replacement, size prefix_len) { - assert(word.s); assert(word.len >= prefix_len); - assert(replacement.len >= 0); - - s8 retstr = {0}; - retstr.len = word.len - prefix_len + replacement.len; - retstr.s = new (u8, retstr.len); - if (replacement.len) - memcpy(retstr.s, replacement.s, (size_t)replacement.len); - memcpy(retstr.s + replacement.len, word.s + prefix_len, (size_t)(word.len - prefix_len)); + word.s += prefix_len; + word.len -= prefix_len; + s8 replstr = concat(replacement, word); - buf_push(deinfs, retstr); + buf_push(deinfs, replstr); } /* @@ -266,6 +251,7 @@ static void check_past(s8 word) { static void check_masu(s8 word) { IF_ENDSWITH_CONVERT_ITOU("ます"); IF_ENDSWITH_CONVERT_ITOU("ません"); + IF_ENDSWITH_CONVERT_ITOU("ませんでした"); } static void check_shimau(s8 word) { @@ -314,6 +300,7 @@ static void check_potential(s8 word) { IF_ENDSWITH_REPLACE("せる", "す"); IF_ENDSWITH_REPLACE("ける", "く"); + IF_ENDSWITH_REPLACE("げる", "ぐ"); IF_ENDSWITH_REPLACE("べる", "ぶ"); IF_ENDSWITH_REPLACE("てる", "つ"); IF_ENDSWITH_REPLACE("める", "む"); @@ -338,6 +325,20 @@ static void check_concurrent(s8 word) { IF_ENDSWITH_CONVERT_ITOU("ながら"); } +static void check_imperative(s8 word) { + // TODO: This is still missing some + IF_ENDSWITH_REPLACE("べろ", "べる"); + IF_ENDSWITH_REPLACE("じろ", "じる"); + IF_ENDSWITH_REPLACE("せ", "す"); + IF_ENDSWITH_REPLACE("け", "く"); + IF_ENDSWITH_REPLACE("げ", "ぐ"); + IF_ENDSWITH_REPLACE("べ", "ぶ"); + IF_ENDSWITH_REPLACE("て", "つ"); + IF_ENDSWITH_REPLACE("え", "う"); + IF_ENDSWITH_REPLACE("ね", "ぬ"); + IF_ENDSWITH_REPLACE("め", "む"); +} + static void deinflect_one_iter(s8 word) { check_shimau(word); check_adjective(word); @@ -350,6 +351,7 @@ static void deinflect_one_iter(s8 word) { check_potential(word); check_conditional(word); check_concurrent(word); + check_imperative(word); kanjify(word); } diff --git a/src/test.c b/src/test.c deleted file mode 100644 index 369cf87..0000000 --- a/src/test.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "deinflector.h" -#include "util.h" -#include - -#define passed(cond) ((cond) ? "passed" : "not passed") - -int main(int argc, char **argv) { - const s8 all_kata = S("アイウエオカキクケコサシスセソタチツテトナニヌネノハヒ" - "フヘホマミムメモヤユヨラリルレロワヲン" - "ガギグゲゴザジズゼゾダヂヅデド" - "バビブベボ" - "パピプペポ"); - const s8 all_hira = S("あいうえおかきくけこさしすせそたちつてとなにぬねのはひ" - "ふへほまみむめもやゆよらりるれろわをん" - "がぎぐげござじずぜぞだぢづでど" - "ばびぶべぼ" - "ぱぴぷぺぽ"); - s8 converted_hira = s8dup(all_kata); - kata2hira(all_kata); - printf("kata2hira(): %s\n", passed(s8equals(converted_hira, - all_hira))); // Still needs - // fuzzy testing - // though - frees8(&converted_hira); - - const s8 expected_pth = S("This is/a/test, if/building/paths/works/as expected"); - s8 built_pth = buildpath(S("This is"), S("a"), S("test, if"), S("building"), S("paths"), - S("works"), S("as expected")); - printf("buildpath(): %s\n", passed(s8equals(expected_pth, built_pth))); - frees8(&built_pth); -} diff --git a/src/tests.c b/src/tests.c new file mode 100644 index 0000000..86fe804 --- /dev/null +++ b/src/tests.c @@ -0,0 +1,97 @@ +#include + +#include "deinflector.h" +#include "util.h" + +#define passed(cond) ((cond) ? "passed" : "not passed") + +static int test_kana_conversion(void) { + const s8 all_kata = S("アイウエオカキクケコサシスセソタチツテトナニヌネノハヒ" + "フヘホマミムメモヤユヨラリルレロワヲン" + "ガギグゲゴザジズゼゾダヂヅデド" + "バビブベボ" + "パピプペポ"); + const s8 all_hira = S("あいうえおかきくけこさしすせそたちつてとなにぬねのはひ" + "ふへほまみむめもやゆよらりるれろわをん" + "がぎぐげござじずぜぞだぢづでど" + "ばびぶべぼ" + "ぱぴぷぺぽ"); + _drop_(frees8) s8 converted_hira = s8dup(all_kata); + kata2hira(converted_hira); + + return s8equals(converted_hira, all_hira); +} + +#define DEINFLECT(expected_deinf, ...) \ + do { \ + char **words = (char *[]){__VA_ARGS__, NULL}; \ + for (char **wordptr = words; *wordptr != NULL; wordptr++) { \ + s8 *deinfs = deinflect(fromcstr_(*wordptr)); \ + i32 contains = 0; \ + for (size_t i = 0; i < buf_size(deinfs); i++) { \ + if (s8equals(deinfs[i], S(expected_deinf))) { \ + contains = 1; \ + break; \ + } \ + } \ + if (!contains) { \ + printf("(%s) Deinflection test for '%s' FAILED!\n Deinflections are:\n", __func__, \ + *wordptr); \ + for (size_t i = 0; i < buf_size(deinfs); i++) { \ + printf("%.*s\n", (int)deinfs[i].len, (char *)deinfs[i].s); \ + } \ + return 0; \ + } \ + } \ + } while (0) + +static int test_deinflections(void) { + /* -- Ichidan verbs -- */ + DEINFLECT("信じる", "信じない", "信じます", "信じません", "信じた", "信じなかった", + "信じました", "信じませんでした", "信じて", "信じなくて", "信じられる", + "信じられない", "信じられる", "信じられない", "信じさせる", "信じさせない", + "信じさせられる", "信じさせられない", "信じろ"); + + /* -- Godan verbs -- */ + /* ku-ending */ + DEINFLECT("叩く", "叩かない", "叩きます", "叩きません", "叩いた", "叩かなかった", "叩きました", + "叩きませんでした", "叩いて", "叩かなくて", "叩ける", "叩けない", "叩かれる", + "叩かれない", "叩かせる", "叩かせない", "叩かせられる", "叩かせられない", "叩け"); + /* u-ending */ + DEINFLECT("買う", "買わない", "買います", "買いません", "買った", "買わなかった", "買いました", + "買いませんでした", "買って", "買わなくて", "買える", "買えない", "買われる", "買われない", + "買わせる", "買わせない", "買わせられる", "買わせられない", "買え"); + /* gu-ending */ + DEINFLECT("泳ぐ", "泳がない", "泳ぎます", "泳ぎません", "泳いだ", "泳がなかった", "泳ぎました", + "泳ぎませんでした", "泳いで", "泳がなくて", "泳げる", "泳げない", "泳がれる", "泳がれない", + "泳がせる", "泳がせない", "泳がせられる", "泳がせられない", "泳げ"); + /* mu-ending */ + DEINFLECT("読む", "読まない", "読みます", "読みません", "読んだ", "読まなかった", "読みました", + "読みませんでした", "読んで", "読まなくて", "読める", "読めない", "読まれる", "読まれない", + "読ませる", "読ませない", "読ませられる", "読ませられない", "読め"); + /* bu-ending */ + DEINFLECT("遊ぶ", "遊べ"); + /* tsu-ending */ + DEINFLECT("勝つ", "勝て"); + + /* -- i-adjectives -- */ + DEINFLECT("軽い", "軽くない", "軽かった", "軽くなかった"); + + return 1; +} + +#define TEST(call) \ + do { \ + if ((call) == 0) { \ + exit(EXIT_FAILURE); \ + } else { \ + printf("%s: Success\n", #call); \ + } \ + } while (0) + +int main(int argc, char **argv) { + TEST(test_kana_conversion()); + TEST(test_deinflections()); + + return EXIT_SUCCESS; +} diff --git a/src/util.c b/src/util.c index c88fdc8..a3a0ba1 100644 --- a/src/util.c +++ b/src/util.c @@ -190,19 +190,19 @@ s8 unescape(s8 str) { return str; } -s8 concat_(s8 *strings, const s8 stopper) { +s8 concat_(s8 *strings) { size len = 0; - for (s8 *s = strings; !s8equals(*s, stopper); s++) + for (s8 *s = strings; !s8equals(*s, S8_STOPPER); s++) len += s->len; s8 ret = news8(len); s8 p = ret; - for (s8 *s = strings; !s8equals(*s, stopper); s++) + for (s8 *s = strings; !s8equals(*s, S8_STOPPER); s++) p = s8copy(p, *s); return ret; } -s8 buildpath_(s8 *pathcomps, const s8 stopper) { +s8 buildpath_(s8 *pathcomps) { #ifdef _WIN32 s8 sep = S("\\"); #else @@ -210,7 +210,7 @@ s8 buildpath_(s8 *pathcomps, const s8 stopper) { #endif size pathlen = 0; bool first = true; - for (s8 *pc = pathcomps; !s8equals(*pc, stopper); pc++) { + for (s8 *pc = pathcomps; !s8equals(*pc, S8_STOPPER); pc++) { if (!first) pathlen += sep.len; pathlen += pc->len; @@ -222,7 +222,7 @@ s8 buildpath_(s8 *pathcomps, const s8 stopper) { s8 p = retpath; first = true; - for (s8 *pc = pathcomps; !s8equals(*pc, stopper); pc++) { + for (s8 *pc = pathcomps; !s8equals(*pc, S8_STOPPER); pc++) { if (!first) p = s8copy(p, sep); p = s8copy(p, *pc);