Add support for false-positives, etc.
authorali <ali@juiblex.co.uk>
Mon Feb 06 23:55:27 2012 +0000 (2012-02-06)
changeset 172c88fd553e5d
parent 16 7294f2bea92d
child 18 6faf1a95a207
Add support for false-positives, etc.
test/harness/Makefile.am
test/harness/testcase.c
test/harness/testcase.h
test/harness/testcaseio.c
test/harness/warningsparser.c
     1.1 --- a/test/harness/Makefile.am	Thu Feb 02 23:32:12 2012 +0000
     1.2 +++ b/test/harness/Makefile.am	Mon Feb 06 23:55:27 2012 +0000
     1.3 @@ -5,5 +5,5 @@
     1.4  
     1.5  loupe_test_SOURCES=loupe-test.c testcase.c testcase.h testcaseio.c \
     1.6  	testcaseio.h testcaseparser.c testcaseparser.h testcaseinput.c \
     1.7 -	testcaseinput.h
     1.8 +	testcaseinput.h warningsparser.c warningsparser.h
     1.9  loupe_test_LDADD=../../bl/libbl.la
     2.1 --- a/test/harness/testcase.c	Thu Feb 02 23:32:12 2012 +0000
     2.2 +++ b/test/harness/testcase.c	Mon Feb 06 23:55:27 2012 +0000
     2.3 @@ -239,6 +239,218 @@
     2.4  }
     2.5  
     2.6  /*
     2.7 + * Parse a warning of the form:
     2.8 + *	[blank line]
     2.9 + *	<echoed line> (ignored)
    2.10 + *	"    Line " <number> [" column " <number>] " - " <text> "\n"
    2.11 + * If not specified, the column is returned as 0.
    2.12 + * Returns: the number of bytes parsed, or -1 on error.
    2.13 + */
    2.14 +static ssize_t testcase_parse_warning(Testcase *testcase,const char *output,
    2.15 +  guint *line,guint *column,char **text)
    2.16 +{
    2.17 +    ssize_t offset=0;
    2.18 +    guint64 tmp;
    2.19 +    char *s,*endp;
    2.20 +    if (output[offset]!='\n')
    2.21 +    {
    2.22 +	g_print("%s: FAIL\n",testcase->basename);
    2.23 +	g_print("Unexpected output from bookloupe:\n");
    2.24 +	print_unexpected(output,offset);
    2.25 +	return -1;
    2.26 +    }
    2.27 +    offset++;
    2.28 +    s=strchr(output+offset,'\n');
    2.29 +    if (!s)
    2.30 +    {
    2.31 +	g_print("%s: FAIL\n",testcase->basename);
    2.32 +	g_print("Missing new-line in output from bookloupe:\n");
    2.33 +	print_unexpected(output,offset);
    2.34 +	return -1;
    2.35 +    }
    2.36 +    offset=s-output+1;
    2.37 +    if (!g_str_has_prefix(output+offset,"    Line "))
    2.38 +    {
    2.39 +	g_print("%s: FAIL\n",testcase->basename);
    2.40 +	g_print("Unexpected output from bookloupe:\n");
    2.41 +	offset+=common_prefix_length(output+offset,"    Line ");
    2.42 +	print_unexpected(output,offset);
    2.43 +	return -1;
    2.44 +    }
    2.45 +    offset+=9;
    2.46 +    tmp=g_ascii_strtoull(output+offset,&endp,10);
    2.47 +    if (tmp<1 || tmp>G_MAXUINT || tmp==G_MAXUINT64)
    2.48 +    {
    2.49 +	g_print("%s: FAIL\n",testcase->basename);
    2.50 +	g_print("Unexpected output from bookloupe:\n");
    2.51 +	print_unexpected(output,offset);
    2.52 +	return -1;
    2.53 +    }
    2.54 +    *line=tmp;
    2.55 +    offset=endp-output;
    2.56 +    if (g_str_has_prefix(output+offset," column "))
    2.57 +    {
    2.58 +	offset+=8;
    2.59 +	tmp=g_ascii_strtoull(output+offset,&endp,10);
    2.60 +	if (tmp<1 || tmp>G_MAXUINT || tmp==G_MAXUINT64)
    2.61 +	{
    2.62 +	    g_print("%s: FAIL\n",testcase->basename);
    2.63 +	    g_print("Unexpected output from bookloupe:\n");
    2.64 +	    print_unexpected(output,offset);
    2.65 +	    return -1;
    2.66 +	}
    2.67 +	*column=tmp;
    2.68 +	offset=endp-output;
    2.69 +    }
    2.70 +    else
    2.71 +	*column=0;
    2.72 +    if (!g_str_has_prefix(output+offset," - "))
    2.73 +    {
    2.74 +	g_print("%s: FAIL\n",testcase->basename);
    2.75 +	g_print("Unexpected output from bookloupe:\n");
    2.76 +	offset+=common_prefix_length(output+offset," - ");
    2.77 +	print_unexpected(output,offset);
    2.78 +	return -1;
    2.79 +    }
    2.80 +    offset+=3;
    2.81 +    s=strchr(output+offset,'\n');
    2.82 +    if (!s)
    2.83 +    {
    2.84 +	g_print("%s: FAIL\n",testcase->basename);
    2.85 +	g_print("Missing new-line in output from bookloupe:\n");
    2.86 +	print_unexpected(output,offset);
    2.87 +	return -1;
    2.88 +    }
    2.89 +    *text=g_strndup(output+offset,s-(output+offset));
    2.90 +    return s-output+1;
    2.91 +}
    2.92 +
    2.93 +/*
    2.94 + * Check the warnings produced by bookloupe against either the
    2.95 + * unstructured testcase->expected or the structured testcase->warnings
    2.96 + * as appropriate.
    2.97 + */
    2.98 +static gboolean testcase_check_warnings(Testcase *testcase,const char *output,
    2.99 +  char **xfail)
   2.100 +{
   2.101 +    gboolean r=TRUE;
   2.102 +    size_t offset;
   2.103 +    ssize_t off;
   2.104 +    int i,count_false_positive,count_false_negative;
   2.105 +    int total_false_positive,total_false_negative;
   2.106 +    char *text;
   2.107 +    guint *counts,line,column;
   2.108 +    GSList *link,*link2;
   2.109 +    TestcaseWarning *warning;
   2.110 +    TestcaseLocation *location;
   2.111 +    *xfail=NULL;
   2.112 +    if (testcase->expected)
   2.113 +    {
   2.114 +	if (strcmp(output,testcase->expected))
   2.115 +	{
   2.116 +	    g_print("%s: FAIL\n",testcase->basename);
   2.117 +	    offset=common_prefix_length(output,testcase->expected);
   2.118 +	    if (!offset && !output[offset])
   2.119 +		g_print("Unexpected zero warnings from bookloupe.\n");
   2.120 +	    else
   2.121 +	    {
   2.122 +		g_print("Unexpected output from bookloupe:\n");
   2.123 +		print_unexpected(output,offset);
   2.124 +	    }
   2.125 +	    return FALSE;
   2.126 +	}
   2.127 +	return TRUE;
   2.128 +    }
   2.129 +    counts=g_new0(guint,g_slist_length(testcase->warnings));
   2.130 +    for(offset=0;output[offset];)
   2.131 +    {
   2.132 +	off=testcase_parse_warning(testcase,output+offset,&line,&column,&text);
   2.133 +	if (off<0)
   2.134 +	{
   2.135 +	    r=FALSE;
   2.136 +	    break;
   2.137 +	}
   2.138 +	offset+=off;
   2.139 +	for(link=testcase->warnings,i=0;link;link=link->next,i++)
   2.140 +	{
   2.141 +	    warning=link->data;
   2.142 +	    if (strcmp(warning->text,text))
   2.143 +		continue;
   2.144 +	    for(link2=warning->locations;link2;link2=link2->next)
   2.145 +	    {
   2.146 +		location=link2->data;
   2.147 +		if (location->line!=line || location->column!=column)
   2.148 +		    continue;
   2.149 +		counts[i]++;
   2.150 +		break;
   2.151 +	    }
   2.152 +	    if (link2)
   2.153 +		break;
   2.154 +	}
   2.155 +	if (!link)
   2.156 +	{
   2.157 +	    g_print("%s: FAIL\n",testcase->basename);
   2.158 +	    g_print("Unexpected warning from bookloupe:\n");
   2.159 +	    if (column)
   2.160 +		g_print("    Line %u column %u - %s\n",line,column,text);
   2.161 +	    else
   2.162 +		g_print("    Line %u - %s\n",line,text);
   2.163 +	    r=FALSE;
   2.164 +	    g_free(text);
   2.165 +	    break;
   2.166 +	}
   2.167 +	g_free(text);
   2.168 +    }
   2.169 +    count_false_positive=total_false_positive=0;
   2.170 +    count_false_negative=total_false_negative=0;
   2.171 +    for(link=testcase->warnings,i=0;r && link;link=link->next,i++)
   2.172 +    {
   2.173 +	warning=link->data;
   2.174 +	if (!counts[i] && warning->is_real && !warning->xfail)
   2.175 +	{
   2.176 +	    location=warning->locations->data;
   2.177 +	    g_print("%s: FAIL\n",testcase->basename);
   2.178 +	    g_print("Missing warning from bookloupe:\n");
   2.179 +	    if (location->column)
   2.180 +		g_print("    Line %u column %u - %s\n",location->line,
   2.181 +		  location->column,warning->text);
   2.182 +	    else
   2.183 +		g_print("    Line %u - %s\n",location->line,warning->text);
   2.184 +	    r=FALSE;
   2.185 +	    break;
   2.186 +	}
   2.187 +	else if (warning->xfail)
   2.188 +	{
   2.189 +	    if (warning->is_real)
   2.190 +	    {
   2.191 +		total_false_negative++;
   2.192 +		if (!counts[i])
   2.193 +		    count_false_negative++;
   2.194 +	    }
   2.195 +	    else if (!warning->is_real)
   2.196 +	    {
   2.197 +		total_false_positive++;
   2.198 +		if (counts[i])
   2.199 +		    count_false_positive++;
   2.200 +	    }
   2.201 +	}
   2.202 +    }
   2.203 +    g_free(counts);
   2.204 +    if (count_false_positive && count_false_negative)
   2.205 +	*xfail=g_strdup_printf(
   2.206 +	  "with %d of %d false positives and %d of %d false negatives",
   2.207 +	  count_false_positive,total_false_positive,
   2.208 +	  count_false_negative,total_false_negative);
   2.209 +    else if (count_false_positive)
   2.210 +	*xfail=g_strdup_printf("with %d of %d false positives",
   2.211 +	  count_false_positive,total_false_positive);
   2.212 +    else if (count_false_negative)
   2.213 +	*xfail=g_strdup_printf("with %d of %d false negatives",
   2.214 +	  count_false_negative,total_false_negative);
   2.215 +    return r;
   2.216 +}
   2.217 +
   2.218 +/*
   2.219   * Run a testcase, returning FALSE on fail or error and
   2.220   * TRUE on pass or expected-fail.
   2.221   * Suitable message(s) will be printed in all cases.
   2.222 @@ -247,8 +459,8 @@
   2.223  {
   2.224      gboolean r;
   2.225      size_t pos,offset;
   2.226 -    GString *header,*expected;
   2.227 -    char *output,*filename,*s;
   2.228 +    GString *header;
   2.229 +    char *output,*filename,*s,*xfail=NULL;
   2.230      GError *error=NULL;
   2.231      if (!testcase_create_input_files(testcase,&error))
   2.232      {
   2.233 @@ -257,7 +469,7 @@
   2.234  	g_error_free(error);
   2.235  	return FALSE;
   2.236      }
   2.237 -    if (testcase->expected)
   2.238 +    if (testcase->expected || testcase->warnings)
   2.239  	r=testcase_spawn_bookloupe(testcase,&output,&error);
   2.240      else
   2.241      {
   2.242 @@ -280,17 +492,16 @@
   2.243  	g_error_free(error);
   2.244  	return FALSE;
   2.245      }
   2.246 -    if (testcase->expected)
   2.247 +    if (testcase->expected || testcase->warnings)
   2.248      {
   2.249  	header=g_string_new("\n\nFile: ");
   2.250  	g_string_append(header,filename);
   2.251  	g_string_append(header,"\n");
   2.252 -	expected=g_string_new(testcase->expected);
   2.253  	if (!g_str_has_prefix(output,header->str))
   2.254  	{
   2.255  	    g_print("%s: FAIL\n",testcase->basename);
   2.256 +	    g_print("Unexpected header from bookloupe:\n");
   2.257  	    offset=common_prefix_length(output,header->str);
   2.258 -	    g_print("Unexpected header from bookloupe:\n");
   2.259  	    print_unexpected(output,offset);
   2.260  	    r=FALSE;
   2.261  	}
   2.262 @@ -304,36 +515,39 @@
   2.263  	    else
   2.264  	    {
   2.265  		g_print("%s: FAIL\n",testcase->basename);
   2.266 -		offset=common_prefix_length(output,header->str);
   2.267  		g_print("Unterminated summary from bookloupe:\n%s\n",
   2.268  		  output+pos);
   2.269  		r=FALSE;
   2.270  	    }
   2.271  	}
   2.272 -	if (r && strcmp(output+pos,expected->str))
   2.273 -	{
   2.274 -	    g_print("%s: FAIL\n",testcase->basename);
   2.275 -	    offset=common_prefix_length(output+pos,expected->str);
   2.276 -	    if (!offset && !output[pos+offset])
   2.277 -		g_print("Unexpected zero warnings from bookloupe.\n");
   2.278 -	    else
   2.279 -	    {
   2.280 -		g_print("Unexpected output from bookloupe:\n");
   2.281 -		print_unexpected(output+pos,offset);
   2.282 -	    }
   2.283 -	    r=FALSE;
   2.284 -	}
   2.285  	g_string_free(header,TRUE);
   2.286 -	g_string_free(expected,TRUE);
   2.287 +	r=testcase_check_warnings(testcase,output+pos,&xfail);
   2.288      }
   2.289      g_free(filename);
   2.290      g_free(output);
   2.291      if (r)
   2.292 -	g_print("%s: PASS\n",testcase->basename);
   2.293 +    {
   2.294 +	if (xfail)
   2.295 +	    g_print("%s: PASS (%s)\n",testcase->basename,xfail);
   2.296 +	else
   2.297 +	    g_print("%s: PASS\n",testcase->basename);
   2.298 +    }
   2.299 +    g_free(xfail);
   2.300      return r;
   2.301  }
   2.302  
   2.303  /*
   2.304 + * Free a testcase warning.
   2.305 + */
   2.306 +void testcase_warning_free(TestcaseWarning *warning)
   2.307 +{
   2.308 +    g_slist_foreach(warning->locations,(GFunc)g_free,NULL);
   2.309 +    g_slist_free(warning->locations);
   2.310 +    g_free(warning->text);
   2.311 +    g_free(warning);
   2.312 +}
   2.313 +
   2.314 +/*
   2.315   * Free a testcase.
   2.316   */
   2.317  void testcase_free(Testcase *testcase)
   2.318 @@ -342,6 +556,8 @@
   2.319      g_slist_foreach(testcase->inputs,(GFunc)testcase_input_free,NULL);
   2.320      g_slist_free(testcase->inputs);
   2.321      g_free(testcase->expected);
   2.322 +    g_slist_foreach(testcase->warnings,(GFunc)testcase_warning_free,NULL);
   2.323 +    g_slist_free(testcase->warnings);
   2.324      g_free(testcase->encoding);
   2.325      g_strfreev(testcase->options);
   2.326      g_free(testcase);
     3.1 --- a/test/harness/testcase.h	Thu Feb 02 23:32:12 2012 +0000
     3.2 +++ b/test/harness/testcase.h	Mon Feb 06 23:55:27 2012 +0000
     3.3 @@ -10,10 +10,35 @@
     3.4  } TestcaseError;
     3.5  
     3.6  typedef struct {
     3.7 +    guint line;
     3.8 +    guint column;		/* or 0 for unspecified */
     3.9 +} TestcaseLocation;
    3.10 +
    3.11 +typedef struct {
    3.12 +    /*
    3.13 +     * Does this warning relate to a real problem in the etext
    3.14 +     * (eg., error and false-negative).
    3.15 +     */
    3.16 +    gboolean is_real;
    3.17 +    /*
    3.18 +     * Do we "expect" BOOKLOUPE to get this wrong
    3.19 +     * (eg., false-negative and false-positive)
    3.20 +     */
    3.21 +    gboolean xfail;
    3.22 +    /*
    3.23 +     * For real problems, the first location should be the
    3.24 +     * actual location of the problem.
    3.25 +     */
    3.26 +    GSList *locations;
    3.27 +    char *text;
    3.28 +} TestcaseWarning;
    3.29 +
    3.30 +typedef struct {
    3.31      char *basename;
    3.32      char *tmpdir;
    3.33      GSList *inputs;
    3.34      char *expected;
    3.35 +    GSList *warnings;
    3.36      char *encoding;	/* The character encoding to talk to BOOKLOUPE in */
    3.37      char **options;
    3.38      enum {
     4.1 --- a/test/harness/testcaseio.c	Thu Feb 02 23:32:12 2012 +0000
     4.2 +++ b/test/harness/testcaseio.c	Mon Feb 06 23:55:27 2012 +0000
     4.3 @@ -6,6 +6,7 @@
     4.4  #include "testcaseparser.h"
     4.5  #include "testcaseinput.h"
     4.6  #include "testcaseio.h"
     4.7 +#include "warningsparser.h"
     4.8  
     4.9  /*
    4.10   * Read a testcase in from a file.
    4.11 @@ -17,6 +18,8 @@
    4.12      Testcase *testcase;
    4.13      TestcaseParser *parser;
    4.14      TestcaseInput *input=NULL;
    4.15 +    GMarkupParseContext *context;
    4.16 +    GError *err=NULL;
    4.17      char *s,*arg;
    4.18      const char *tag,*text;
    4.19      gboolean found_tag=FALSE;
    4.20 @@ -64,8 +67,25 @@
    4.21  		testcase->flags|=TESTCASE_TMP_DIR;
    4.22  	    g_free(arg);
    4.23  	}
    4.24 -	else if (!testcase->expected && !strcmp(tag,"EXPECTED"))
    4.25 +	else if (!testcase->expected && !testcase->warnings &&
    4.26 +	  !strcmp(tag,"EXPECTED"))
    4.27  	    testcase->expected=g_strdup(text);
    4.28 +	else if (!testcase->expected && !testcase->warnings &&
    4.29 +	  !strcmp(tag,"WARNINGS"))
    4.30 +	{
    4.31 +	    context=warnings_parse_context_new(testcase);
    4.32 +	    if (!g_markup_parse_context_parse(context,text,-1,&err) ||
    4.33 +	      !g_markup_parse_context_end_parse(context,&err))
    4.34 +	    {
    4.35 +		g_markup_parse_context_free(context);
    4.36 +		g_printerr("%s\n",err->message);
    4.37 +		g_clear_error(&err);
    4.38 +		testcase_free(testcase);
    4.39 +		testcase_parser_free(parser);
    4.40 +		return NULL;
    4.41 +	    }
    4.42 +	    g_markup_parse_context_free(context);
    4.43 +	}
    4.44  	else if (!testcase->encoding && !strcmp(tag,"ENCODING"))
    4.45  	    testcase->encoding=g_strchomp(g_strdup(text));
    4.46  	else if (!testcase->encoding && !strcmp(tag,"OPTIONS"))
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/test/harness/warningsparser.c	Mon Feb 06 23:55:27 2012 +0000
     5.3 @@ -0,0 +1,244 @@
     5.4 +#include <stdlib.h>
     5.5 +#include <string.h>
     5.6 +#include <glib.h>
     5.7 +#include "testcase.h"
     5.8 +#include "warningsparser.h"
     5.9 +
    5.10 +/*
    5.11 + * A GMarkupParser for the contents of a WARNINGS tag.
    5.12 + */
    5.13 +
    5.14 +typedef struct {
    5.15 +    Testcase *testcase;
    5.16 +    TestcaseWarning *warning;
    5.17 +    TestcaseLocation *location;
    5.18 +    enum {
    5.19 +	WARNINGS_INIT,
    5.20 +	WARNINGS_IN_EXPECTED,
    5.21 +	WARNINGS_IN_WARNING,
    5.22 +	WARNINGS_IN_AT,
    5.23 +	WARNINGS_IN_TEXT,
    5.24 +	WARNINGS_DONE,
    5.25 +    } state;
    5.26 +} WarningsBaton;
    5.27 +
    5.28 +static void warnings_parser_start_element(GMarkupParseContext *context,
    5.29 +  const char *element_name,const char **attribute_names,
    5.30 +  const char **attribute_values,void *user_data,GError **error)
    5.31 +{
    5.32 +    int i;
    5.33 +    guint64 tmp;
    5.34 +    char *endp;
    5.35 +    WarningsBaton *baton=user_data;
    5.36 +    switch(baton->state)
    5.37 +    {
    5.38 +	case WARNINGS_INIT:
    5.39 +	    if (strcmp(element_name,"expected"))
    5.40 +		g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_UNKNOWN_ELEMENT,
    5.41 +		  "Unknown root element: '%s'",element_name);
    5.42 +	    else if (attribute_names[0])
    5.43 +		g_set_error(error,G_MARKUP_ERROR,
    5.44 +		  G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
    5.45 +		  "Unknown attribute on element 'expected': '%s'",
    5.46 +		  attribute_names[0]);
    5.47 +	    else
    5.48 +		baton->state=WARNINGS_IN_EXPECTED;
    5.49 +	    break;
    5.50 +	case WARNINGS_IN_EXPECTED:
    5.51 +	    baton->warning=g_new0(TestcaseWarning,1);
    5.52 +	    if (!strcmp(element_name,"error"))
    5.53 +		baton->warning->is_real=TRUE;
    5.54 +	    else if (!strcmp(element_name,"false-positive"))
    5.55 +		baton->warning->xfail=TRUE;
    5.56 +	    else if (!strcmp(element_name,"false-negative"))
    5.57 +		baton->warning->is_real=baton->warning->xfail=TRUE;
    5.58 +	    else
    5.59 +	    {
    5.60 +		g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_UNKNOWN_ELEMENT,
    5.61 +		  "Unknown element in 'expected': '%s'",element_name);
    5.62 +		g_free(baton->warning);
    5.63 +		baton->warning=NULL;
    5.64 +		return;
    5.65 +	    }
    5.66 +	    if (attribute_names[0])
    5.67 +	    {
    5.68 +		g_set_error(error,G_MARKUP_ERROR,
    5.69 +		  G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
    5.70 +		  "Unknown attribute on element '%s': '%s'",element_name,
    5.71 +		  attribute_names[0]);
    5.72 +		g_free(baton->warning);
    5.73 +		baton->warning=NULL;
    5.74 +		return;
    5.75 +	    }
    5.76 +	    else
    5.77 +		baton->state=WARNINGS_IN_WARNING;
    5.78 +	    break;
    5.79 +	case WARNINGS_IN_WARNING:
    5.80 +	    if (!strcmp(element_name,"at"))
    5.81 +	    {
    5.82 +		baton->location=g_new0(TestcaseLocation,1);
    5.83 +		for(i=0;attribute_names[i];i++)
    5.84 +		{
    5.85 +		    if (!strcmp(attribute_names[i],"line"))
    5.86 +		    {
    5.87 +			tmp=g_ascii_strtoull(attribute_values[i],&endp,0);
    5.88 +			if (tmp<1 || tmp>G_MAXUINT || tmp==G_MAXUINT64)
    5.89 +			{
    5.90 +			    g_set_error(error,G_MARKUP_ERROR,
    5.91 +			      G_MARKUP_ERROR_INVALID_CONTENT,"Invalid value "
    5.92 +			      "for attribute 'line' on element '%s': '%s'",
    5.93 +			      element_name,attribute_values[i]);
    5.94 +			    return;
    5.95 +			}
    5.96 +			baton->location->line=(guint)tmp;
    5.97 +		    }
    5.98 +		    else if (!strcmp(attribute_names[i],"column"))
    5.99 +		    {
   5.100 +			tmp=g_ascii_strtoull(attribute_values[i],&endp,0);
   5.101 +			if (tmp<1 || tmp>G_MAXUINT || tmp==G_MAXUINT64)
   5.102 +			{
   5.103 +			    g_set_error(error,G_MARKUP_ERROR,
   5.104 +			      G_MARKUP_ERROR_INVALID_CONTENT,"Invalid value "
   5.105 +			      "for attribute 'column' on element '%s': '%s'",
   5.106 +			      element_name,attribute_values[i]);
   5.107 +			    return;
   5.108 +			}
   5.109 +			baton->location->column=(guint)tmp;
   5.110 +		    }
   5.111 +		    else
   5.112 +		    {
   5.113 +			g_set_error(error,G_MARKUP_ERROR,
   5.114 +			  G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
   5.115 +			  "Unknown attribute on element '%s': '%s'",
   5.116 +			  element_name,attribute_names[i]);
   5.117 +			return;
   5.118 +		    }
   5.119 +		}
   5.120 +		if (!baton->location->line)
   5.121 +		{
   5.122 +		    g_set_error(error,G_MARKUP_ERROR,
   5.123 +		      G_MARKUP_ERROR_MISSING_ATTRIBUTE,
   5.124 +		      "Missing attribute on element '%s': 'line'",element_name);
   5.125 +		    return;
   5.126 +		}
   5.127 +		baton->state=WARNINGS_IN_AT;
   5.128 +	    }
   5.129 +	    else if (!strcmp(element_name,"text"))
   5.130 +	    {
   5.131 +		if (attribute_names[0])
   5.132 +		{
   5.133 +		    g_set_error(error,G_MARKUP_ERROR,
   5.134 +		      G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
   5.135 +		      "Unknown attribute on element 'text': '%s'",
   5.136 +		      attribute_names[0]);
   5.137 +		    return;
   5.138 +		}
   5.139 +		baton->state=WARNINGS_IN_TEXT;
   5.140 +	    }
   5.141 +	    break;
   5.142 +	case WARNINGS_IN_AT:
   5.143 +	    g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_UNKNOWN_ELEMENT,
   5.144 +	      "Unknown element in 'at': '%s'",element_name);
   5.145 +	    return;
   5.146 +	case WARNINGS_IN_TEXT:
   5.147 +	    g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_UNKNOWN_ELEMENT,
   5.148 +	      "Unknown element in 'text': '%s'",element_name);
   5.149 +	    return;
   5.150 +	default:
   5.151 +	    g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_UNKNOWN_ELEMENT,
   5.152 +	      "Unexpected element: '%s'",element_name);
   5.153 +	    return;
   5.154 +    }
   5.155 +}
   5.156 +
   5.157 +static void warnings_parser_end_element(GMarkupParseContext *context,
   5.158 +  const char *element_name,void *user_data,GError **error)
   5.159 +{
   5.160 +    WarningsBaton *baton=user_data;
   5.161 +    switch(baton->state)
   5.162 +    {
   5.163 +	case WARNINGS_IN_EXPECTED:
   5.164 +	    baton->testcase->warnings=
   5.165 +	      g_slist_reverse(baton->testcase->warnings);
   5.166 +	    baton->state=WARNINGS_DONE;
   5.167 +	    break;
   5.168 +	case WARNINGS_IN_WARNING:
   5.169 +	    baton->warning->locations=
   5.170 +	      g_slist_reverse(baton->warning->locations);
   5.171 +	    baton->testcase->warnings=g_slist_prepend(baton->testcase->warnings,
   5.172 +	      baton->warning);
   5.173 +	    baton->warning=NULL;
   5.174 +	    baton->state=WARNINGS_IN_EXPECTED;
   5.175 +	    break;
   5.176 +	case WARNINGS_IN_AT:
   5.177 +	    baton->warning->locations=g_slist_prepend(baton->warning->locations,
   5.178 +	      baton->location);
   5.179 +	    baton->location=NULL;
   5.180 +	    baton->state=WARNINGS_IN_WARNING;
   5.181 +	    break;
   5.182 +	case WARNINGS_IN_TEXT:
   5.183 +	    baton->state=WARNINGS_IN_WARNING;
   5.184 +	    break;
   5.185 +	default:
   5.186 +	    g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_UNKNOWN_ELEMENT,
   5.187 +	      "Unexpected element ending: '%s'",element_name);
   5.188 +	    return;
   5.189 +    }
   5.190 +}
   5.191 +
   5.192 +static void warnings_parser_text(GMarkupParseContext *context,
   5.193 +  const char *text,gsize text_len,void *user_data,GError **error)
   5.194 +{
   5.195 +    char *s,*t;
   5.196 +    WarningsBaton *baton=user_data;
   5.197 +    switch(baton->state)
   5.198 +    {
   5.199 +	case WARNINGS_IN_EXPECTED:
   5.200 +	    if (strspn(text," \t\n")!=text_len)
   5.201 +		g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT,
   5.202 +		  "The 'expected' tag does not take any content");
   5.203 +	    break;
   5.204 +	case WARNINGS_IN_WARNING:
   5.205 +	    if (strspn(text," \t\n")!=text_len)
   5.206 +		g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT,
   5.207 +		  "The warning tags do not take any content");
   5.208 +	    break;
   5.209 +	case WARNINGS_IN_AT:
   5.210 +	    if (strspn(text," \t\n")!=text_len)
   5.211 +		g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT,
   5.212 +		  "The 'at' tag does not take any content");
   5.213 +	    break;
   5.214 +	case WARNINGS_IN_TEXT:
   5.215 +	    s=g_strdup(text+strspn(text," \t\n"));
   5.216 +	    g_strchomp(s);
   5.217 +	    if (baton->warning->text)
   5.218 +	    {
   5.219 +		t=g_strconcat(baton->warning->text,s,NULL);
   5.220 +		g_free(baton->warning->text);
   5.221 +		g_free(s);
   5.222 +		baton->warning->text=t;
   5.223 +	    }
   5.224 +	    else
   5.225 +		baton->warning->text=s;
   5.226 +	    break;
   5.227 +	default:
   5.228 +	    g_set_error(error,G_MARKUP_ERROR,G_MARKUP_ERROR_INVALID_CONTENT,
   5.229 +	      "Unexpected content: '%s'",text);
   5.230 +	    return;
   5.231 +    }
   5.232 +}
   5.233 +
   5.234 +GMarkupParseContext *warnings_parse_context_new(Testcase *testcase)
   5.235 +{
   5.236 +    static GMarkupParser parser={0};
   5.237 +    WarningsBaton *baton;
   5.238 +    parser.start_element=warnings_parser_start_element;
   5.239 +    parser.end_element=warnings_parser_end_element;
   5.240 +    parser.text=warnings_parser_text;
   5.241 +    baton=g_new0(WarningsBaton,1);
   5.242 +    baton->testcase=testcase;
   5.243 +    baton->state=WARNINGS_INIT;
   5.244 +    return g_markup_parse_context_new(&parser,
   5.245 +      G_MARKUP_TREAT_CDATA_AS_TEXT|G_MARKUP_PREFIX_ERROR_POSITION,
   5.246 +      baton,g_free);
   5.247 +}