#include #include #include #include #include #include #include "testcase.h" GQuark testcase_error_quark(void) { return g_quark_from_static_string("testcase-error-quark"); } /* * As write(), but with error handling. */ static size_t write_file(int fd,const char *buf,size_t count,GError **error) { if (write(fd,buf,count)str,'\n'); if (bol) bol++; else bol=string->str; col=0; s=bol; endp=string->str+differs_at; while(sstr,col,""); g_string_free(string,TRUE); } /* * Replace \n with \r\n and U+240A (visible symbol for LF) with \n */ char *unix2dos(const char *text) { gunichar c; const gunichar visible_lf=0x240A; GString *string; string=g_string_new(NULL); while(*text) { c=g_utf8_get_char(text); text=g_utf8_next_char(text); if (c=='\n') g_string_append(string,"\r\n"); else if (c==visible_lf) g_string_append_c(string,'\n'); else g_string_append_unichar(string,c); } return g_string_free(string,FALSE); } gboolean spawn_bootloupe(const char *encoding,const char *standard_input, char **standard_output,char **filename,GError **error) { gboolean r; int fd,exit_status; size_t n,pos,offset; char input[]="TEST-XXXXXX"; char *command[3]; char *output,*s,*t; GError *tmp_err=NULL; if (standard_input) { if (encoding) { t=unix2dos(standard_input); s=g_convert(t,-1,encoding,"UTF-8",NULL,&n,&tmp_err); g_free(t); if (!s) { g_propagate_prefixed_error(error,tmp_err, "Conversion to %s failed: ",encoding); return FALSE; } } else { s=unix2dos(standard_input); n=strlen(s); } } else { n=0; s=NULL; } fd=g_mkstemp(input); if (n && write_file(fd,s,n,error)!=n) { g_free(s); close(fd); (void)remove(input); return FALSE; } g_free(s); close(fd); command[0]=getenv("BOOKLOUPE"); if (!command[0]) command[0]="." G_DIR_SEPARATOR_S "bookloupe"; command[1]=input; command[2]=NULL; if (standard_output) { r=spawn_sync(command,&s,&exit_status,error); if (r) { if (encoding) { output=g_convert(s,-1,"UTF-8",encoding,NULL,NULL,&tmp_err); g_free(s); if (!output) { g_propagate_prefixed_error(error,tmp_err, "Conversion from %s failed: ",encoding); r=FALSE; } } else { output=s; if (!g_utf8_validate(s,-1,NULL)) { g_set_error_literal(error,TESTCASE_ERROR, TESTCASE_ERROR_FAILED, "bookloupe output is not valid UTF-8"); r=FALSE; } } } } else { r=spawn_sync(command,NULL,&exit_status,error); output=NULL; } (void)remove(input); if (r && exit_status) { g_set_error(error,TESTCASE_ERROR,TESTCASE_ERROR_FAILED, "bookloupe exited with code %d",exit_status); r=FALSE; } if (r && filename) *filename=g_strdup(input); if (r && standard_output) *standard_output=output; return r; } /* * Run a testcase, returning FALSE on fail or error and * TRUE on pass or expected-fail. * Suitable message(s) will be printed in all cases. */ gboolean testcase_run(Testcase *testcase) { gboolean r; size_t pos,offset; GString *header,*expected; char *output,*filename,*s; GError *error=NULL; if (testcase->expected) r=spawn_bootloupe(testcase->encoding,testcase->input,&output,&filename, &error); else { r=spawn_bootloupe(testcase->encoding,testcase->input,NULL,NULL,&error); output=filename=NULL; } if (!r) { fprintf(stderr,"%s: FAIL\n",testcase->basename); fprintf(stderr,"%s\n",error->message); g_error_free(error); return FALSE; } if (testcase->expected) { header=g_string_new("\n\nFile: "); g_string_append(header,filename); g_string_append(header,"\n"); expected=g_string_new(testcase->expected); if (!g_str_has_prefix(output,header->str)) { fprintf(stderr,"%s: FAIL\n",testcase->basename); offset=common_prefix_length(output,header->str); fprintf(stderr,"Unexpected header from bookloupe:\n"); print_unexpected(output,offset); r=FALSE; } pos=header->len; if (r) { /* Skip the summary */ s=strstr(output+pos,"\n\n"); if (s) pos=s-output+2; else { fprintf(stderr,"%s: FAIL\n",testcase->basename); offset=common_prefix_length(output,header->str); fprintf(stderr,"Unterminated summary from bookloupe:\n%s\n", output+pos); r=FALSE; } } if (r && strcmp(output+pos,expected->str)) { fprintf(stderr,"%s: FAIL\n",testcase->basename); offset=common_prefix_length(output+pos,expected->str); if (!offset && !output[pos+offset]) fprintf(stderr,"Unexpected zero warnings from bookloupe.\n"); else { fprintf(stderr,"Unexpected output from bookloupe:\n"); print_unexpected(output+pos,offset); } r=FALSE; } g_string_free(header,TRUE); g_string_free(expected,TRUE); } g_free(output); g_free(filename); if (r) fprintf(stderr,"%s: PASS\n",testcase->basename); return r; } /* * Free a testcase. */ void testcase_free(Testcase *testcase) { g_free(testcase->basename); g_free(testcase->input); g_free(testcase->expected); g_free(testcase->encoding); g_free(testcase); }