6 #include <glib/gstdio.h>
9 #include "testcaseinput.h"
10 #include "testcaseoutput.h"
12 GQuark testcase_error_quark(void)
14 return g_quark_from_static_string("testcase-error-quark");
18 * Return the length (in bytes) of any common prefix between s1 and s2.
19 * The returned length will always represent an exact number of characters.
21 size_t common_prefix_length(const char *s1,const char *s2)
27 c1=g_utf8_get_char(s1);
28 c2=g_utf8_get_char(s2);
31 s1=g_utf8_next_char(s1);
32 s2=g_utf8_next_char(s2);
37 void print_unexpected(const char *unexpected,gsize differs_at)
41 const char *endp,*bol,*s;
43 endp=strchr(unexpected+differs_at,'\n');
45 endp=unexpected+strlen(unexpected);
46 string=g_string_new_len(unexpected,endp-unexpected);
47 bol=strrchr(string->str,'\n');
54 endp=string->str+differs_at;
58 s=g_utf8_next_char(s);
61 else if (g_unichar_iswide(c))
63 else if (!g_unichar_iszerowidth(c))
66 g_print("%s\n%*s^\n",string->str,col,"");
67 g_string_free(string,TRUE);
71 * Create all the input files needed by a testcase and, if required,
72 * a temporary directory in which to store them.
74 gboolean testcase_create_input_files(Testcase *testcase,GError **error)
77 if (testcase->flags&TESTCASE_TMP_DIR)
79 testcase->tmpdir=g_strdup("TEST-XXXXXX");
80 if (!g_mkdtemp(testcase->tmpdir))
82 g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
83 "Failed to create temporary directory: %s",g_strerror(errno));
84 g_free(testcase->tmpdir);
85 testcase->tmpdir=NULL;
89 for(link=testcase->inputs;link;link=link->next)
90 if (!testcase_input_create(testcase,link->data,error))
92 for(link2=testcase->inputs;link2!=link;link2=link2->next)
93 (void)testcase_input_remove(testcase,link2->data,NULL);
96 (void)g_rmdir(testcase->tmpdir);
97 g_free(testcase->tmpdir);
98 testcase->tmpdir=NULL;
106 * Remove all the input files used by a testcase and, if created,
107 * the temporary directory in which they are stored.
109 gboolean testcase_remove_input_files(Testcase *testcase,GError **error)
112 GError *tmp_err=NULL;
113 gboolean retval=TRUE;
114 for(link=testcase->inputs;link;link=link->next)
115 if (!testcase_input_remove(testcase,link->data,&tmp_err))
117 if (error && !*error)
118 g_propagate_error(error,tmp_err);
120 g_clear_error(&tmp_err);
123 if (testcase->tmpdir)
125 if (g_rmdir(testcase->tmpdir))
127 if (error && !*error)
128 g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
129 "Failed to remove temporary directory: %s",g_strerror(errno));
132 g_free(testcase->tmpdir);
133 testcase->tmpdir=NULL;
139 * Replace every occurance of an input file name in <str> with the
140 * filename which holds that input. For input files with fixed names,
141 * this is a noop. For input files which use the "XXXXXX" sequence
142 * to create a unique filename, the XXXXXX will be replaced with the
143 * 6 characters that were chosen to be unique.
145 char *testcase_resolve_input_files(Testcase *testcase,const char *str)
150 TestcaseInput *input;
151 GString *filename=g_string_new(str);
152 for(link=testcase->inputs;link;link=link->next)
155 if (!input->name_used)
157 g_warning("%s: Input file uninstantiated",input->name);
163 s=strstr(filename->str+offset,input->name);
167 g_string_overwrite(filename,pos,input->name_used);
168 offset=pos+strlen(input->name);
172 return g_string_free(filename,FALSE);
176 * Verify that all the output files specified by a testcase are present
177 * with the expected contents.
179 gboolean testcase_verify_output_files(Testcase *testcase)
182 GError *tmp_err=NULL;
183 gboolean retval=TRUE;
186 TestcaseOutput *output;
187 for(link=testcase->outputs;link;link=link->next)
190 if (!testcase_output_read(testcase,output,&contents,NULL,&tmp_err))
192 g_print("%s: FAIL\n",testcase->basename);
193 g_print("%s\n",tmp_err->message);
194 g_clear_error(&tmp_err);
200 if (strcmp(contents,output->contents))
202 g_print("%s: FAIL\n",testcase->basename);
203 offset=common_prefix_length(contents,output->contents);
204 if (!offset && !contents[offset])
205 g_print("%s: Unexpected empty output from bookloupe.\n",
209 g_print("%s: Unexpected output from bookloupe:\n",
211 print_unexpected(contents,offset);
219 for(link=testcase->outputs;link;link=link->next)
220 if (!testcase_output_remove(testcase,link->data,&tmp_err))
224 g_print("%s: FAIL\n",testcase->basename);
225 g_print("%s\n",tmp_err->message);
228 g_clear_error(&tmp_err);
233 gboolean testcase_spawn_bookloupe(Testcase *testcase,char **standard_output,
240 GError *tmp_err=NULL;
241 if (testcase->options)
242 argv=g_new(char *,g_strv_length(testcase->options)+3);
244 argv=g_new(char *,3);
245 s=getenv("BOOKLOUPE");
248 argv[0]=path_to_absolute(s);
249 for(i=0;testcase->options && testcase->options[i];i++)
250 argv[i+1]=testcase_resolve_input_files(testcase,testcase->options[i]);
251 argv[i+1]=testcase_resolve_input_files(testcase,"TEST-XXXXXX");
255 r=spawn_sync(testcase->tmpdir,argv,&s,&exit_status,error);
258 if (testcase->encoding)
260 output=g_convert(s,-1,"UTF-8",testcase->encoding,NULL,NULL,
265 g_propagate_prefixed_error(error,tmp_err,
266 "Conversion from %s failed: ",testcase->encoding);
273 if (!g_utf8_validate(s,-1,NULL))
275 g_set_error_literal(error,TESTCASE_ERROR,
276 TESTCASE_ERROR_FAILED,
277 "bookloupe output is not valid UTF-8");
285 r=spawn_sync(testcase->tmpdir,argv,NULL,&exit_status,error);
289 if (r && exit_status)
291 g_set_error(error,TESTCASE_ERROR,TESTCASE_ERROR_FAILED,
292 "bookloupe exited with code %d",exit_status);
295 if (r && standard_output)
296 *standard_output=output;
301 * Parse a warning of the form:
303 * <echoed line> (ignored)
304 * " Line " <number> [" column " <number>] " - " <text> "\n"
305 * If not specified, the column is returned as 0.
306 * Returns: the number of bytes parsed, or -1 on error.
308 static ssize_t testcase_parse_warning(Testcase *testcase,const char *output,
309 guint *line,guint *column,char **text)
314 if (output[offset]!='\n')
316 g_print("%s: FAIL\n",testcase->basename);
317 g_print("Unexpected output from bookloupe:\n");
318 print_unexpected(output,offset);
322 s=strchr(output+offset,'\n');
325 g_print("%s: FAIL\n",testcase->basename);
326 g_print("Missing new-line in output from bookloupe:\n");
327 print_unexpected(output,offset);
331 if (!g_str_has_prefix(output+offset," Line "))
333 g_print("%s: FAIL\n",testcase->basename);
334 g_print("Unexpected output from bookloupe:\n");
335 offset+=common_prefix_length(output+offset," Line ");
336 print_unexpected(output,offset);
340 tmp=g_ascii_strtoull(output+offset,&endp,10);
341 if (tmp<1 || tmp>G_MAXUINT || tmp==G_MAXUINT64)
343 g_print("%s: FAIL\n",testcase->basename);
344 g_print("Unexpected output from bookloupe:\n");
345 print_unexpected(output,offset);
350 if (g_str_has_prefix(output+offset," column "))
353 tmp=g_ascii_strtoull(output+offset,&endp,10);
354 if (tmp<1 || tmp>G_MAXUINT || tmp==G_MAXUINT64)
356 g_print("%s: FAIL\n",testcase->basename);
357 g_print("Unexpected output from bookloupe:\n");
358 print_unexpected(output,offset);
366 if (!g_str_has_prefix(output+offset," - "))
368 g_print("%s: FAIL\n",testcase->basename);
369 g_print("Unexpected output from bookloupe:\n");
370 offset+=common_prefix_length(output+offset," - ");
371 print_unexpected(output,offset);
375 s=strchr(output+offset,'\n');
378 g_print("%s: FAIL\n",testcase->basename);
379 g_print("Missing new-line in output from bookloupe:\n");
380 print_unexpected(output,offset);
383 *text=g_strndup(output+offset,s-(output+offset));
388 * Check the warnings produced by bookloupe against either the
389 * unstructured testcase->expected or the structured testcase->warnings
392 static gboolean testcase_check_warnings(Testcase *testcase,const char *output,
398 int i,count_false_positive,count_false_negative;
399 int total_false_positive,total_false_negative;
401 guint *counts,line,column;
403 TestcaseWarning *warning;
404 TestcaseLocation *location;
406 if (testcase->expected)
408 if (strcmp(output,testcase->expected))
410 g_print("%s: FAIL\n",testcase->basename);
411 offset=common_prefix_length(output,testcase->expected);
412 if (!offset && !output[offset])
413 g_print("Unexpected zero warnings from bookloupe.\n");
416 g_print("Unexpected output from bookloupe:\n");
417 print_unexpected(output,offset);
423 counts=g_new0(guint,g_slist_length(testcase->warnings));
424 for(offset=0;output[offset];)
426 off=testcase_parse_warning(testcase,output+offset,&line,&column,&text);
433 for(link=testcase->warnings,i=0;link;link=link->next,i++)
436 if (strcmp(warning->text,text))
438 for(link2=warning->locations;link2;link2=link2->next)
440 location=link2->data;
441 if (location->line!=line || location->column!=column)
451 g_print("%s: FAIL\n",testcase->basename);
452 g_print("Unexpected warning from bookloupe:\n");
454 g_print(" Line %u column %u - %s\n",line,column,text);
456 g_print(" Line %u - %s\n",line,text);
463 count_false_positive=total_false_positive=0;
464 count_false_negative=total_false_negative=0;
465 for(link=testcase->warnings,i=0;r && link;link=link->next,i++)
468 if (!counts[i] && warning->is_real && !warning->xfail)
470 location=warning->locations->data;
471 g_print("%s: FAIL\n",testcase->basename);
472 g_print("Missing warning from bookloupe:\n");
473 if (location->column)
474 g_print(" Line %u column %u - %s\n",location->line,
475 location->column,warning->text);
477 g_print(" Line %u - %s\n",location->line,warning->text);
481 else if (warning->xfail)
483 if (warning->is_real)
485 total_false_negative++;
487 count_false_negative++;
489 else if (!warning->is_real)
491 total_false_positive++;
493 count_false_positive++;
498 if (count_false_positive && count_false_negative)
499 *xfail=g_strdup_printf(
500 "with %d of %d false positives and %d of %d false negatives",
501 count_false_positive,total_false_positive,
502 count_false_negative,total_false_negative);
503 else if (count_false_positive)
504 *xfail=g_strdup_printf("with %d of %d false positives",
505 count_false_positive,total_false_positive);
506 else if (count_false_negative)
507 *xfail=g_strdup_printf("with %d of %d false negatives",
508 count_false_negative,total_false_negative);
513 * Run a testcase, returning FALSE on fail or error and
514 * TRUE on pass or expected-fail.
515 * Suitable message(s) will be printed in all cases.
517 gboolean testcase_run(Testcase *testcase)
522 char *filename,*s,*xfail=NULL;
524 if (!testcase_create_input_files(testcase,&error))
526 g_print("%s: FAIL\n",testcase->basename);
527 g_print("%s\n",error->message);
531 r=testcase_spawn_bookloupe(testcase,&testcase->test_output,&error);
534 g_print("%s: FAIL\n",testcase->basename);
535 g_print("%s\n",error->message);
537 (void)testcase_remove_input_files(testcase,NULL);
540 filename=testcase_resolve_input_files(testcase,"TEST-XXXXXX");
541 if (!testcase_remove_input_files(testcase,&error))
543 g_print("%s: FAIL\n",testcase->basename);
544 g_print("%s\n",error->message);
548 if (testcase->expected || testcase->warnings)
550 header=g_string_new("\n\nFile: ");
551 g_string_append(header,filename);
552 g_string_append(header,"\n");
553 if (!g_str_has_prefix(testcase->test_output,header->str))
555 g_print("%s: FAIL\n",testcase->basename);
556 g_print("Unexpected header from bookloupe:\n");
557 offset=common_prefix_length(testcase->test_output,header->str);
558 print_unexpected(testcase->test_output,offset);
564 /* Skip the summary */
565 s=strstr(testcase->test_output+pos,"\n\n");
567 pos=s-testcase->test_output+2;
570 g_print("%s: FAIL\n",testcase->basename);
571 g_print("Unterminated summary from bookloupe:\n%s\n",
572 testcase->test_output+pos);
576 g_string_free(header,TRUE);
577 r=testcase_check_warnings(testcase,testcase->test_output+pos,&xfail);
579 if (!testcase_verify_output_files(testcase))
585 g_print("%s: PASS (%s)\n",testcase->basename,xfail);
587 g_print("%s: PASS\n",testcase->basename);
594 * Run a testcase, returning FALSE on error.
595 * Bookloupe's output or a suitable error message will be shown.
597 gboolean testcase_show_output(Testcase *testcase)
602 r=testcase_create_input_files(testcase,&error);
605 r&=testcase_spawn_bookloupe(testcase,&output,&error);
606 r&=testcase_remove_input_files(testcase,&error);
609 g_print("%s",output);
612 g_print("%s\n",error->message);
619 * Free a testcase warning.
621 void testcase_warning_free(TestcaseWarning *warning)
623 g_slist_foreach(warning->locations,(GFunc)g_free,NULL);
624 g_slist_free(warning->locations);
625 g_free(warning->text);
632 void testcase_free(Testcase *testcase)
634 g_free(testcase->basename);
635 g_slist_foreach(testcase->inputs,(GFunc)testcase_input_free,NULL);
636 g_slist_free(testcase->inputs);
637 g_free(testcase->expected);
638 g_slist_foreach(testcase->warnings,(GFunc)testcase_warning_free,NULL);
639 g_slist_free(testcase->warnings);
640 g_free(testcase->encoding);
641 g_strfreev(testcase->options);
642 g_free(testcase->test_output);