test/harness/testcase.c
author ali <ali@juiblex.co.uk>
Fri Jan 27 21:40:35 2012 +0000 (2012-01-27)
changeset 7 721e468c10f3
parent 6 faab25d520dd
child 8 cf332d440466
permissions -rw-r--r--
Add support for non-ASCII testcases
ali@0
     1
#include <stdlib.h>
ali@0
     2
#include <stdio.h>
ali@0
     3
#include <string.h>
ali@0
     4
#include <unistd.h>
ali@0
     5
#include <errno.h>
ali@0
     6
#ifdef WIN32
ali@0
     7
#include <io.h>
ali@3
     8
#endif
ali@0
     9
#include <fcntl.h>
ali@5
    10
#include <bl/bl.h>
ali@0
    11
#include "testcase.h"
ali@0
    12
ali@7
    13
GQuark testcase_error_quark(void)
ali@7
    14
{
ali@7
    15
    return g_quark_from_static_string("testcase-error-quark");
ali@7
    16
}
ali@7
    17
ali@0
    18
#if !HAVE_MKSTEMP
ali@0
    19
/*
ali@0
    20
 * An insecure implementation of mkstemp(), for those platforms that
ali@0
    21
 * don't support it.
ali@0
    22
 */
ali@0
    23
int mkstemp(char *template)
ali@0
    24
{
ali@0
    25
    int fd;
ali@0
    26
    char *s;
ali@0
    27
    for(;;)
ali@0
    28
    {
ali@6
    29
	s=g_strdup(template);
ali@0
    30
	mktemp(s);
ali@0
    31
	if (!*s)
ali@0
    32
	{
ali@0
    33
	    errno=EEXIST;
ali@6
    34
	    g_free(s);
ali@0
    35
	    return -1;
ali@0
    36
	}
ali@0
    37
	fd=open(s,O_RDWR|O_CREAT|O_EXCL,0600);
ali@0
    38
	if (fd>0)
ali@0
    39
	{
ali@0
    40
	    strcpy(template,s);
ali@6
    41
	    g_free(s);
ali@0
    42
	    return fd;
ali@0
    43
	}
ali@0
    44
	else
ali@6
    45
	    g_free(s);
ali@0
    46
    }
ali@0
    47
}
ali@0
    48
#endif	/* !HAVE_MKSTEMP */
ali@0
    49
ali@0
    50
/*
ali@0
    51
 * As write(), but always convert NL to CR NL.
ali@0
    52
 */
ali@7
    53
static size_t write_text(int fd,const char *buf,size_t count,GError **error)
ali@0
    54
{
ali@0
    55
    size_t i;
ali@0
    56
    FILE *fp;
ali@0
    57
    fd=dup(fd);
ali@0
    58
    if (fd<0)
ali@0
    59
	return -1;
ali@0
    60
#ifdef WIN32
ali@0
    61
    if (_setmode(fd,_O_BINARY)<0)
ali@0
    62
    {
ali@0
    63
	close(fd);
ali@7
    64
	g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
ali@7
    65
	  "Failed to set mode of bookloupe input file to binary: %s",
ali@7
    66
	  g_strerror(errno));
ali@0
    67
	return -1;
ali@0
    68
    }
ali@0
    69
#endif
ali@0
    70
    fp=fdopen(fd,"wb");
ali@0
    71
    if (!fp)
ali@0
    72
    {
ali@0
    73
	close(fd);
ali@7
    74
	g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
ali@7
    75
	  "Failed to open stream to bookloupe input file: %s",
ali@7
    76
	  g_strerror(errno));
ali@0
    77
	return -1;
ali@0
    78
    }
ali@0
    79
    for(i=0;i<count;i++)
