comparison mupdf-source/thirdparty/leptonica/src/regutils.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 /*====================================================================*
2 - Copyright (C) 2001 Leptonica. All rights reserved.
3 -
4 - Redistribution and use in source and binary forms, with or without
5 - modification, are permitted provided that the following conditions
6 - are met:
7 - 1. Redistributions of source code must retain the above copyright
8 - notice, this list of conditions and the following disclaimer.
9 - 2. Redistributions in binary form must reproduce the above
10 - copyright notice, this list of conditions and the following
11 - disclaimer in the documentation and/or other materials
12 - provided with the distribution.
13 -
14 - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
18 - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *====================================================================*/
26
27
28 /*!
29 * \file regutils.c
30 * <pre>
31 *
32 * Regression test utilities
33 * l_int32 regTestSetup()
34 * l_int32 regTestCleanup()
35 * l_int32 regTestCompareValues()
36 * l_int32 regTestCompareStrings()
37 * l_int32 regTestComparePix()
38 * l_int32 regTestCompareSimilarPix()
39 * l_int32 regTestCheckFile()
40 * l_int32 regTestCompareFiles()
41 * l_int32 regTestWritePixAndCheck()
42 * l_int32 regTestWriteDataAndCheck()
43 * char *regTestGenLocalFilename()
44 *
45 * Static function
46 * char *getRootNameFromArgv0()
47 *
48 * These functions are for testing and development. They are not intended
49 * for use with programs that run in a production environment, such as a
50 * cloud service with unrestricted access.
51 *
52 * See regutils.h for how to use this. Here is a minimal setup:
53 *
54 * main(int argc, char **argv) {
55 * ...
56 * L_REGPARAMS *rp;
57 *
58 * if (regTestSetup(argc, argv, &rp))
59 * return 1;
60 * ...
61 * regTestWritePixAndCheck(rp, pix, IFF_PNG); // 0
62 * ...
63 * return regTestCleanup(rp);
64 * }
65 * </pre>
66 */
67
68 #ifdef HAVE_CONFIG_H
69 #include <config_auto.h>
70 #endif /* HAVE_CONFIG_H */
71
72 #include <string.h>
73 #include "allheaders.h"
74
75 extern l_int32 NumImageFileFormatExtensions;
76 extern const char *ImageFileFormatExtensions[];
77
78 static char *getRootNameFromArgv0(const char *argv0);
79
80
81 /*--------------------------------------------------------------------*
82 * Regression test utilities *
83 *--------------------------------------------------------------------*/
84 /*!
85 * \brief regTestSetup()
86 *
87 * \param[in] argc from invocation; can be either 1 or 2
88 * \param[in] argv to regtest: %argv[1] is one of these:
89 * "generate", "compare", "display"
90 * \param[out] prp all regression params
91 * \return 0 if OK, 1 on error
92 *
93 * <pre>
94 * Notes:
95 * (1) Call this function with the args to the reg test. The first arg
96 * is the name of the reg test. There are three cases:
97 * Case 1:
98 * There is either only one arg, or the second arg is "compare".
99 * This is the mode in which you run a regression test
100 * (or a set of them), looking for failures and logging
101 * the results to a file. The output, which includes
102 * logging of all reg test failures plus a SUCCESS or
103 * FAILURE summary for each test, is appended to the file
104 * "/tmp/lept/reg_results.txt. For this case, as in Case 2,
105 * the display field in rp is set to FALSE, preventing
106 * image display.
107 * Case 2:
108 * The second arg is "generate". This will cause
109 * generation of new golden files for the reg test.
110 * The results of the reg test are not recorded, and
111 * the display field in rp is set to FALSE.
112 * Case 3:
113 * The second arg is "display". The test will run and
114 * files will be written. Comparisons with golden files
115 * will not be carried out, so the only notion of success
116 * or failure is with tests that do not involve golden files.
117 * The display field in rp is TRUE, and this is used by
118 * pixDisplayWithTitle().
119 * (2) See regutils.h for examples of usage.
120 * </pre>
121 */
122 l_ok
123 regTestSetup(l_int32 argc,
124 char **argv,
125 L_REGPARAMS **prp)
126 {
127 char *testname, *vers;
128 char errormsg[64];
129 L_REGPARAMS *rp;
130
131 if (argc != 1 && argc != 2) {
132 snprintf(errormsg, sizeof(errormsg),
133 "Syntax: %s [ [compare] | generate | display ]", argv[0]);
134 return ERROR_INT(errormsg, __func__, 1);
135 }
136
137 if ((testname = getRootNameFromArgv0(argv[0])) == NULL)
138 return ERROR_INT("invalid root", __func__, 1);
139
140 setLeptDebugOK(1); /* required for testing */
141
142 rp = (L_REGPARAMS *)LEPT_CALLOC(1, sizeof(L_REGPARAMS));
143 *prp = rp;
144 rp->testname = testname;
145 rp->index = -1; /* increment before each test */
146
147 /* Initialize to true. A failure in any test is registered
148 * as a failure of the regression test. */
149 rp->success = TRUE;
150
151 /* Make sure the lept/regout subdirectory exists */
152 lept_mkdir("lept/regout");
153
154 /* Only open a stream to a temp file for the 'compare' case */
155 if (argc == 1 || !strcmp(argv[1], "compare")) {
156 rp->mode = L_REG_COMPARE;
157 rp->tempfile = stringNew("/tmp/lept/regout/regtest_output.txt");
158 rp->fp = fopenWriteStream(rp->tempfile, "wb");
159 if (rp->fp == NULL) {
160 rp->success = FALSE;
161 return ERROR_INT_1("stream not opened for tempfile",
162 rp->tempfile, __func__, 1);
163 }
164 } else if (!strcmp(argv[1], "generate")) {
165 rp->mode = L_REG_GENERATE;
166 lept_mkdir("lept/golden");
167 } else if (!strcmp(argv[1], "display")) {
168 rp->mode = L_REG_DISPLAY;
169 rp->display = TRUE;
170 } else {
171 LEPT_FREE(rp);
172 snprintf(errormsg, sizeof(errormsg),
173 "Syntax: %s [ [generate] | compare | display ]", argv[0]);
174 return ERROR_INT(errormsg, __func__, 1);
175 }
176
177 /* Print out test name and both the leptonica and
178 * image library versions */
179 lept_stderr("\n////////////////////////////////////////////////\n"
180 "//////////////// %s_reg ///////////////\n"
181 "////////////////////////////////////////////////\n",
182 rp->testname);
183 vers = getLeptonicaVersion();
184 lept_stderr("%s : ", vers);
185 LEPT_FREE(vers);
186 vers = getImagelibVersions();
187 lept_stderr("%s\n", vers);
188 LEPT_FREE(vers);
189
190 rp->tstart = startTimerNested();
191 return 0;
192 }
193
194
195 /*!
196 * \brief regTestCleanup()
197 *
198 * \param[in] rp regression test parameters
199 * \return 0 if OK, 1 on error
200 *
201 * <pre>
202 * Notes:
203 * (1) This copies anything written to the temporary file to the
204 * output file /tmp/lept/reg_results.txt.
205 * </pre>
206 */
207 l_ok
208 regTestCleanup(L_REGPARAMS *rp)
209 {
210 char result[512];
211 char *results_file; /* success/failure output in 'compare' mode */
212 char *text, *message;
213 l_int32 retval;
214 size_t nbytes;
215
216 if (!rp)
217 return ERROR_INT("rp not defined", __func__, 1);
218
219 lept_stderr("Time: %7.3f sec\n", stopTimerNested(rp->tstart));
220
221 /* If generating golden files or running in display mode, release rp */
222 if (!rp->fp) {
223 LEPT_FREE(rp->testname);
224 LEPT_FREE(rp->tempfile);
225 LEPT_FREE(rp);
226 return 0;
227 }
228
229 /* Compare mode: read back data from temp file */
230 fclose(rp->fp);
231 text = (char *)l_binaryRead(rp->tempfile, &nbytes);
232 LEPT_FREE(rp->tempfile);
233 if (!text) {
234 rp->success = FALSE;
235 LEPT_FREE(rp->testname);
236 LEPT_FREE(rp);
237 return ERROR_INT("text not returned", __func__, 1);
238 }
239
240 /* Prepare result message */
241 if (rp->success)
242 snprintf(result, sizeof(result), "SUCCESS: %s_reg\n", rp->testname);
243 else
244 snprintf(result, sizeof(result), "FAILURE: %s_reg\n", rp->testname);
245 message = stringJoin(text, result);
246 LEPT_FREE(text);
247 results_file = stringNew("/tmp/lept/reg_results.txt");
248 fileAppendString(results_file, message);
249 retval = (rp->success) ? 0 : 1;
250 LEPT_FREE(results_file);
251 LEPT_FREE(message);
252
253 LEPT_FREE(rp->testname);
254 LEPT_FREE(rp);
255 return retval;
256 }
257
258
259 /*!
260 * \brief regTestCompareValues()
261 *
262 * \param[in] rp regtest parameters
263 * \param[in] val1 typ. the golden value
264 * \param[in] val2 typ. the value computed
265 * \param[in] delta allowed max absolute difference
266 * \return 0 if OK, 1 on error
267 * Note: a failure in comparison is not an error
268 */
269 l_ok
270 regTestCompareValues(L_REGPARAMS *rp,
271 l_float32 val1,
272 l_float32 val2,
273 l_float32 delta)
274 {
275 l_float32 diff;
276
277 if (!rp)
278 return ERROR_INT("rp not defined", __func__, 1);
279
280 rp->index++;
281 diff = L_ABS(val2 - val1);
282
283 /* Record on failure */
284 if (diff > delta) {
285 if (rp->fp) {
286 fprintf(rp->fp,
287 "Failure in %s_reg: value comparison for index %d\n"
288 "difference = %f but allowed delta = %f\n",
289 rp->testname, rp->index, diff, delta);
290 }
291 lept_stderr("Failure in %s_reg: value comparison for index %d\n"
292 "difference = %f but allowed delta = %f\n",
293 rp->testname, rp->index, diff, delta);
294 rp->success = FALSE;
295 }
296 return 0;
297 }
298
299
300 /*!
301 * \brief regTestCompareStrings()
302 *
303 * \param[in] rp regtest parameters
304 * \param[in] string1 typ. the expected string
305 * \param[in] bytes1 size of string1
306 * \param[in] string2 typ. the computed string
307 * \param[in] bytes2 size of string2
308 * \return 0 if OK, 1 on error
309 * Note: a failure in comparison is not an error
310 */
311 l_ok
312 regTestCompareStrings(L_REGPARAMS *rp,
313 l_uint8 *string1,
314 size_t bytes1,
315 l_uint8 *string2,
316 size_t bytes2)
317 {
318 l_int32 same;
319 char buf[256];
320
321 if (!rp)
322 return ERROR_INT("rp not defined", __func__, 1);
323
324 rp->index++;
325 l_binaryCompare(string1, bytes1, string2, bytes2, &same);
326
327 /* Output on failure */
328 if (!same) {
329 /* Write the two strings to file */
330 snprintf(buf, sizeof(buf), "/tmp/lept/regout/string1_%d_%zu",
331 rp->index, bytes1);
332 l_binaryWrite(buf, "w", string1, bytes1);
333 snprintf(buf, sizeof(buf), "/tmp/lept/regout/string2_%d_%zu",
334 rp->index, bytes2);
335 l_binaryWrite(buf, "w", string2, bytes2);
336
337 /* Report comparison failure */
338 snprintf(buf, sizeof(buf), "/tmp/lept/regout/string*_%d_*", rp->index);
339 if (rp->fp) {
340 fprintf(rp->fp,
341 "Failure in %s_reg: string comp for index %d; "
342 "written to %s\n", rp->testname, rp->index, buf);
343 }
344 lept_stderr("Failure in %s_reg: string comp for index %d; "
345 "written to %s\n", rp->testname, rp->index, buf);
346 rp->success = FALSE;
347 }
348 return 0;
349 }
350
351
352 /*!
353 * \brief regTestComparePix()
354 *
355 * \param[in] rp regtest parameters
356 * \param[in] pix1, pix2 to be tested for equality
357 * \return 0 if OK, 1 on error
358 * Note: a failure in comparison is not an error
359 *
360 * <pre>
361 * Notes:
362 * (1) This function compares two pix for equality. On failure,
363 * this writes to stderr.
364 * </pre>
365 */
366 l_ok
367 regTestComparePix(L_REGPARAMS *rp,
368 PIX *pix1,
369 PIX *pix2)
370 {
371 l_int32 same;
372
373 if (!rp)
374 return ERROR_INT("rp not defined", __func__, 1);
375 if (!pix1 || !pix2) {
376 rp->success = FALSE;
377 return ERROR_INT("pix1 and pix2 not both defined", __func__, 1);
378 }
379
380 rp->index++;
381 pixEqual(pix1, pix2, &same);
382
383 /* Record on failure */
384 if (!same) {
385 if (rp->fp) {
386 fprintf(rp->fp, "Failure in %s_reg: pix comparison for index %d\n",
387 rp->testname, rp->index);
388 }
389 lept_stderr("Failure in %s_reg: pix comparison for index %d\n",
390 rp->testname, rp->index);
391 rp->success = FALSE;
392 }
393 return 0;
394 }
395
396
397 /*!
398 * \brief regTestCompareSimilarPix()
399 *
400 * \param[in] rp regtest parameters
401 * \param[in] pix1, pix2 to be tested for near equality
402 * \param[in] mindiff minimum pixel difference to be counted; > 0
403 * \param[in] maxfract maximum fraction of pixels allowed to have
404 * diff greater than or equal to mindiff
405 * \param[in] printstats use 1 to print normalized histogram to stderr
406 * \return 0 if OK, 1 on error
407 * Note: a failure in similarity comparison is not an error
408 *
409 * <pre>
410 * Notes:
411 * (1) This function compares two pix for near equality. On failure,
412 * this writes to stderr.
413 * (2) The pix are similar if the fraction of non-conforming pixels
414 * does not exceed %maxfract. Pixels are non-conforming if
415 * the difference in pixel values equals or exceeds %mindiff.
416 * Typical values might be %mindiff = 15 and %maxfract = 0.01.
417 * (3) The input images must have the same size and depth. The
418 * pixels for comparison are typically subsampled from the images.
419 * (4) Normally, use %printstats = 0. In debugging mode, to see
420 * the relation between %mindiff and the minimum value of
421 * %maxfract for success, set this to 1.
422 * </pre>
423 */
424 l_ok
425 regTestCompareSimilarPix(L_REGPARAMS *rp,
426 PIX *pix1,
427 PIX *pix2,
428 l_int32 mindiff,
429 l_float32 maxfract,
430 l_int32 printstats)
431 {
432 l_int32 w, h, factor, similar;
433
434 if (!rp)
435 return ERROR_INT("rp not defined", __func__, 1);
436 if (!pix1 || !pix2) {
437 rp->success = FALSE;
438 return ERROR_INT("pix1 and pix2 not both defined", __func__, 1);
439 }
440
441 rp->index++;
442 pixGetDimensions(pix1, &w, &h, NULL);
443 factor = L_MAX(w, h) / 400;
444 factor = L_MAX(1, L_MIN(factor, 4)); /* between 1 and 4 */
445 pixTestForSimilarity(pix1, pix2, factor, mindiff, maxfract, 0.0,
446 &similar, printstats);
447
448 /* Record on failure */
449 if (!similar) {
450 if (rp->fp) {
451 fprintf(rp->fp,
452 "Failure in %s_reg: pix similarity comp for index %d\n",
453 rp->testname, rp->index);
454 }
455 lept_stderr("Failure in %s_reg: pix similarity comp for index %d\n",
456 rp->testname, rp->index);
457 rp->success = FALSE;
458 }
459 return 0;
460 }
461
462
463 /*!
464 * \brief regTestCheckFile()
465 *
466 * \param[in] rp regtest parameters
467 * \param[in] localname name of output file from reg test
468 * \return 0 if OK, 1 on error
469 * Note: a failure in comparison is not an error
470 *
471 * <pre>
472 * Notes:
473 * (1) This function does one of three things, depending on the mode:
474 * * "generate": makes a "golden" file as a copy of %localname.
475 * * "compare": compares %localname contents with the golden file
476 * * "display": this does nothing
477 * (2) The canonical format of the golden filenames is:
478 * /tmp/lept/golden/[root of main name]_golden.[index].
479 * [ext of localname]
480 * e.g.,
481 * /tmp/lept/golden/maze_golden.0.png
482 * (3) The local file can be made in any subdirectory of /tmp/lept,
483 * including /tmp/lept/regout/.
484 * (4) It is important to add an extension to the local name, such as
485 * /tmp/lept/maze/file1.png (extension ".png")
486 * because the extension is added to the name of the golden file.
487 * </pre>
488 */
489 l_ok
490 regTestCheckFile(L_REGPARAMS *rp,
491 const char *localname)
492 {
493 char *ext;
494 char namebuf[256];
495 l_int32 ret, same, format;
496 PIX *pix1, *pix2;
497
498 if (!rp)
499 return ERROR_INT("rp not defined", __func__, 1);
500 if (!localname) {
501 rp->success = FALSE;
502 return ERROR_INT("local name not defined", __func__, 1);
503 }
504 if (rp->mode != L_REG_GENERATE && rp->mode != L_REG_COMPARE &&
505 rp->mode != L_REG_DISPLAY) {
506 rp->success = FALSE;
507 return ERROR_INT("invalid mode", __func__, 1);
508 }
509 rp->index++;
510
511 /* If display mode, no generation and no testing */
512 if (rp->mode == L_REG_DISPLAY) return 0;
513
514 /* Generate the golden file name; used in 'generate' and 'compare' */
515 splitPathAtExtension(localname, NULL, &ext);
516 snprintf(namebuf, sizeof(namebuf), "/tmp/lept/golden/%s_golden.%02d%s",
517 rp->testname, rp->index, ext);
518 LEPT_FREE(ext);
519
520 /* Generate mode. No testing. */
521 if (rp->mode == L_REG_GENERATE) {
522 /* Save the file as a golden file */
523 ret = fileCopy(localname, namebuf);
524 #if 0 /* Enable for details on writing of golden files */
525 if (!ret) {
526 char *local = genPathname(localname, NULL);
527 char *golden = genPathname(namebuf, NULL);
528 L_INFO("Copy: %s to %s\n", __func__, local, golden);
529 LEPT_FREE(local);
530 LEPT_FREE(golden);
531 }
532 #endif
533 return ret;
534 }
535
536 /* Compare mode: test and record on failure. This can be used
537 * for all image formats, as well as for all files of serialized
538 * data, such as boxa, pta, etc. In all cases except for
539 * GIF compressed images, we compare the files to see if they
540 * are identical. GIF doesn't support RGB images; to write
541 * a 32 bpp RGB image in GIF, we do a lossy quantization to
542 * 256 colors, so the cycle read-RGB/write-GIF is not idempotent.
543 * And although the read/write cycle for GIF images with bpp <= 8
544 * is idempotent in the image pixels, it is not idempotent in the
545 * actual file bytes; tests comparing file bytes before and after
546 * a GIF read/write cycle will fail. So for GIF we uncompress
547 * the two images and compare the actual pixels. PNG is both
548 * lossless and idempotent in file bytes on read/write, so it is
549 * not necessary to compare pixels. (Comparing pixels requires
550 * decompression, and thus would increase the regression test
551 * time. JPEG is lossy and not idempotent in the image pixels,
552 * so no tests are constructed that would require it. */
553 findFileFormat(localname, &format);
554 if (format == IFF_GIF) {
555 same = 0;
556 pix1 = pixRead(localname);
557 pix2 = pixRead(namebuf);
558 pixEqual(pix1, pix2, &same);
559 pixDestroy(&pix1);
560 pixDestroy(&pix2);
561 } else {
562 filesAreIdentical(localname, namebuf, &same);
563 }
564 if (!same) {
565 fprintf(rp->fp, "Failure in %s_reg, index %d: comparing %s with %s\n",
566 rp->testname, rp->index, localname, namebuf);
567 lept_stderr("Failure in %s_reg, index %d: comparing %s with %s\n",
568 rp->testname, rp->index, localname, namebuf);
569 rp->success = FALSE;
570 }
571
572 return 0;
573 }
574
575
576 /*!
577 * \brief regTestCompareFiles()
578 *
579 * \param[in] rp regtest parameters
580 * \param[in] index1 of one output file from reg test
581 * \param[in] index2 of another output file from reg test
582 * \return 0 if OK, 1 on error
583 * Note: a failure in comparison is not an error
584 *
585 * <pre>
586 * Notes:
587 * (1) This only does something in "compare" mode.
588 * (2) The canonical format of the golden filenames is:
589 * /tmp/lept/golden/[root of main name]_golden.[index].
590 * [ext of localname]
591 * e.g.,
592 * /tmp/lept/golden/maze_golden.0.png
593 * </pre>
594 */
595 l_ok
596 regTestCompareFiles(L_REGPARAMS *rp,
597 l_int32 index1,
598 l_int32 index2)
599 {
600 char *name1, *name2;
601 char namebuf[256];
602 l_int32 same;
603 SARRAY *sa;
604
605 if (!rp)
606 return ERROR_INT("rp not defined", __func__, 1);
607 if (index1 < 0 || index2 < 0) {
608 rp->success = FALSE;
609 return ERROR_INT("index1 and/or index2 is negative", __func__, 1);
610 }
611 if (index1 == index2) {
612 rp->success = FALSE;
613 return ERROR_INT("index1 must differ from index2", __func__, 1);
614 }
615
616 rp->index++;
617 if (rp->mode != L_REG_COMPARE) return 0;
618
619 /* Generate the golden file names */
620 snprintf(namebuf, sizeof(namebuf), "%s_golden.%02d", rp->testname, index1);
621 sa = getSortedPathnamesInDirectory("/tmp/lept/golden", namebuf, 0, 0);
622 if (sarrayGetCount(sa) != 1) {
623 sarrayDestroy(&sa);
624 rp->success = FALSE;
625 L_ERROR("golden file %s not found\n", __func__, namebuf);
626 return 1;
627 }
628 name1 = sarrayGetString(sa, 0, L_COPY);
629 sarrayDestroy(&sa);
630
631 snprintf(namebuf, sizeof(namebuf), "%s_golden.%02d", rp->testname, index2);
632 sa = getSortedPathnamesInDirectory("/tmp/lept/golden", namebuf, 0, 0);
633 if (sarrayGetCount(sa) != 1) {
634 sarrayDestroy(&sa);
635 rp->success = FALSE;
636 LEPT_FREE(name1);
637 L_ERROR("golden file %s not found\n", __func__, namebuf);
638 return 1;
639 }
640 name2 = sarrayGetString(sa, 0, L_COPY);
641 sarrayDestroy(&sa);
642
643 /* Test and record on failure */
644 filesAreIdentical(name1, name2, &same);
645 if (!same) {
646 fprintf(rp->fp,
647 "Failure in %s_reg, index %d: comparing %s with %s\n",
648 rp->testname, rp->index, name1, name2);
649 lept_stderr("Failure in %s_reg, index %d: comparing %s with %s\n",
650 rp->testname, rp->index, name1, name2);
651 rp->success = FALSE;
652 }
653
654 LEPT_FREE(name1);
655 LEPT_FREE(name2);
656 return 0;
657 }
658
659
660 /*!
661 * \brief regTestWritePixAndCheck()
662 *
663 * \param[in] rp regtest parameters
664 * \param[in] pix to be written
665 * \param[in] format of output pix
666 * \return 0 if OK, 1 on error
667 * Note: a failure in comparison is not an error
668 *
669 * <pre>
670 * Notes:
671 * (1) This function makes it easy to write the pix in a numbered
672 * sequence of files, and either to:
673 * (a) write the golden file ("generate" arg to regression test)
674 * (b) make a local file and "compare" with the golden file
675 * (c) make a local file and "display" the results
676 * (2) The canonical format of the local filename is:
677 * /tmp/lept/regout/[root of main name].[count].[format extension]
678 * e.g., for scale_reg,
679 * /tmp/lept/regout/scale.0.png
680 * The golden file name mirrors this in the usual way.
681 * (3) The check is done between the written files, which requires
682 * the files to be identical. The exception is for GIF, which
683 * only requires that all pixels in the decoded pix are identical.
684 * </pre>
685 */
686 l_ok
687 regTestWritePixAndCheck(L_REGPARAMS *rp,
688 PIX *pix,
689 l_int32 format)
690 {
691 char namebuf[256];
692
693 if (!rp)
694 return ERROR_INT("rp not defined", __func__, 1);
695 if (!pix) {
696 rp->success = FALSE;
697 return ERROR_INT("pix not defined", __func__, 1);
698 }
699 if (format < 0 || format >= NumImageFileFormatExtensions) {
700 rp->success = FALSE;
701 return ERROR_INT("invalid format", __func__, 1);
702 }
703
704 /* Use bmp format for testing if library for requested
705 * format for jpeg, png or tiff is not available */
706 changeFormatForMissingLib(&format);
707
708 /* Generate the local file name */
709 snprintf(namebuf, sizeof(namebuf), "/tmp/lept/regout/%s.%02d.%s",
710 rp->testname, rp->index + 1, ImageFileFormatExtensions[format]);
711
712 /* Write the local file */
713 if (pixGetDepth(pix) < 8)
714 pixSetPadBits(pix, 0);
715 pixWrite(namebuf, pix, format);
716
717 /* Either write the golden file ("generate") or check the
718 local file against an existing golden file ("compare") */
719 regTestCheckFile(rp, namebuf);
720
721 return 0;
722 }
723
724
725 /*!
726 * \brief regTestWriteDataAndCheck()
727 *
728 * \param[in] rp regtest parameters
729 * \param[in] data to be written
730 * \param[in] nbytes of data to be written
731 * \param[in] ext filename extension (e.g.: "ba", "pta")
732 * \return 0 if OK, 1 on error
733 * Note: a failure in comparison is not an error
734 *
735 * <pre>
736 * Notes:
737 * (1) This function makes it easy to write data in a numbered
738 * sequence of files, and either to:
739 * (a) write the golden file ("generate" arg to regression test)
740 * (b) make a local file and "compare" with the golden file
741 * (c) make a local file and "display" the results
742 * (2) The canonical format of the local filename is:
743 * /tmp/lept/regout/[root of main name].[count].[ext]
744 * e.g., for the first boxaa in quadtree_reg,
745 * /tmp/lept/regout/quadtree.0.baa
746 * The golden file name mirrors this in the usual way.
747 * (3) The data can be anything. It is most useful for serialized
748 * output of data, such as boxa, pta, etc.
749 * (4) The file extension is arbitrary. It is included simply
750 * to make the content type obvious when examining written files.
751 * (5) The check is done between the written files, which requires
752 * the files to be identical.
753 * </pre>
754 */
755 l_ok
756 regTestWriteDataAndCheck(L_REGPARAMS *rp,
757 void *data,
758 size_t nbytes,
759 const char *ext)
760 {
761 char namebuf[256];
762
763 if (!rp)
764 return ERROR_INT("rp not defined", __func__, 1);
765 if (!data || nbytes == 0) {
766 rp->success = FALSE;
767 return ERROR_INT("data not defined or size == 0", __func__, 1);
768 }
769
770 /* Generate the local file name */
771 snprintf(namebuf, sizeof(namebuf), "/tmp/lept/regout/%s.%02d.%s",
772 rp->testname, rp->index + 1, ext);
773
774 /* Write the local file */
775 l_binaryWrite(namebuf, "w", data, nbytes);
776
777 /* Either write the golden file ("generate") or check the
778 local file against an existing golden file ("compare") */
779 regTestCheckFile(rp, namebuf);
780 return 0;
781 }
782
783
784 /*!
785 * \brief regTestGenLocalFilename()
786 *
787 * \param[in] rp regtest parameters
788 * \param[in] index use -1 for current index
789 * \param[in] format of image; e.g., IFF_PNG
790 * \return filename if OK, or NULL on error
791 *
792 * <pre>
793 * Notes:
794 * (1) This is used to get the name of a file in the regout
795 * subdirectory, that has been made and is used to test against
796 * the golden file. You can either specify a particular index
797 * value, or with %index == -1, this returns the most recently
798 * written file. The latter case lets you read a pix from a
799 * file that has just been written with regTestWritePixAndCheck(),
800 * which is useful for testing formatted read/write functions.
801 *
802 * </pre>
803 */
804 char *
805 regTestGenLocalFilename(L_REGPARAMS *rp,
806 l_int32 index,
807 l_int32 format)
808 {
809 char buf[64];
810 l_int32 ind;
811
812 if (!rp)
813 return (char *)ERROR_PTR("rp not defined", __func__, NULL);
814
815 ind = (index >= 0) ? index : rp->index;
816 snprintf(buf, sizeof(buf), "/tmp/lept/regout/%s.%02d.%s",
817 rp->testname, ind, ImageFileFormatExtensions[format]);
818 return stringNew(buf);
819 }
820
821
822 /*!
823 * \brief getRootNameFromArgv0()
824 *
825 * \param[in] argv0
826 * \return root name without the '_reg', or NULL on error
827 *
828 * <pre>
829 * Notes:
830 * (1) For example, from psioseg_reg, we want to extract
831 * just 'psioseg' as the root.
832 * (2) In unix with autotools, the executable is not X,
833 * but ./.libs/lt-X. So in addition to stripping out the
834 * last 4 characters of the tail, we have to check for
835 * the '-' and strip out the "lt-" prefix if we find it.
836 * </pre>
837 */
838 static char *
839 getRootNameFromArgv0(const char *argv0)
840 {
841 l_int32 len;
842 char *root;
843
844 splitPathAtDirectory(argv0, NULL, &root);
845 if ((len = strlen(root)) <= 4) {
846 LEPT_FREE(root);
847 return (char *)ERROR_PTR("invalid argv0; too small", __func__, NULL);
848 }
849
850 #ifndef _WIN32
851 {
852 char *newroot;
853 l_int32 loc;
854 if (stringFindSubstr(root, "-", &loc)) {
855 newroot = stringNew(root + loc + 1); /* strip out "lt-" */
856 LEPT_FREE(root);
857 root = newroot;
858 len = strlen(root);
859 }
860 len -= 4; /* remove the "_reg" suffix */
861 }
862 #else
863 if (strstr(root, ".exe") != NULL)
864 len -= 4;
865 if (strstr(root, "_reg") == root + len - 4)
866 len -= 4;
867 #endif /* ! _WIN32 */
868
869 root[len] = '\0'; /* terminate */
870 return root;
871 }