LCOV - code coverage report
Current view: top level - src/tests - token.c (source / functions) Hit Total Coverage
Test: passgen-test.info Lines: 170 170 100.0 %
Date: 2024-11-29 06:05:05 Functions: 11 11 100.0 %

          Line data    Source code
       1             : #include "passgen/parser/token.h"
       2             : #include "tests.h"
       3             : 
       4             : #define CODEPOINT_MAX 0x10FFFF
       5             : 
       6             : // Test that normal (non-escaped) tokens are parsed corredly. They should
       7             : // just be fed through.
       8           1 : test_result test_token_normal(void) {
       9             :     passgen_token_parser parser;
      10             :     passgen_token token;
      11           1 :     passgen_token_parser_init(&parser);
      12             : 
      13     1114112 :     for(uint32_t codepoint = 0; codepoint < CODEPOINT_MAX; codepoint++) {
      14     1114111 :         if(codepoint == '\\') {
      15           1 :             continue;
      16             :         }
      17     1114110 :         int ret = passgen_token_parse(&parser, &token, 1, codepoint);
      18     1114110 :         assert_eq(ret, PASSGEN_TOKEN_INIT);
      19     1114110 :         assert_eq(parser.state, PASSGEN_TOKEN_INIT);
      20     1114110 :         assert_eq(token.codepoint, codepoint);
      21             :     }
      22             : 
      23           1 :     return test_ok;
      24             : }
      25             : 
      26             : // Test that regular escapes are handled correctly: they should resolve
      27             : // to the appropriate characters.
      28           1 : test_result test_token_escaped(void) {
      29             :     passgen_token_parser parser;
      30             :     passgen_token token;
      31           1 :     passgen_token_parser_init(&parser);
      32             : 
      33           1 :     uint32_t escapes[][2] = {
      34             :         {'\\', '\\'},
      35             :         {'a', '\a'},
      36             :         {'b', '\b'},
      37             :         {'f', '\f'},
      38             :         {'r', '\r'},
      39             :         {'n', '\n'},
      40             :         {'t', '\t'},
      41             :         {'v', '\v'},
      42             :         {'e', '\033'},
      43             :         {0, 0}};
      44             : 
      45          10 :     for(size_t i = 0; escapes[i][0]; i++) {
      46           9 :         assert_eq(
      47             :             passgen_token_parse(&parser, &token, 1, '\\'),
      48             :             PASSGEN_TOKEN_ESCAPED);
      49           9 :         assert_eq(parser.state, PASSGEN_TOKEN_ESCAPED);
      50           9 :         assert_eq(
      51             :             passgen_token_parse(&parser, &token, 1, escapes[i][0]),
      52             :             PASSGEN_TOKEN_INIT);
      53           9 :         assert_eq(parser.state, PASSGEN_TOKEN_INIT);
      54           9 :         assert_eq(token.codepoint, escapes[i][1]);
      55             :     }
      56             : 
      57           1 :     return test_ok;
      58             : }
      59             : 
      60             : // Test that regular characters have the escape bit set when parsed with
      61             : // leading backslashes.
      62           1 : test_result test_token_special_escaped(void) {
      63             :     passgen_token_parser parser;
      64             :     passgen_token token;
      65           1 :     passgen_token_parser_init(&parser);
      66             : 
      67     1114112 :     for(uint32_t codepoint = 0; codepoint < CODEPOINT_MAX; codepoint++) {
      68     1114111 :         switch(codepoint) {
      69          10 :             case '\\':
      70             :             case 'a':
      71             :             case 'b':
      72             :             case 'f':
      73             :             case 'r':
      74             :             case 'n':
      75             :             case 't':
      76             :             case 'v':
      77             :             case 'u':
      78             :             case 'e':
      79          10 :                 continue;
      80     1114101 :             default:
      81     1114101 :                 break;
      82             :         }
      83     1114101 :         int ret = passgen_token_parse(&parser, &token, 1, '\\');
      84     1114101 :         assert_eq(ret, PASSGEN_TOKEN_ESCAPED);
      85     1114101 :         assert_eq(parser.state, PASSGEN_TOKEN_ESCAPED);
      86             : 
      87     1114101 :         ret = passgen_token_parse(&parser, &token, 1, codepoint);
      88     1114101 :         assert_eq(ret, PASSGEN_TOKEN_INIT);
      89     1114101 :         assert_eq(parser.state, PASSGEN_TOKEN_INIT);
      90     1114101 :         assert_eq(token.codepoint, (codepoint | PASSGEN_TOKEN_ESCAPED_BIT));
      91             :     }
      92             : 
      93           1 :     return test_ok;
      94             : }
      95             : 
      96             : // Test that escaped unicode tokens (eg: \u{ffff}) get parsed correctly.
      97           1 : test_result test_token_unicode(void) {
      98             :     passgen_token_parser parser;
      99             :     passgen_token token;
     100           1 :     passgen_token_parser_init(&parser);
     101             : 
     102             :     char buffer[8];
     103     1114112 :     for(uint32_t codepoint = 0; codepoint < CODEPOINT_MAX; codepoint++) {
     104     1114111 :         sprintf(buffer, "%x", codepoint);
     105     1114111 :         int ret = passgen_token_parse(&parser, &token, 1, '\\');
     106     1114111 :         assert_eq(ret, PASSGEN_TOKEN_ESCAPED);
     107             : 
     108     1114111 :         ret = passgen_token_parse(&parser, &token, 1, 'u');
     109     1114111 :         assert_eq(ret, PASSGEN_TOKEN_UNICODE);
     110             : 
     111     1114111 :         ret = passgen_token_parse(&parser, &token, 1, '{');
     112     1114111 :         assert_eq(ret, PASSGEN_TOKEN_UNICODE_PAYLOAD);
     113     1114111 :         assert_eq(parser.data.unicode_payload.length, 0);
     114             : 
     115     6680297 :         for(size_t i = 0; buffer[i]; i++) {
     116     5566186 :             ret = passgen_token_parse(&parser, &token, 1, buffer[i]);
     117     5566186 :             assert_eq(ret, PASSGEN_TOKEN_UNICODE_PAYLOAD);
     118     5566186 :             assert_eq(parser.data.unicode_payload.length, i + 1);
     119             :         }
     120             : 
     121     1114111 :         ret = passgen_token_parse(&parser, &token, 1, '}');
     122     1114111 :         assert_eq(ret, PASSGEN_TOKEN_INIT);
     123     1114111 :         assert_eq(token.codepoint, codepoint);
     124             :     }
     125             : 
     126           1 :     return test_ok;
     127             : }
     128             : 
     129             : // Test that passing any character that is not an opening brace after \u
     130             : // causes an error state (so \u{FC} is fine, but \u[ is not).
     131           1 : test_result test_token_unicode_error_start(void) {
     132             :     passgen_token_parser parser;
     133             :     passgen_token token;
     134           1 :     passgen_token_parser_init(&parser);
     135             : 
     136           1 :     uint32_t chars[] =
     137             :         {'a', 'b', 'c', 'x', 'y', '0', '9', '-', '_', '}', '[', 0};
     138             : 
     139          12 :     for(size_t i = 0; chars[i]; i++) {
     140          11 :         passgen_token_parser_init(&parser);
     141          11 :         assert_eq(
     142             :             passgen_token_parse(&parser, &token, 1, '\\'),
     143             :             PASSGEN_TOKEN_ESCAPED);
     144          11 :         assert_eq(
     145             :             passgen_token_parse(&parser, &token, 1, 'u'),
     146             :             PASSGEN_TOKEN_UNICODE);
     147          11 :         assert_eq(
     148             :             passgen_token_parse(&parser, &token, 1, chars[i]),
     149             :             PASSGEN_TOKEN_ERROR_UNICODE_START);
     150             :     }
     151             : 
     152           1 :     return test_ok;
     153             : }
     154             : 
     155             : // Test that passing any character that is not a hexadecimal character after \u{
     156             : // causes an error state (so \u{FC} is fine, but \u{ZZ} is not).
     157           1 : test_result test_token_unicode_error_payload(void) {
     158             :     passgen_token_parser parser;
     159             :     passgen_token token;
     160             : 
     161           1 :     uint32_t chars[] = {'x', ' ', '_', '-', '!', '+', '=', 'w', 'g', '[', 0};
     162             : 
     163          11 :     for(size_t i = 0; chars[i]; i++) {
     164          10 :         passgen_token_parser_init(&parser);
     165          10 :         assert_eq(
     166             :             passgen_token_parse(&parser, &token, 1, '\\'),
     167             :             PASSGEN_TOKEN_ESCAPED);
     168          10 :         assert_eq(
     169             :             passgen_token_parse(&parser, &token, 1, 'u'),
     170             :             PASSGEN_TOKEN_UNICODE);
     171          10 :         assert_eq(
     172             :             passgen_token_parse(&parser, &token, 1, '{'),
     173             :             PASSGEN_TOKEN_UNICODE_PAYLOAD);
     174          10 :         assert_eq(
     175             :             passgen_token_parse(&parser, &token, 1, chars[i]),
     176             :             PASSGEN_TOKEN_ERROR_UNICODE_PAYLOAD);
     177             :     }
     178             : 
     179           1 :     return test_ok;
     180             : }
     181             : 
     182             : // Test that passing any character that is not an opening brace after \u
     183             : // causes an error state (so \u{FC} is fine, but \u[ is not).
     184           1 : test_result test_token_unicode_error_len(void) {
     185             :     passgen_token_parser parser;
     186             :     passgen_token token;
     187             : 
     188           1 :     const uint32_t inputs[][7] = {
     189             :         {'0', '0', '0', '0', '0', '0', '0'},
     190             :         {'1', '0', 'f', 'f', 'f', 'f', '0'},
     191             :         {'f', 'f', 'f', 'f', 'f', 'f', 'f'},
     192             :         {'0', '1', '2', '3', '4', '5', '6'},
     193             :         {0}};
     194             : 
     195           5 :     for(size_t i = 0; inputs[i][0]; i++) {
     196           4 :         passgen_token_parser_init(&parser);
     197           4 :         assert_eq(
     198             :             passgen_token_parse(&parser, &token, 1, '\\'),
     199             :             PASSGEN_TOKEN_ESCAPED);
     200           4 :         assert_eq(
     201             :             passgen_token_parse(&parser, &token, 1, 'u'),
     202             :             PASSGEN_TOKEN_UNICODE);
     203           4 :         assert_eq(
     204             :             passgen_token_parse(&parser, &token, 1, '{'),
     205             :             PASSGEN_TOKEN_UNICODE_PAYLOAD);
     206           4 :         assert_eq(parser.data.unicode_payload.length, 0);
     207          28 :         for(size_t c = 0; c < 6; c++) {
     208          24 :             assert_eq(
     209             :                 passgen_token_parse(&parser, &token, 1, inputs[i][c]),
     210             :                 PASSGEN_TOKEN_UNICODE_PAYLOAD);
     211          24 :             assert_eq(parser.data.unicode_payload.length, c + 1);
     212             :         }
     213           4 :         assert_eq(
     214             :             passgen_token_parse(&parser, &token, 1, inputs[i][6]),
     215             :             PASSGEN_TOKEN_ERROR_UNICODE_PAYLOAD_LEN);
     216           4 :         assert_eq(parser.data.unicode_payload.length, 7);
     217             :     }
     218             : 
     219           1 :     return test_ok;
     220             : }
     221             : 
     222           1 : test_result test_token_state_string(void) {
     223             :     // initial state
     224           1 :     assert(passgen_token_state_string(PASSGEN_TOKEN_INIT) != NULL);
     225             : 
     226             :     // multi-charpoint states
     227           1 :     assert(passgen_token_state_string(PASSGEN_TOKEN_ESCAPED) != NULL);
     228           1 :     assert(passgen_token_state_string(PASSGEN_TOKEN_UNICODE) != NULL);
     229           1 :     assert(passgen_token_state_string(PASSGEN_TOKEN_UNICODE_PAYLOAD) != NULL);
     230             : 
     231             :     // error states
     232           1 :     assert(
     233             :         passgen_token_state_string(PASSGEN_TOKEN_ERROR_UNICODE_START) != NULL);
     234           1 :     assert(
     235             :         passgen_token_state_string(PASSGEN_TOKEN_ERROR_UNICODE_PAYLOAD) !=
     236             :         NULL);
     237           1 :     assert(
     238             :         passgen_token_state_string(PASSGEN_TOKEN_ERROR_UNICODE_PAYLOAD_LEN) !=
     239             :         NULL);
     240             : 
     241             :     // undefined
     242           1 :     assert(
     243             :         passgen_token_state_string(PASSGEN_TOKEN_UNICODE_PAYLOAD + 1) == NULL);
     244           1 :     assert(
     245             :         passgen_token_state_string(
     246             :             PASSGEN_TOKEN_ERROR_UNICODE_PAYLOAD_LEN - 1) == NULL);
     247             : 
     248           1 :     return test_ok;
     249             : }
     250             : 
     251             : // Test that the token parser correctly keeps track of byte and codepoint
     252             : // offsets.
     253           1 : test_result test_token_normal_offsets(void) {
     254           1 :     passgen_token_parser parser = {0};
     255           1 :     passgen_token token = {0};
     256             : 
     257             : #define PARSE(cp, width)                                   \
     258             :     assert(                                                \
     259             :         passgen_token_parse(&parser, &token, width, cp) == \
     260             :         PASSGEN_TOKEN_INIT);                               \
     261             :     assert(parser.state == PASSGEN_TOKEN_INIT);            \
     262             :     assert(token.codepoint == cp);
     263             : 
     264           1 :     PARSE('a', 1);
     265           1 :     assert(parser.offset == 1);
     266           1 :     assert(parser.byte_offset == 1);
     267           1 :     assert(token.offset == 0);
     268           1 :     assert(token.byte_offset == 0);
     269             : 
     270           1 :     PARSE('b', 1);
     271           1 :     assert(parser.offset == 2);
     272           1 :     assert(parser.byte_offset == 2);
     273           1 :     assert(token.offset == 1);
     274           1 :     assert(token.byte_offset == 1);
     275             : 
     276           1 :     PARSE('c', 2);
     277           1 :     assert(parser.offset == 3);
     278           1 :     assert(parser.byte_offset == 4);
     279           1 :     assert(token.offset == 2);
     280           1 :     assert(token.byte_offset == 2);
     281             : 
     282           1 :     PARSE(' ', 2);
     283           1 :     assert(parser.offset == 4);
     284           1 :     assert(parser.byte_offset == 6);
     285           1 :     assert(token.offset == 3);
     286           1 :     assert(token.byte_offset == 4);
     287             : 
     288           1 :     PARSE('!', 3);
     289           1 :     assert(parser.offset == 5);
     290           1 :     assert(parser.byte_offset == 9);
     291           1 :     assert(token.offset == 4);
     292           1 :     assert(token.byte_offset == 6);
     293             : 
     294           1 :     PARSE('[', 3);
     295           1 :     assert(parser.offset == 6);
     296           1 :     assert(parser.byte_offset == 12);
     297           1 :     assert(token.offset == 5);
     298           1 :     assert(token.byte_offset == 9);
     299             : 
     300           1 :     PARSE(']', 4);
     301           1 :     assert(parser.offset == 7);
     302           1 :     assert(parser.byte_offset == 16);
     303           1 :     assert(token.offset == 6);
     304           1 :     assert(token.byte_offset == 12);
     305             : 
     306             : #undef PARSE
     307             : 
     308           1 :     return test_ok;
     309             : }
     310             : 
     311             : // Test that the token parser correctly keeps track of byte and codepoint
     312             : // offsets.
     313           1 : test_result test_token_multi_offsets(void) {
     314           1 :     passgen_token_parser parser = {0};
     315           1 :     passgen_token token = {0};
     316             : 
     317           1 :     assert(
     318             :         passgen_token_parse(&parser, &token, 1, '\\') == PASSGEN_TOKEN_ESCAPED);
     319           1 :     assert(passgen_token_parse(&parser, &token, 1, '[') == PASSGEN_TOKEN_INIT);
     320           1 :     assert(parser.offset == 2);
     321           1 :     assert(parser.byte_offset == 2);
     322           1 :     assert(token.offset == 0);
     323           1 :     assert(token.byte_offset == 0);
     324             : 
     325           1 :     assert(
     326             :         passgen_token_parse(&parser, &token, 1, '\\') == PASSGEN_TOKEN_ESCAPED);
     327           1 :     assert(passgen_token_parse(&parser, &token, 1, ']') == PASSGEN_TOKEN_INIT);
     328           1 :     assert(parser.offset == 4);
     329           1 :     assert(parser.byte_offset == 4);
     330           1 :     assert(token.offset == 2);
     331           1 :     assert(token.byte_offset == 2);
     332             : 
     333           1 :     assert(
     334             :         passgen_token_parse(&parser, &token, 1, '\\') == PASSGEN_TOKEN_ESCAPED);
     335           1 :     assert(
     336             :         passgen_token_parse(&parser, &token, 1, 'u') == PASSGEN_TOKEN_UNICODE);
     337           1 :     assert(
     338             :         passgen_token_parse(&parser, &token, 1, '{') ==
     339             :         PASSGEN_TOKEN_UNICODE_PAYLOAD);
     340           1 :     assert(
     341             :         passgen_token_parse(&parser, &token, 1, '0') ==
     342             :         PASSGEN_TOKEN_UNICODE_PAYLOAD);
     343           1 :     assert(
     344             :         passgen_token_parse(&parser, &token, 1, 'a') ==
     345             :         PASSGEN_TOKEN_UNICODE_PAYLOAD);
     346           1 :     assert(passgen_token_parse(&parser, &token, 1, '}') == PASSGEN_TOKEN_INIT);
     347           1 :     assert(parser.offset == 10);
     348           1 :     assert(parser.byte_offset == 10);
     349           1 :     assert(token.offset == 4);
     350           1 :     assert(token.byte_offset == 4);
     351             : 
     352           1 :     return test_ok;
     353             : }
     354             : 
     355             : // Test that parsing any character in an error state simply returns that error
     356             : // state.
     357           1 : test_result test_token_error_propagation(void) {
     358             :     passgen_token_parser parser;
     359             :     passgen_token token;
     360           1 :     int errors[] = {
     361             :         PASSGEN_TOKEN_ERROR_UNICODE_START,
     362             :         PASSGEN_TOKEN_ERROR_UNICODE_PAYLOAD,
     363             :         PASSGEN_TOKEN_ERROR_UNICODE_PAYLOAD_LEN,
     364             :         0};
     365             : 
     366           4 :     for(size_t i = 0; errors[i]; i++) {
     367           3 :         passgen_token_parser_init(&parser);
     368           3 :         parser.state = errors[i];
     369           3 :         assert_eq(passgen_token_parse(&parser, &token, 1, 'a'), errors[i]);
     370             :     }
     371             : 
     372           1 :     return test_ok;
     373             : }

Generated by: LCOV version 1.14