#include #include #include #include #include #ifdef WIN32 #include #endif #include #include #include "testcase.h" GQuark testcase_error_quark(void) { return g_quark_from_static_string("testcase-error-quark"); } #if !HAVE_MKSTEMP /* * An insecure implementation of mkstemp(), for those platforms that * don't support it. */ int mkstemp(char *template) { int fd; char *s; for(;;) { s=g_strdup(template); mktemp(s); if (!*s) { errno=EEXIST; g_free(s); return -1; } fd=open(s,O_RDWR|O_CREAT|O_EXCL,0600); if (fd>0) { strcpy(template,s); g_free(s); return fd; } else g_free(s); } } #endif /* !HAVE_MKSTEMP */ /* * As write(), but always convert NL to CR NL. */ static size_t write_text(int fd,const char *buf,size_t count,GError **error) { size_t i; FILE *fp; fd=dup(fd); if (fd<0) return -1; #ifdef WIN32 if (_setmode(fd,_O_BINARY)<0) { close(fd); g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno), "Failed to set mode of bookloupe input file to binary: %s", g_strerror(errno)); return -1; } #endif fp=fdopen(fd,"wb"); if (!fp) { close(fd); g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno), "Failed to open stream to bookloupe input file: %s", g_strerror(errno)); return -1; } for(i=0;istr,'\n'); if (bol) bol++; else bol=string->str; col=differs_at-(bol-string->str); fprintf(stderr,"%s\n%*s^\n",string->str,col,""); g_string_free(string,TRUE); } 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; FILE *fp; char input[]="TEST-XXXXXX"; char *command[3]; char *output,*s; GError *tmp_err=NULL; if (standard_input) { if (encoding) { s=g_convert(standard_input,-1,encoding,"UTF-8",NULL,&n,&tmp_err); if (!s) { g_propagate_prefixed_error(error,tmp_err, "Conversion to %s failed: ",encoding); return FALSE; } } else { s=g_strdup(standard_input); n=strlen(s); } } else { n=0; s=NULL; } fd=mkstemp(input); if (n && write_text(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); }