6 #include <glib/gstdio.h>
9 #include "testcaseinput.h"
11 GQuark testcase_error_quark(void)
13 return g_quark_from_static_string("testcase-error-quark");
17 * Return the length (in bytes) of any common prefix between s1 and s2.
18 * The returned length will always represent an exact number of characters.
20 size_t common_prefix_length(const char *s1,const char *s2)
26 c1=g_utf8_get_char(s1);
27 c2=g_utf8_get_char(s2);
30 s1=g_utf8_next_char(s1);
31 s2=g_utf8_next_char(s2);
36 void print_unexpected(const char *unexpected,gsize differs_at)
40 const char *endp,*bol,*s;
42 endp=strchr(unexpected+differs_at,'\n');
44 endp=unexpected+strlen(unexpected);
45 string=g_string_new_len(unexpected,endp-unexpected);
46 bol=strrchr(string->str,'\n');
53 endp=string->str+differs_at;
57 s=g_utf8_next_char(s);
60 else if (g_unichar_iswide(c))
62 else if (!g_unichar_iszerowidth(c))
65 g_print("%s\n%*s^\n",string->str,col,"");
66 g_string_free(string,TRUE);
70 * Create all the input files needed by a testcase and, if required,
71 * a temporary directory in which to store them.
73 gboolean testcase_create_input_files(Testcase *testcase,GError **error)
76 if (testcase->flags&TESTCASE_TMP_DIR)
78 testcase->tmpdir=g_strdup("TEST-XXXXXX");
79 if (!g_mkdtemp(testcase->tmpdir))
81 g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
82 "Failed to create temporary directory: %s",g_strerror(errno));
83 g_free(testcase->tmpdir);
84 testcase->tmpdir=NULL;
88 for(link=testcase->inputs;link;link=link->next)
89 if (!testcase_input_create(testcase,link->data,error))
91 for(link2=testcase->inputs;link2!=link;link2=link2->next)
92 (void)testcase_input_remove(testcase,link2->data,NULL);
95 (void)g_rmdir(testcase->tmpdir);
96 g_free(testcase->tmpdir);
97 testcase->tmpdir=NULL;
105 * Remove all the input files used by a testcase and, if created,
106 * the temporary directory in which they are stored.
108 gboolean testcase_remove_input_files(Testcase *testcase,GError **error)
111 GError *tmp_err=NULL;
112 gboolean retval=TRUE;
113 for(link=testcase->inputs;link;link=link->next)
114 if (!testcase_input_remove(testcase,link->data,&tmp_err))
116 if (error && !*error)
117 g_propagate_error(error,tmp_err);
119 g_clear_error(&tmp_err);
122 if (testcase->tmpdir)
124 if (g_rmdir(testcase->tmpdir))
126 if (error && !*error)
127 g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
128 "Failed to remove temporary directory: %s",g_strerror(errno));
131 g_free(testcase->tmpdir);
132 testcase->tmpdir=NULL;
138 * Replace every occurance of an input file name in <str> with the
139 * filename which holds that input. For input files with fixed names,
140 * this is a noop. For input files which use the "XXXXXX" sequence
141 * to create a unique filename, the XXXXXX will be replaced with the
142 * 6 characters that were chosen to be unique.
144 char *testcase_resolve_input_files(Testcase *testcase,const char *str)
149 TestcaseInput *input;
150 GString *filename=g_string_new(str);
151 for(link=testcase->inputs;link;link=link->next)
154 if (!input->name_used)
156 g_warning("%s: Input file uninstantiated",input->name);
162 s=strstr(filename->str+offset,input->name);
166 g_string_overwrite(filename,pos,input->name_used);
167 offset=pos+strlen(input->name);
171 return g_string_free(filename,FALSE);
174 gboolean testcase_spawn_bookloupe(Testcase *testcase,char **standard_output,
181 GError *tmp_err=NULL;
182 if (testcase->options)
183 argv=g_new(char *,g_strv_length(testcase->options)+3);
185 argv=g_new(char *,3);
186 s=getenv("BOOKLOUPE");
189 argv[0]=path_to_absolute(s);
190 for(i=0;testcase->options && testcase->options[i];i++)
191 argv[i+1]=testcase_resolve_input_files(testcase,testcase->options[i]);
192 argv[i+1]=testcase_resolve_input_files(testcase,"TEST-XXXXXX");
196 r=spawn_sync(testcase->tmpdir,argv,&s,&exit_status,error);
199 if (testcase->encoding)
201 output=g_convert(s,-1,"UTF-8",testcase->encoding,NULL,NULL,
206 g_propagate_prefixed_error(error,tmp_err,
207 "Conversion from %s failed: ",testcase->encoding);
214 if (!g_utf8_validate(s,-1,NULL))
216 g_set_error_literal(error,TESTCASE_ERROR,
217 TESTCASE_ERROR_FAILED,
218 "bookloupe output is not valid UTF-8");
226 r=spawn_sync(testcase->tmpdir,argv,NULL,&exit_status,error);
230 if (r && exit_status)
232 g_set_error(error,TESTCASE_ERROR,TESTCASE_ERROR_FAILED,
233 "bookloupe exited with code %d",exit_status);
236 if (r && standard_output)
237 *standard_output=output;
242 * Run a testcase, returning FALSE on fail or error and
243 * TRUE on pass or expected-fail.
244 * Suitable message(s) will be printed in all cases.
246 gboolean testcase_run(Testcase *testcase)
250 GString *header,*expected;
251 char *output,*filename,*s;
253 if (!testcase_create_input_files(testcase,&error))
255 g_print("%s: FAIL\n",testcase->basename);
256 g_print("%s\n",error->message);
260 if (testcase->expected)
261 r=testcase_spawn_bookloupe(testcase,&output,&error);
264 r=testcase_spawn_bookloupe(testcase,NULL,&error);
269 g_print("%s: FAIL\n",testcase->basename);
270 g_print("%s\n",error->message);
272 (void)testcase_remove_input_files(testcase,NULL);
275 filename=testcase_resolve_input_files(testcase,"TEST-XXXXXX");
276 if (!testcase_remove_input_files(testcase,&error))
278 g_print("%s: FAIL\n",testcase->basename);
279 g_print("%s\n",error->message);
283 if (testcase->expected)
285 header=g_string_new("\n\nFile: ");
286 g_string_append(header,filename);
287 g_string_append(header,"\n");
288 expected=g_string_new(testcase->expected);
289 if (!g_str_has_prefix(output,header->str))
291 g_print("%s: FAIL\n",testcase->basename);
292 offset=common_prefix_length(output,header->str);
293 g_print("Unexpected header from bookloupe:\n");
294 print_unexpected(output,offset);
300 /* Skip the summary */
301 s=strstr(output+pos,"\n\n");
306 g_print("%s: FAIL\n",testcase->basename);
307 offset=common_prefix_length(output,header->str);
308 g_print("Unterminated summary from bookloupe:\n%s\n",
313 if (r && strcmp(output+pos,expected->str))
315 g_print("%s: FAIL\n",testcase->basename);
316 offset=common_prefix_length(output+pos,expected->str);
317 if (!offset && !output[pos+offset])
318 g_print("Unexpected zero warnings from bookloupe.\n");
321 g_print("Unexpected output from bookloupe:\n");
322 print_unexpected(output+pos,offset);
326 g_string_free(header,TRUE);
327 g_string_free(expected,TRUE);
332 g_print("%s: PASS\n",testcase->basename);
339 void testcase_free(Testcase *testcase)
341 g_free(testcase->basename);
342 g_slist_foreach(testcase->inputs,(GFunc)testcase_input_free,NULL);
343 g_slist_free(testcase->inputs);
344 g_free(testcase->expected);
345 g_free(testcase->encoding);
346 g_strfreev(testcase->options);