Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. #include <inttypes.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4.  
  5. #include <json.h>
  6.  
  7. #include <parserutils/input/inputstream.h>
  8.  
  9. #include <hubbub/hubbub.h>
  10.  
  11. #include "utils/utils.h"
  12.  
  13. #include "tokeniser/tokeniser.h"
  14.  
  15. #include "testutils.h"
  16.  
  17. typedef struct context {
  18.         const uint8_t *input;
  19.         size_t input_len;
  20.  
  21.         struct array_list *output;
  22.         int output_index;
  23.         size_t char_off;
  24.  
  25.         const char *last_start_tag;
  26.         struct array_list *content_model;
  27.         bool process_cdata;
  28. } context;
  29.  
  30. static void run_test(context *ctx);
  31. static hubbub_error token_handler(const hubbub_token *token, void *pw);
  32.  
  33. static void *myrealloc(void *ptr, size_t len, void *pw)
  34. {
  35.         UNUSED(pw);
  36.  
  37.         return realloc(ptr, len);
  38. }
  39.  
  40. int main(int argc, char **argv)
  41. {
  42.         struct json_object *json;
  43.         struct array_list *tests;
  44.         struct lh_entry *entry;
  45.         char *key;
  46.         struct json_object *val;
  47.         int i;
  48.         context ctx;
  49.  
  50.         if (argc != 2) {
  51.                 printf("Usage: %s <filename>\n", argv[0]);
  52.                 return 1;
  53.         }
  54.  
  55.         json = json_object_from_file(argv[1]);
  56.         assert(!is_error(json));
  57.  
  58.         assert(strcmp((char *) ((json_object_get_object(json)->head)->k),
  59.                         "tests") == 0);
  60.  
  61.         /* Get array of tests */
  62.         tests = json_object_get_array((struct json_object *)
  63.                         (json_object_get_object(json)->head)->v);
  64.  
  65.         for (i = 0; i < array_list_length(tests); i++) {
  66.                 /* Get test */
  67.                 struct json_object *test =
  68.                         (struct json_object *) array_list_get_idx(tests, i);
  69.  
  70.                 ctx.last_start_tag = NULL;
  71.                 ctx.content_model = NULL;
  72.                 ctx.process_cdata = false;
  73.  
  74.                 /* Extract settings */
  75.                 for (entry = json_object_get_object(test)->head; entry;
  76.                                 entry = entry->next) {
  77.                         key = (char *) entry->k;
  78.                         val = (struct json_object *) entry->v;
  79.  
  80.                         if (strcmp(key, "description") == 0) {
  81.                                 printf("Test: %s\n",
  82.                                         json_object_get_string(val));
  83.                         } else if (strcmp(key, "input") == 0) {
  84.                                 int len = json_object_get_string_len(val);
  85.                                 ctx.input = (const uint8_t *)
  86.                                                 json_object_get_string(val);
  87.                                 ctx.input_len = len;
  88.                         } else if (strcmp(key, "output") == 0) {
  89.                                 ctx.output = json_object_get_array(val);
  90.                                 ctx.output_index = 0;
  91.                                 ctx.char_off = 0;
  92.                         } else if (strcmp(key, "lastStartTag") == 0) {
  93.                                 ctx.last_start_tag = (const char *)
  94.                                                 json_object_get_string(val);
  95.                         } else if (strcmp(key, "contentModelFlags") == 0) {
  96.                                 ctx.content_model =
  97.                                                 json_object_get_array(val);
  98.                         } else if (strcmp(key, "processCDATA") == 0) {
  99.                                 ctx.process_cdata =
  100.                                         json_object_get_boolean(val);
  101.                         }
  102.                 }
  103.  
  104.                 /* And run the test */
  105.                 run_test(&ctx);
  106.         }
  107.  
  108.         printf("PASS\n");
  109.  
  110.         return 0;
  111. }
  112.  
  113. void run_test(context *ctx)
  114. {
  115.         parserutils_inputstream *stream;
  116.         hubbub_tokeniser *tok;
  117.         hubbub_tokeniser_optparams params;
  118.         int i, max_i;
  119.         size_t j;
  120.         struct array_list *outputsave = ctx->output;
  121.  
  122.         if (ctx->content_model == NULL) {
  123.                 max_i = 1;
  124.         } else {
  125.                 max_i = array_list_length(ctx->content_model);
  126.         }
  127.  
  128.         /* We test for each of the content models specified */
  129.         for (i = 0; i < max_i; i++) {
  130.                 /* Reset expected output */
  131.                 ctx->output = outputsave;
  132.                 ctx->output_index = 0;
  133.                 ctx->char_off = 0;
  134.  
  135.                 assert(parserutils_inputstream_create("UTF-8", 0, NULL,
  136.                                 myrealloc, NULL, &stream) == PARSERUTILS_OK);
  137.  
  138.                 assert(hubbub_tokeniser_create(stream, myrealloc, NULL, &tok) ==
  139.                                 HUBBUB_OK);
  140.  
  141.                 if (ctx->last_start_tag != NULL) {
  142.                         /* Fake up a start tag, in PCDATA state */
  143.                         size_t len = strlen(ctx->last_start_tag) + 3;
  144.                         uint8_t *buf = alloca(len);
  145.  
  146.                         snprintf((char *) buf, len, "<%s>",
  147.                                         ctx->last_start_tag);
  148.  
  149.                         assert(parserutils_inputstream_append(stream,
  150.                                 buf, len - 1) == HUBBUB_OK);
  151.  
  152.                         assert(hubbub_tokeniser_run(tok) == HUBBUB_OK);
  153.                 }
  154.  
  155.                 if (ctx->process_cdata) {
  156.                         params.process_cdata = ctx->process_cdata;
  157.                         assert(hubbub_tokeniser_setopt(tok,
  158.                                         HUBBUB_TOKENISER_PROCESS_CDATA,
  159.                                         &params) == HUBBUB_OK);
  160.                 }
  161.  
  162.                 params.token_handler.handler = token_handler;
  163.                 params.token_handler.pw = ctx;
  164.                 assert(hubbub_tokeniser_setopt(tok,
  165.                                 HUBBUB_TOKENISER_TOKEN_HANDLER,
  166.                                 &params) == HUBBUB_OK);
  167.  
  168.                 if (ctx->content_model == NULL) {
  169.                         params.content_model.model =
  170.                                         HUBBUB_CONTENT_MODEL_PCDATA;
  171.                 } else {
  172.                         const char *cm = json_object_get_string(
  173.                                 (struct json_object *)
  174.                                 array_list_get_idx(ctx->content_model, i));
  175.  
  176.                         if (strcmp(cm, "PCDATA") == 0) {
  177.                                 params.content_model.model =
  178.                                                 HUBBUB_CONTENT_MODEL_PCDATA;
  179.                         } else if (strcmp(cm, "RCDATA") == 0) {
  180.                                 params.content_model.model =
  181.                                                 HUBBUB_CONTENT_MODEL_RCDATA;
  182.                         } else if (strcmp(cm, "CDATA") == 0) {
  183.                                 params.content_model.model =
  184.                                                 HUBBUB_CONTENT_MODEL_CDATA;
  185.                         } else {
  186.                                 params.content_model.model =
  187.                                         HUBBUB_CONTENT_MODEL_PLAINTEXT;
  188.                         }
  189.                 }
  190.                 assert(hubbub_tokeniser_setopt(tok,
  191.                                 HUBBUB_TOKENISER_CONTENT_MODEL,
  192.                                 &params) == HUBBUB_OK);
  193.  
  194.                 printf("Input: '%.*s' (%d)\n", (int) ctx->input_len,
  195.                                 (const char *) ctx->input,
  196.                                 (int) ctx->input_len);
  197.  
  198.                 for (j = 0; j < ctx->input_len; j++) {
  199.                         assert(parserutils_inputstream_append(stream,
  200.                                         ctx->input + j, 1) ==
  201.                                                         HUBBUB_OK);
  202.  
  203.                         assert(hubbub_tokeniser_run(tok) == HUBBUB_OK);
  204.                 }
  205.  
  206.                 assert(parserutils_inputstream_append(stream, NULL, 0) ==
  207.                                 HUBBUB_OK);
  208.  
  209.                 assert(hubbub_tokeniser_run(tok) == HUBBUB_OK);
  210.  
  211.                 hubbub_tokeniser_destroy(tok);
  212.  
  213.                 parserutils_inputstream_destroy(stream);
  214.         }
  215. }
  216.  
  217. hubbub_error token_handler(const hubbub_token *token, void *pw)
  218. {
  219.         static const char *token_names[] = {
  220.                 "DOCTYPE", "StartTag", "EndTag",
  221.                 "Comment", "Character", "EOF"
  222.         };
  223.         size_t i;
  224.         context *ctx = (context *) pw;
  225.         struct json_object *obj = NULL;
  226.         struct array_list *items;
  227.  
  228.         for (; ctx->output_index < array_list_length(ctx->output);
  229.                         ctx->output_index++) {
  230.                 /* Get object for index */
  231.                 obj = (struct json_object *)
  232.                                 array_list_get_idx(ctx->output,
  233.                                                 ctx->output_index);
  234.  
  235.                 /* If it's not a string, we've found the expected output */
  236.                 if (json_object_get_type(obj) != json_type_string)
  237.                         break;
  238.  
  239.                 /* Otherwise, it must be a parse error */
  240.                 assert(strcmp(json_object_get_string(obj),
  241.                                  "ParseError") == 0);
  242.         }
  243.  
  244.         /* If we've run off the end, this is an error -- the tokeniser has
  245.          * produced more tokens than expected. We allow for the generation
  246.          * of a terminating EOF token, however. */
  247.         assert("too many tokens" &&
  248.                         (ctx->output_index < array_list_length(ctx->output) ||
  249.                         token->type == HUBBUB_TOKEN_EOF));
  250.  
  251.         /* Got a terminating EOF -- no error */
  252.         if (ctx->output_index >= array_list_length(ctx->output))
  253.                 return HUBBUB_OK;
  254.  
  255.         /* Now increment the output index so we don't re-expect this token */
  256.         ctx->output_index++;
  257.  
  258.         /* Expected output must be an array */
  259.         assert(json_object_get_type(obj) == json_type_array);
  260.  
  261.         items = json_object_get_array(obj);
  262.  
  263.         printf("got %s: expected %s\n", token_names[token->type],
  264.                         json_object_get_string((struct json_object *)
  265.                                 array_list_get_idx(items, 0)));
  266.  
  267.         /* Make sure we got the token we expected */
  268.         assert(strcmp(token_names[token->type],
  269.                         json_object_get_string((struct json_object *)
  270.                                 array_list_get_idx(items, 0))) == 0);
  271.  
  272.         switch (token->type) {
  273.         case HUBBUB_TOKEN_DOCTYPE:
  274.         {
  275.                 const char *expname = json_object_get_string(
  276.                                 array_list_get_idx(items, 1));
  277.                 const char *exppub = json_object_get_string(
  278.                                 array_list_get_idx(items, 2));
  279.                 const char *expsys = json_object_get_string(
  280.                                 array_list_get_idx(items, 3));
  281.                 bool expquirks = !json_object_get_boolean(
  282.                                 array_list_get_idx(items, 4));
  283.                 const char *gotname = (const char *)
  284.                                 token->data.doctype.name.ptr;
  285.                 const char *gotpub, *gotsys;
  286.  
  287.                 printf("'%.*s' %sids:\n",
  288.                                 (int) token->data.doctype.name.len,
  289.                                 gotname,
  290.                                 token->data.doctype.force_quirks ?
  291.                                                 "(force-quirks) " : "");
  292.  
  293.                 if (token->data.doctype.public_missing) {
  294.                         gotpub = NULL;
  295.                         printf("\tpublic: missing\n");
  296.                 } else {
  297.                         gotpub = (const char *)
  298.                                 token->data.doctype.public_id.ptr;
  299.                         printf("\tpublic: '%.*s' (%d)\n",
  300.                                 (int) token->data.doctype.public_id.len,
  301.                                 gotpub,
  302.                                 (int) token->data.doctype.public_id.len);
  303.                 }
  304.  
  305.                 if (token->data.doctype.system_missing) {
  306.                         gotsys = NULL;
  307.                         printf("\tsystem: missing\n");
  308.                 } else {
  309.                         gotsys = (const char *)
  310.                                 token->data.doctype.system_id.ptr;
  311.                         printf("\tsystem: '%.*s' (%d)\n",
  312.                                 (int) token->data.doctype.system_id.len,
  313.                                 gotsys,
  314.                                 (int) token->data.doctype.system_id.len);
  315.                 }
  316.  
  317.                 assert(token->data.doctype.name.len == strlen(expname));
  318.                 assert(strncmp(gotname, expname, strlen(expname)) == 0);
  319.  
  320.                 assert((exppub == NULL) ==
  321.                                 (token->data.doctype.public_missing == true));
  322.                 if (exppub) {
  323.                         assert(token->data.doctype.public_id.len == strlen(exppub));
  324.                         assert(strncmp(gotpub, exppub, strlen(exppub)) == 0);
  325.                 }
  326.  
  327.                 assert((expsys == NULL) ==
  328.                                 (token->data.doctype.system_missing == true));
  329.                 if (gotsys) {
  330.                         assert(token->data.doctype.system_id.len == strlen(expsys));
  331.                         assert(strncmp(gotsys, expsys, strlen(expsys)) == 0);
  332.                 }
  333.  
  334.                 assert(expquirks == token->data.doctype.force_quirks);
  335.         }
  336.                 break;
  337.         case HUBBUB_TOKEN_START_TAG:
  338.         {
  339.                 const char *expname = json_object_get_string(
  340.                                 array_list_get_idx(items, 1));
  341.                 struct lh_entry *expattrs = json_object_get_object(
  342.                                 array_list_get_idx(items, 2))->head;
  343.                 bool self_closing = json_object_get_boolean(
  344.                                 array_list_get_idx(items, 3));
  345.  
  346.                 const char *tagname = (const char *)
  347.                                 token->data.tag.name.ptr;
  348.  
  349.                 printf("expected: '%s' %s\n",
  350.                                 expname,
  351.                                 (self_closing) ? "(self-closing) " : "");
  352.  
  353.                 printf("     got: '%.*s' %s\n",
  354.                                 (int) token->data.tag.name.len,
  355.                                 tagname,
  356.                                 (token->data.tag.self_closing) ?
  357.                                                 "(self-closing) " : "");
  358.  
  359.                 if (token->data.tag.n_attributes > 0) {
  360.                         printf("attributes:\n");
  361.                 }
  362.  
  363.                 assert(token->data.tag.name.len == strlen(expname));
  364.                 assert(strncmp(tagname, expname, strlen(expname)) == 0);
  365.  
  366.                 assert((token->data.tag.n_attributes == 0) ==
  367.                                 (expattrs == NULL));
  368.  
  369.                 assert(self_closing == token->data.tag.self_closing);
  370.  
  371.                 for (i = 0; i < token->data.tag.n_attributes; i++) {
  372.                         char *expname = (char *) expattrs->k;
  373.                         const char *expval = json_object_get_string(
  374.                                         (struct json_object *) expattrs->v);
  375.                         const char *gotname = (const char *)
  376.                                 token->data.tag.attributes[i].name.ptr;
  377.                         size_t namelen =
  378.                                 token->data.tag.attributes[i].name.len;
  379.                         const char *gotval = (const char *)
  380.                                 token->data.tag.attributes[i].value.ptr;
  381.                         size_t vallen =
  382.                                 token->data.tag.attributes[i].value.len;
  383.  
  384.                         printf("\t'%.*s' = '%.*s'\n",
  385.                                         (int) namelen, gotname,
  386.                                         (int) vallen, gotval);
  387.  
  388.                         assert(namelen == strlen(expname));
  389.                         assert(strncmp(gotname, expname,
  390.                                                 strlen(expname)) == 0);
  391.                         assert(vallen == strlen(expval));
  392.                         assert(strncmp(gotval, expval, strlen(expval)) == 0);
  393.  
  394.                         expattrs = expattrs->next;
  395.                 }
  396.  
  397.                 assert(expattrs == NULL);
  398.         }
  399.                 break;
  400.         case HUBBUB_TOKEN_END_TAG:
  401.         {
  402.                 const char *expname = json_object_get_string(
  403.                                 array_list_get_idx(items, 1));
  404.                 const char *tagname = (const char *)
  405.                                 token->data.tag.name.ptr;
  406.  
  407.                 printf("'%.*s' %s\n",
  408.                                 (int) token->data.tag.name.len,
  409.                                 tagname,
  410.                                 (token->data.tag.n_attributes > 0) ?
  411.                                                 "attributes:" : "");
  412.  
  413.                 assert(token->data.tag.name.len == strlen(expname));
  414.                 assert(strncmp(tagname, expname, strlen(expname)) == 0);
  415.         }
  416.                 break;
  417.         case HUBBUB_TOKEN_COMMENT:
  418.         {
  419.                 const char *expstr = json_object_get_string(
  420.                                 array_list_get_idx(items, 1));
  421.                 const char *gotstr = (const char *)
  422.                                 token->data.comment.ptr;
  423.  
  424.                 printf("expected: '%s'\n", expstr);
  425.                 printf("     got: '%.*s'\n",
  426.                                 (int) token->data.comment.len, gotstr);
  427.  
  428.                 assert(token->data.comment.len == strlen(expstr));
  429.                 assert(strncmp(gotstr, expstr, strlen(expstr)) == 0);
  430.         }
  431.                 break;
  432.         case HUBBUB_TOKEN_CHARACTER:
  433.         {
  434.                 int expstrlen = json_object_get_string_len(
  435.                                 array_list_get_idx(items, 1));
  436.                 const char *expstr = json_object_get_string(
  437.                                 array_list_get_idx(items, 1));
  438.                 const char *gotstr = (const char *)
  439.                                 token->data.character.ptr;
  440.                 size_t len = min(token->data.character.len,
  441.                                 expstrlen - ctx->char_off);
  442.  
  443.                 printf("expected: '%.*s'\n",
  444.                                 (int) len, expstr + ctx->char_off);
  445.                 printf("     got: '%.*s'\n",
  446.                                 (int) token->data.character.len, gotstr);
  447.  
  448.                 assert(memcmp(gotstr, expstr + ctx->char_off, len) == 0);
  449.  
  450.                 if (len < token->data.character.len) {
  451.                         /* Expected token only contained part of the data
  452.                          * Calculate how much is left, then try again with
  453.                          * the next expected token */
  454.                         hubbub_token t;
  455.  
  456.                         t.type = HUBBUB_TOKEN_CHARACTER;
  457.                         t.data.character.ptr += len;
  458.                         t.data.character.len -= len;
  459.  
  460.                         ctx->char_off = 0;
  461.  
  462.                         token_handler(&t, pw);
  463.                 } else if (strlen(expstr + ctx->char_off) >
  464.                                 token->data.character.len) {
  465.                         /* Tokeniser output only contained part of the data
  466.                          * in the expected token; calculate the offset into
  467.                          * the token and process the remainder next time */
  468.                         ctx->char_off += len;
  469.                         ctx->output_index--;
  470.                 } else {
  471.                         /* Exact match - clear offset */
  472.                         ctx->char_off = 0;
  473.                 }
  474.         }
  475.                 break;
  476.         case HUBBUB_TOKEN_EOF:
  477.                 printf("\n");
  478.                 break;
  479.         }
  480.  
  481.         return HUBBUB_OK;
  482. }
  483.