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