ali@0
    80
    {
ali@0
    81
	if (buf[i]=='\n')
ali@0
    82
	    if (putc('\r',fp)==EOF)
ali@0
    83
	    {
ali@7
    84
		g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
ali@7
    85
		  "Error writing bookloupe input file: %s",g_strerror(errno));
ali@0
    86
		(void)fclose(fp);
ali@0
    87
		return -1;
ali@0
    88
	    }
ali@0
    89
	if (putc(buf[i],fp)==EOF)
ali@0
    90
	{
ali@7
    91
	    g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
ali@7
    92
	      "Error writing bookloupe input file: %s",g_strerror(errno));
ali@0
    93
	    (void)fclose(fp);
ali@0
    94
	    return -1;
ali@0
    95
	}
ali@0
    96
    }
ali@0
    97
    if (fclose(fp))
ali@7
    98
    {
ali@7
    99
	g_set_error(error,G_FILE_ERROR,g_file_error_from_errno(errno),
ali@7
   100
	  "Error writing bookloupe input file: %s",g_strerror(errno));
ali@0
   101
	return -1;
ali@7
   102
    }
ali@0
   103
    return count;
ali@0
   104
}
ali@0
   105
ali@0
   106
/*
ali@0
   107
 * Return the length (in bytes) or any common prefix between s1 and s2.
ali@0
   108
 */
ali@0
   109
size_t common_prefix_length(const char *s1,const char *s2)
ali@0
   110
{
ali@0
   111
    size_t i;
ali@0
   112
    for(i=0;s1[i] && s2[i] && s1[i]==s2[i];i++)
ali@0
   113
	;
ali@0
   114
    return i;
ali@0
   115
}
ali@0
   116
ali@7
   117
void print_unexpected(const char *unexpected,gsize differs_at)
ali@7
   118
{
ali@7
   119
    int col;
ali@7
   120
    const char *endp,*bol;
ali@7
   121
    GString *string;
ali@7
   122
    endp=strchr(unexpected+differs_at,'\n');
ali@7
   123
    if (!endp)
ali@7
   124
	endp=unexpected+strlen(unexpected);
ali@7
   125
    string=g_string_new_len(unexpected,endp-unexpected);
ali@7
   126
    bol=strrchr(string->str,'\n');
ali@7
   127
    if (bol)
ali@7
   128
	bol++;
ali@7
   129
    else
ali@7
   130
	bol=string->str;
ali@7
   131
    col=differs_at-(bol-string->str);
ali@7
   132
    fprintf(stderr,"%s\n%*s^\n",string->str,col,"");
ali@7
   133
    g_string_free(string,TRUE);
ali@7
   134
}
ali@7
   135
ali@7
   136
gboolean spawn_bootloupe(const char *encoding,const char *standard_input,
ali@7
   137
  char **standard_output,char **filename,GError **error)
