1
/*
2
 * Copyright © 2006 Red Hat, Inc.
3
 *
4
 * Permission to use, copy, modify, distribute, and sell this software
5
 * and its documentation for any purpose is hereby granted without
6
 * fee, provided that the above copyright notice appear in all copies
7
 * and that both that copyright notice and this permission notice
8
 * appear in supporting documentation, and that the name of
9
 * Red Hat, Inc. not be used in advertising or publicity pertaining to
10
 * distribution of the software without specific, written prior
11
 * permission. Red Hat, Inc. makes no representations about the
12
 * suitability of this software for any purpose.  It is provided "as
13
 * is" without express or implied warranty.
14
 *
15
 * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
16
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17
 * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
18
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
19
 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
21
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22
 *
23
 * Author: Kristian Høgsberg <krh@redhat.com>
24
 */
25

            
26
#include "cairo-test.h"
27

            
28
#include <stdio.h>
29
#include <string.h>
30
#include <errno.h>
31

            
32
#ifndef _WIN32
33
#include <unistd.h>
34
#endif
35

            
36
#if CAIRO_HAS_PS_SURFACE
37
#include <cairo-ps.h>
38
#endif
39

            
40
#if CAIRO_HAS_PDF_SURFACE
41
#include <cairo-pdf.h>
42
#endif
43

            
44
#if CAIRO_HAS_SVG_SURFACE
45
#include <cairo-svg.h>
46
#endif
47

            
48
#include "cairo-test.h"
49

            
50
/* The main test suite doesn't test the *_create_for_stream
51
 * constructors for the PDF, PS and SVG surface, so we do that here.
52
 * We draw to an in-memory buffer using the stream constructor and
53
 * compare the output to the contents of a file written using the
54
 * file constructor.
55
 */
56

            
57
#define MAX_OUTPUT_SIZE 4096
58

            
59
#define WIDTH_IN_INCHES  3
60
#define HEIGHT_IN_INCHES 3
61
#define WIDTH_IN_POINTS  (WIDTH_IN_INCHES  * 72.0)
62
#define HEIGHT_IN_POINTS (HEIGHT_IN_INCHES * 72.0)
63

            
64
#define BASENAME "create-for-stream.out"
65

            
66
static cairo_test_status_t
67
draw (cairo_t *cr, int width, int height)
68
{
69
    /* Just draw a rectangle. */
70

            
71
    cairo_rectangle (cr, width / 10., height /10.,
72
		     width - 2 * width / 10.,
73
		     height - 2 * height /10.);
74
    cairo_fill (cr);
75

            
76
    cairo_show_page (cr);
77

            
78
    return CAIRO_TEST_SUCCESS;
79
}
80

            
81
static void
82
draw_to (cairo_surface_t *surface)
83
{
84
    cairo_t *cr;
85

            
86
    cr = cairo_create (surface);
87

            
88
    draw (cr, WIDTH_IN_POINTS, HEIGHT_IN_POINTS);
89

            
90
    cairo_destroy (cr);
91
}
92

            
93
typedef struct _write_closure {
94
    const cairo_test_context_t *ctx;
95
    char buffer[MAX_OUTPUT_SIZE];
96
    size_t index;
97
    cairo_test_status_t status;
98
} write_closure_t;
99

            
100
static cairo_status_t
101
bad_write (void		*closure,
102
	   const unsigned char	*data,
103
	   unsigned int	 length)
104
{
105
    return CAIRO_STATUS_WRITE_ERROR;
106
}
107

            
108
static cairo_status_t
109
test_write (void		*closure,
110
	    const unsigned char	*data,
111
	    unsigned int	 length)
112
{
113
    write_closure_t *wc = closure;
114

            
115
    if (wc->index + length >= sizeof wc->buffer) {
116
	cairo_test_log (wc->ctx, "Error: out of bounds in write callback\n");
117
	wc->status = CAIRO_TEST_FAILURE;
118
	return CAIRO_STATUS_SUCCESS;
119
    }
120

            
121
    memcpy (&wc->buffer[wc->index], data, length);
122
    wc->index += length;
123

            
124
    return CAIRO_STATUS_SUCCESS;
125
}
126

            
127

            
128
typedef cairo_surface_t *
129
(*file_constructor_t) (const char	       *filename,
130
		       double			width_in_points,
131
		       double			height_in_points);
