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