1 #ifdef HAVE_CONFIG_H
2 #include <config.h>
3 #endif
4 #include <assert.h>
5 #include <setjmp.h>
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <math.h>
10 #include <limits.h>
11
12 #ifdef HAVE_DIRENT_H
13 #include <dirent.h>
14 #endif
15 #ifdef HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
18 #ifdef HAVE_SYS_STAT_H
19 #include <sys/stat.h>
20 #endif
21 #ifdef HAVE_SYS_TYPES_H
22 #include <sys/types.h>
23 #endif
24
25 #ifdef _WIN32
26 #include "readdir.h"
27 #include <errno.h>
28 #endif
29
30 /* GDTEST_TOP_DIR is defined in other compile ways except msys
31 * test_config.h is created by windows/msys/run_test.sh*/
32 #ifndef GDTEST_TOP_DIR
33 #include <test_config.h>
34 #endif
35
36 #include "gd.h"
37
38 #include "gdtest.h"
39
40 /* max is already defined in windows/msvc */
41 #ifndef max
max(int a,int b)42 static inline int max(int a, int b) {return a > b ? a : b;}
43 #endif
44
gdSilence(int priority,const char * format,va_list args)45 void gdSilence(int priority, const char *format, va_list args)
46 {
47 (void)priority;
48 (void)format;
49 (void)args;
50 }
51
gdTestImageFromPng(const char * filename)52 gdImagePtr gdTestImageFromPng(const char *filename)
53 {
54 gdImagePtr image;
55 FILE *fp;
56
57 /* If the path is relative, then assume it's in the tests/ dir. */
58 if (filename[0] == '/' || filename[0] == '.'
59 #ifdef _WIN32
60 || filename[1] == ':'
61 #endif
62 ) {
63 fp = fopen(filename, "rb");
64 } else {
65 fp = gdTestFileOpen(filename);
66 }
67
68 if (fp == NULL) {
69 return NULL;
70 }
71
72 image = gdImageCreateFromPng(fp);
73 fclose(fp);
74 return image;
75 }
76
77 static char *tmpdir_base;
78
79 /* This is kind of hacky, but it's meant to be simple. */
_clean_dir(const char * dir)80 static void _clean_dir(const char *dir)
81 {
82 DIR *d;
83 struct dirent *de;
84
85 d = opendir(dir);
86 if (d == NULL)
87 return;
88
89 if (chdir(dir) != 0)
90 goto done;
91
92 while ((de = readdir(d)) != NULL) {
93 struct stat st;
94
95 if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
96 continue;
97 #ifdef _WIN32
98 {
99 WIN32_FILE_ATTRIBUTE_DATA data;
100
101 if (!GetFileAttributesEx(de->d_name, GetFileExInfoStandard, &data)) {
102 continue;
103 }
104 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
105 _clean_dir(de->d_name);
106 } else {
107 unlink(de->d_name);
108 }
109 }
110 #else
111 if (lstat(de->d_name, &st) != 0)
112 continue;
113
114 if (S_ISDIR(st.st_mode))
115 _clean_dir(de->d_name);
116 else
117 unlink(de->d_name);
118 #endif
119 }
120
121 if (chdir("..")) {
122 /* Ignore. */;
123 }
124
125 done:
126 closedir(d);
127 rmdir(dir);
128 }
129
tmpdir_cleanup(void)130 static void tmpdir_cleanup(void)
131 {
132 _clean_dir(tmpdir_base);
133 free(tmpdir_base);
134 }
135
136 #ifdef _WIN32
strrstr(char * haystack,char * needle)137 char* strrstr (char* haystack, char* needle)
138 {
139 int needle_length = strlen(needle);
140 char * haystack_end = haystack + strlen(haystack) - needle_length;
141 char * p;
142 int i;
143
144 for(p = haystack_end; p >= haystack; --p)
145 {
146 for(i = 0; i < needle_length; ++i) {
147 if(p[i] != needle[i])
148 goto next;
149 }
150 return p;
151
152 next:;
153 }
154 return 0;
155 }
156
157
158 typedef VOID (WINAPI *MyGetSystemTimeAsFileTime)(LPFILETIME lpSystemTimeAsFileTime);
159
get_time_func(void)160 static MyGetSystemTimeAsFileTime get_time_func(void)
161 {
162 MyGetSystemTimeAsFileTime timefunc = NULL;
163 HMODULE hMod = GetModuleHandle("kernel32.dll");
164
165 if (hMod) {
166 /* Max possible resolution <1us, win8/server2012 */
167 timefunc = (MyGetSystemTimeAsFileTime)GetProcAddress(hMod, "GetSystemTimePreciseAsFileTime");
168
169 if(!timefunc) {
170 /* 100ns blocks since 01-Jan-1641 */
171 timefunc = (MyGetSystemTimeAsFileTime)GetProcAddress(hMod, "GetSystemTimeAsFileTime");
172 }
173 }
174
175 return timefunc;
176 }
177 static MyGetSystemTimeAsFileTime timefunc = NULL;
getfilesystemtime(struct timeval * tv)178 static int getfilesystemtime(struct timeval *tv)
179 {
180 FILETIME ft;
181 unsigned __int64 ff = 0;
182 ULARGE_INTEGER fft;
183
184 if (timefunc == NULL) {
185 timefunc = get_time_func();
186 }
187 timefunc(&ft);
188
189 /*
190 * Do not cast a pointer to a FILETIME structure to either a
191 * ULARGE_INTEGER* or __int64* value because it can cause alignment faults on 64-bit Windows.
192 * via http://technet.microsoft.com/en-us/library/ms724284(v=vs.85).aspx
193 */
194 fft.HighPart = ft.dwHighDateTime;
195 fft.LowPart = ft.dwLowDateTime;
196 ff = fft.QuadPart;
197
198 ff /= 10ULL; /* convert to microseconds */
199 ff -= 11644473600000000ULL; /* convert to unix epoch */
200
201 tv->tv_sec = (long)(ff / 1000000ULL);
202 tv->tv_usec = (long)(ff % 1000000ULL);
203
204 return 0;
205 }
206
207 static char *
mkdtemp(char * tmpl)208 mkdtemp (char *tmpl)
209 {
210 static const char letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
211 static const int NLETTERS = sizeof (letters) - 1;
212 static int counter = 0;
213 char *XXXXXX;
214 struct timeval tv;
215 __int64 value;
216 int count;
217
218 /* find the last occurrence of "XXXXXX" */
219 XXXXXX = strrstr(tmpl, "XXXXXX");
220
221 if (!XXXXXX || strncmp (XXXXXX, "XXXXXX", 6)) {
222 errno = EINVAL;
223 return NULL;
224 }
225
226 /* Get some more or less random data. */
227 getfilesystemtime(&tv);
228 value = (tv.tv_usec ^ tv.tv_sec) + counter++;
229
230 for (count = 0; count < 100; value += 7777, ++count) {
231 __int64 v = value;
232
233 /* Fill in the random bits. */
234 XXXXXX[0] = letters[v % NLETTERS];
235 v /= NLETTERS;
236 XXXXXX[1] = letters[v % NLETTERS];
237 v /= NLETTERS;
238 XXXXXX[2] = letters[v % NLETTERS];
239 v /= NLETTERS;
240 XXXXXX[3] = letters[v % NLETTERS];
241 v /= NLETTERS;
242 XXXXXX[4] = letters[v % NLETTERS];
243 v /= NLETTERS;
244 XXXXXX[5] = letters[v % NLETTERS];
245
246 /* tmpl is in UTF-8 on Windows, thus use g_mkdir() */
247 if (mkdir(tmpl) == 0) {
248 return tmpl;
249 }
250 printf("failed to create directory\n");
251 if (errno != EEXIST)
252 /* Any other error will apply also to other names we might
253 * try, and there are 2^32 or so of them, so give up now.
254 */
255 return NULL;
256 }
257
258 /* We got out of the loop because we ran out of combinations to try. */
259 errno = EEXIST;
260 return NULL;
261 }
262 #endif
263
gdTestTempDir(void)264 const char *gdTestTempDir(void)
265 {
266 if (tmpdir_base == NULL) {
267 char *tmpdir;
268 #ifdef _WIN32
269 char tmpdir_root[MAX_PATH];
270 size_t tmpdir_root_len = GetTempPath(MAX_PATH, tmpdir_root);
271 gdTestAssert(!(tmpdir_root_len > MAX_PATH || (tmpdir_root_len == 0)));
272 gdTestAssert((tmpdir_root_len + 30 < MAX_PATH));
273 #else
274 char *tmpdir_root;
275 tmpdir_root = getenv("TMPDIR");
276 if (tmpdir_root == NULL)
277 tmpdir_root = "/tmp";
278 #endif
279
280 /* The constant here is a lazy over-estimate. */
281 tmpdir = malloc(strlen(tmpdir_root) + 30);
282 gdTestAssert(tmpdir != NULL);
283 #ifdef _WIN32
284 sprintf(tmpdir, "%sgdtest.XXXXXX", tmpdir_root);
285 #else
286 sprintf(tmpdir, "%s/gdtest.XXXXXX", tmpdir_root);
287 #endif
288 tmpdir_base = mkdtemp(tmpdir);
289 gdTestAssert(tmpdir_base != NULL);
290
291 atexit(tmpdir_cleanup);
292 }
293
294 return tmpdir_base;
295 }
296
gdTestTempFile(const char * template)297 char *gdTestTempFile(const char *template)
298 {
299 const char *tempdir = gdTestTempDir();
300 char *ret;
301
302 #ifdef _WIN32
303 {
304 char *tmpfilename;
305 UINT error;
306
307 ret = malloc(MAX_PATH);
308 gdTestAssert(ret != NULL);
309 if (template == NULL) {
310 error = GetTempFileName(tempdir,
311 "gdtest",
312 0,
313 ret);
314 gdTestAssert(error != 0);
315 } else {
316 sprintf(ret, "%s\\%s", tempdir, template);
317 }
318 }
319 #else
320 if (template == NULL) {
321 template = "gdtemp.XXXXXX";
322 }
323 ret = malloc(strlen(tempdir) + 10 + strlen(template));
324 gdTestAssert(ret != NULL);
325 sprintf(ret, "%s/%s", tempdir, template);
326
327 if (strstr(template, "XXXXXX") != NULL) {
328 int fd = mkstemp(ret);
329 gdTestAssert(fd != -1);
330 close(fd);
331 }
332 #endif
333 return ret;
334 }
335
gdTestTempFp(void)336 FILE *gdTestTempFp(void)
337 {
338 char *file = gdTestTempFile(NULL);
339 FILE *fp = fopen(file, "wb");
340 gdTestAssert(fp != NULL);
341 free(file);
342 return fp;
343 }
344
gdTestFilePathV(const char * path,va_list args)345 char *gdTestFilePathV(const char *path, va_list args)
346 {
347 size_t len;
348 const char *p;
349 char *file;
350 va_list args_len;
351
352 /* Figure out how much space we need. */
353 va_copy(args_len, args);
354 len = strlen(GDTEST_TOP_DIR) + 1;
355 p = path;
356 do {
357 len += strlen(p) + 1;
358 } while ((p = va_arg(args_len, const char *)) != NULL);
359 va_end(args_len);
360
361 /* Now build the path. */
362 file = malloc(len);
363 gdTestAssert(file != NULL);
364 strcpy(file, GDTEST_TOP_DIR);
365 p = path;
366 do {
367 #ifdef _WIN32
368 strcat(file, "\\");
369 #else
370 strcat(file, "/");
371 #endif
372 strcat(file, p);
373
374 } while ((p = va_arg(args, const char *)) != NULL);
375 va_end(args);
376
377 return file;
378 }
379
gdTestFilePathX(const char * path,...)380 char *gdTestFilePathX(const char *path, ...)
381 {
382 va_list args;
383 va_start(args, path);
384 return gdTestFilePathV(path, args);
385 }
386
gdTestFileOpenX(const char * path,...)387 FILE *gdTestFileOpenX(const char *path, ...)
388 {
389 va_list args;
390 FILE *fp;
391 char *file;
392
393 va_start(args, path);
394 file = gdTestFilePathV(path, args);
395 fp = fopen(file, "rb");
396 gdTestAssert(fp != NULL);
397 free(file);
398 return fp;
399 }
400
401 /* Compare two buffers, returning the number of pixels that are
402 * different and the maximum difference of any single color channel in
403 * result_ret.
404 *
405 * This function should be rewritten to compare all formats supported by
406 * cairo_format_t instead of taking a mask as a parameter.
407 */
gdTestImageDiff(gdImagePtr buf_a,gdImagePtr buf_b,gdImagePtr buf_diff,CuTestImageResult * result_ret)408 void gdTestImageDiff(gdImagePtr buf_a, gdImagePtr buf_b,
409 gdImagePtr buf_diff, CuTestImageResult *result_ret)
410 {
411 int x, y;
412 int c1, c2;
413 # define UP_DIFF(var) result_ret->max_diff = max((var), result_ret->max_diff)
414
415 for (y = 0; y < gdImageSY(buf_a); y++) {
416 for (x = 0; x < gdImageSX(buf_a); x++) {
417 c1 = gdImageGetTrueColorPixel(buf_a, x, y);
418 c2 = gdImageGetTrueColorPixel(buf_b, x, y);
419
420 /* check if the pixels are the same */
421 if (c1 != c2) {
422 int r1,b1,g1,a1,r2,b2,g2,a2;
423 unsigned int diff_a,diff_r,diff_g,diff_b;
424
425 a1 = gdTrueColorGetAlpha(c1);
426 a2 = gdTrueColorGetAlpha(c2);
427 diff_a = abs (a1 - a2);
428 diff_a *= 4; /* emphasize */
429
430 if (diff_a) {
431 diff_a += 128; /* make sure it's visible */
432 }
433 if (diff_a > gdAlphaMax) {
434 diff_a = gdAlphaMax/2;
435 }
436
437 r1 = gdTrueColorGetRed(c1);
438 r2 = gdTrueColorGetRed(c2);
439 diff_r = abs (r1 - r2);
440 // diff_r *= 4; /* emphasize */
441 if (diff_r) {
442 diff_r += gdRedMax/2; /* make sure it's visible */
443 }
444 if (diff_r > 255) {
445 diff_r = 255;
446 }
447 UP_DIFF(diff_r);
448
449 g1 = gdTrueColorGetGreen(c1);
450 g2 = gdTrueColorGetGreen(c2);
451 diff_g = abs (g1 - g2);
452
453 diff_g *= 4; /* emphasize */
454 if (diff_g) {
455 diff_g += gdGreenMax/2; /* make sure it's visible */
456 }
457 if (diff_g > 255) {
458 diff_g = 255;
459 }
460 UP_DIFF(diff_g);
461
462 b1 = gdTrueColorGetBlue(c1);
463 b2 = gdTrueColorGetBlue(c2);
464 diff_b = abs (b1 - b2);
465 diff_b *= 4; /* emphasize */
466 if (diff_b) {
467 diff_b += gdBlueMax/2; /* make sure it's visible */
468 }
469 if (diff_b > 255) {
470 diff_b = 255;
471 }
472 UP_DIFF(diff_b);
473
474 result_ret->pixels_changed++;
475 if (buf_diff) gdImageSetPixel(buf_diff, x,y, gdTrueColorAlpha(diff_r, diff_g, diff_b, diff_a));
476 } else {
477 if (buf_diff) gdImageSetPixel(buf_diff, x,y, gdTrueColorAlpha(255,255,255,0));
478 }
479 }
480 }
481 # undef UP_DIFF
482 }
483
484
485 /* Return the largest difference between two corresponding pixels and
486 * channels. */
gdMaxPixelDiff(gdImagePtr a,gdImagePtr b)487 unsigned int gdMaxPixelDiff(gdImagePtr a, gdImagePtr b)
488 {
489 int diff = 0;
490 int x, y;
491
492 if (a == NULL || b == NULL || a->sx != b->sx || a->sy != b->sy)
493 return UINT_MAX;
494
495 for (x = 0; x < a->sx; x++) {
496 for (y = 0; y < a->sy; y++) {
497 int c1, c2;
498
499 c1 = gdImageGetTrueColorPixel(a, x, y);
500 c2 = gdImageGetTrueColorPixel(b, x, y);
501 if (c1 == c2) continue;
502
503 diff = max(diff, abs(gdTrueColorGetAlpha(c1) - gdTrueColorGetAlpha(c2)));
504 diff = max(diff, abs(gdTrueColorGetRed(c1) - gdTrueColorGetRed(c2)));
505 diff = max(diff, abs(gdTrueColorGetGreen(c1) - gdTrueColorGetGreen(c2)));
506 diff = max(diff, abs(gdTrueColorGetBlue(c1) - gdTrueColorGetBlue(c2)));
507 }/* for */
508 }/* for */
509
510 return diff;
511 }
512
gdTestImageCompareToImage(const char * file,unsigned int line,const char * message,gdImagePtr expected,gdImagePtr actual)513 int gdTestImageCompareToImage(const char* file, unsigned int line, const char* message,
514 gdImagePtr expected, gdImagePtr actual)
515 {
516 unsigned int width_a, height_a;
517 unsigned int width_b, height_b;
518 gdImagePtr surface_diff = NULL;
519 CuTestImageResult result = {0, 0};
520
521 (void)message;
522
523 if (!actual) {
524 _gdTestErrorMsg(file, line, "Image is NULL\n");
525 goto fail;
526 }
527
528 width_a = gdImageSX(expected);
529 height_a = gdImageSY(expected);
530 width_b = gdImageSX(actual);
531 height_b = gdImageSY(actual);
532
533 if (width_a != width_b || height_a != height_b) {
534 _gdTestErrorMsg(file, line,
535 "Image size mismatch: (%ux%u) vs. (%ux%u)\n for %s vs. buffer\n",
536 width_a, height_a,
537 width_b, height_b,
538 file);
539 goto fail;
540 }
541
542 surface_diff = gdImageCreateTrueColor(width_a, height_a);
543
544 gdTestImageDiff(expected, actual, surface_diff, &result);
545 if (result.pixels_changed>0) {
546 char file_diff[255];
547 char file_out[1024];
548 FILE *fp;
549 int len, p;
550
551 _gdTestErrorMsg(file, line,
552 "Total pixels changed: %d with a maximum channel difference of %d.\n",
553 result.pixels_changed,
554 result.max_diff
555 );
556
557 p = len = strlen(file);
558 p--;
559
560 /* Use only the filename (and store it in the bld dir not the src dir
561 */
562 while(p > 0 && (file[p] != '/' && file[p] != '\\')) {
563 p--;
564 }
565 sprintf(file_diff, "%s_%u_diff.png", file + p + 1, line);
566 sprintf(file_out, "%s_%u_out.png", file + p + 1, line);
567
568 fp = fopen(file_diff, "wb");
569 if (!fp) goto fail;
570 gdImagePng(surface_diff,fp);
571 fclose(fp);
572 gdImageDestroy(surface_diff);
573
574 fp = fopen(file_out, "wb");
575 if (!fp) goto fail;
576 gdImagePng(actual, fp);
577 fclose(fp);
578 return 0;
579 } else {
580 if (surface_diff) {
581 gdImageDestroy(surface_diff);
582 }
583 return 1;
584 }
585
586 fail:
587 if (surface_diff) {
588 gdImageDestroy(surface_diff);
589 }
590 return 1;
591 }
592
gdTestImageCompareToFile(const char * file,unsigned int line,const char * message,const char * expected_file,gdImagePtr actual)593 int gdTestImageCompareToFile(const char* file, unsigned int line, const char* message,
594 const char *expected_file, gdImagePtr actual)
595 {
596 gdImagePtr expected = 0;
597 int res = 1;
598
599 expected = gdTestImageFromPng(expected_file);
600
601 if (!expected) {
602 _gdTestErrorMsg(file, line, "Cannot open PNG <%s>\n", expected_file);
603 res = 0;
604 } else {
605 res = gdTestImageCompareToImage(file, line, message, expected, actual);
606 gdImageDestroy(expected);
607 }
608 return res;
609 }
610
611 static int failureCount = 0;
612
gdNumFailures()613 int gdNumFailures() {
614 return failureCount;
615 }
616
_gdTestAssert(const char * file,unsigned int line,int condition)617 int _gdTestAssert(const char* file, unsigned int line, int condition)
618 {
619 if (condition) return 1;
620 _gdTestErrorMsg(file, line, "Assert failed in <%s:%i>\n", file, line);
621
622 ++failureCount;
623
624 return 0;
625 }
626
_gdTestAssertMsg(const char * file,unsigned int line,int condition,const char * message,...)627 int _gdTestAssertMsg(const char* file, unsigned int line, int condition, const char* message, ...)
628 {
629 va_list args;
630
631 if (condition) return 1;
632
633 fprintf(stderr, "%s:%u: ", file, line);
634 va_start(args, message);
635 vfprintf(stderr, message, args);
636 va_end(args);
637
638 fflush(stderr);
639
640 ++failureCount;
641
642 return 0;
643 }
644
_gdTestErrorMsg(const char * file,unsigned int line,const char * format,...)645 int _gdTestErrorMsg(const char* file, unsigned int line, const char* format, ...) /* {{{ */
646 {
647 va_list args;
648
649 fprintf(stderr, "%s:%u: ", file, line);
650 va_start(args, format);
651 vfprintf(stderr, format, args);
652 va_end(args);
653 fflush(stderr);
654
655 ++failureCount;
656
657 return 0;
658 }
659 /* }}} */
660