/* Unit tests of mhtoken.c Copyright © 2021-2023 Samuel Lidén Borell 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. */ #include "../mhtoken.c" #define MHPARSE #include "parsecommon.h" #include "unittest.h" #include "alltests.h" #include "testcommon.h" #include #include /** Runs before each test case */ static void before_test(void) { set_mhparse(1); /* Set mode. */ } /** Runs after each test case */ static void after_test(void) { verify_errors(); } static void test_mhtok_empty(void) { testcommon_empty(); } static void test_mhtok_newline_single(void) { testcommon_newline_single(); } static void test_mhtok_newline_splitted(void) { testcommon_newline_splitted(); } static void test_mhtok_newline_eof1(void) { testcommon_newline_eof1(); } static void test_mhtok_newline_eof2(void) { testcommon_newline_eof2(); } static void test_mhtok_null(void) { testcommon_null(); } static void test_mhtok_utf8_2min(void) { testcommon_utf8_2min(); } static void test_mhtok_utf8_2max(void) { testcommon_utf8_2max(); } static void test_mhtok_utf8_2split(void) { testcommon_utf8_2split(); } static void test_mhtok_utf8_2overlong(void) { testcommon_utf8_2overlong(); } static void test_mhtok_utf8_2eof(void) { testcommon_utf8_2eof(); } static void test_mhtok_utf8_2badcont(void) { testcommon_utf8_2badcont(); } static void test_mhtok_utf8_3min(void) { testcommon_utf8_3min(); } static void test_mhtok_utf8_3max(void) { testcommon_utf8_3max(); } static void test_mhtok_utf8_3split(void) { testcommon_utf8_3split(); } static void test_mhtok_utf8_3overlong(void) { testcommon_utf8_3overlong(); } static void test_mhtok_utf8_3eof2(void) { testcommon_utf8_3eof2(); } static void test_mhtok_utf8_3nullchar3(void) { testcommon_utf8_3nullchar3(); } static void test_mhtok_utf8_4min(void) { testcommon_utf8_4min(); } static void test_mhtok_utf8_4max(void) { testcommon_utf8_4max(); } static void test_mhtok_utf8_4split(void) { testcommon_utf8_4split(); } static void test_mhtok_utf8_4overlong(void) { testcommon_utf8_4overlong(); } static void test_mhtok_utf8_4overflow(void) { testcommon_utf8_4overflow(); } static void test_mhtok_utf8_4eof2(void) { testcommon_utf8_4eof2(); } static void test_mhtok_utf8_4eof3(void) { testcommon_utf8_4eof3(); } static void test_mhtok_utf8_4nullchar3(void) { testcommon_utf8_4nullchar3(); } static void test_mhtok_utf8_4nullchar4(void) { testcommon_utf8_4nullchar4(); } static void test_mhtok_utf8_forbidden(void) { testcommon_utf8_forbidden(); } static void test_mhtok_comment(void) { testcommon_comment(); } static void test_mhtok_comment_badchar(void) { testcommon_comment_badchar(); } static void test_mhtok_multiline_comment(void) { testcommon_multiline_comment(); } static void test_mhtok_multiline_comment_badstart(void) { testcommon_multiline_comment_badstart(); } static void test_mhtok_indentation(void) { struct CSlul *ctx = create_ctx(CSLUL_P_MODULEHEADER); char *buff = cut0(" \t "); /* Tab counts as 1 character */ TEST_ERROR(buff, 3, 0, CSLUL_MHT_NEEDDATA, 1, 4, 0, MHTInIndentation, CSLUL_E_INDENTEDATTR, 1, 1); TEST_ONE(buff, 3, 0, CSLUL_MHT_NEEDDATA, 1, 7, 0, MHTInIndentation); tfree(buff); buff = cut0("\n"); TEST_ONE(buff, 1, 0, CSLUL_MHT_NEEDDATA, 2, 1, 0, MHTDone); tfree(buff); free_ctx(ctx); } static void test_mhtok_indentation_tab(void) { struct CSlul *ctx = create_ctx(CSLUL_P_MODULEHEADER); char *buff = cut0("\t\t "); /* Tab counts as 1 character */ TEST_ERROR(buff, 3, 0, CSLUL_MHT_NEEDDATA, 1, 4, 0, MHTInIndentation, CSLUL_E_INDENTEDATTR, 1, 1); tfree(buff); free_ctx(ctx); } /** Tests parsing an attribute that is splitted across two buffers */ static void test_mhtok_attr_splitted(void) { struct CSlul *ctx = create_ctx(CSLUL_P_MODULEHEADER); char *buff = cut0("\\ab"); TEST_ONE(buff, 3, 0, CSLUL_MHT_NEEDDATA, 1, 4, 0, MHTAttrName); buff[0] = 'c'; buff[1] = '\n'; TEST_ONE(buff, 2, 0, CSLUL_MHT_AttrName, 1, 5, 0, MHTAttrSpaceStart); ASSERT_TOKEN_VALUE("abc", 1, 1); tfree(buff); free_ctx(ctx); } /** Tests parsing multiple attributes in the same buffer */ static void test_mhtok_attr_multi(void) { struct CSlul *ctx = create_ctx(CSLUL_P_MODULEHEADER); char *buff = cut0("\\ab_c1\n" "\\d\n"); TEST_ONE(buff, 10, 1, CSLUL_MHT_AttrName, 1, 7, 0, MHTAttrSpaceStart); ASSERT_TOKEN_VALUE("ab_c1", 1, 1); TEST_CONT(CSLUL_MHT_AttrName, 2, 3, 0, MHTAttrSpaceStart); ASSERT_TOKEN_VALUE("d", 2, 1); TEST_CONT(CSLUL_MHT_EOF, 3, 1, 0, MHTDone); tfree(buff); free_ctx(ctx); } /** Tests parsing an attribute with EOF after */ static void test_mhtok_attr_eof(void) { struct CSlul *ctx = create_ctx(CSLUL_P_MODULEHEADER); char *buff = cut0("\\abc"); TEST_ONE(buff, 4, 1, CSLUL_MHT_AttrName, 1, 5, 0, MHTAttrSpaceStart); ASSERT_TOKEN_VALUE("abc", 1, 1); tfree(buff); free_ctx(ctx); } /** Tests parsing an attribute with an illegal character */ static void test_mhtok_attr_badchar(void) { struct CSlul *ctx = create_ctx(CSLUL_P_MODULEHEADER); char *buff = cut0("\\ab+=test"); TEST_ONE(buff, 9, 0, CSLUL_MHT_AttrName, 1, 4, 0, MHTAttrSpaceStart); ASSERT_TOKEN_VALUE("ab", 1, 1); expect_error(CSLUL_E_BADATTRCHAR, 1, 4); TEST_CONT(CSLUL_MHT_NEEDDATA, 1, 10, 0, MHTInComment); tfree(buff); buff = cut0("\n"); TEST_ONE(buff, 1, 1, CSLUL_MHT_EOF, 2, 1, 0, MHTDone); tfree(buff); free_ctx(ctx); } /** Tests parsing an attribute with a missing \\ */ static void test_mhtok_attr_nobackslash(void) { struct CSlul *ctx = create_ctx(CSLUL_P_MODULEHEADER); char *buff = cut0("abc "); /* Error is reported by the parser, not the tokenizer */ TEST_ONE(buff, 4, 0, CSLUL_MHT_BareIdentifier, 1, 4, 0, MHTAttrSpaceStart); ASSERT_TOKEN_VALUE("abc", 1, 1); tfree(buff); free_ctx(ctx); } /** Tests parsing a module header followed by a start of a declaration */ static void test_mhtok_end_of_header(void) { struct CSlul *ctx = create_ctx(CSLUL_P_MODULEHEADER); char *buff = cut0("\\slul x\ntype "); TEST_ONE(buff, 13, 0, CSLUL_MHT_AttrName, 1, 6, 0, MHTAttrSpaceStart); TEST_CONT(CSLUL_MHT_AttrValue, 1, 8, 0, MHTAttrSpaceStart); ctx->module.min_langver = LANGVER_BAD; /* simulate that language version is set */ TEST_CONT(CSLUL_MHT_BareIdentifier, 2, 5, 0, MHTAttrSpaceStart); tfree(buff); free_ctx(ctx); } /** Tests parsing a backslash with EOF in the same buffer */ static void test_mhtok_backslash_eof1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_MODULEHEADER); char *buff = cut0("\\"); TEST_ERROR(buff, 1, 1, CSLUL_MHT_EOF, 1, 2, 0, MHTAttrStart, CSLUL_E_UNEXPECTEDEOF, 1, 2); tfree(buff); free_ctx(ctx); } /** Tests parsing a backslash with EOF in the next buffer */ static void test_mhtok_backslash_eof2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_MODULEHEADER); char *buff = cut0("\\"); TEST_ONE(buff, 1, 0, CSLUL_MHT_NEEDDATA, 1, 2, 0, MHTAttrStart); expect_error(CSLUL_E_UNEXPECTEDEOF, 1, 2); TEST_ONE(buff, 0, 1, CSLUL_MHT_EOF, 1, 2, 0, MHTAttrStart); tfree(buff); free_ctx(ctx); } /** Tests input with missing newline at EOF */ static void test_mhtok_eof_newline(void) { struct CSlul *ctx = create_ctx(CSLUL_P_MODULEHEADER); char *buff = cut0("\\d"); TEST_ONE(buff, 2, 1, CSLUL_MHT_AttrName, 1, 3, 0, MHTAttrSpaceStart); expect_error(CSLUL_E_NOEOFNEWLINE, 1, 3); TEST_CONT(CSLUL_MHT_EOF, 1, 3, 0, MHTAttrSpaceStart); tfree(buff); free_ctx(ctx); } static void test_mhtok_value(void) { struct CSlul *ctx = create_ctx(CSLUL_P_MODULEHEADER); char *buff = cut0("\\name va\xc2\xa4lue\n"); TEST_ONE(buff, 14, 1, CSLUL_MHT_AttrName, 1, 6, 0, MHTAttrSpaceStart); ASSERT_TOKEN_VALUE("name", 1, 1); TEST_CONT(CSLUL_MHT_AttrValue, 1, 13, 1, MHTAttrSpaceStart); ASSERT_RAW_VALUE("va\xc2\xa4lue", 1, 7); TEST_CONT(CSLUL_MHT_EOF, 2, 1, 0, MHTDone); tfree(buff); free_ctx(ctx); } static void test_mhtok_value_spaces(void) { struct CSlul *ctx = create_ctx(CSLUL_P_MODULEHEADER); char *buff = cut0("\\name value \n"); TEST_ONE(buff, 17, 1, CSLUL_MHT_AttrName, 1, 6, 0, MHTAttrSpaceStart); ASSERT_TOKEN_VALUE("name", 1, 1); TEST_CONT(CSLUL_MHT_AttrValue, 1, 15, 0, MHTAttrSpaceStart); ASSERT_RAW_VALUE("value", 1, 10); expect_error(CSLUL_E_TRAILINGSPACE, 1, 17); TEST_CONT(CSLUL_MHT_EOF, 2, 1, 0, MHTDone); tfree(buff); free_ctx(ctx); } static void test_mhtok_value_nospaces_splitted(void) { struct CSlul *ctx = create_ctx(CSLUL_P_MODULEHEADER); char *buff = cut0("\\"); TEST_ONE(buff, 1, 0, CSLUL_MHT_NEEDDATA, 1, 2, 0, MHTAttrStart); buff[0] = 'd'; TEST_ONE(buff, 1, 0, CSLUL_MHT_NEEDDATA, 1, 3, 0, MHTAttrName); buff[0] = ' '; TEST_ONE(buff, 1, 0, CSLUL_MHT_AttrName, 1, 3, 0, MHTAttrSpaceStart); ASSERT_TOKEN_VALUE("d", 1, 1); TEST_CONT(CSLUL_MHT_NEEDDATA, 1, 4, 0, MHTAttrSpace); buff[0] = 'v'; TEST_ONE(buff, 1, 0, CSLUL_MHT_NEEDDATA, 1, 5, 0, MHTAttrValue); buff[0] = '\xc2'; TEST_ONE(buff, 1, 0, CSLUL_MHT_NEEDDATA, 1, 6, 0, MHTAttrValue); buff[0] = '\xa4'; TEST_ONE(buff, 1, 0, CSLUL_MHT_NEEDDATA, 1, 6, 1, MHTAttrValue); buff[0] = '\n'; TEST_ONE(buff, 1, 1, CSLUL_MHT_AttrValue, 1, 6, 0, MHTAttrSpaceStart); ASSERT_RAW_VALUE("v\xc2\xa4", 1, 4); TEST_CONT(CSLUL_MHT_EOF, 2, 1, 0, MHTDone); tfree(buff); free_ctx(ctx); } static void test_mhtok_value_spaces_splitted(void) { struct CSlul *ctx = create_ctx(CSLUL_P_MODULEHEADER); char *buff = cut0("\\d "); TEST_ONE(buff, 3, 0, CSLUL_MHT_AttrName, 1, 3, 0, MHTAttrSpaceStart); ASSERT_TOKEN_VALUE("d", 1, 1); TEST_CONT(CSLUL_MHT_NEEDDATA, 1, 4, 0, MHTAttrSpace); tfree(buff); buff = cut0(" "); TEST_ONE(buff, 3, 0, CSLUL_MHT_NEEDDATA, 1, 7, 0, MHTAttrSpace); buff[1] = 'v'; TEST_ONE(buff, 3, 0, CSLUL_MHT_AttrValue, 1, 9, 0, MHTAttrSpaceStart); ASSERT_RAW_VALUE("v", 1, 8); TEST_CONT(CSLUL_MHT_NEEDDATA, 1, 10, 0, MHTAttrSpace); tfree(buff); buff = cut0("\n"); TEST_ERROR(buff, 1, 1, CSLUL_MHT_EOF, 2, 1, 0, MHTDone, CSLUL_E_TRAILINGSPACE, 1, 10); tfree(buff); free_ctx(ctx); } const TestFunctionInfo tests_mhtoken[] = { TEST_BEFORE(before_test) TEST_AFTER(after_test) TEST_INFO(test_mhtok_empty) TEST_INFO(test_mhtok_newline_single) TEST_INFO(test_mhtok_newline_splitted) TEST_INFO(test_mhtok_newline_eof1) TEST_INFO(test_mhtok_newline_eof2) TEST_INFO(test_mhtok_null) TEST_INFO(test_mhtok_utf8_2min) TEST_INFO(test_mhtok_utf8_2max) TEST_INFO(test_mhtok_utf8_2split) TEST_INFO(test_mhtok_utf8_2overlong) TEST_INFO(test_mhtok_utf8_2eof) TEST_INFO(test_mhtok_utf8_2badcont) TEST_INFO(test_mhtok_utf8_3min) TEST_INFO(test_mhtok_utf8_3max) TEST_INFO(test_mhtok_utf8_3split) TEST_INFO(test_mhtok_utf8_3overlong) TEST_INFO(test_mhtok_utf8_3eof2) TEST_INFO(test_mhtok_utf8_3nullchar3) TEST_INFO(test_mhtok_utf8_4min) TEST_INFO(test_mhtok_utf8_4max) TEST_INFO(test_mhtok_utf8_4split) TEST_INFO(test_mhtok_utf8_4overlong) TEST_INFO(test_mhtok_utf8_4overflow) TEST_INFO(test_mhtok_utf8_4eof2) TEST_INFO(test_mhtok_utf8_4eof3) TEST_INFO(test_mhtok_utf8_4nullchar3) TEST_INFO(test_mhtok_utf8_4nullchar4) TEST_INFO(test_mhtok_utf8_forbidden) TEST_INFO(test_mhtok_indentation) TEST_INFO(test_mhtok_indentation_tab) TEST_INFO(test_mhtok_comment) TEST_INFO(test_mhtok_comment_badchar) TEST_INFO(test_mhtok_multiline_comment) TEST_INFO(test_mhtok_multiline_comment_badstart) TEST_INFO(test_mhtok_attr_splitted) TEST_INFO(test_mhtok_attr_multi) TEST_INFO(test_mhtok_attr_eof) TEST_INFO(test_mhtok_attr_badchar) TEST_INFO(test_mhtok_attr_nobackslash) TEST_INFO(test_mhtok_end_of_header) TEST_INFO(test_mhtok_backslash_eof1) TEST_INFO(test_mhtok_backslash_eof2) TEST_INFO(test_mhtok_eof_newline) TEST_INFO(test_mhtok_value) TEST_INFO(test_mhtok_value_spaces) TEST_INFO(test_mhtok_value_nospaces_splitted) TEST_INFO(test_mhtok_value_spaces_splitted) TEST_END };