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