blob: 71b9ac6af523c39ffb230433a1fe7373186b2885 [file] [log] [blame]
/*
* Copyright © 2008 Ryan Lortie
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* See the included COPYING file for more information.
*/
#include <string.h>
#include <stdio.h>
#include <glib.h>
/* keep track of GString instances to make sure nothing leaks */
static int strings_allocated;
/* === the GMarkupParser functions === */
static void
subparser_start_element (GMarkupParseContext *context,
const gchar *element_name,
const gchar **attribute_names,
const gchar **attribute_values,
gpointer user_data,
GError **error)
{
g_string_append_printf (user_data, "{%s}", element_name);
/* we don't like trouble... */
if (strcmp (element_name, "trouble") == 0)
g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
"we don't like trouble");
}
static void
subparser_end_element (GMarkupParseContext *context,
const gchar *element_name,
gpointer user_data,
GError **error)
{
g_string_append_printf (user_data, "{/%s}", element_name);
}
static void
subparser_error (GMarkupParseContext *context,
GError *error,
gpointer user_data)
{
g_string_free (user_data, TRUE);
strings_allocated--;
}
static GMarkupParser subparser_parser =
{
subparser_start_element,
subparser_end_element,
NULL,
NULL,
subparser_error
};
/* convenience functions for a parser that does not
* replay the starting tag into the subparser...
*/
static void
subparser_start (GMarkupParseContext *ctx)
{
gpointer user_data;
user_data = g_string_new (NULL);
strings_allocated++;
g_markup_parse_context_push (ctx, &subparser_parser, user_data);
}
static char *
subparser_end (GMarkupParseContext *ctx,
GError **error)
{
GString *string;
char *result;
string = g_markup_parse_context_pop (ctx);
result = string->str;
g_string_free (string, FALSE);
strings_allocated--;
if (result == NULL || result[0] == '\0')
{
g_free (result);
g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
"got no data");
return NULL;
}
return result;
}
/* convenience functions for a parser that -does-
* replay the starting tag into the subparser...
*/
static gboolean
replay_parser_start (GMarkupParseContext *ctx,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
GError **error)
{
GError *tmp_error = NULL;
gpointer user_data;
user_data = g_string_new (NULL);
strings_allocated++;
subparser_parser.start_element (ctx, element_name,
attribute_names, attribute_values,
user_data, &tmp_error);
if (tmp_error)
{
g_propagate_error (error, tmp_error);
g_string_free (user_data, TRUE);
strings_allocated--;
return FALSE;
}
g_markup_parse_context_push (ctx, &subparser_parser, user_data);
return TRUE;
}
static char *
replay_parser_end (GMarkupParseContext *ctx,
GError **error)
{
GError *tmp_error = NULL;
GString *string;
char *result;
string = g_markup_parse_context_pop (ctx);
subparser_parser.end_element (ctx, g_markup_parse_context_get_element (ctx),
string, &tmp_error);
if (tmp_error)
{
g_propagate_error (error, tmp_error);
g_string_free (string, TRUE);
strings_allocated--;
return NULL;
}
result = string->str;
g_string_free (string, FALSE);
strings_allocated--;
if (result == NULL || result[0] == '\0')
{
g_free (result);
g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
"got no data");
return NULL;
}
return result;
}
/* === start interface between subparser and calling parser === */
static void subparser_start (GMarkupParseContext *ctx);
static char *subparser_end (GMarkupParseContext *ctx,
GError **error);
/* === end interface between subparser and calling parser === */
/* === start interface between replay parser and calling parser === */
static gboolean replay_parser_start (GMarkupParseContext *ctx,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
GError **error);
static char *replay_parser_end (GMarkupParseContext *ctx,
GError **error);
/* === end interface between replay parser and calling parser === */
/* now comes our parser for the test.
*
* we recognise the tags <test> and <sub>.
* <test> is ignored.
* <sub> invokes the subparser (no replay).
*
* "unknown tags" are passed to the reply subparser
* (so the unknown tag is fed to the subparser...)
*/
static void
start_element (GMarkupParseContext *context,
const gchar *element_name,
const gchar **attribute_names,
const gchar **attribute_values,
gpointer user_data,
GError **error)
{
g_string_append_printf (user_data, "<%s>", element_name);
if (strcmp (element_name, "test") == 0)
{
/* do nothing */
}
else if (strcmp (element_name, "sub") == 0)
{
/* invoke subparser */
subparser_start (context);
}
else
{
/* unknown tag. invoke replay subparser */
if (!replay_parser_start (context, element_name,
attribute_names, attribute_values,
error))
return;
}
}
static void
end_element (GMarkupParseContext *context,
const gchar *element_name,
gpointer user_data,
GError **error)
{
if (strcmp (element_name, "test") == 0)
{
/* do nothing */
}
else if (strcmp (element_name, "sub") == 0)
{
char *result;
if ((result = subparser_end (context, error)) == NULL)
return;
g_string_append_printf (user_data, "<<%s>>", result);
g_free (result);
}
else
{
char *result;
if ((result = replay_parser_end (context, error)) == NULL)
return;
g_string_append_printf (user_data, "[[%s]]", result);
g_free (result);
}
g_string_append_printf (user_data, "</%s>", element_name);
}
static GMarkupParser parser =
{
start_element,
end_element,
NULL,
NULL,
NULL
};
typedef struct
{
const char *markup;
const char *result;
const char *error_message;
} TestCase;
static void
test (gconstpointer user_data)
{
const TestCase *tc = user_data;
GMarkupParseContext *ctx;
GString *string;
gboolean result;
GError *error;
error = NULL;
string = g_string_new (NULL);
ctx = g_markup_parse_context_new (&parser, 0, string, NULL);
result = g_markup_parse_context_parse (ctx, tc->markup,
strlen (tc->markup), &error);
if (result)
result = g_markup_parse_context_end_parse (ctx, &error);
g_markup_parse_context_free (ctx);
g_assert (strings_allocated == 0);
if (result)
{
if (tc->error_message)
g_error ("expected failure (about '%s') passed!\n"
" in: %s\n out: %s",
tc->error_message, tc->markup, string->str);
}
else
{
if (!tc->error_message)
g_error ("unexpected failure: '%s'\n"
" in: %s\n out: %s",
error->message, tc->markup, string->str);
if (!strstr (error->message, tc->error_message))
g_error ("failed for the wrong reason.\n"
" expecting message about '%s'\n"
" got message '%s'\n"
" in: %s\n out: %s",
tc->error_message, error->message, tc->markup, string->str);
}
if (strcmp (string->str, tc->result) != 0)
g_error ("got the wrong result.\n"
" expected: '%s'\n"
" got: '%s'\n"
" input: %s",
tc->result, string->str, tc->markup);
if (error)
g_error_free (error);
g_string_free (string, TRUE);
}
TestCase test_cases[] = /* successful runs */
{
/* in */ /* out */ /* error */
{ "<test/>", "<test></test>", NULL },
{ "<sub><foo/></sub>", "<sub><<{foo}{/foo}>></sub>", NULL },
{ "<sub><foo/><bar/></sub>", "<sub><<{foo}{/foo}{bar}{/bar}>></sub>", NULL },
{ "<foo><bar/></foo>", "<foo>[[{foo}{bar}{/bar}{/foo}]]</foo>", NULL },
{ "<foo><x/><y/></foo>", "<foo>[[{foo}{x}{/x}{y}{/y}{/foo}]]</foo>", NULL },
{ "<foo/>", "<foo>[[{foo}{/foo}]]</foo>", NULL },
{ "<sub><foo/></sub><bar/>", "<sub><<{foo}{/foo}>></sub>"
"<bar>[[{bar}{/bar}]]</bar>", NULL }
};
TestCase error_cases[] = /* error cases */
{
/* in */ /* out */ /* error */
{ "<foo><>", "<foo>", ">"},
{ "", "", "empty" },
{ "<trouble/>", "<trouble>", "trouble" },
{ "<sub><trouble>", "<sub>", "trouble" },
{ "<foo><trouble>", "<foo>", "trouble" },
{ "<sub></sub>", "<sub>", "no data" },
{ "<sub/>", "<sub>", "no data" }
};
#define add_tests(func, basename, array) \
G_STMT_START { \
gsize __add_tests_i; \
\
for (__add_tests_i = 0; \
__add_tests_i < G_N_ELEMENTS (array); \
__add_tests_i++) \
{ \
char *testname; \
\
testname = g_strdup_printf ("%s/%" G_GSIZE_FORMAT, \
basename, __add_tests_i); \
g_test_add_data_func (testname, &array[__add_tests_i], func); \
g_free (testname); \
} \
} G_STMT_END
int
main (int argc, char **argv)
{
g_setenv ("LC_ALL", "C", TRUE);
g_test_init (&argc, &argv, NULL);
add_tests (test, "/glib/markup/subparser/success", test_cases);
add_tests (test, "/glib/markup/subparser/failure", error_cases);
return g_test_run ();
}