X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=core%2Ftest%2Fsrc%2Fcom%2Fgoogle%2Fzxing%2Fcommon%2FAbstractBlackBoxTestCase.java;h=5048343968b60e2da499b57ca977932299835d42;hb=9ceee6853d82d86e881fc3d6d7928ddd47df4cd0;hp=396f5ed9787cc89cd8c5306b9db04ff4493c6b03;hpb=dcc226a09d2f2017b741366688a582a3b67bfadf;p=zxing.git diff --git a/core/test/src/com/google/zxing/common/AbstractBlackBoxTestCase.java b/core/test/src/com/google/zxing/common/AbstractBlackBoxTestCase.java index 396f5ed9..50483439 100644 --- a/core/test/src/com/google/zxing/common/AbstractBlackBoxTestCase.java +++ b/core/test/src/com/google/zxing/common/AbstractBlackBoxTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2008 Google Inc. + * Copyright 2008 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,12 +17,14 @@ package com.google.zxing.common; import com.google.zxing.BarcodeFormat; +import com.google.zxing.BinaryBitmap; import com.google.zxing.DecodeHintType; -import com.google.zxing.MonochromeBitmapSource; +import com.google.zxing.LuminanceSource; import com.google.zxing.Reader; import com.google.zxing.ReaderException; import com.google.zxing.Result; -import com.google.zxing.client.j2se.BufferedImageMonochromeBitmapSource; +import com.google.zxing.ResultMetadataType; +import com.google.zxing.client.j2se.BufferedImageLuminanceSource; import junit.framework.TestCase; import javax.imageio.ImageIO; @@ -35,16 +37,20 @@ import java.io.FileInputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStreamReader; +import java.nio.charset.Charset; +import java.util.ArrayList; import java.util.Hashtable; -import java.util.Vector; +import java.util.List; +import java.util.Map; +import java.util.Properties; /** - * @author srowen@google.com (Sean Owen) + * @author Sean Owen * @author dswitkin@google.com (Daniel Switkin) */ public abstract class AbstractBlackBoxTestCase extends TestCase { - private static final Hashtable TRY_HARDER_HINT; + protected static final Hashtable TRY_HARDER_HINT; static { TRY_HARDER_HINT = new Hashtable(); TRY_HARDER_HINT.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); @@ -58,16 +64,51 @@ public abstract class AbstractBlackBoxTestCase extends TestCase { } }; + public static class SummaryResults { + private int totalFound; + private int totalMustPass; + private int totalTests; + + public SummaryResults() { + totalFound = 0; + totalMustPass = 0; + totalTests = 0; + } + + public SummaryResults(int found, int mustPass, int total) { + totalFound = found; + totalMustPass = mustPass; + totalTests = total; + } + + public void add(SummaryResults other) { + totalFound += other.totalFound; + totalMustPass += other.totalMustPass; + totalTests += other.totalTests; + } + + public String toString() { + return "\nSUMMARY RESULTS:\n Decoded " + totalFound + " images out of " + totalTests + + " (" + (totalFound * 100 / totalTests) + "%, " + totalMustPass + " required)"; + } + } + private static class TestResult { private final int mustPassCount; + private final int tryHarderCount; private final float rotation; - TestResult(int mustPassCount, float rotation) { + + TestResult(int mustPassCount, int tryHarderCount, float rotation) { this.mustPassCount = mustPassCount; + this.tryHarderCount = tryHarderCount; this.rotation = rotation; } public int getMustPassCount() { return mustPassCount; } + public int getTryHarderCount() { + return tryHarderCount; + } public float getRotation() { return rotation; } @@ -76,103 +117,183 @@ public abstract class AbstractBlackBoxTestCase extends TestCase { private final File testBase; private final Reader barcodeReader; private final BarcodeFormat expectedFormat; - private Vector testResults; + private final List testResults; - protected AbstractBlackBoxTestCase(File testBase, + protected AbstractBlackBoxTestCase(String testBasePathSuffix, Reader barcodeReader, BarcodeFormat expectedFormat) { + // A little workaround to prevent aggravation in my IDE + File testBase = new File(testBasePathSuffix); + if (!testBase.exists()) { + // try starting with 'core' since the test base is often given as the project root + testBase = new File("core/" + testBasePathSuffix); + } this.testBase = testBase; this.barcodeReader = barcodeReader; this.expectedFormat = expectedFormat; - testResults = new Vector(); + testResults = new ArrayList(); } /** * Adds a new test for the current directory of images. * * @param mustPassCount The number of images which must decode for the test to pass. + * @param tryHarderCount The number of images which must pass using the try harder flag. * @param rotation The rotation in degrees clockwise to use for this test. */ - protected void addTest(int mustPassCount, float rotation) { - testResults.add(new TestResult(mustPassCount, rotation)); + protected void addTest(int mustPassCount, int tryHarderCount, float rotation) { + testResults.add(new TestResult(mustPassCount, tryHarderCount, rotation)); + } + + protected File[] getImageFiles() { + assertTrue("Please run from the 'core' directory", testBase.exists()); + return testBase.listFiles(IMAGE_NAME_FILTER); } + protected Reader getReader() { + return barcodeReader; + } + + protected Hashtable getHints() { + return null; + } + + // This workaround is used because AbstractNegativeBlackBoxTestCase overrides this method but does + // not return SummaryResults. public void testBlackBox() throws IOException { + testBlackBoxCountingResults(true); + } + + public SummaryResults testBlackBoxCountingResults(boolean assertOnFailure) throws IOException { assertFalse(testResults.isEmpty()); - assertTrue("Please run from the 'core' directory", testBase.exists()); - File[] imageFiles = testBase.listFiles(IMAGE_NAME_FILTER); - int[] passedCounts = new int[testResults.size()]; + File[] imageFiles = getImageFiles(); + int testCount = testResults.size(); + int[] passedCounts = new int[testCount]; + int[] tryHarderCounts = new int[testCount]; for (File testImage : imageFiles) { System.out.println("Starting " + testImage.getAbsolutePath()); BufferedImage image = ImageIO.read(testImage); String testImageFileName = testImage.getName(); - File expectedTextFile = new File(testBase, - testImageFileName.substring(0, testImageFileName.indexOf('.')) + ".txt"); + String fileBaseName = testImageFileName.substring(0, testImageFileName.indexOf('.')); + File expectedTextFile = new File(testBase, fileBaseName + ".txt"); String expectedText = readFileAsString(expectedTextFile); - for (int x = 0; x < testResults.size(); x++) { - if (doTestOneImage(image, testResults.get(x).getRotation(), expectedText)) { + File expectedMetadataFile = new File(testBase, fileBaseName + ".metadata.txt"); + Properties expectedMetadata = new Properties(); + if (expectedMetadataFile.exists()) { + expectedMetadata.load(new FileInputStream(expectedMetadataFile)); + } + + for (int x = 0; x < testCount; x++) { + float rotation = testResults.get(x).getRotation(); + BufferedImage rotatedImage = rotateImage(image, rotation); + LuminanceSource source = new BufferedImageLuminanceSource(rotatedImage); + BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); + if (decode(bitmap, rotation, expectedText, expectedMetadata, false)) { passedCounts[x]++; } + if (decode(bitmap, rotation, expectedText, expectedMetadata, true)) { + tryHarderCounts[x]++; + } } } - for (int x = 0; x < testResults.size(); x++) { - System.out.println("Rotation " + testResults.get(x).getRotation() + " degrees: " + passedCounts[x] + - " of " + imageFiles.length + " images passed (" + testResults.get(x).getMustPassCount() + + // Print the results of all tests first + int totalFound = 0; + int totalMustPass = 0; + for (int x = 0; x < testCount; x++) { + System.out.println("Rotation " + testResults.get(x).getRotation() + " degrees:"); + System.out.println(" " + passedCounts[x] + " of " + imageFiles.length + " images passed (" + + testResults.get(x).getMustPassCount() + " required)"); + System.out.println(" " + tryHarderCounts[x] + " of " + imageFiles.length + + " images passed with try harder (" + testResults.get(x).getTryHarderCount() + " required)"); - assertTrue("Rotation " + testResults.get(x).getRotation() + " degrees: Too many images failed", - passedCounts[x] >= testResults.get(x).getMustPassCount()); + totalFound += passedCounts[x]; + totalFound += tryHarderCounts[x]; + totalMustPass += testResults.get(x).getMustPassCount(); + totalMustPass += testResults.get(x).getTryHarderCount(); + } + + int totalTests = imageFiles.length * testCount * 2; + System.out.println("TOTALS:\n Decoded " + totalFound + " images out of " + totalTests + + " (" + (totalFound * 100 / totalTests) + "%, " + totalMustPass + " required)"); + if (totalFound > totalMustPass) { + System.out.println(" *** Test too lax by " + (totalFound - totalMustPass) + " images"); + } else if (totalFound < totalMustPass) { + System.out.println(" *** Test failed by " + (totalMustPass - totalFound) + " images"); + } + + // Then run through again and assert if any failed + if (assertOnFailure) { + for (int x = 0; x < testCount; x++) { + assertTrue("Rotation " + testResults.get(x).getRotation() + + " degrees: Too many images failed", + passedCounts[x] >= testResults.get(x).getMustPassCount()); + assertTrue("Try harder, Rotation " + testResults.get(x).getRotation() + + " degrees: Too many images failed", + tryHarderCounts[x] >= testResults.get(x).getTryHarderCount()); + } } + return new SummaryResults(totalFound, totalMustPass, totalTests); } - private boolean doTestOneImage(BufferedImage image, float rotationInDegrees, String expectedText) { - BufferedImage rotatedImage = rotateImage(image, rotationInDegrees); - MonochromeBitmapSource source = new BufferedImageMonochromeBitmapSource(rotatedImage); + private boolean decode(BinaryBitmap source, + float rotation, + String expectedText, + Properties expectedMetadata, + boolean tryHarder) { Result result; + String suffix = " (" + (tryHarder ? "try harder, " : "") + "rotation: " + rotation + ')'; + try { - result = barcodeReader.decode(source); + Hashtable hints = getHints(); + if (tryHarder) { + if (hints == null) { + hints = TRY_HARDER_HINT; + } else { + hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); + } + } + result = barcodeReader.decode(source, hints); } catch (ReaderException re) { - System.out.println(re); + System.out.println(re + suffix); return false; } if (!expectedFormat.equals(result.getBarcodeFormat())) { System.out.println("Format mismatch: expected '" + expectedFormat + "' but got '" + - result.getBarcodeFormat() + "' (rotation: " + rotationInDegrees + ')'); + result.getBarcodeFormat() + '\'' + suffix); return false; } String resultText = result.getText(); if (!expectedText.equals(resultText)) { System.out.println("Mismatch: expected '" + expectedText + "' but got '" + resultText + - "' (rotation: " + rotationInDegrees + ')'); + '\'' + suffix); return false; } - // Try "try harder" mode - try { - result = barcodeReader.decode(source, TRY_HARDER_HINT); - } catch (ReaderException re) { - fail("Normal mode succeeded but \"try harder\" failed"); - return false; - } - if (!expectedFormat.equals(result.getBarcodeFormat())) { - System.out.println("Try Harder Format mismatch: expected '" + expectedFormat + "' but got '" + - result.getBarcodeFormat() + "' (rotation: " + rotationInDegrees + ')'); - } else if (!expectedText.equals(resultText)) { - System.out.println("Try Harder Mismatch: expected '" + expectedText + "' but got '" + - resultText + "' (rotation: " + rotationInDegrees + ')'); + Hashtable resultMetadata = result.getResultMetadata(); + for (Map.Entry metadatum : expectedMetadata.entrySet()) { + ResultMetadataType key = ResultMetadataType.valueOf(metadatum.getKey().toString()); + Object expectedValue = metadatum.getValue(); + Object actualValue = resultMetadata == null ? null : resultMetadata.get(key); + if (!expectedValue.equals(actualValue)) { + System.out.println("Metadata mismatch: for key '" + key + "' expected '" + expectedValue + + "' but got '" + actualValue + '\''); + return false; + } } + return true; } private static String readFileAsString(File file) throws IOException { StringBuilder result = new StringBuilder((int) file.length()); - InputStreamReader reader = new InputStreamReader(new FileInputStream(file), "UTF-8"); + InputStreamReader reader = new InputStreamReader(new FileInputStream(file), Charset.forName("UTF8")); try { char[] buffer = new char[256]; int charsRead; @@ -185,15 +306,15 @@ public abstract class AbstractBlackBoxTestCase extends TestCase { return result.toString(); } - private static BufferedImage rotateImage(BufferedImage original, float degrees) { + protected static BufferedImage rotateImage(BufferedImage original, float degrees) { if (degrees == 0.0f) { return original; } else { AffineTransform at = new AffineTransform(); - at.rotate(Math.toRadians(degrees), original.getWidth() / 2.0f, original.getHeight() / 2.0f); + at.rotate(Math.toRadians(degrees), original.getWidth() / 2.0, original.getHeight() / 2.0); BufferedImageOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_BICUBIC); return op.filter(original, null); } } -} \ No newline at end of file +}