test/harness/testcase.c
changeset 7 721e468c10f3
parent 6 faab25d520dd
child 8 cf332d440466
     1.1 --- a/test/harness/testcase.c	Fri Jan 27 16:18:02 2012 +0000
     1.2 +++ b/test/harness/testcase.c	Fri Jan 27 21:40:35 2012 +0000
     1.3 @@ -10,6 +10,11 @@
     1.4  #include <bl/bl.h>
     1.5  #include "testcase.h"
     1.6  
     1.7 +GQuark testcase_error_quark(void)
     1.8 +{
     1.9 +    return g_quark_from_static_string("testcase-error-quark");
    1.10 +}
    1.11 +
    1.12  #if !HAVE_MKSTEMP
    1.13  /*
    1.14   * An insecure implementation of mkstemp(), for those platforms that
    1.15 @@ -45,7 +50,7 @@
    1.16  /*
    1.17   * As write(), but always convert NL to CR NL.
    1.18   */
    1.19 -static size_t write_text(int fd,const char *buf,size_t count)
    1.20 +static size_t write_text(int fd,const char *buf,size_t count,GError **error)
    1.21  {
    1.22      size_t i;
    1.23      FILE *fp;
    1.24 @@ -56,6 +61,9 @@
    1.25      if (_setmode(fd,_O_BINARY)<0)
    1.26      {
    1.27  	close(fd);
    1.28 +	g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
    1.29 +	  "Failed to set mode of bookloupe input file to binary: %s",
    1.30 +	  g_strerror(errno));
    1.31  	return -1;
    1.32      }
    1.33  #endif
    1.34 @@ -63,6 +71,9 @@
    1.35      if (!fp)
    1.36      {
    1.37  	close(fd);
    1.38 +	g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
    1.39 +	  "Failed to open stream to bookloupe input file: %s",
    1.40 +	  g_strerror(errno));
    1.41  	return -1;
    1.42      }
    1.43      for(i=0;i<count;i++)
    1.44 @@ -70,17 +81,25 @@
    1.45  	if (buf[i]=='\n')
    1.46  	    if (putc('\r',fp)==EOF)
    1.47  	    {
    1.48 +		g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
    1.49 +		  "Error writing bookloupe input file: %s",g_strerror(errno));
    1.50  		(void)fclose(fp);
    1.51  		return -1;
    1.52  	    }
    1.53  	if (putc(buf[i],fp)==EOF)
    1.54  	{
    1.55 +	    g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
    1.56 +	      "Error writing bookloupe input file: %s",g_strerror(errno));
    1.57  	    (void)fclose(fp);
    1.58  	    return -1;
    1.59  	}
    1.60      }
    1.61      if (fclose(fp))
    1.62 +    {
    1.63 +	g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
    1.64 +	  "Error writing bookloupe input file: %s",g_strerror(errno));
    1.65  	return -1;
    1.66 +    }
    1.67      return count;
    1.68  }
    1.69  
    1.70 @@ -95,6 +114,122 @@
    1.71      return i;
    1.72  }
    1.73  
    1.74 +void print_unexpected(const char *unexpected,gsize differs_at)
    1.75 +{
    1.76 +    int col;
    1.77 +    const char *endp,*bol;
    1.78 +    GString *string;
    1.79 +    endp=strchr(unexpected+differs_at,'\n');
    1.80 +    if (!endp)
    1.81 +	endp=unexpected+strlen(unexpected);
    1.82 +    string=g_string_new_len(unexpected,endp-unexpected);
    1.83 +    bol=strrchr(string->str,'\n');
    1.84 +    if (bol)
    1.85 +	bol++;
    1.86 +    else
    1.87 +	bol=string->str;
    1.88 +    col=differs_at-(bol-string->str);
    1.89 +    fprintf(stderr,"%s\n%*s^\n",string->str,col,"");
    1.90 +    g_string_free(string,TRUE);
    1.91 +}
    1.92 +
    1.93 +gboolean spawn_bootloupe(const char *encoding,const char *standard_input,
    1.94 +  char **standard_output,char **filename,GError **error)
    1.95 +{
    1.96 +    gboolean r;
    1.97 +    int fd,exit_status;
    1.98 +    size_t n,pos,offset;
    1.99 +    FILE *fp;
   1.100 +    char input[]="TEST-XXXXXX";
   1.101 +    char *command[3];
   1.102 +    char *output,*s;
   1.103 +    GError *tmp_err=NULL;
   1.104 +    if (standard_input)
   1.105 +    {
   1.106 +	if (encoding)
   1.107 +	{
   1.108 +	    s=g_convert(standard_input,-1,encoding,"UTF-8",NULL,&n,&tmp_err);
   1.109 +	    if (!s)
   1.110 +	    {
   1.111 +		g_propagate_prefixed_error(error,tmp_err,
   1.112 +		  "Conversion to %s failed: ",encoding);
   1.113 +		return FALSE;
   1.114 +	    }
   1.115 +	}
   1.116 +	else
   1.117 +	{
   1.118 +	    s=g_strdup(standard_input);
   1.119 +	    n=strlen(s);
   1.120 +	}
   1.121 +    }
   1.122 +    else
   1.123 +    {
   1.124 +	n=0;
   1.125 +	s=NULL;
   1.126 +    }
   1.127 +    fd=mkstemp(input);
   1.128 +    if (n && write_text(fd,s,n,error)!=n)
   1.129 +    {
   1.130 +	g_free(s);
   1.131 +	close(fd);
   1.132 +	(void)remove(input);
   1.133 +	return FALSE;
   1.134 +    }
   1.135 +    g_free(s);
   1.136 +    close(fd);
   1.137 +    command[0]=getenv("BOOKLOUPE");
   1.138 +    if (!command[0])
   1.139 +	command[0]="." G_DIR_SEPARATOR_S "bookloupe";
   1.140 +    command[1]=input;
   1.141 +    command[2]=NULL;
   1.142 +    if (standard_output)
   1.143 +    {
   1.144 +	r=spawn_sync(command,&s,&exit_status,error);
   1.145 +	if (r)
   1.146 +	{
   1.147 +	    if (encoding)
   1.148 +	    {
   1.149 +		output=g_convert(s,-1,"UTF-8",encoding,NULL,NULL,&tmp_err);
   1.150 +		g_free(s);
   1.151 +		if (!output)
   1.152 +		{
   1.153 +		    g_propagate_prefixed_error(error,tmp_err,
   1.154 +		      "Conversion from %s failed: ",encoding);
   1.155 +		    r=FALSE;
   1.156 +		}
   1.157 +	    }
   1.158 +	    else
   1.159 +	    {
   1.160 +		output=s;
   1.161 +		if (!g_utf8_validate(s,-1,NULL))
   1.162 +		{
   1.163 +		    g_set_error_literal(error,TESTCASE_ERROR,
   1.164 +		      TESTCASE_ERROR_FAILED,
   1.165 +		      "bookloupe output is not valid UTF-8");
   1.166 +		    r=FALSE;
   1.167 +		}
   1.168 +	    }
   1.169 +	}
   1.170 +    }
   1.171 +    else
   1.172 +    {
   1.173 +	r=spawn_sync(command,NULL,&exit_status,error);
   1.174 +	output=NULL;
   1.175 +    }
   1.176 +    (void)remove(input);
   1.177 +    if (r && exit_status)
   1.178 +    {
   1.179 +	g_set_error(error,TESTCASE_ERROR,TESTCASE_ERROR_FAILED,
   1.180 +	  "bookloupe exited with code %d",exit_status);
   1.181 +	r=FALSE;
   1.182 +    }
   1.183 +    if (r && filename)
   1.184 +	*filename=g_strdup(input);
   1.185 +    if (r && standard_output)
   1.186 +	*standard_output=output;
   1.187 +    return r;
   1.188 +}
   1.189 +
   1.190  /*
   1.191   * Run a testcase, returning FALSE on fail or error and
   1.192   * TRUE on pass or expected-fail.
   1.193 @@ -103,92 +238,76 @@
   1.194  gboolean testcase_run(Testcase *testcase)
   1.195  {
   1.196      gboolean r;
   1.197 -    int fd,exit_status,col;
   1.198 -    size_t n,pos,offset,header_len;
   1.199 -    FILE *fp;
   1.200 -    char input[]="TEST-XXXXXX";
   1.201 -    char *endp,*bol;
   1.202 -    char *command[3];
   1.203 -    GString *expected,*report;
   1.204 -    char *output;
   1.205 -    fd=mkstemp(input);
   1.206 -    if (testcase->input)
   1.207 -	n=strlen(testcase->input);
   1.208 +    size_t pos,offset;
   1.209 +    GString *header,*expected;
   1.210 +    char *output,*filename,*s;
   1.211 +    GError *error=NULL;
   1.212 +    if (testcase->expected)
   1.213 +	r=spawn_bootloupe(testcase->encoding,testcase->input,&output,&filename,
   1.214 +	  &error);
   1.215      else
   1.216 -	n=0;
   1.217 -    if (n && write_text(fd,testcase->input,n)!=n)
   1.218      {
   1.219 -	perror(input);
   1.220 -	close(fd);
   1.221 -	(void)remove(input);
   1.222 +	r=spawn_bootloupe(testcase->encoding,testcase->input,NULL,NULL,&error);
   1.223 +        output=filename=NULL;
   1.224 +    }
   1.225 +    if (!r)
   1.226 +    {
   1.227 +	fprintf(stderr,"%s: FAIL\n",testcase->basename);
   1.228 +	fprintf(stderr,"%s\n",error->message);
   1.229 +	g_error_free(error);
   1.230  	return FALSE;
   1.231      }
   1.232 -    close(fd);
   1.233 -    command[0]=getenv("BOOKLOUPE");
   1.234 -    if (!command[0])
   1.235 -	command[0]="." G_DIR_SEPARATOR_S "bookloupe";
   1.236 -    command[1]=input;
   1.237 -    command[2]=NULL;
   1.238 -    if (testcase->expected)
   1.239 -	r=spawn_sync(command,&output,&exit_status);
   1.240 -    else
   1.241 -    {
   1.242 -	r=spawn_sync(command,NULL,&exit_status);
   1.243 -	output=NULL;
   1.244 -    }
   1.245 -    (void)remove(input);
   1.246 -    if (!r)
   1.247 -	return FALSE;
   1.248      if (testcase->expected)
   1.249      {
   1.250 -	expected=g_string_new("\n\nFile: ");
   1.251 -	g_string_append(expected,input);
   1.252 -	g_string_append(expected,"\n\n\n");
   1.253 -	header_len=expected->len;
   1.254 -	g_string_append(expected,testcase->expected);
   1.255 +	header=g_string_new("\n\nFile: ");
   1.256 +	g_string_append(header,filename);
   1.257 +	g_string_append(header,"\n");
   1.258 +	expected=g_string_new(testcase->expected);
   1.259 +	if (!g_str_has_prefix(output,header->str))
   1.260 +	{
   1.261 +	    fprintf(stderr,"%s: FAIL\n",testcase->basename);
   1.262 +	    offset=common_prefix_length(output,header->str);
   1.263 +	    fprintf(stderr,"Unexpected header from bookloupe:\n");
   1.264 +	    print_unexpected(output,offset);
   1.265 +	    r=FALSE;
   1.266 +	}
   1.267 +	pos=header->len;
   1.268 +	if (r)
   1.269 +	{
   1.270 +	    /* Skip the summary */
   1.271 +	    s=strstr(output+pos,"\n\n");
   1.272 +	    if (s)
   1.273 +		pos=s-output+2;
   1.274 +	    else
   1.275 +	    {
   1.276 +		fprintf(stderr,"%s: FAIL\n",testcase->basename);
   1.277 +		offset=common_prefix_length(output,header->str);
   1.278 +		fprintf(stderr,"Unterminated summary from bookloupe:\n%s\n",
   1.279 +		  output+pos);
   1.280 +		r=FALSE;
   1.281 +	    }
   1.282 +	}
   1.283 +	if (r && strcmp(output+pos,expected->str))
   1.284 +	{
   1.285 +	    fprintf(stderr,"%s: FAIL\n",testcase->basename);
   1.286 +	    offset=common_prefix_length(output+pos,expected->str);
   1.287 +	    if (!offset && !output[pos+offset])
   1.288 +		fprintf(stderr,"Unexpected zero warnings from bookloupe.\n");
   1.289 +	    else
   1.290 +	    {
   1.291 +		fprintf(stderr,"Unexpected output from bookloupe:\n");
   1.292 +		print_unexpected(output+pos,offset);
   1.293 +	    }
   1.294 +	    r=FALSE;
   1.295 +	}
   1.296 +	g_string_free(header,TRUE);
   1.297 +	g_string_free(expected,TRUE);
   1.298      }
   1.299 -    else
   1.300 -    {
   1.301 -	expected=NULL;
   1.302 -	header_len=0;
   1.303 -    }
   1.304 -    if (expected && strcmp(output,expected->str))
   1.305 -    {
   1.306 -	fprintf(stderr,"%s: FAIL\n",testcase->basename);
   1.307 -	offset=common_prefix_length(output,expected->str);
   1.308 -	if (offset==header_len && !output[offset])
   1.309 -	    fprintf(stderr,"Unexpected zero warnings from bookloupe.\n");
   1.310 -	else
   1.311 -	{
   1.312 -	    endp=strchr(output+offset,'\n');
   1.313 -	    if (!endp)
   1.314 -		endp=output+strlen(output);
   1.315 -	    report=g_string_new(NULL);
   1.316 -	    g_string_append_len(report,output,endp-output);
   1.317 -	    bol=strrchr(report->str,'\n');
   1.318 -	    if (bol)
   1.319 -		bol++;
   1.320 -	    else
   1.321 -		bol=report->str;
   1.322 -	    col=offset-(bol-report->str);
   1.323 -	    fprintf(stderr,"Unexpected output from bookloupe:\n");
   1.324 -	    if (report->len>=header_len)
   1.325 -		fprintf(stderr,"%s\n%*s^\n",report->str+header_len,col,"");
   1.326 -	    else
   1.327 -		fprintf(stderr,"%s\n%*s^\n",report->str,col,"");
   1.328 -	    g_string_free(report,TRUE);
   1.329 -	}
   1.330 -	g_string_free(expected,TRUE);
   1.331 -	g_free(output);
   1.332 -	return FALSE;
   1.333 -    }
   1.334 -    g_string_free(expected,TRUE);
   1.335      g_free(output);
   1.336 -    if (exit_status)
   1.337 -	fprintf(stderr,"bookloupe exited with code %d\n",r);
   1.338 -    if (!exit_status)
   1.339 +    g_free(filename);
   1.340 +    if (r)
   1.341  	fprintf(stderr,"%s: PASS\n",testcase->basename);
   1.342 -    return !exit_status;
   1.343 +    return r;
   1.344  }
   1.345  
   1.346  /*
   1.347 @@ -199,5 +318,6 @@
   1.348      g_free(testcase->basename);
   1.349      g_free(testcase->input);
   1.350      g_free(testcase->expected);
   1.351 +    g_free(testcase->encoding);
   1.352      g_free(testcase);
   1.353  }