Allow API version-specific view tests; use relative image distance

pull/77/merge
Alinson S. Xavier 10 years ago
parent 285f79ed95
commit 53e737c169

@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
P=/sdcard/Android/data/org.isoron.uhabits/cache/Failed/ P=/sdcard/Android/data/org.isoron.uhabits/cache/Failed/
adb pull $P adb pull $P Failed/
adb shell rm -rf $P adb shell rm -r $P

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

@ -53,7 +53,7 @@ public class CheckmarkViewTest extends ViewTest
@Test @Test
public void render_checked() throws IOException public void render_checked() throws IOException
{ {
assertRenders(view, "Views/CheckmarkView/checked.png"); assertRenders(view, "CheckmarkView/checked.png");
} }
@Test @Test
@ -62,7 +62,7 @@ public class CheckmarkViewTest extends ViewTest
habit.repetitions.toggle(DateHelper.getStartOfToday()); habit.repetitions.toggle(DateHelper.getStartOfToday());
view.refreshData(); view.refreshData();
assertRenders(view, "Views/CheckmarkView/unchecked.png"); assertRenders(view, "CheckmarkView/unchecked.png");
} }
@Test @Test
@ -75,13 +75,13 @@ public class CheckmarkViewTest extends ViewTest
habit.repetitions.toggle(today - 2 * day); habit.repetitions.toggle(today - 2 * day);
view.refreshData(); view.refreshData();
assertRenders(view, "Views/CheckmarkView/implicitly_checked.png"); assertRenders(view, "CheckmarkView/implicitly_checked.png");
} }
@Test @Test
public void render_largeSize() throws IOException public void render_largeSize() throws IOException
{ {
measureView(dpToPixels(300), dpToPixels(300), view); measureView(dpToPixels(300), dpToPixels(300), view);
assertRenders(view, "Views/CheckmarkView/large_size.png"); assertRenders(view, "CheckmarkView/large_size.png");
} }
} }

@ -52,7 +52,7 @@ public class RingViewTest extends ViewTest
public void render_base() throws IOException public void render_base() throws IOException
{ {
measureView(dpToPixels(100), dpToPixels(100), view); measureView(dpToPixels(100), dpToPixels(100), view);
assertRenders(view, "Views/RingView/render.png"); assertRenders(view, "RingView/render.png");
} }
@Test @Test
@ -61,7 +61,7 @@ public class RingViewTest extends ViewTest
view.setLabel("The quick brown fox jumps over the lazy fox"); view.setLabel("The quick brown fox jumps over the lazy fox");
measureView(dpToPixels(100), dpToPixels(100), view); measureView(dpToPixels(100), dpToPixels(100), view);
assertRenders(view, "Views/RingView/renderLongLabel.png"); assertRenders(view, "RingView/renderLongLabel.png");
} }
@Test @Test
@ -73,6 +73,6 @@ public class RingViewTest extends ViewTest
view.setColor(ColorHelper.palette[5]); view.setColor(ColorHelper.palette[5]);
measureView(dpToPixels(200), dpToPixels(200), view); measureView(dpToPixels(200), dpToPixels(200), view);
assertRenders(view, "Views/RingView/renderDifferentParams.png"); assertRenders(view, "RingView/renderDifferentParams.png");
} }
} }

