Add support for non-ASCII testcases
authorali <ali@juiblex.co.uk>
Fri Jan 27 21:40:35 2012 +0000 (2012-01-27)
changeset 7721e468c10f3
parent 6 faab25d520dd
child 8 cf332d440466
Add support for non-ASCII testcases
bl/spawn.c
bl/spawn.h
test/compatibility/Makefile.am
test/compatibility/non-ascii.tst
test/harness/testcase.c
test/harness/testcase.h
test/harness/testcaseio.c
test/harness/testcaseparser.c
     1.1 --- a/bl/spawn.c	Fri Jan 27 16:18:02 2012 +0000
     1.2 +++ b/bl/spawn.c	Fri Jan 27 21:40:35 2012 +0000
     1.3 @@ -7,26 +7,22 @@
     1.4  
     1.5  #define SPAWN_BUFSIZE	128
     1.6  
     1.7 -gboolean spawn_sync(char **argv,char **standard_output,int *exit_status)
     1.8 +gboolean spawn_sync(char **argv,char **standard_output,int *exit_status,
     1.9 +  GError **error)
    1.10  {
    1.11  /* Don't use g_spawn_sync on WIN32 for now to avoid needing the helper */
    1.12  #ifndef WIN32
    1.13 -    char *standard_error;
    1.14 -    GError *error=NULL;
    1.15 +    char *standard_error=NULL;
    1.16      gboolean retval;
    1.17      GSpawnFlags flags=G_SPAWN_SEARCH_PATH;
    1.18      if (!standard_output)
    1.19  	flags=G_SPAWN_STDOUT_TO_DEV_NULL;
    1.20      retval=g_spawn_sync(NULL,argv,NULL,flags,NULL,NULL,standard_output,
    1.21 -      &standard_error,exit_status,&error);
    1.22 -    fputs(standard_error,stderr);
    1.23 +      &standard_error,exit_status,error);
    1.24 +    if (standard_error)
    1.25 +	fputs(standard_error,stderr);
    1.26      g_free(standard_error);
    1.27 -    if (!retval)
    1.28 -    {
    1.29 -	fprintf(stderr,"%s\n",error->message);
    1.30 -	g_error_free(error);
    1.31 -    }
    1.32 -    else if (exit_status)
    1.33 +    if (retval && exit_status)
    1.34  	*exit_status=WEXITSTATUS(*exit_status);
    1.35      return retval;
    1.36  #else
    1.37 @@ -45,7 +41,8 @@
    1.38      g_string_free(command_line,TRUE);
    1.39      if (!fp)
    1.40      {
    1.41 -	perror(command_line->str);
    1.42 +	g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
    1.43 +	  "%s: %s",command_line->str,g_strerror(errno));
    1.44  	return FALSE;
    1.45      }
    1.46      string=g_string_new(NULL);
    1.47 @@ -56,7 +53,8 @@
    1.48  	n=fread(string->str+len,1,SPAWN_BUFSIZE,fp);
    1.49  	if (n<0)
    1.50  	{
    1.51 -	    perror("fread");
    1.52 +	    g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
    1.53 +	      "Error reading from bookloupe: %s",g_strerror(errno));
    1.54  	    (void)pclose(fp);
    1.55  	    g_string_free(string,TRUE);
    1.56  	    return FALSE;
    1.57 @@ -66,7 +64,8 @@
    1.58      r=pclose(fp);
    1.59      if (r<0)
    1.60      {
    1.61 -	perror("pclose");
    1.62 +	g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
    1.63 +	  "Error reading from bookloupe: %s",g_strerror(errno));
    1.64  	g_string_free(string,TRUE);
    1.65  	return FALSE;
    1.66      }
     2.1 --- a/bl/spawn.h	Fri Jan 27 16:18:02 2012 +0000
     2.2 +++ b/bl/spawn.h	Fri Jan 27 21:40:35 2012 +0000
     2.3 @@ -3,6 +3,7 @@
     2.4  
     2.5  #include <glib.h>
     2.6  
     2.7 -gboolean spawn_sync(char **argv,char **standard_output,int *exit_status);
     2.8 +gboolean spawn_sync(char **argv,char **standard_output,int *exit_status,
     2.9 +  GError **error);
    2.10  
    2.11  #endif /* BL_SPAWN_H */
     3.1 --- a/test/compatibility/Makefile.am	Fri Jan 27 16:18:02 2012 +0000
     3.2 +++ b/test/compatibility/Makefile.am	Fri Jan 27 21:40:35 2012 +0000
     3.3 @@ -2,6 +2,6 @@
     3.4  TESTS=missing-space.tst spaced-punctuation.tst html-tag.tst html-symbol.tst \
     3.5  	spaced-doublequote.tst mismatched-quotes.tst he-be.tst digits.tst \
     3.6  	extra-period.tst ellipsis.tst short-line.tst abbreviation.tst \
     3.7 -	example.tst
     3.8 +	example.tst non-ascii.tst
     3.9  
    3.10  dist_pkgdata_DATA=$(TESTS)
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/test/compatibility/non-ascii.tst	Fri Jan 27 21:40:35 2012 +0000
     4.3 @@ -0,0 +1,8 @@
     4.4 +**************** ENCODING ****************
     4.5 +ISO-8859-1
     4.6 +**************** INPUT ****************
     4.7 +"Hello," he said, "I wanted to bave a tête-à-tête with you."
     4.8 +**************** EXPECTED ****************
     4.9 +
    4.10 +"Hello," he said, "I wanted to bave a tête-à-tête with you."
    4.11 +    Line 1 column 31 - Query word bave - not reporting duplicates
     5.1 --- a/test/harness/testcase.c	Fri Jan 27 16:18:02 2012 +0000
     5.2 +++ b/test/harness/testcase.c	Fri Jan 27 21:40:35 2012 +0000
     5.3 @@ -10,6 +10,11 @@
     5.4  #include <bl/bl.h>
     5.5  #include "testcase.h"
     5.6  
     5.7 +GQuark testcase_error_quark(void)
     5.8 +{
     5.9 +    return g_quark_from_static_string("testcase-error-quark");
    5.10 +}
    5.11 +
    5.12  #if !HAVE_MKSTEMP
    5.13  /*
    5.14   * An insecure implementation of mkstemp(), for those platforms that
    5.15 @@ -45,7 +50,7 @@
    5.16  /*
    5.17   * As write(), but always convert NL to CR NL.
    5.18   */
    5.19 -static size_t write_text(int fd,const char *buf,size_t count)
    5.20 +static size_t write_text(int fd,const char *buf,size_t count,GError **error)
    5.21  {
    5.22      size_t i;
    5.23      FILE *fp;
    5.24 @@ -56,6 +61,9 @@
    5.25      if (_setmode(fd,_O_BINARY)<0)
    5.26      {
    5.27  	close(fd);
    5.28 +	g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
    5.29 +	  "Failed to set mode of bookloupe input file to binary: %s",
    5.30 +	  g_strerror(errno));
    5.31  	return -1;
    5.32      }
    5.33  #endif
    5.34 @@ -63,6 +71,9 @@
    5.35      if (!fp)
    5.36      {
    5.37  	close(fd);
    5.38 +	g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
    5.39 +	  "Failed to open stream to bookloupe input file: %s",
    5.40 +	  g_strerror(errno));
    5.41  	return -1;
    5.42      }
    5.43      for(i=0;i<count;i++)
    5.44 @@ -70,17 +81,25 @@
    5.45  	if (buf[i]=='\n')
    5.46  	    if (putc('\r',fp)==EOF)
    5.47  	    {
    5.48 +		g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
    5.49 +		  "Error writing bookloupe input file: %s",g_strerror(errno));
    5.50  		(void)fclose(fp);
    5.51  		return -1;
    5.52  	    }
    5.53  	if (putc(buf[i],fp)==EOF)
    5.54  	{
    5.55 +	    g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
    5.56 +	      "Error writing bookloupe input file: %s",g_strerror(errno));
    5.57  	    (void)fclose(fp);
    5.58  	    return -1;
    5.59  	}
    5.60      }
    5.61      if (fclose(fp))
    5.62 +    {
    5.63 +	g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
    5.64 +	  "Error writing bookloupe input file: %s",g_strerror(errno));
    5.65  	return -1;
    5.66 +    }
    5.67      return count;
    5.68  }
    5.69  
    5.70 @@ -95,6 +114,122 @@
    5.71      return i;
    5.72  }
    5.73  
    5.74 +void print_unexpected(const char *unexpected,gsize differs_at)
    5.75 +{
    5.76 +    int col;
    5.77 +    const char *endp,*bol;
    5.78 +    GString *string;
    5.79 +    endp=strchr(unexpected+differs_at,'\n');
    5.80 +    if (!endp)
    5.81 +	endp=unexpected+strlen(unexpected);
    5.82 +    string=g_string_new_len(unexpected,endp-unexpected);
    5.83 +    bol=strrchr(string->str,'\n');
    5.84 +    if (bol)
    5.85 +	bol++;
    5.86 +    else
    5.87 +	bol=string->str;
    5.88 +    col=differs_at-(bol-string->str);
    5.89 +    fprintf(stderr,"%s\n%*s^\n",string->str,col,"");
    5.90 +    g_string_free(string,TRUE);
    5.91 +}
    5.92 +
    5.93 +gboolean spawn_bootloupe(const char *encoding,const char *standard_input,
    5.94 +  char **standard_output,char **filename,GError **error)
    5.95 +{
    5.96 +    gboolean r;
    5.97 +    int fd,exit_status;
    5.98 +    size_t n,pos,offset;
    5.99 +    FILE *fp;
   5.100 +    char input[]="TEST-XXXXXX";
   5.101 +    char *command[3];
   5.102 +    char *output,*s;
   5.103 +    GError *tmp_err=NULL;
   5.104 +    if (standard_input)
   5.105 +    {
   5.106 +	if (encoding)
   5.107 +	{
   5.108 +	    s=g_convert(standard_input,-1,encoding,"UTF-8",NULL,&n,&tmp_err);
   5.109 +	    if (!s)
   5.110 +	    {
   5.111 +		g_propagate_prefixed_error(error,tmp_err,
   5.112 +		  "Conversion to %s failed: ",encoding);
   5.113 +		return FALSE;
   5.114 +	    }
   5.115 +	}
   5.116 +	else
   5.117 +	{
   5.118 +	    s=g_strdup(standard_input);
   5.119 +	    n=strlen(s);
   5.120 +	}
   5.121 +    }
   5.122 +    else
   5.123 +    {
   5.124 +	n=0;
   5.125 +	s=NULL;
   5.126 +    }
   5.127 +    fd=mkstemp(input);
   5.128 +    if (n && write_text(fd,s,n,error)!=n)
   5.129 +    {
   5.130 +	g_free(s);
   5.131 +	close(fd);
   5.132 +	(void)remove(input);
   5.133 +	return FALSE;
   5.134 +    }
   5.135 +    g_free(s);
   5.136 +    close(fd);
   5.137 +    command[0]=getenv("BOOKLOUPE");
   5.138 +    if (!command[0])
   5.139 +	command[0]="." G_DIR_SEPARATOR_S "bookloupe";
   5.140 +    command[1]=input;
   5.141 +    command[2]=NULL;
   5.142 +    if (standard_output)
   5.143 +    {
   5.144 +	r=spawn_sync(command,&s,&exit_status,error);
   5.145 +	if (r)
   5.146 +	{
   5.147 +	    if (encoding)
   5.148 +	    {
   5.149 +		output=g_convert(s,-1,"UTF-8",encoding,NULL,NULL,&tmp_err);
   5.150 +		g_free(s);
   5.151 +		if (!output)
   5.152 +		{
   5.153 +		    g_propagate_prefixed_error(error,tmp_err,
   5.154 +		      "Conversion from %s failed: ",encoding);
   5.155 +		    r=FALSE;
   5.156 +		}
   5.157 +	    }
   5.158 +	    else
   5.159 +	    {
   5.160 +		output=s;
   5.161 +		if (!g_utf8_validate(s,-1,NULL))
   5.162 +		{
   5.163 +		    g_set_error_literal(error,TESTCASE_ERROR,
   5.164 +		      TESTCASE_ERROR_FAILED,
   5.165 +		      "bookloupe output is not valid UTF-8");
   5.166 +		    r=FALSE;
   5.167 +		}
   5.168 +	    }
   5.169 +	}
   5.170 +    }
   5.171 +    else
   5.172 +    {
   5.173 +	r=spawn_sync(command,NULL,&exit_status,error);
   5.174 +	output=NULL;
   5.175 +    }
   5.176 +    (void)remove(input);
   5.177 +    if (r && exit_status)
   5.178 +    {
   5.179 +	g_set_error(error,TESTCASE_ERROR,TESTCASE_ERROR_FAILED,
   5.180 +	  "bookloupe exited with code %d",exit_status);
   5.181 +	r=FALSE;
   5.182 +    }
   5.183 +    if (r && filename)
   5.184 +	*filename=g_strdup(input);
   5.185 +    if (r && standard_output)
   5.186 +	*standard_output=output;
   5.187 +    return r;
   5.188 +}
   5.189 +
   5.190  /*
   5.191   * Run a testcase, returning FALSE on fail or error and
   5.192   * TRUE on pass or expected-fail.
   5.193 @@ -103,92 +238,76 @@
   5.194  gboolean testcase_run(Testcase *testcase)
   5.195  {
   5.196      gboolean r;
   5.197 -    int fd,exit_status,col;
   5.198 -    size_t n,pos,offset,header_len;
   5.199 -    FILE *fp;
   5.200 -    char input[]="TEST-XXXXXX";
   5.201 -    char *endp,*bol;
   5.202 -    char *command[3];
   5.203 -    GString *expected,*report;
   5.204 -    char *output;
   5.205 -    fd=mkstemp(input);
   5.206 -    if (testcase->input)
   5.207 -	n=strlen(testcase->input);
   5.208 +    size_t pos,offset;
   5.209 +    GString *header,*expected;
   5.210 +    char *output,*filename,*s;
   5.211 +    GError *error=NULL;
   5.212 +    if (testcase->expected)
   5.213 +	r=spawn_bootloupe(testcase->encoding,testcase->input,&output,&filename,
   5.214 +	  &error);
   5.215      else
   5.216 -	n=0;
   5.217 -    if (n && write_text(fd,testcase->input,n)!=n)
   5.218      {
   5.219 -	perror(input);
   5.220 -	close(fd);
   5.221 -	(void)remove(input);
   5.222 +	r=spawn_bootloupe(testcase->encoding,testcase->input,NULL,NULL,&error);
   5.223 +        output=filename=NULL;
   5.224 +    }
   5.225 +    if (!r)
   5.226 +    {
   5.227 +	fprintf(stderr,"%s: FAIL\n",testcase->basename);
   5.228 +	fprintf(stderr,"%s\n",error->message);
   5.229 +	g_error_free(error);
   5.230  	return FALSE;
   5.231      }
   5.232 -    close(fd);
   5.233 -    command[0]=getenv("BOOKLOUPE");
   5.234 -    if (!command[0])
   5.235 -	command[0]="." G_DIR_SEPARATOR_S "bookloupe";
   5.236 -    command[1]=input;
   5.237 -    command[2]=NULL;
   5.238 -    if (testcase->expected)
   5.239 -	r=spawn_sync(command,&output,&exit_status);
   5.240 -    else
   5.241 -    {
   5.242 -	r=spawn_sync(command,NULL,&exit_status);
   5.243 -	output=NULL;
   5.244 -    }
   5.245 -    (void)remove(input);
   5.246 -    if (!r)
   5.247 -	return FALSE;
   5.248      if (testcase->expected)
   5.249      {
   5.250 -	expected=g_string_new("\n\nFile: ");
   5.251 -	g_string_append(expected,input);
   5.252 -	g_string_append(expected,"\n\n\n");
   5.253 -	header_len=expected->len;
   5.254 -	g_string_append(expected,testcase->expected);
   5.255 +	header=g_string_new("\n\nFile: ");
   5.256 +	g_string_append(header,filename);
   5.257 +	g_string_append(header,"\n");
   5.258 +	expected=g_string_new(testcase->expected);
   5.259 +	if (!g_str_has_prefix(output,header->str))
   5.260 +	{
   5.261 +	    fprintf(stderr,"%s: FAIL\n",testcase->basename);
   5.262 +	    offset=common_prefix_length(output,header->str);
   5.263 +	    fprintf(stderr,"Unexpected header from bookloupe:\n");
   5.264 +	    print_unexpected(output,offset);
   5.265 +	    r=FALSE;
   5.266 +	}
   5.267 +	pos=header->len;
   5.268 +	if (r)
   5.269 +	{
   5.270 +	    /* Skip the summary */
   5.271 +	    s=strstr(output+pos,"\n\n");
   5.272 +	    if (s)
   5.273 +		pos=s-output+2;
   5.274 +	    else
   5.275 +	    {
   5.276 +		fprintf(stderr,"%s: FAIL\n",testcase->basename);
   5.277 +		offset=common_prefix_length(output,header->str);
   5.278 +		fprintf(stderr,"Unterminated summary from bookloupe:\n%s\n",
   5.279 +		  output+pos);
   5.280 +		r=FALSE;
   5.281 +	    }
   5.282 +	}
   5.283 +	if (r && strcmp(output+pos,expected->str))
   5.284 +	{
   5.285 +	    fprintf(stderr,"%s: FAIL\n",testcase->basename);
   5.286 +	    offset=common_prefix_length(output+pos,expected->str);
   5.287 +	    if (!offset && !output[pos+offset])
   5.288 +		fprintf(stderr,"Unexpected zero warnings from bookloupe.\n");
   5.289 +	    else
   5.290 +	    {
   5.291 +		fprintf(stderr,"Unexpected output from bookloupe:\n");
   5.292 +		print_unexpected(output+pos,offset);
   5.293 +	    }
   5.294 +	    r=FALSE;
   5.295 +	}
   5.296 +	g_string_free(header,TRUE);
   5.297 +	g_string_free(expected,TRUE);
   5.298      }
   5.299 -    else
   5.300 -    {
   5.301 -	expected=NULL;
   5.302 -	header_len=0;
   5.303 -    }
   5.304 -    if (expected && strcmp(output,expected->str))
   5.305 -    {
   5.306 -	fprintf(stderr,"%s: FAIL\n",testcase->basename);
   5.307 -	offset=common_prefix_length(output,expected->str);
   5.308 -	if (offset==header_len && !output[offset])
   5.309 -	    fprintf(stderr,"Unexpected zero warnings from bookloupe.\n");
   5.310 -	else
   5.311 -	{
   5.312 -	    endp=strchr(output+offset,'\n');
   5.313 -	    if (!endp)
   5.314 -		endp=output+strlen(output);
   5.315 -	    report=g_string_new(NULL);
   5.316 -	    g_string_append_len(report,output,endp-output);
   5.317 -	    bol=strrchr(report->str,'\n');
   5.318 -	    if (bol)
   5.319 -		bol++;
   5.320 -	    else
   5.321 -		bol=report->str;
   5.322 -	    col=offset-(bol-report->str);
   5.323 -	    fprintf(stderr,"Unexpected output from bookloupe:\n");
   5.324 -	    if (report->len>=header_len)
   5.325 -		fprintf(stderr,"%s\n%*s^\n",report->str+header_len,col,"");
   5.326 -	    else
   5.327 -		fprintf(stderr,"%s\n%*s^\n",report->str,col,"");
   5.328 -	    g_string_free(report,TRUE);
   5.329 -	}
   5.330 -	g_string_free(expected,TRUE);
   5.331 -	g_free(output);
   5.332 -	return FALSE;
   5.333 -    }
   5.334 -    g_string_free(expected,TRUE);
   5.335      g_free(output);
   5.336 -    if (exit_status)
   5.337 -	fprintf(stderr,"bookloupe exited with code %d\n",r);
   5.338 -    if (!exit_status)
   5.339 +    g_free(filename);
   5.340 +    if (r)
   5.341  	fprintf(stderr,"%s: PASS\n",testcase->basename);
   5.342 -    return !exit_status;
   5.343 +    return r;
   5.344  }
   5.345  
   5.346  /*
   5.347 @@ -199,5 +318,6 @@
   5.348      g_free(testcase->basename);
   5.349      g_free(testcase->input);
   5.350      g_free(testcase->expected);
   5.351 +    g_free(testcase->encoding);
   5.352      g_free(testcase);
   5.353  }
     6.1 --- a/test/harness/testcase.h	Fri Jan 27 16:18:02 2012 +0000
     6.2 +++ b/test/harness/testcase.h	Fri Jan 27 21:40:35 2012 +0000
     6.3 @@ -3,15 +3,23 @@
     6.4  
     6.5  #include <glib.h>
     6.6  
     6.7 +#define TESTCASE_ERROR testcase_error_quark()
     6.8 +
     6.9 +typedef enum {
    6.10 +    TESTCASE_ERROR_FAILED
    6.11 +} TestcaseError;
    6.12 +
    6.13  typedef struct {
    6.14      char *basename;
    6.15      char *input;
    6.16      char *expected;
    6.17 +    char *encoding;	/* The character encoding to talk to BOOKLOUPE in */
    6.18      enum {
    6.19  	TESTCASE_XFAIL=1<<0,
    6.20      } flags;
    6.21  } Testcase;
    6.22  
    6.23 +GQuark testcase_error_quark(void);
    6.24  gboolean testcase_run(Testcase *testcase);
    6.25  void testcase_free(Testcase *testcase);
    6.26  
     7.1 --- a/test/harness/testcaseio.c	Fri Jan 27 16:18:02 2012 +0000
     7.2 +++ b/test/harness/testcaseio.c	Fri Jan 27 21:40:35 2012 +0000
     7.3 @@ -38,6 +38,8 @@
     7.4  	    testcase->input=g_strdup(text);
     7.5  	else if (!testcase->expected && !strcmp(tag,"EXPECTED"))
     7.6  	    testcase->expected=g_strdup(text);
     7.7 +	else if (!testcase->encoding && !strcmp(tag,"ENCODING"))
     7.8 +	    testcase->encoding=g_strdup(text);
     7.9  	else
    7.10  	{
    7.11  	    fprintf(stderr,"%s: Not a valid testcase (%s)\n",filename,tag);
     8.1 --- a/test/harness/testcaseparser.c	Fri Jan 27 16:18:02 2012 +0000
     8.2 +++ b/test/harness/testcaseparser.c	Fri Jan 27 21:40:35 2012 +0000
     8.3 @@ -90,12 +90,20 @@
     8.4  TestcaseParser *testcase_parser_new_from_file(const char *filename)
     8.5  {
     8.6      TestcaseParser *parser;
     8.7 +    gsize len;
     8.8      parser=g_new0(TestcaseParser,1);
     8.9 -    if (!file_get_contents_text(filename,&parser->contents,NULL))
    8.10 +    if (!file_get_contents_text(filename,&parser->contents,&len))
    8.11      {
    8.12  	g_free(parser);
    8.13  	return NULL;
    8.14      }
    8.15 +    if (!g_utf8_validate(parser->contents,len,NULL))
    8.16 +    {
    8.17 +	fprintf(stderr,"%s: Does not contain valid UTF-8\n",filename);
    8.18 +	g_free(parser->contents);
    8.19 +	g_free(parser);
    8.20 +	return NULL;
    8.21 +    }
    8.22      parser->filename=g_strdup(filename);
    8.23      return parser;
    8.24  }