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 }