ali@7
   138
{
ali@7
   139
    gboolean r;
ali@7
   140
    int fd,exit_status;
ali@7
   141
    size_t n,pos,offset;
ali@7
   142
    FILE *fp;
ali@7
   143
    char input[]="TEST-XXXXXX";
ali@7
   144
    char *command[3];
ali@7
   145
    char *output,*s;
ali@7
   146
    GError *tmp_err=NULL;
ali@7
   147
    if (standard_input)
ali@7
   148
    {
ali@7
   149
	if (encoding)
ali@7
   150
	{
ali@7
   151
	    s=g_convert(standard_input,-1,encoding,"UTF-8",NULL,&n,&tmp_err);
ali@7
   152
	    if (!s)
ali@7
   153
	    {
ali@7
   154
		g_propagate_prefixed_error(error,tmp_err,
ali@7
   155
		  "Conversion to %s failed: ",encoding);
ali@7
   156
		return FALSE;
ali@7
   157
	    }
ali@7
   158
	}
ali@7
   159
	else
ali@7
   160
	{
ali@7
   161
	    s=g_strdup(standard_input);
ali@7
   162
	    n=strlen(s);
ali@7
   163
	}
ali@7
   164
    }
ali@7
   165
    else
ali@7
   166
    {
ali@7
   167
	n=0;
ali@7
   168
	s=NULL;
ali@7
   169
    }
ali@7
   170
    fd=mkstemp(input);
ali@7
   171
    if (n && write_text(fd,s,n,error)!=n)
ali@7
   172
    {
ali@7
   173
	g_free(s);
ali@7
   174
	close(fd);
ali@7
   175
	(void)remove(input);
ali@7
   176
	return FALSE;
ali@7
   177
    }
ali@7
   178
    g_free(s);
ali@7
   179
    close(fd);
ali@7
   180
    command[0]=getenv("BOOKLOUPE");
ali@7
   181
    if (!command[0])
ali@7
   182
	command[0]="." G_DIR_SEPARATOR_S "bookloupe";
ali@7
   183
    command[1]=input;
ali@7
   184
    command[2]=NULL;
ali@7
   185
    if (standard_output)
ali@7
   186
    {
ali@7
   187
	r=spawn_sync(command,&s,&exit_status,error);
ali@7
   188
	if (r)
ali@7
   189
	{
ali@7
   190
	    if (encoding)
ali@7
   191
	    {
ali@7
   192
		output=g_convert(s,-1,"UTF-8",encoding,NULL,NULL,&tmp_err);
ali@7
   193
		g_free(s);
ali@7
   194
		if (!output)
ali@7
   195
		{
ali@7
   196
		    g_propagate_prefixed_error(error,tmp_err,
ali@7
   197
		      "Conversion from %s failed: ",encoding);
ali@7
   198
		    r=FALSE;
ali@7
   199
		}
ali@7
   200
	    }
ali@7
   201
	    else
ali@7
   202
	    {
ali@7
   203
		output=s;
ali@7
   204
		if (!g_utf8_validate(s,-1,NULL))
ali@7
   205
		{
ali@7
   206
		    g_set_error_literal(error,TESTCASE_ERROR,
ali@7
   207
		      TESTCASE_ERROR_FAILED,
ali@7
   208
		      "bookloupe output is not valid UTF-8");
ali@7
   209
		    r=FALSE;
ali@7
   210
		}
ali@7
   211
	    }
ali@7
   212
	}
ali@7
   213
    }
ali@7
   214
    else
ali@7
   215
    {
ali@7
   216
	r=spawn_sync(command,NULL,&exit_status,error);
ali@7
   217
	output=NULL;
ali@7
   218
    }
ali@7
   219
    (void)remove(input);
ali@7
   220
    if (r && exit_status)
ali@7
   221
    {
ali@7
   222
	g_set_error(error,TESTCASE_ERROR,TESTCASE_ERROR_FAILED,
ali@7
   223
	  "bookloupe exited with code %d",exit_status);
ali@7
   224
	r=FALSE;
ali@7
   225
    }
ali@7
   226
    if (r && filename)
ali@7
   227
	*filename=g_strdup(input);
ali@7
   228
    if (r && standard_output)
ali@7
   229
	*standard_output=output;
ali@7
   230
    return r;
ali@7
   231
}
ali@7
   232
ali@0
   233
/*
ali@0
   234
 * Run a testcase, returning FALSE on fail or error and
ali@0
   235
 * TRUE on pass or expected-fail.
ali@0
   236
 * Suitable message(s) will be printed in all cases.
ali@0
   237
 */
ali@6
   238