@ -37,7 +37,8 @@ import static junit.framework.Assert.fail;
public class ViewTest public class ViewTest
{ {
protected static final int SIMILARITY_CUTOFF = 6000; protected static final double SIMILARITY_CUTOFF = 0.02;
public static final int HISTOGRAM_BIN_SIZE = 8;
protected Context testContext; protected Context testContext;
protected Context targetContext; protected Context targetContext;
@ -61,37 +62,39 @@ public class ViewTest
protected void assertRenders(View view, String expectedImagePath) throws IOException protected void assertRenders(View view, String expectedImagePath) throws IOException
{ {
StringBuilder errorMessage = new StringBuilder(); StringBuilder errorMessage = new StringBuilder();
expectedImagePath = getVersionedViewAssetPath(expectedImagePath);
view.setDrawingCacheEnabled(true); view.setDrawingCacheEnabled(true);
view.buildDrawingCache(); view.buildDrawingCache();
Bitmap actual = view.getDrawingCache();
Bitmap expected = getBitmapFromAssets(expectedImagePath);
Bitmap actualBitmap = view.getDrawingCache(); int width = actual.getWidth();
Bitmap expectedBitmap = getBitmapFromAssets(expectedImagePath); int height = actual.getHeight();
Bitmap scaledExpectedBitmap = Bitmap.createScaledBitmap(expectedBitmap, Bitmap scaledExpected = Bitmap.createScaledBitmap(expected, width, height, true);
actualBitmap.getWidth(), actualBitmap.getHeight(), false);
double distance;
boolean similarEnough = true; boolean similarEnough = true;
long distance;
if ((distance = compareHistograms(getHistogram(actualBitmap), getHistogram( if ((distance = compareHistograms(getHistogram(actual), getHistogram(scaledExpected))) > SIMILARITY_CUTOFF)
scaledExpectedBitmap))) > SIMILARITY_CUTOFF)
{ {
similarEnough = false; similarEnough = false;
errorMessage.append(String.format( errorMessage.append(String.format(
"Rendered image has wrong histogram (distance=%d). ", "Rendered image has wrong histogram (distance=%f). ",
distance)); distance));
} }
if(!similarEnough) if(!similarEnough)
{ {
String path = saveBitmap(expectedImagePath, actualBitmap); saveBitmap(expectedImagePath, ".scaledExpected", scaledExpected);
String path = saveBitmap(expectedImagePath, ".actual", actual);
errorMessage.append(String.format("Actual rendered image " + "saved to %s", path)); errorMessage.append(String.format("Actual rendered image " + "saved to %s", path));
fail(errorMessage.toString()); fail(errorMessage.toString());
} }
actualBitmap.recycle(); actual.recycle();
expectedBitmap.recycle(); expected.recycle();
scaledExpectedBitmap.recycle(); scaledExpected.recycle();
} }
private Bitmap getBitmapFromAssets(String path) throws IOException private Bitmap getBitmapFromAssets(String path) throws IOException
@ -100,11 +103,35 @@ public class ViewTest
return BitmapFactory.decodeStream(stream); return BitmapFactory.decodeStream(stream);
} }
private String saveBitmap(String filename, Bitmap bitmap) private String getVersionedViewAssetPath(String path)
{
String result = null;
if (android.os.Build.VERSION.SDK_INT >= 21)
{
try
{
String vpath = "views-v21/" + path;
testContext.getAssets().open(vpath);
result = vpath;
}
catch (IOException e)
{
// ignored
}
}
if(result == null)
result = "views/" + path;
return result;
}
private String saveBitmap(String filename, String suffix, Bitmap bitmap)
throws IOException throws IOException
{ {
String absolutePath = String.format("%s/Failed/%s", targetContext.getExternalCacheDir(), String absolutePath = String.format("%s/Failed/%s", targetContext.getExternalCacheDir(),
filename.replaceAll("\\.png$", ".actual.png")); filename.replaceAll("\\.png$", suffix + ".png"));
new File(absolutePath).getParentFile().mkdirs(); new File(absolutePath).getParentFile().mkdirs();
FileOutputStream out = new FileOutputStream(absolutePath); FileOutputStream out = new FileOutputStream(absolutePath);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
@ -114,7 +141,7 @@ public class ViewTest
private int[][] getHistogram(Bitmap bitmap) private int[][] getHistogram(Bitmap bitmap)
{ {
int histogram[][] = new int[4][256]; int histogram[][] = new int[4][256 / HISTOGRAM_BIN_SIZE];
for(int x = 0; x < bitmap.getWidth(); x++) for(int x = 0; x < bitmap.getWidth(); x++)
{ {
@ -128,29 +155,35 @@ public class ViewTest
(color ) & 0xff //blue (color ) & 0xff //blue
}; };
histogram[0][argb[0]]++; histogram[0][argb[0] / HISTOGRAM_BIN_SIZE]++;
histogram[1][argb[1]]++; histogram[1][argb[1] / HISTOGRAM_BIN_SIZE]++;
histogram[2][argb[2]]++; histogram[2][argb[2] / HISTOGRAM_BIN_SIZE]++;
histogram[3][argb[3]]++; histogram[3][argb[3] / HISTOGRAM_BIN_SIZE]++;
} }
} }
return histogram; return histogram;
} }
private long compareHistograms(int[][] actualHistogram, int[][] expectedHistogram) private double compareHistograms(int[][] actualHistogram, int[][] expectedHistogram)
{ {
long distance = 0; long diff = 0;
long total = 0;
for(int i = 0; i < 255; i ++) for(int i = 0; i < 256 / HISTOGRAM_BIN_SIZE; i ++)
{ {
distance += Math.abs(actualHistogram[0][i] - expectedHistogram[0][i]); diff += Math.abs(actualHistogram[0][i] - expectedHistogram[0][i]);
distance += Math.abs(actualHistogram[1][i] - expectedHistogram[1][i]); diff += Math.abs(actualHistogram[1][i] - expectedHistogram[1][i]);
distance += Math.abs(actualHistogram[2][i] - expectedHistogram[2][i]); diff += Math.abs(actualHistogram[2][i] - expectedHistogram[2][i]);
distance += Math.abs(actualHistogram[3][i] - expectedHistogram[3][i]); diff += Math.abs(actualHistogram[3][i] - expectedHistogram[3][i]);
total += actualHistogram[0][i];
total += actualHistogram[1][i];
total += actualHistogram[2][i];
total += actualHistogram[3][i];
} }
return distance; return (double) diff / total / 2;
} }
protected int dpToPixels(int dp) protected int dpToPixels(int dp)

Loading…
Cancel
Save