(root)/
glib-2.79.0/
glib/
tests/
markup-parse.c
       1  #undef G_DISABLE_ASSERT
       2  #undef G_LOG_DOMAIN
       3  
       4  #include <locale.h>
       5  #include <string.h>
       6  #include <stdio.h>
       7  #include <glib.h>
       8  
       9  static int depth = 0;
      10  static GString *string;
      11  
      12  static void
      13  indent (int extra)
      14  {
      15    int i = 0;
      16    while (i < depth)
      17      {
      18        g_string_append (string, "  ");
      19        ++i;
      20      }
      21  }
      22  
      23  static void
      24  start_element_handler  (GMarkupParseContext *context,
      25                          const gchar         *element_name,
      26                          const gchar        **attribute_names,
      27                          const gchar        **attribute_values,
      28                          gpointer             user_data,
      29                          GError             **error)
      30  {
      31    int i;
      32    
      33    indent (0);
      34    g_string_append_printf (string, "ELEMENT '%s'\n", element_name);
      35  
      36    i = 0;
      37    while (attribute_names[i] != NULL)
      38      {
      39        indent (1);
      40  
      41        g_string_append_printf (string, "%s=\"%s\"\n",
      42                                attribute_names[i],
      43                                attribute_values[i]);
      44        
      45        ++i;
      46      }
      47    
      48    ++depth;
      49  }
      50  
      51  static void
      52  end_element_handler (GMarkupParseContext *context,
      53                       const gchar         *element_name,
      54                       gpointer             user_data,
      55                       GError             **error)
      56  {
      57    --depth;
      58    indent (0);
      59    g_string_append_printf (string, "END '%s'\n", element_name);
      60    }
      61  
      62  static void
      63  text_handler (GMarkupParseContext *context,
      64                const gchar         *text,
      65                gsize                text_len,
      66                gpointer             user_data,
      67                GError             **error)
      68  {
      69    indent (0);
      70    g_string_append_printf (string, "TEXT '%.*s'\n", (int)text_len, text);
      71  }
      72  
      73  
      74  static void
      75  passthrough_handler (GMarkupParseContext *context,
      76                       const gchar         *passthrough_text,
      77                       gsize                text_len,
      78                       gpointer             user_data,
      79                       GError             **error)
      80  {
      81    indent (0);
      82  
      83    g_string_append_printf (string, "PASS '%.*s'\n", (int)text_len, passthrough_text);
      84  }
      85  
      86  static void
      87  error_handler (GMarkupParseContext *context,
      88                 GError              *error,
      89                 gpointer             user_data)
      90  {
      91    g_string_append_printf (string, "ERROR %s\n", error->message);
      92  }
      93  
      94  static const GMarkupParser parser = {
      95    start_element_handler,
      96    end_element_handler,
      97    text_handler,
      98    passthrough_handler,
      99    error_handler
     100  };
     101  
     102  static const GMarkupParser silent_parser = {
     103    NULL,
     104    NULL,
     105    NULL,
     106    NULL,
     107    error_handler
     108  };
     109  
     110  static int
     111  test_in_chunks (const gchar       *contents,
     112                  gint               length,
     113                  gint               chunk_size,
     114                  GMarkupParseFlags  flags)
     115  {
     116    GMarkupParseContext *context;
     117    int i = 0;
     118    
     119    context = g_markup_parse_context_new (&silent_parser, flags, NULL, NULL);
     120  
     121    while (i < length)
     122      {
     123        int this_chunk = MIN (length - i, chunk_size);
     124  
     125        if (!g_markup_parse_context_parse (context,
     126                                           contents + i,
     127                                           this_chunk,
     128                                           NULL))
     129          {
     130            g_markup_parse_context_free (context);
     131            return 1;
     132          }
     133  
     134        i += this_chunk;
     135      }
     136        
     137    if (!g_markup_parse_context_end_parse (context, NULL))
     138      {
     139        g_markup_parse_context_free (context);
     140        return 1;
     141      }
     142  
     143    g_markup_parse_context_free (context);
     144  
     145    return 0;
     146  }
     147  
     148  /* Load the given @filename and parse it multiple times with different chunking
     149   * and length handling. All results should be equal. %TRUE is returned if the
     150   * file was parsed successfully on every attempt; %FALSE if it failed to parse
     151   * on every attempt. The test aborts if some attempts succeed and some fail. */
     152  static gboolean
     153  test_file (const gchar       *filename,
     154             GMarkupParseFlags  flags)
     155  {
     156    gchar *contents = NULL, *contents_unterminated = NULL;
     157    gsize length_bytes;
     158    GError *local_error = NULL;
     159    GMarkupParseContext *context;
     160    gint line, col;
     161    guint n_failures = 0;
     162    guint n_tests = 0;
     163    const gsize chunk_sizes_bytes[] = { 1, 2, 5, 12, 1024 };
     164    gsize i;
     165    GString *first_string = NULL;
     166  
     167    g_file_get_contents (filename, &contents, &length_bytes, &local_error);
     168    g_assert_no_error (local_error);
     169  
     170    /* Make a copy of the contents with no trailing nul. */
     171    contents_unterminated = g_malloc (length_bytes);
     172    if (contents_unterminated != NULL)
     173      memcpy (contents_unterminated, contents, length_bytes);
     174  
     175    /* Test with nul termination. */
     176    context = g_markup_parse_context_new (&parser, flags, NULL, NULL);
     177    g_assert (g_markup_parse_context_get_user_data (context) == NULL);
     178    g_markup_parse_context_get_position (context, &line, &col);
     179    g_assert_cmpint (line, ==, 1);
     180    g_assert_cmpint (col, ==, 1);
     181  
     182    if (!g_markup_parse_context_parse (context, contents, -1, NULL) ||
     183        !g_markup_parse_context_end_parse (context, NULL))
     184      n_failures++;
     185    n_tests++;
     186  
     187    g_markup_parse_context_free (context);
     188  
     189    /* FIXME: Swap out the error string so we only return one copy of it, not
     190     * @n_tests copies. This should be fixed properly by eliminating the global
     191     * state in this file. */
     192    first_string = g_steal_pointer (&string);
     193    string = g_string_new ("");
     194  
     195    /* With the length specified explicitly and a nul terminator present (since
     196     * g_file_get_contents() always adds one). */
     197    if (test_in_chunks (contents, length_bytes, length_bytes, flags) != 0)
     198      n_failures++;
     199    n_tests++;
     200  
     201    /* With the length specified explicitly and no nul terminator present. */
     202    if (test_in_chunks (contents_unterminated, length_bytes, length_bytes, flags) != 0)
     203      n_failures++;
     204    n_tests++;
     205  
     206    /* In various sized chunks. */
     207    for (i = 0; i < G_N_ELEMENTS (chunk_sizes_bytes); i++)
     208      {
     209        if (test_in_chunks (contents, length_bytes, chunk_sizes_bytes[i], flags) != 0)
     210          n_failures++;
     211        n_tests++;
     212      }
     213  
     214    g_free (contents);
     215    g_free (contents_unterminated);
     216  
     217    /* FIXME: Restore the error string. */
     218    g_string_free (string, TRUE);
     219    string = g_steal_pointer (&first_string);
     220  
     221    /* We expect the file to either always be parsed successfully, or never be
     222     * parsed successfully. There’s a bug in GMarkup if it sometimes parses
     223     * successfully depending on how you chunk or terminate the input. */
     224    if (n_failures > 0)
     225      g_assert_cmpint (n_failures, ==, n_tests);
     226  
     227    return (n_failures == 0);
     228  }
     229  
     230  static gchar *
     231  get_expected_filename (const gchar       *filename,
     232                         GMarkupParseFlags  flags)
     233  {
     234    gchar *f, *p, *expected;
     235  
     236    f = g_strdup (filename);
     237    p = strstr (f, ".gmarkup");
     238    if (p)
     239      *p = 0;
     240    if (flags == 0)
     241      expected = g_strconcat (f, ".expected", NULL);
     242    else if (flags == G_MARKUP_TREAT_CDATA_AS_TEXT)
     243      expected = g_strconcat (f, ".cdata-as-text", NULL);
     244    else
     245      g_assert_not_reached ();
     246  
     247    g_free (f);
     248  
     249    return expected;
     250  }
     251  
     252  static void
     253  test_parse (gconstpointer d)
     254  {
     255    const gchar *filename = d;
     256    gchar *expected_file;
     257    gchar *expected;
     258    gboolean valid_input;
     259    GError *error = NULL;
     260    gboolean res;
     261  
     262    valid_input = strstr (filename, "valid") != NULL;
     263    expected_file = get_expected_filename (filename, 0);
     264  
     265    depth = 0;
     266    string = g_string_sized_new (0);
     267  
     268    res = test_file (filename, 0);
     269    g_assert_cmpint (res, ==, valid_input);
     270  
     271    g_file_get_contents (expected_file, &expected, NULL, &error);
     272    g_assert_no_error (error);
     273    g_assert_cmpstr (string->str, ==, expected);
     274    g_free (expected);
     275  
     276    g_string_free (string, TRUE);
     277  
     278    g_free (expected_file);
     279  
     280    expected_file = get_expected_filename (filename, G_MARKUP_TREAT_CDATA_AS_TEXT);
     281    if (g_file_test (expected_file, G_FILE_TEST_EXISTS))
     282      {
     283        depth = 0;
     284        string = g_string_sized_new (0);
     285  
     286        res = test_file (filename, G_MARKUP_TREAT_CDATA_AS_TEXT);
     287        g_assert_cmpint (res, ==, valid_input);
     288  
     289        g_file_get_contents (expected_file, &expected, NULL, &error);
     290        g_assert_no_error (error);
     291        g_assert_cmpstr (string->str, ==, expected);
     292        g_free (expected);
     293  
     294        g_string_free (string, TRUE);
     295      }
     296  
     297    g_free (expected_file);
     298  }
     299  
     300  int
     301  main (int argc, char *argv[])
     302  {
     303    GDir *dir;
     304    GError *error;
     305    const gchar *name;
     306    gchar *path;
     307  
     308    g_setenv ("LC_ALL", "C", TRUE);
     309    setlocale (LC_ALL, "");
     310  
     311    g_test_init (&argc, &argv, NULL);
     312  
     313    /* allow to easily generate expected output for new test cases */
     314    if (argc > 1)
     315      {
     316        gint arg = 1;
     317        GMarkupParseFlags flags = G_MARKUP_DEFAULT_FLAGS;
     318  
     319        if (strcmp (argv[1], "--cdata-as-text") == 0)
     320          {
     321            flags = G_MARKUP_TREAT_CDATA_AS_TEXT;
     322            arg = 2;
     323          }
     324        string = g_string_sized_new (0);
     325        test_file (argv[arg], flags);
     326        g_print ("%s", string->str);
     327        return 0;
     328      }
     329  
     330    error = NULL;
     331    path = g_test_build_filename (G_TEST_DIST, "markups", NULL);
     332    dir = g_dir_open (path, 0, &error);
     333    g_free (path);
     334    g_assert_no_error (error);
     335    while ((name = g_dir_read_name (dir)) != NULL)
     336      {
     337        if (!strstr (name, "gmarkup"))
     338          continue;
     339  
     340        path = g_strdup_printf ("/markup/parse/%s", name);
     341        g_test_add_data_func_full (path, g_test_build_filename (G_TEST_DIST, "markups", name, NULL),
     342                                   test_parse, g_free);
     343        g_free (path);
     344      }
     345    g_dir_close (dir);
     346  
     347    return g_test_run ();
     348  }