132

            
133
typedef cairo_surface_t *
134
(*stream_constructor_t) (cairo_write_func_t	write_func,
135
			 void		       *closure,
136
			 double			width_in_points,
137
			 double			height_in_points);
138

            
139
static cairo_test_status_t
140
test_surface (const cairo_test_context_t *ctx,
141
	      const char                 *backend,
142
	      const char		 *filename,
143
	      file_constructor_t	 file_constructor,
144
	      stream_constructor_t	 stream_constructor)
145
{
146
    cairo_surface_t *surface;
147
    write_closure_t wc;
148
    char file_contents[MAX_OUTPUT_SIZE];
149
    cairo_status_t status;
150
    FILE *fp;
151

            
152
    /* test propagation of user errors */
153
    surface = stream_constructor (bad_write, &wc,
154
				  WIDTH_IN_POINTS, HEIGHT_IN_POINTS);
155

            
156
    status = cairo_surface_status (surface);
157
    if (status) {
158
	cairo_test_log (ctx,
159
			"%s: Failed to create surface for stream.\n",
160
			backend);
161
	return CAIRO_TEST_FAILURE;
162
    }
163

            
164
    draw_to (surface);
165

            
166
    cairo_surface_finish (surface);
167
    status = cairo_surface_status (surface);
168
    cairo_surface_destroy (surface);
169

            
170
    if (status != CAIRO_STATUS_WRITE_ERROR) {
171
	cairo_test_log (ctx,
172
			"%s: Error: expected \"write error\" from bad_write(), but received \"%s\".\n",
173
			backend, cairo_status_to_string (status));
174
	return CAIRO_TEST_FAILURE;
175
    }
176

            
177
    /* test propagation of file errors - for now this is unix-only */
178
#ifndef _WIN32
179
    if (access("/dev/full", W_OK) == 0) {
180
	surface = file_constructor ("/dev/full", WIDTH_IN_POINTS, HEIGHT_IN_POINTS);
181
	cairo_surface_finish (surface);
182
	status = cairo_surface_status (surface);
183
	cairo_surface_destroy (surface);
184

            
185
	if (status != CAIRO_STATUS_WRITE_ERROR) {
186
	    cairo_test_log (ctx,
187
			    "%s: Error: expected \"write error\" from /dev/full, but received \"%s\".\n",
188
			    backend, cairo_status_to_string (status));
189
	    return CAIRO_TEST_FAILURE;
190
	}
191
    } else {
192
	cairo_test_log (ctx,
193
			"/dev/full does not exist; skipping write test.\n");
194
    }
195
#endif
196

            
197
    /* construct the real surface */
198
    wc.ctx = ctx;
199
    wc.status = CAIRO_TEST_SUCCESS;
200
    wc.index = 0;
201

            
202
    surface = stream_constructor (test_write, &wc,
203
				  WIDTH_IN_POINTS, HEIGHT_IN_POINTS);
204

            
205
    status = cairo_surface_status (surface);
206
    if (status) {
207
	cairo_test_log (ctx,
208
			"%s: Failed to create surface for stream.\n", backend);
209
	return CAIRO_TEST_FAILURE;
210
    }
211

            
212
    draw_to (surface);
213

            
214
    cairo_surface_destroy (surface);
215

            
216
    if (wc.status != CAIRO_TEST_SUCCESS) {
217
	/* Error already reported. */
218
	return wc.status;
219
    }
220

            
221
    surface = file_constructor (filename,
222
				WIDTH_IN_POINTS, HEIGHT_IN_POINTS);
223

            
224
    status = cairo_surface_status (surface);
225
    if (status) {
226
	cairo_test_log (ctx, "%s: Failed to create surface for file %s: %s.\n",
227
			backend, filename, cairo_status_to_string (status));
228
	return CAIRO_TEST_FAILURE;
229
    }
230

            
231
    draw_to (surface);
232

            
233
    cairo_surface_destroy (surface);
234

            
235
    fp = fopen (filename, "r");
236
    if (fp == NULL) {
237
	cairo_test_log (ctx, "%s: Failed to open %s for reading: %s.\n",
238
			backend, filename, strerror (errno));
239
	return CAIRO_TEST_FAILURE;
240
    }
241

            
242
    if (fread (file_contents, 1, wc.index, fp) != wc.index) {
243
	cairo_test_log (ctx, "%s: Failed to read %s: %s.\n",
244
			backend, filename, strerror (errno));
245
	fclose (fp);
246
	return CAIRO_TEST_FAILURE;
247
    }
248

            
249
    if (memcmp (file_contents, wc.buffer, wc.index) != 0) {
250
	cairo_test_log (ctx, "%s: Stream based output differ from file output for %s.\n",
251
			backend, filename);
252
	fclose (fp);
253
	return CAIRO_TEST_FAILURE;
254
    }
255

            
256
    fclose (fp);
257

            
258
    return CAIRO_TEST_SUCCESS;
259
}
260

            
261
static cairo_test_status_t
262
1
preamble (cairo_test_context_t *ctx)
263
{
264
1
    cairo_test_status_t status = CAIRO_TEST_UNTESTED;
265
    cairo_test_status_t test_status;
266
    char *filename;
267
1
    const char *path = cairo_test_mkdir (CAIRO_TEST_OUTPUT_DIR) ? CAIRO_TEST_OUTPUT_DIR : ".";
268

            
269
#if CAIRO_HAS_PS_SURFACE
270
2
    if (cairo_test_is_target_enabled (ctx, "ps2") ||
271
1
	cairo_test_is_target_enabled (ctx, "ps3"))
272
    {
273
	if (status == CAIRO_TEST_UNTESTED)
274
	    status = CAIRO_TEST_SUCCESS;
275

            
276
	xasprintf (&filename, "%s/%s", path, BASENAME ".ps");
277
	test_status = test_surface (ctx, "ps", filename,
278
				    cairo_ps_surface_create,
279
				    cairo_ps_surface_create_for_stream);
280
	cairo_test_log (ctx, "TEST: %s TARGET: %s RESULT: %s\n",
281
			ctx->test->name, "ps",
282
			test_status ? "FAIL" : "PASS");
283
	if (status == CAIRO_TEST_SUCCESS)
284
	    status = test_status;
285
	free (filename);
286
    }
287
#endif
288

            
289
#if CAIRO_HAS_PDF_SURFACE
290
1
    if (cairo_test_is_target_enabled (ctx, "pdf")) {
291
	if (status == CAIRO_TEST_UNTESTED)
292
	    status = CAIRO_TEST_SUCCESS;
293

            
294
	xasprintf (&filename, "%s/%s", path, BASENAME ".pdf");
295
	test_status = test_surface (ctx, "pdf", filename,
296
				    cairo_pdf_surface_create,
297
				    cairo_pdf_surface_create_for_stream);
298
	cairo_test_log (ctx, "TEST: %s TARGET: %s RESULT: %s\n",
299
			ctx->test->name, "pdf",
300
			test_status ? "FAIL" : "PASS");
301
	if (status == CAIRO_TEST_SUCCESS)
302
	    status = test_status;
303
	free (filename);
304
    }
305
#endif
306

            
307
#if CAIRO_HAS_SVG_SURFACE
308
2
    if (cairo_test_is_target_enabled (ctx, "svg11") ||
309
1
	cairo_test_is_target_enabled (ctx, "svg12"))
310
    {
311
	if (status == CAIRO_TEST_UNTESTED)
312
	    status = CAIRO_TEST_SUCCESS;
313

            
314
	xasprintf (&filename, "%s/%s", path, BASENAME ".svg");
315
	test_status = test_surface (ctx, "svg", filename,
316
				    cairo_svg_surface_create,
317
				    cairo_svg_surface_create_for_stream);
318
	cairo_test_log (ctx, "TEST: %s TARGET: %s RESULT: %s\n",
319
			ctx->test->name, "svg",
320
			test_status ? "FAIL" : "PASS");
321
	if (status == CAIRO_TEST_SUCCESS)
322
	    status = test_status;
323
	free (filename);
324
    }
325
#endif
326

            
327
1
    return status;
328
}
329

            
330
1
CAIRO_TEST (create_for_stream,
331
	    "Checks creating vector surfaces with user defined I/O\n",
332
	    "stream", /* keywords */
333
	    "target=vector", /* requirements */
334
	    0, 0,
335
	    preamble, NULL)