BlackboxTestRunner.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  1. /*
  2. * Copyright 2016 Nu-book Inc.
  3. * Copyright 2019 Axel Waggershauser
  4. */
  5. // SPDX-License-Identifier: Apache-2.0
  6. #include "BlackboxTestRunner.h"
  7. #include "ImageLoader.h"
  8. #include "ReadBarcode.h"
  9. #include "Utf.h"
  10. #include "ZXAlgorithms.h"
  11. #include <fmt/core.h>
  12. #include <fmt/ostream.h>
  13. #include <chrono>
  14. #include <exception>
  15. #include <fstream>
  16. #include <map>
  17. #include <optional>
  18. #include <set>
  19. #include <sstream>
  20. #include <string>
  21. #include <string_view>
  22. #include <vector>
  23. namespace ZXing::Test {
  24. namespace {
  25. struct PureTag {} pure;
  26. struct TestCase
  27. {
  28. struct TC
  29. {
  30. std::string name = {};
  31. int minPassCount = 0; // The number of images which must decode for the test to pass.
  32. int maxMisreads = 0; // Maximum number of successfully read images with the wrong contents.
  33. std::set<fs::path> notDetectedFiles = {};
  34. std::map<fs::path, std::string> misReadFiles = {};
  35. };
  36. TC tc[2] = {};
  37. int rotation = 0; // The rotation in degrees clockwise to use for this test.
  38. TestCase(int mntf, int mnts, int mmf, int mms, int r) : tc{{"fast", mntf, mmf}, {"slow", mnts, mms}}, rotation(r) {}
  39. TestCase(int mntf, int mnts, int r) : TestCase(mntf, mnts, 0, 0, r) {}
  40. TestCase(int mntp, int mmp, PureTag) : tc{{"pure", mntp, mmp}} {}
  41. };
  42. struct FalsePositiveTestCase
  43. {
  44. int maxAllowed; // Maximum number of images which can fail due to successfully reading the wrong contents
  45. int rotation; // The rotation in degrees clockwise to use for this test.
  46. };
  47. }
  48. // Helper for `compareResult()` - map `key` to Barcode property, converting value to std::string
  49. static std::string getBarcodeValue(const Barcode& barcode, const std::string& key)
  50. {
  51. if (key == "contentType")
  52. return ToString(barcode.contentType());
  53. if (key == "ecLevel")
  54. return barcode.ecLevel();
  55. if (key == "orientation")
  56. return std::to_string(barcode.orientation());
  57. if (key == "symbologyIdentifier")
  58. return barcode.symbologyIdentifier();
  59. if (key == "sequenceSize")
  60. return std::to_string(barcode.sequenceSize());
  61. if (key == "sequenceIndex")
  62. return std::to_string(barcode.sequenceIndex());
  63. if (key == "sequenceId")
  64. return barcode.sequenceId();
  65. if (key == "isLastInSequence")
  66. return barcode.isLastInSequence() ? "true" : "false";
  67. if (key == "isPartOfSequence")
  68. return barcode.isPartOfSequence() ? "true" : "false";
  69. if (key == "isMirrored")
  70. return barcode.isMirrored() ? "true" : "false";
  71. if (key == "isInverted")
  72. return barcode.isInverted() ? "true" : "false";
  73. if (key == "readerInit")
  74. return barcode.readerInit() ? "true" : "false";
  75. return fmt::format("***Unknown key '{}'***", key);
  76. }
  77. // Read ".result.txt" file contents `expected` with lines "key=value" and compare to `actual`
  78. static bool compareResult(const Barcode& barcode, const std::string& expected, std::string& actual)
  79. {
  80. bool ret = true;
  81. actual.clear();
  82. actual.reserve(expected.size());
  83. std::stringstream expectedLines(expected);
  84. std::string expectedLine;
  85. while (std::getline(expectedLines, expectedLine)) {
  86. if (expectedLine.empty() || expectedLine[0] == '#')
  87. continue;
  88. auto equals = expectedLine.find('=');
  89. if (equals == std::string::npos) {
  90. actual += "***Bad format, missing equals***\n";
  91. return false;
  92. }
  93. std::string key = expectedLine.substr(0, equals);
  94. std::string expectedValue = expectedLine.substr(equals + 1);
  95. std::string actualValue = getBarcodeValue(barcode, key);
  96. if (actualValue != expectedValue) {
  97. ret = false;
  98. actualValue += " ***Mismatch***";
  99. }
  100. actual += key + '=' + actualValue + '\n';
  101. }
  102. return ret;
  103. }
  104. static std::string checkResult(const fs::path& imgPath, std::string_view expectedFormat, const Barcode& barcode)
  105. {
  106. if (auto format = ToString(barcode.format()); expectedFormat != format)
  107. return fmt::format("Format mismatch: expected '{}' but got '{}'", expectedFormat, format);
  108. auto readFile = [imgPath](const char* ending) {
  109. std::ifstream ifs(fs::path(imgPath).replace_extension(ending), std::ios::binary);
  110. return ifs ? std::optional(std::string(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>())) : std::nullopt;
  111. };
  112. if (auto expected = readFile(".result.txt")) {
  113. std::string actual;
  114. if (!compareResult(barcode, *expected, actual))
  115. return fmt::format("Result mismatch: expected\n{} but got\n{}", *expected, actual);
  116. }
  117. if (auto expected = readFile(".txt")) {
  118. expected = EscapeNonGraphical(*expected);
  119. auto utf8Result = barcode.text(TextMode::Escaped);
  120. return utf8Result != *expected ? fmt::format("Content mismatch: expected '{}' but got '{}'", *expected, utf8Result) : "";
  121. }
  122. if (auto expected = readFile(".bin")) {
  123. ByteArray binaryExpected(*expected);
  124. return barcode.bytes() != binaryExpected
  125. ? fmt::format("Content mismatch: expected '{}' but got '{}'", ToHex(binaryExpected), ToHex(barcode.bytes()))
  126. : "";
  127. }
  128. return "Error reading file";
  129. }
  130. static int failed = 0;
  131. static int extra = 0;
  132. static int totalImageLoadTime = 0;
  133. int timeSince(std::chrono::steady_clock::time_point startTime)
  134. {
  135. auto duration = std::chrono::steady_clock::now() - startTime;
  136. return narrow_cast<int>(std::chrono::duration_cast<std::chrono::milliseconds>(duration).count());
  137. }
  138. // pre-load images into cache, so the disc io time does not end up in the timing measurement
  139. void preloadImageCache(const std::vector<fs::path>& imgPaths)
  140. {
  141. auto startTime = std::chrono::steady_clock::now();
  142. ImageLoader::clearCache();
  143. for (const auto& imgPath : imgPaths)
  144. ImageLoader::load(imgPath);
  145. totalImageLoadTime += timeSince(startTime);
  146. }
  147. static std::string printPositiveTestStats(int imageCount, const TestCase::TC& tc)
  148. {
  149. int passCount = imageCount - Size(tc.misReadFiles) - Size(tc.notDetectedFiles);
  150. fmt::print(" | {}: {:3} of {:3}, misread {} of {}", tc.name, passCount, tc.minPassCount, Size(tc.misReadFiles), tc.maxMisreads);
  151. std::string failures;
  152. if (passCount < tc.minPassCount && !tc.notDetectedFiles.empty()) {
  153. failures += fmt::format(" Not detected ({}):", tc.name);
  154. for (const auto& f : tc.notDetectedFiles)
  155. failures += fmt::format(" {}", f.filename().string());
  156. failures += "\n";
  157. failed += tc.minPassCount - passCount;
  158. }
  159. extra += std::max(0, passCount - tc.minPassCount);
  160. if (passCount > tc.minPassCount)
  161. failures += fmt::format(" Unexpected detections ({}): {}\n", tc.name, passCount - tc.minPassCount);
  162. if (Size(tc.misReadFiles) > tc.maxMisreads) {
  163. failures += fmt::format(" Read error ({}):", tc.name);
  164. for (const auto& [path, error] : tc.misReadFiles)
  165. failures += fmt::format(" {}: {}\n", path.filename().string(), error);
  166. failed += Size(tc.misReadFiles) - tc.maxMisreads;
  167. }
  168. return failures;
  169. }
  170. static std::vector<fs::path> getImagesInDirectory(const fs::path& directory)
  171. {
  172. std::vector<fs::path> result;
  173. for (const auto& entry : fs::directory_iterator(directory))
  174. if (fs::is_regular_file(entry.status()) &&
  175. Contains({".png", ".jpg", ".pgm", ".gif"}, entry.path().extension()))
  176. result.push_back(entry.path());
  177. preloadImageCache(result);
  178. return result;
  179. }
  180. static void doRunTests(const fs::path& directory, std::string_view format, int totalTests, const std::vector<TestCase>& tests,
  181. ReaderOptions opts)
  182. {
  183. auto imgPaths = getImagesInDirectory(directory);
  184. auto folderName = directory.stem();
  185. if (Size(imgPaths) != totalTests)
  186. fmt::print("TEST {} => Expected number of tests: {}, got: {} => FAILED\n", folderName.string(), totalTests, imgPaths.size());
  187. for (auto& test : tests) {
  188. fmt::print("{:20} @ {:3}, {:3}", folderName.string(), test.rotation, Size(imgPaths));
  189. std::vector<int> times;
  190. std::string failures;
  191. for (auto tc : test.tc) {
  192. if (tc.name.empty())
  193. break;
  194. auto startTime = std::chrono::steady_clock::now();
  195. opts.setTryDownscale(tc.name == "slow_");
  196. opts.setDownscaleFactor(2);
  197. opts.setDownscaleThreshold(180);
  198. opts.setTryHarder(tc.name == "slow");
  199. opts.setTryRotate(tc.name == "slow");
  200. opts.setTryInvert(tc.name == "slow");
  201. opts.setIsPure(tc.name == "pure");
  202. if (opts.isPure())
  203. opts.setBinarizer(Binarizer::FixedThreshold);
  204. for (const auto& imgPath : imgPaths) {
  205. auto barcode = ReadBarcode(ImageLoader::load(imgPath).rotated(test.rotation), opts);
  206. if (barcode.isValid()) {
  207. auto error = checkResult(imgPath, format, barcode);
  208. if (!error.empty())
  209. tc.misReadFiles[imgPath] = error;
  210. } else {
  211. tc.notDetectedFiles.insert(imgPath);
  212. }
  213. }
  214. times.push_back(timeSince(startTime));
  215. failures += printPositiveTestStats(Size(imgPaths), tc);
  216. }
  217. fmt::print(" | time: {:3} vs {:3} ms\n", times.front(), times.back());
  218. if (!failures.empty())
  219. fmt::print("\n{}\n", failures);
  220. }
  221. }
  222. static Barcode readMultiple(const std::vector<fs::path>& imgPaths, std::string_view format)
  223. {
  224. Barcodes allBarcodes;
  225. for (const auto& imgPath : imgPaths) {
  226. auto barcodes = ReadBarcodes(ImageLoader::load(imgPath),
  227. ReaderOptions().setFormats(BarcodeFormatFromString(format)).setTryDownscale(false));
  228. allBarcodes.insert(allBarcodes.end(), barcodes.begin(), barcodes.end());
  229. }
  230. return MergeStructuredAppendSequence(allBarcodes);
  231. }
  232. static void doRunStructuredAppendTest(const fs::path& directory, std::string_view format, int totalTests,
  233. const std::vector<TestCase>& tests)
  234. {
  235. auto imgPaths = getImagesInDirectory(directory);
  236. auto folderName = directory.stem();
  237. std::map<fs::path, std::vector<fs::path>> imageGroups;
  238. for (const auto& imgPath : imgPaths) {
  239. std::string fn = imgPath.filename().string();
  240. auto p = fn.find_last_of('-');
  241. imageGroups[imgPath.parent_path() / fn.substr(0, p)].push_back(imgPath);
  242. }
  243. if (Size(imageGroups) != totalTests)
  244. fmt::print("TEST {} => Expected number of tests: {}, got: {} => FAILED\n", folderName.string(), totalTests,
  245. imageGroups.size());
  246. for (auto& test : tests) {
  247. fmt::print("{:20} @ {:3}, {:3}", folderName.string(), test.rotation, Size(imgPaths));
  248. auto tc = test.tc[0];
  249. auto startTime = std::chrono::steady_clock::now();
  250. for (const auto& [testPath, testImgPaths] : imageGroups) {
  251. auto barcode = readMultiple(testImgPaths, format);
  252. if (barcode.isValid()) {
  253. auto error = checkResult(testPath, format, barcode);
  254. if (!error.empty())
  255. tc.misReadFiles[testPath] = error;
  256. } else {
  257. tc.notDetectedFiles.insert(testPath);
  258. }
  259. }
  260. auto failures = printPositiveTestStats(Size(imageGroups), tc);
  261. fmt::print(" | time: {:3} ms\n", timeSince(startTime));
  262. if (!failures.empty())
  263. fmt::print("\n{}\n", failures);
  264. }
  265. }
  266. int runBlackBoxTests(const fs::path& testPathPrefix, const std::set<std::string>& includedTests)
  267. {
  268. auto hasTest = [&includedTests](const fs::path& dir) {
  269. auto stem = dir.stem().string();
  270. return includedTests.empty() || Contains(includedTests, stem) ||
  271. Contains(includedTests, stem.substr(0, stem.size() - 2));
  272. };
  273. auto runTests = [&](std::string_view directory, std::string_view format, int total,
  274. const std::vector<TestCase>& tests, const ReaderOptions& opts = ReaderOptions()) {
  275. if (hasTest(directory))
  276. doRunTests(testPathPrefix / directory, format, total, tests, opts);
  277. };
  278. auto runStructuredAppendTest = [&](std::string_view directory, std::string_view format, int total,
  279. const std::vector<TestCase>& tests) {
  280. if (hasTest(directory))
  281. doRunStructuredAppendTest(testPathPrefix / directory, format, total, tests);
  282. };
  283. try
  284. {
  285. auto startTime = std::chrono::steady_clock::now();
  286. // clang-format off
  287. // Expected failures:
  288. // abc-inverted.png (fast) - fast does not try inverted
  289. // az-thick.png (pure)
  290. runTests("aztec-1", "Aztec", 31, {
  291. { 30, 31, 0 },
  292. { 30, 31, 90 },
  293. { 30, 31, 180 },
  294. { 30, 31, 270 },
  295. { 29, 0, pure },
  296. });
  297. runTests("aztec-2", "Aztec", 22, {
  298. { 21, 21, 0 },
  299. { 21, 21, 90 },
  300. { 21, 21, 180 },
  301. { 21, 21, 270 },
  302. });
  303. runTests("datamatrix-1", "DataMatrix", 29, {
  304. { 29, 29, 0 },
  305. { 0, 27, 90 },
  306. { 0, 27, 180 },
  307. { 0, 27, 270 },
  308. { 28, 0, pure },
  309. });
  310. runTests("datamatrix-2", "DataMatrix", 13, {
  311. { 13, 13, 0 },
  312. { 0, 13, 90 },
  313. { 0, 13, 180 },
  314. { 0, 13, 270 },
  315. });
  316. runTests("datamatrix-3", "DataMatrix", 21, {
  317. { 20, 21, 0 },
  318. { 0, 21, 90 },
  319. { 0, 21, 180 },
  320. { 0, 21, 270 },
  321. });
  322. runTests("datamatrix-4", "DataMatrix", 21, {
  323. { 21, 21, 0 },
  324. { 0, 21, 90 },
  325. { 0, 21, 180 },
  326. { 0, 21, 270 },
  327. { 19, 0, pure },
  328. });
  329. runTests("dxfilmedge-1", "DXFilmEdge", 3, {
  330. { 1, 3, 0 },
  331. { 0, 3, 180 },
  332. });
  333. runTests("codabar-1", "Codabar", 11, {
  334. { 11, 11, 0 },
  335. { 11, 11, 180 },
  336. });
  337. runTests("codabar-2", "Codabar", 4, {
  338. { 2, 3, 0 },
  339. { 2, 3, 180 },
  340. });
  341. runTests("code39-1", "Code39", 4, {
  342. { 4, 4, 0 },
  343. { 4, 4, 180 },
  344. });
  345. runTests("code39-2", "Code39", 2, {
  346. { 2, 2, 0 },
  347. { 2, 2, 180 },
  348. });
  349. runTests("code39-3", "Code39", 12, {
  350. { 12, 12, 0 },
  351. { 12, 12, 180 },
  352. });
  353. runTests("code93-1", "Code93", 3, {
  354. { 3, 3, 0 },
  355. { 3, 3, 180 },
  356. });
  357. runTests("code128-1", "Code128", 6, {
  358. { 6, 6, 0 },
  359. { 6, 6, 180 },
  360. });
  361. runTests("code128-2", "Code128", 22, {
  362. { 19, 22, 0 },
  363. { 20, 22, 180 },
  364. });
  365. runTests("code128-3", "Code128", 2, {
  366. { 2, 2, 0 },
  367. { 2, 2, 180 },
  368. });
  369. runTests("ean8-1", "EAN-8", 9, {
  370. { 9, 9, 0 },
  371. { 9, 9, 180 },
  372. { 8, 0, pure },
  373. });
  374. runTests("ean13-1", "EAN-13", 32, {
  375. { 26, 30, 0 },
  376. { 25, 30, 180 },
  377. });
  378. runTests("ean13-2", "EAN-13", 24, {
  379. { 7, 13, 0 },
  380. { 7, 13, 180 },
  381. });
  382. runTests("ean13-3", "EAN-13", 21, {
  383. { 20, 21, 0 },
  384. { 21, 21, 180 },
  385. });
  386. runTests("ean13-4", "EAN-13", 22, {
  387. { 6, 13, 0 },
  388. { 7, 13, 180 },
  389. });
  390. runTests("ean13-extension-1", "EAN-13", 5, {
  391. { 3, 5, 0 },
  392. { 3, 5, 180 },
  393. }, ReaderOptions().setEanAddOnSymbol(EanAddOnSymbol::Require));
  394. runTests("itf-1", "ITF", 14, {
  395. { 13, 14, 0 },
  396. { 13, 14, 180 },
  397. });
  398. runTests("itf-2", "ITF", 6, {
  399. { 6, 6, 0 },
  400. { 6, 6, 180 },
  401. });
  402. runTests("maxicode-1", "MaxiCode", 9, {
  403. { 9, 9, 0 },
  404. });
  405. runTests("maxicode-2", "MaxiCode", 4, {
  406. { 0, 0, 0 },
  407. });
  408. runTests("upca-1", "UPC-A", 12, {
  409. { 10, 12, 0 },
  410. { 11, 12, 180 },
  411. });
  412. runTests("upca-2", "UPC-A", 36, {
  413. { 17, 22, 0 },
  414. { 17, 22, 180 },
  415. });
  416. runTests("upca-3", "UPC-A", 21, {
  417. { 7, 11, 0 },
  418. { 8, 11, 180 },
  419. });
  420. runTests("upca-4", "UPC-A", 19, {
  421. { 8, 12, 0, 1, 0 },
  422. { 9, 12, 0, 1, 180 },
  423. });
  424. runTests("upca-5", "UPC-A", 32, {
  425. { 18, 20, 0 },
  426. { 18, 20, 180 },
  427. });
  428. runTests("upca-extension-1", "UPC-A", 6, {
  429. { 4, 4, 0 },
  430. { 3, 4, 180 },
  431. }, ReaderOptions().setEanAddOnSymbol(EanAddOnSymbol::Require));
  432. runTests("upce-1", "UPC-E", 3, {
  433. { 3, 3, 0 },
  434. { 3, 3, 180 },
  435. { 3, 0, pure },
  436. });
  437. runTests("upce-2", "UPC-E", 28, {
  438. { 18, 22, 0, 1, 0 },
  439. { 19, 22, 1, 1, 180 },
  440. });
  441. runTests("upce-3", "UPC-E", 11, {
  442. { 5, 7, 0 },
  443. { 6, 7, 180 },
  444. });
  445. runTests("rss14-1", "DataBar", 6, {
  446. { 6, 6, 0 },
  447. { 6, 6, 180 },
  448. });
  449. runTests("rss14-2", "DataBar", 14, {
  450. { 10, 11, 0 },
  451. { 10, 11, 180 },
  452. });
  453. runTests("rssexpanded-1", "DataBarExpanded", 34, {
  454. { 34, 34, 0 },
  455. { 34, 34, 180 },
  456. { 34, 0, pure },
  457. });
  458. runTests("rssexpanded-2", "DataBarExpanded", 15, {
  459. { 13, 15, 0 },
  460. { 13, 15, 180 },
  461. });
  462. runTests("rssexpanded-3", "DataBarExpanded", 118, {
  463. { 118, 118, 0 },
  464. { 118, 118, 180 },
  465. { 118, 0, pure },
  466. });
  467. runTests("rssexpandedstacked-1", "DataBarExpanded", 65, {
  468. { 55, 65, 0 },
  469. { 55, 65, 180 },
  470. { 60, 0, pure },
  471. });
  472. runTests("rssexpandedstacked-2", "DataBarExpanded", 2, {
  473. { 2, 2, 0 },
  474. { 2, 2, 180 },
  475. });
  476. runTests("databarltd-1", "DataBarLimited", 2, {
  477. { 2, 2, 0 },
  478. { 2, 2, 180 },
  479. { 2, 0, pure },
  480. });
  481. runTests("qrcode-1", "QRCode", 16, {
  482. { 16, 16, 0 },
  483. { 16, 16, 90 },
  484. { 16, 16, 180 },
  485. { 16, 16, 270 },
  486. });
  487. runTests("qrcode-2", "QRCode", 51, {
  488. { 45, 48, 0 },
  489. { 45, 48, 90 },
  490. { 45, 48, 180 },
  491. { 45, 48, 270 },
  492. { 22, 1, pure }, // the misread is the 'outer' symbol in 16.png
  493. });
  494. runTests("qrcode-3", "QRCode", 28, {
  495. { 28, 28, 0 },
  496. { 28, 28, 90 },
  497. { 28, 28, 180 },
  498. { 28, 28, 270 },
  499. });
  500. runTests("qrcode-4", "QRCode", 41, {
  501. { 31, 31, 0 },
  502. { 31, 31, 90 },
  503. { 31, 31, 180 },
  504. { 31, 31, 270 },
  505. });
  506. runTests("qrcode-5", "QRCode", 16, {
  507. { 16, 16, 0 },
  508. { 16, 16, 90 },
  509. { 16, 16, 180 },
  510. { 16, 16, 270 },
  511. { 4, 0, pure },
  512. });
  513. runTests("qrcode-6", "QRCode", 15, {
  514. { 15, 15, 0 },
  515. { 15, 15, 90 },
  516. { 15, 15, 180 },
  517. { 15, 15, 270 },
  518. });
  519. runStructuredAppendTest("qrcode-7", "QRCode", 1, {
  520. { 1, 1, 0 },
  521. });
  522. runTests("microqrcode-1", "MicroQRCode", 16, {
  523. { 15, 15, 0 },
  524. { 14, 14, 90 },
  525. { 14, 14, 180 }, // ughs: 1 result is platform/compiler dependent (e.g. -march=core2 vs. haswell)
  526. { 15, 15, 270 },
  527. { 9, 0, pure },
  528. });
  529. runTests("rmqrcode-1", "rMQRCode", 3, {
  530. { 2, 3, 0 },
  531. { 2, 3, 90 },
  532. { 2, 3, 180 },
  533. { 2, 3, 270 },
  534. { 2, 2, pure },
  535. });
  536. runTests("pdf417-1", "PDF417", 17, {
  537. { 16, 17, 0 },
  538. { 1, 17, 90 },
  539. { 16, 17, 180 },
  540. { 1, 17, 270 },
  541. { 16, 0, pure },
  542. });
  543. runTests("pdf417-2", "PDF417", 25, {
  544. { 25, 25, 0 },
  545. { 0, 25, 90 },
  546. { 25, 25, 180 },
  547. { 0, 25, 270 },
  548. });
  549. runTests("pdf417-3", "PDF417", 16, {
  550. { 16, 16, 0 },
  551. { 0, 16, 90 },
  552. { 16, 16, 180 },
  553. { 0, 16, 270 },
  554. { 7, 0, pure },
  555. });
  556. runStructuredAppendTest("pdf417-4", "PDF417", 3, {
  557. { 3, 3, 0 },
  558. });
  559. runTests("falsepositives-1", "None", 27, {
  560. { 0, 0, 0, 0, 0 },
  561. { 0, 0, 0, 0, 90 },
  562. { 0, 0, 0, 0, 180 },
  563. { 0, 0, 0, 0, 270 },
  564. { 0, 0, pure },
  565. });
  566. runTests("falsepositives-2", "None", 25, {
  567. { 0, 0, 0, 0, 0 },
  568. { 0, 0, 0, 0, 90 },
  569. { 0, 0, 0, 0, 180 },
  570. { 0, 0, 0, 0, 270 },
  571. { 0, 0, pure },
  572. });
  573. // clang-format on
  574. int totalTime = timeSince(startTime);
  575. int decodeTime = totalTime - totalImageLoadTime;
  576. fmt::print("load time: {} ms.\n", totalImageLoadTime);
  577. fmt::print("decode time: {} ms.\n", decodeTime);
  578. fmt::print("total time: {} ms.\n", totalTime);
  579. if (failed)
  580. fmt::print("WARNING: {} tests failed.\n", failed);
  581. if (extra)
  582. fmt::print("INFO: {} tests succeeded unexpectedly.\n", extra);
  583. return failed;
  584. }
  585. catch (const std::exception& e) {
  586. fmt::print("{}\n", e.what());
  587. }
  588. catch (...) {
  589. fmt::print("Internal error\n");
  590. }
  591. return -1;
  592. }
  593. } // ZXing::Test