gboolean testcase_run(Testcase *testcase)
ali@0
   239
{
ali@6
   240
    gboolean r;
ali@7
   241
    size_t pos,offset;
ali@7
   242
    GString *header,*expected;
ali@7
   243
    char *output,*filename,*s;
ali@7
   244
    GError *error=NULL;
ali@7
   245
    if (testcase->expected)
ali@7
   246
	r=spawn_bootloupe(testcase->encoding,testcase->input,&output,&filename,
ali@7
   247
	  &error);
ali@0
   248
    else
ali@0
   249
    {
ali@7
   250
	r=spawn_bootloupe(testcase->encoding,testcase->input,NULL,NULL,&error);
ali@7
   251
        output=filename=NULL;
ali@7
   252
    }
ali@7
   253
    if (!r)
ali@7
   254
    {
ali@7
   255
	fprintf(stderr,"%s: FAIL\n",testcase->basename);
ali@7
   256
	fprintf(stderr,"%s\n",error->message);
ali@7
   257
	g_error_free(error);
ali@0
   258
	return FALSE;
ali@0
   259
    }
ali@0
   260
    if (testcase->expected)
ali@0
   261
    {
ali@7
   262
	header=g_string_new("\n\nFile: ");
ali@7
   263
	g_string_append(header,filename);
ali@7
   264
	g_string_append(header,"\n");
ali@7
   265
	expected=g_string_new(testcase->expected);
ali@7
   266
	if (!g_str_has_prefix(output,header->str))
ali@7
   267
	{
ali@7
   268
	    fprintf(stderr,"%s: FAIL\n",testcase->basename);
ali@7
   269
	    offset=common_prefix_length(output,header->str);
ali@7
   270
	    fprintf(stderr,"Unexpected header from bookloupe:\n");
ali@7
   271
	    print_unexpected(output,offset);
ali@7
   272
	    r=FALSE;
ali@7
   273
	}
ali@7
   274
	pos=header->len;
ali@7
   275
	if (r)
ali@7
   276
	{
ali@7
   277
	    /* Skip the summary */
ali@7
   278
	    s=strstr(output+pos,"\n\n");
ali@7
   279
	    if (s)
ali@7
   280
		pos=s-output+2;
ali@7
   281
	    else
ali@7
   282
	    {
ali@7
   283
		fprintf(stderr,"%s: FAIL\n",testcase->basename);
ali@7
   284
		offset=common_prefix_length(output,header->str);
ali@7
   285
		fprintf(stderr,"Unterminated summary from bookloupe:\n%s\n",
ali@7
   286
		  output+pos);
ali@7
   287
		r=FALSE;
ali@7
   288
	    }
ali@7
   289
	}
ali@7
   290
	if (r && strcmp(output+pos,expected->str))
ali@7
   291
	{
ali@7
   292
	    fprintf(stderr,"%s: FAIL\n",testcase->basename);
ali@7
   293
	    offset=common_prefix_length(output+pos,expected->str);
ali@7
   294
	    if (!offset && !output[pos+offset])
ali@7
   295
		fprintf(stderr,"Unexpected zero warnings from bookloupe.\n");
ali@7
   296
	    else
ali@7
   297
	    {
ali@7
   298
		fprintf(stderr,"Unexpected output from bookloupe:\n");
ali@7
   299
		print_unexpected(output+pos,offset);
ali@7
   300
	    }
ali@7
   301
	    r=FALSE;
ali@7
   302
	}
ali@7
   303
	g_string_free(header,TRUE);
ali@7
   304
	g_string_free(expected,TRUE);
ali@0
   305
    }
ali@6
   306
    g_free(output);
ali@7
   307
    g_free(filename);
ali@7
   308
    if (r)
ali@0
   309
	fprintf(stderr,"%s: PASS\n",testcase->basename);
ali@7
   310
    return r;
ali@0
   311
}
ali@0
   312
ali@0
   313
/*
ali@0
   314
 * Free a testcase.
ali@0
   315
 */
ali@0
   316
void testcase_free(Testcase *testcase)
ali@0
   317
{
ali@6
   318
    g_free(testcase->basename);
ali@6
   319
    g_free(testcase->input);
ali@6
   320
    g_free(testcase->expected);
ali@7
   321
    g_free(testcase->encoding);
ali@6
   322
    g_free(testcase);
ali@0
   323
}