1.1 --- a/test/harness/testcase.c Fri Jan 27 23:59:51 2012 +0000
1.2 +++ b/test/harness/testcase.c Mon Jan 30 00:36:31 2012 +0000
1.3 @@ -1,10 +1,12 @@
1.4 #include <stdlib.h>
1.5 #include <stdio.h>
1.6 #include <string.h>
1.7 -#include <unistd.h>
1.8 #include <errno.h>
1.9 +#include <glib.h>
1.10 +#include <glib/gstdio.h>
1.11 #include <bl/bl.h>
1.12 #include "testcase.h"
1.13 +#include "testcaseinput.h"
1.14
1.15 GQuark testcase_error_quark(void)
1.16 {
1.17 @@ -12,20 +14,6 @@
1.18 }
1.19
1.20 /*
1.21 - * As write(), but with error handling.
1.22 - */
1.23 -static size_t write_file(int fd,const char *buf,size_t count,GError **error)
1.24 -{
1.25 - if (write(fd,buf,count)<count)
1.26 - {
1.27 - g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
1.28 - "Error writing bookloupe input file: %s",g_strerror(errno));
1.29 - return -1;
1.30 - }
1.31 - return count;
1.32 -}
1.33 -
1.34 -/*
1.35 * Return the length (in bytes) of any common prefix between s1 and s2.
1.36 * The returned length will always represent an exact number of characters.
1.37 */
1.38 @@ -79,91 +67,144 @@
1.39 }
1.40
1.41 /*
1.42 - * Replace \n with \r\n and U+240A (visible symbol for LF) with \n
1.43 + * Create all the input files needed by a testcase and, if required,
1.44 + * a temporary directory in which to store them.
1.45 */
1.46 -char *unix2dos(const char *text)
1.47 +gboolean testcase_create_input_files(Testcase *testcase,GError **error)
1.48 {
1.49 - gunichar c;
1.50 - const gunichar visible_lf=0x240A;
1.51 - GString *string;
1.52 - string=g_string_new(NULL);
1.53 - while(*text)
1.54 + GSList *link,*link2;
1.55 + if (testcase->flags&TESTCASE_TMP_DIR)
1.56 {
1.57 - c=g_utf8_get_char(text);
1.58 - text=g_utf8_next_char(text);
1.59 - if (c=='\n')
1.60 - g_string_append(string,"\r\n");
1.61 - else if (c==visible_lf)
1.62 - g_string_append_c(string,'\n');
1.63 - else
1.64 - g_string_append_unichar(string,c);
1.65 + testcase->tmpdir=g_strdup("TEST-XXXXXX");
1.66 + if (!g_mkdtemp(testcase->tmpdir))
1.67 + {
1.68 + g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
1.69 + "Failed to create temporary directory: %s",g_strerror(errno));
1.70 + g_free(testcase->tmpdir);
1.71 + testcase->tmpdir=NULL;
1.72 + return FALSE;
1.73 + }
1.74 }
1.75 - return g_string_free(string,FALSE);
1.76 + for(link=testcase->inputs;link;link=link->next)
1.77 + if (!testcase_input_create(testcase,link->data,error))
1.78 + {
1.79 + for(link2=testcase->inputs;link2!=link;link2=link2->next)
1.80 + (void)testcase_input_remove(testcase,link2->data,NULL);
1.81 + if (testcase->tmpdir)
1.82 + {
1.83 + (void)g_rmdir(testcase->tmpdir);
1.84 + g_free(testcase->tmpdir);
1.85 + testcase->tmpdir=NULL;
1.86 + }
1.87 + return FALSE;
1.88 + }
1.89 + return TRUE;
1.90 }
1.91
1.92 -gboolean spawn_bootloupe(const char *encoding,const char *standard_input,
1.93 - char **standard_output,char **filename,GError **error)
1.94 +/*
1.95 + * Remove all the input files used by a testcase and, if created,
1.96 + * the temporary directory in which they are stored.
1.97 + */
1.98 +gboolean testcase_remove_input_files(Testcase *testcase,GError **error)
1.99 +{
1.100 + GSList *link;
1.101 + GError *tmp_err=NULL;
1.102 + gboolean retval=TRUE;
1.103 + for(link=testcase->inputs;link;link=link->next)
1.104 + if (!testcase_input_remove(testcase,link->data,&tmp_err))
1.105 + {
1.106 + if (error && !*error)
1.107 + g_propagate_error(error,tmp_err);
1.108 + else
1.109 + g_clear_error(&tmp_err);
1.110 + retval=FALSE;
1.111 + }
1.112 + if (testcase->tmpdir)
1.113 + {
1.114 + if (g_rmdir(testcase->tmpdir))
1.115 + {
1.116 + if (error && !*error)
1.117 + g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
1.118 + "Failed to remove temporary directory: %s",g_strerror(errno));
1.119 + retval=FALSE;
1.120 + }
1.121 + g_free(testcase->tmpdir);
1.122 + testcase->tmpdir=NULL;
1.123 + }
1.124 + return retval;
1.125 +}
1.126 +
1.127 +/*
1.128 + * Replace every occurance of an input file name in <str> with the
1.129 + * filename which holds that input. For input files with fixed names,
1.130 + * this is a noop. For input files which use the "XXXXXX" sequence
1.131 + * to create a unique filename, the XXXXXX will be replaced with the
1.132 + * 6 characters that were chosen to be unique.
1.133 + */
1.134 +char *testcase_resolve_input_files(Testcase *testcase,const char *str)
1.135 +{
1.136 + GSList *link;
1.137 + gsize offset,pos;
1.138 + char *s;
1.139 + TestcaseInput *input;
1.140 + GString *filename=g_string_new(str);
1.141 + for(link=testcase->inputs;link;link=link->next)
1.142 + {
1.143 + input=link->data;
1.144 + if (!input->name_used)
1.145 + {
1.146 + g_warning("%s: Input file uninstantiated",input->name);
1.147 + continue;
1.148 + }
1.149 + offset=0;
1.150 + do
1.151 + {
1.152 + s=strstr(filename->str+offset,input->name);
1.153 + if (s)
1.154 + {
1.155 + pos=s-filename->str;
1.156 + g_string_overwrite(filename,pos,input->name_used);
1.157 + offset=pos+strlen(input->name);
1.158 + }
1.159 + } while(s);
1.160 + }
1.161 + return g_string_free(filename,FALSE);
1.162 +}
1.163 +
1.164 +gboolean testcase_spawn_bookloupe(Testcase *testcase,char **standard_output,
1.165 + GError **error)
1.166 {
1.167 gboolean r;
1.168 - int fd,exit_status;
1.169 - size_t n,pos,offset;
1.170 - char input[]="TEST-XXXXXX";
1.171 - char *command[3];
1.172 - char *output,*s,*t;
1.173 + int i,exit_status;
1.174 + char **argv;
1.175 + char *output,*s;
1.176 GError *tmp_err=NULL;
1.177 - if (standard_input)
1.178 - {
1.179 - if (encoding)
1.180 - {
1.181 - t=unix2dos(standard_input);
1.182 - s=g_convert(t,-1,encoding,"UTF-8",NULL,&n,&tmp_err);
1.183 - g_free(t);
1.184 - if (!s)
1.185 - {
1.186 - g_propagate_prefixed_error(error,tmp_err,
1.187 - "Conversion to %s failed: ",encoding);
1.188 - return FALSE;
1.189 - }
1.190 - }
1.191 - else
1.192 - {
1.193 - s=unix2dos(standard_input);
1.194 - n=strlen(s);
1.195 - }
1.196 - }
1.197 + if (testcase->options)
1.198 + argv=g_new(char *,g_strv_length(testcase->options)+3);
1.199 else
1.200 - {
1.201 - n=0;
1.202 - s=NULL;
1.203 - }
1.204 - fd=g_mkstemp(input);
1.205 - if (n && write_file(fd,s,n,error)!=n)
1.206 - {
1.207 - g_free(s);
1.208 - close(fd);
1.209 - (void)remove(input);
1.210 - return FALSE;
1.211 - }
1.212 - g_free(s);
1.213 - close(fd);
1.214 - command[0]=getenv("BOOKLOUPE");
1.215 - if (!command[0])
1.216 - command[0]="." G_DIR_SEPARATOR_S "bookloupe";
1.217 - command[1]=input;
1.218 - command[2]=NULL;
1.219 + argv=g_new(char *,3);
1.220 + s=getenv("BOOKLOUPE");
1.221 + if (!s)
1.222 + s="bookloupe";
1.223 + argv[0]=path_to_absolute(s);
1.224 + for(i=0;testcase->options && testcase->options[i];i++)
1.225 + argv[i+1]=testcase_resolve_input_files(testcase,testcase->options[i]);
1.226 + argv[i+1]=testcase_resolve_input_files(testcase,"TEST-XXXXXX");
1.227 + argv[i+2]=NULL;
1.228 if (standard_output)
1.229 {
1.230 - r=spawn_sync(command,&s,&exit_status,error);
1.231 + r=spawn_sync(testcase->tmpdir,argv,&s,&exit_status,error);
1.232 if (r)
1.233 {
1.234 - if (encoding)
1.235 + if (testcase->encoding)
1.236 {
1.237 - output=g_convert(s,-1,"UTF-8",encoding,NULL,NULL,&tmp_err);
1.238 + output=g_convert(s,-1,"UTF-8",testcase->encoding,NULL,NULL,
1.239 + &tmp_err);
1.240 g_free(s);
1.241 if (!output)
1.242 {
1.243 g_propagate_prefixed_error(error,tmp_err,
1.244 - "Conversion from %s failed: ",encoding);
1.245 + "Conversion from %s failed: ",testcase->encoding);
1.246 r=FALSE;
1.247 }
1.248 }
1.249 @@ -182,18 +223,16 @@
1.250 }
1.251 else
1.252 {
1.253 - r=spawn_sync(command,NULL,&exit_status,error);
1.254 + r=spawn_sync(testcase->tmpdir,argv,NULL,&exit_status,error);
1.255 output=NULL;
1.256 }
1.257 - (void)remove(input);
1.258 + g_strfreev(argv);
1.259 if (r && exit_status)
1.260 {
1.261 g_set_error(error,TESTCASE_ERROR,TESTCASE_ERROR_FAILED,
1.262 "bookloupe exited with code %d",exit_status);
1.263 r=FALSE;
1.264 }
1.265 - if (r && filename)
1.266 - *filename=g_strdup(input);
1.267 if (r && standard_output)
1.268 *standard_output=output;
1.269 return r;
1.270 @@ -211,19 +250,34 @@
1.271 GString *header,*expected;
1.272 char *output,*filename,*s;
1.273 GError *error=NULL;
1.274 + if (!testcase_create_input_files(testcase,&error))
1.275 + {
1.276 + fprintf(stderr,"%s: FAIL\n",testcase->basename);
1.277 + fprintf(stderr,"%s\n",error->message);
1.278 + g_error_free(error);
1.279 + return FALSE;
1.280 + }
1.281 if (testcase->expected)
1.282 - r=spawn_bootloupe(testcase->encoding,testcase->input,&output,&filename,
1.283 - &error);
1.284 + r=testcase_spawn_bookloupe(testcase,&output,&error);
1.285 else
1.286 {
1.287 - r=spawn_bootloupe(testcase->encoding,testcase->input,NULL,NULL,&error);
1.288 - output=filename=NULL;
1.289 + r=testcase_spawn_bookloupe(testcase,NULL,&error);
1.290 + output=NULL;
1.291 }
1.292 if (!r)
1.293 {
1.294 fprintf(stderr,"%s: FAIL\n",testcase->basename);
1.295 fprintf(stderr,"%s\n",error->message);
1.296 g_error_free(error);
1.297 + (void)testcase_remove_input_files(testcase,NULL);
1.298 + return FALSE;
1.299 + }
1.300 + filename=testcase_resolve_input_files(testcase,"TEST-XXXXXX");
1.301 + if (!testcase_remove_input_files(testcase,&error))
1.302 + {
1.303 + fprintf(stderr,"%s: FAIL\n",testcase->basename);
1.304 + fprintf(stderr,"%s\n",error->message);
1.305 + g_error_free(error);
1.306 return FALSE;
1.307 }
1.308 if (testcase->expected)
1.309 @@ -272,8 +326,8 @@
1.310 g_string_free(header,TRUE);
1.311 g_string_free(expected,TRUE);
1.312 }
1.313 + g_free(filename);
1.314 g_free(output);
1.315 - g_free(filename);
1.316 if (r)
1.317 fprintf(stderr,"%s: PASS\n",testcase->basename);
1.318 return r;
1.319 @@ -285,8 +339,10 @@
1.320 void testcase_free(Testcase *testcase)
1.321 {
1.322 g_free(testcase->basename);
1.323 - g_free(testcase->input);
1.324 + g_slist_foreach(testcase->inputs,(GFunc)testcase_input_free,NULL);
1.325 + g_slist_free(testcase->inputs);
1.326 g_free(testcase->expected);
1.327 g_free(testcase->encoding);
1.328 + g_strfreev(testcase->options);
1.329 g_free(testcase);
1.330 }