test/harness/testcase.c
changeset 9 6a13fe0fc19e
parent 8 cf332d440466
child 11 4a80c6053a66
     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  }