| Index: chrome/test/android/javatests/src/org/chromium/chrome/test/util/RenderUtils.java
 | 
| diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/RenderUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/RenderUtils.java
 | 
| deleted file mode 100644
 | 
| index 221f03c75f5d6ceb79e3facc9fb48dae059bf9a0..0000000000000000000000000000000000000000
 | 
| --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/RenderUtils.java
 | 
| +++ /dev/null
 | 
| @@ -1,361 +0,0 @@
 | 
| -// Copyright 2016 The Chromium Authors. All rights reserved.
 | 
| -// Use of this source code is governed by a BSD-style license that can be
 | 
| -// found in the LICENSE file.
 | 
| -
 | 
| -package org.chromium.chrome.test.util;
 | 
| -
 | 
| -import android.app.Activity;
 | 
| -import android.graphics.Bitmap;
 | 
| -import android.graphics.BitmapFactory;
 | 
| -import android.graphics.Color;
 | 
| -import android.graphics.Point;
 | 
| -import android.os.Build;
 | 
| -import android.view.View;
 | 
| -
 | 
| -import org.chromium.base.CommandLine;
 | 
| -import org.chromium.base.Log;
 | 
| -import org.chromium.base.ThreadUtils;
 | 
| -import org.chromium.base.test.util.UrlUtils;
 | 
| -import org.chromium.ui.UiUtils;
 | 
| -
 | 
| -import java.io.File;
 | 
| -import java.io.FileOutputStream;
 | 
| -import java.io.IOException;
 | 
| -import java.util.concurrent.Callable;
 | 
| -
 | 
| -/**
 | 
| - * A collection of methods to aid in creating Render Tests - tests that render UI components.
 | 
| - */
 | 
| -public class RenderUtils {
 | 
| -    private static final String TAG = "RenderUtils";
 | 
| -
 | 
| -    private static final String DIFF_FOLDER_RELATIVE = "/diffs";
 | 
| -
 | 
| -    private static final String FAILURE_FOLDER_RELATIVE = "/failures";
 | 
| -
 | 
| -    private static final String GOLDEN_FOLDER_RELATIVE = "/goldens";
 | 
| -
 | 
| -    /**
 | 
| -     * This is a list of devices that we maintain golden images for. If render tests are being run
 | 
| -     * on a device in this list, golden images should exist and their absence is a test failure.
 | 
| -     * The absence of golden images for devices not on this list doesn't necessarily mean that
 | 
| -     * something is wrong - the tests are just being run on a device that we don't have goldens for.
 | 
| -     */
 | 
| -    private static final String[] RENDER_TEST_DEVICES = { "Nexus 5X", "Nexus 9" };
 | 
| -
 | 
| -    /**
 | 
| -     * Before we know how flaky screenshot tests are going to be we don't want them to cause a
 | 
| -     * full test failure every time they fail. If the tests prove their worth, this will be set to
 | 
| -     * false/removed.
 | 
| -     */
 | 
| -    private static final boolean REPORT_ONLY_DO_NOT_FAIL = true;
 | 
| -
 | 
| -    /**
 | 
| -     * How many pixels can be different in an image before counting the images as different.
 | 
| -     */
 | 
| -    private static final int PIXEL_DIFF_THRESHOLD = 0;
 | 
| -
 | 
| -    private enum ComparisonResult { MATCH, MISMATCH, GOLDEN_NOT_FOUND }
 | 
| -
 | 
| -    /**
 | 
| -     * An exception thrown when a View is checked and it is not equal to the golden.
 | 
| -     */
 | 
| -    public static class ImageMismatchException extends RuntimeException {
 | 
| -        public ImageMismatchException(String message) {
 | 
| -            super(message);
 | 
| -        }
 | 
| -    }
 | 
| -
 | 
| -    /**
 | 
| -     * An exception thrown when a View is checked and the golden could not be found.
 | 
| -     */
 | 
| -    public static class GoldenNotFoundException extends RuntimeException {
 | 
| -        public GoldenNotFoundException(String message) {
 | 
| -            super(message);
 | 
| -        }
 | 
| -    }
 | 
| -
 | 
| -    /**
 | 
| -     * A class to contain all the TestClass level information associated with rendering views.
 | 
| -     */
 | 
| -    public static class ViewRenderer {
 | 
| -        private final Activity mActivity;
 | 
| -        private final String mGoldenFolder;
 | 
| -        private final String mTestClass;
 | 
| -        private String mOutputDirectory;
 | 
| -
 | 
| -        public ViewRenderer(Activity activity, String goldenFolder, String testClass) {
 | 
| -            mActivity = activity;
 | 
| -            mGoldenFolder = goldenFolder;
 | 
| -            mTestClass = testClass;
 | 
| -
 | 
| -            // Render test will output results to subdirectory of |goldenFolder| unless
 | 
| -            // --render-test-output-dir is passed.
 | 
| -            mOutputDirectory = CommandLine.getInstance().getSwitchValue("render-test-output-dir");
 | 
| -            if (mOutputDirectory == null) {
 | 
| -                mOutputDirectory = UrlUtils.getIsolatedTestFilePath(mGoldenFolder);
 | 
| -            }
 | 
| -        }
 | 
| -
 | 
| -        /**
 | 
| -         * Renders the |view| and compares it to a golden view with the |id|. If the rendered image
 | 
| -         * does not match the golden, {@link ImageMismatchException} is thrown. If the golden image
 | 
| -         * is not found {@link GoldenNotFoundException} will be thrown if the test is running on a
 | 
| -         * device where goldens are expected to be present.
 | 
| -         *
 | 
| -         * The 'regenerate-goldens' flag provided to test_runner.py changes the behaviour of this
 | 
| -         * method. In this case the if the image does not match the golden or the golden is not
 | 
| -         * found the rendered image is saved to the device and nothing is thrown.
 | 
| -         *
 | 
| -         * @throws IOException if the rendered image could not be saved to the device.
 | 
| -         */
 | 
| -        public void renderAndCompare(final View view, String id) throws IOException {
 | 
| -            // Compare the image against the Golden.
 | 
| -            Bitmap testBitmap =
 | 
| -                    ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Bitmap>() {
 | 
| -                        @Override
 | 
| -                        public Bitmap call() throws Exception {
 | 
| -                            return UiUtils.generateScaledScreenshot(
 | 
| -                                    view, 0, Bitmap.Config.ARGB_8888);
 | 
| -                        }
 | 
| -                    });
 | 
| -
 | 
| -            String imagename = imageName(mActivity, mTestClass, id);
 | 
| -
 | 
| -            File goldenFile =
 | 
| -                    createPath(UrlUtils.getIsolatedTestFilePath(mGoldenFolder), imagename);
 | 
| -            BitmapFactory.Options options = new BitmapFactory.Options();
 | 
| -            options.inPreferredConfig = testBitmap.getConfig();
 | 
| -            Bitmap goldenBitmap = BitmapFactory.decodeFile(goldenFile.getAbsolutePath(), options);
 | 
| -
 | 
| -            Bitmap diffBitmap = null;
 | 
| -            ComparisonResult result = null;
 | 
| -
 | 
| -            if (goldenBitmap == null) {
 | 
| -                result = ComparisonResult.GOLDEN_NOT_FOUND;
 | 
| -            } else {
 | 
| -                diffBitmap = Bitmap.createBitmap(
 | 
| -                        Math.max(testBitmap.getWidth(), goldenBitmap.getWidth()),
 | 
| -                        Math.max(testBitmap.getHeight(), goldenBitmap.getHeight()),
 | 
| -                        testBitmap.getConfig());
 | 
| -                result = compareBitmapToGolden(testBitmap, goldenBitmap, diffBitmap);
 | 
| -            }
 | 
| -
 | 
| -            if (result == ComparisonResult.MATCH) {
 | 
| -                if (isGenerateMode()) {
 | 
| -                    Log.i(TAG, "%s - generated image equal to golden.", id);
 | 
| -                }
 | 
| -                return;
 | 
| -            }
 | 
| -
 | 
| -            File failureOutputFile =
 | 
| -                    createPath(mOutputDirectory + FAILURE_FOLDER_RELATIVE, imagename);
 | 
| -            saveBitmap(testBitmap, failureOutputFile);
 | 
| -
 | 
| -            if (result != ComparisonResult.GOLDEN_NOT_FOUND) {
 | 
| -                File goldenOutputFile =
 | 
| -                        createPath(mOutputDirectory + GOLDEN_FOLDER_RELATIVE, imagename);
 | 
| -                saveBitmap(goldenBitmap, goldenOutputFile);
 | 
| -
 | 
| -                File diffOutputFile =
 | 
| -                        createPath(mOutputDirectory + DIFF_FOLDER_RELATIVE, imagename);
 | 
| -                saveBitmap(diffBitmap, diffOutputFile);
 | 
| -            }
 | 
| -
 | 
| -            if (isGenerateMode()) {
 | 
| -                Log.i(TAG, "%s - generated image saved to %s.", id,
 | 
| -                        failureOutputFile.getAbsolutePath());
 | 
| -                return;
 | 
| -            }
 | 
| -
 | 
| -            if (REPORT_ONLY_DO_NOT_FAIL) {
 | 
| -                Log.d(TAG, "Image comparison for %s: %s", id, result.name());
 | 
| -                return;
 | 
| -            }
 | 
| -
 | 
| -            if (result == ComparisonResult.GOLDEN_NOT_FOUND) {
 | 
| -                if (onRenderTestDevice()) {
 | 
| -                    throw new GoldenNotFoundException(
 | 
| -                            String.format("Could not find golden image for %s.",
 | 
| -                                    goldenFile.getAbsolutePath()));
 | 
| -                } else {
 | 
| -                    Log.i(TAG, "Could not find golden for %s, but not on a Render Test Device so "
 | 
| -                            + "continue.", goldenFile.getAbsolutePath());
 | 
| -                }
 | 
| -            } else if (result == ComparisonResult.MISMATCH) {
 | 
| -                throw new ImageMismatchException(
 | 
| -                        String.format("Image comparison failed on %s. Failure image saved to %s.",
 | 
| -                                id, failureOutputFile.getAbsolutePath()));
 | 
| -            }
 | 
| -        }
 | 
| -    }
 | 
| -
 | 
| -    /**
 | 
| -     * Whether the tests are being run to generate golden files or to check for equality.
 | 
| -     */
 | 
| -    private static boolean isGenerateMode() {
 | 
| -        return CommandLine.getInstance().hasSwitch("regenerate-goldens");
 | 
| -    }
 | 
| -
 | 
| -    /**
 | 
| -     * Returns whether goldens should exist for the current device.
 | 
| -     */
 | 
| -    private static boolean onRenderTestDevice() {
 | 
| -        for (String model : RENDER_TEST_DEVICES) {
 | 
| -            if (model.equals(Build.MODEL)) return true;
 | 
| -        }
 | 
| -        return false;
 | 
| -    }
 | 
| -
 | 
| -    /**
 | 
| -     * Creates an image name combining the image description with details about the device
 | 
| -     * (eg model, current orientation).
 | 
| -     *
 | 
| -     * This function must be kept in sync with |RE_RENDER_IMAGE_NAME| from
 | 
| -     * src/build/android/pylib/local/device/local_device_instrumentation_test_run.py.
 | 
| -     */
 | 
| -    private static String imageName(Activity activity, String testClass, String desc) {
 | 
| -        Point outSize = new Point();
 | 
| -        activity.getWindowManager().getDefaultDisplay().getSize(outSize);
 | 
| -        String orientation = outSize.x > outSize.y ? "land" : "port";
 | 
| -        return String.format("%s.%s.%s.%s.png",
 | 
| -                testClass, desc, Build.MODEL.replace(' ', '_'), orientation);
 | 
| -    }
 | 
| -
 | 
| -    /**
 | 
| -     * Saves a the given |bitmap| to the |file|.
 | 
| -     */
 | 
| -    private static void saveBitmap(Bitmap bitmap, File file) throws IOException {
 | 
| -        FileOutputStream out = new FileOutputStream(file);
 | 
| -        try {
 | 
| -            bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
 | 
| -        } finally {
 | 
| -            out.close();
 | 
| -        }
 | 
| -    }
 | 
| -
 | 
| -    /**
 | 
| -     * Convenience method to create a File pointing to |filename| in |folder|.
 | 
| -     * @throws IOException
 | 
| -     */
 | 
| -    private static File createPath(String folder, String filename) throws IOException {
 | 
| -        File path = new File(folder);
 | 
| -        if (!path.exists()) {
 | 
| -            if (!path.mkdirs()) {
 | 
| -                throw new IOException("Could not create " + path.getAbsolutePath());
 | 
| -            }
 | 
| -        }
 | 
| -        return new File(path + "/" + filename);
 | 
| -    }
 | 
| -
 | 
| -    /**
 | 
| -     * Returns whether the given |bitmap| is equal to the one stored in |file|.
 | 
| -     */
 | 
| -    private static ComparisonResult compareBitmapToGolden(Bitmap test, Bitmap golden, Bitmap diff) {
 | 
| -        int maxWidth = Math.max(test.getWidth(), golden.getWidth());
 | 
| -        int maxHeight = Math.max(test.getHeight(), golden.getHeight());
 | 
| -        int minWidth = Math.min(test.getWidth(), golden.getWidth());
 | 
| -        int minHeight = Math.min(test.getHeight(), golden.getHeight());
 | 
| -
 | 
| -        int diffPixelsCount = comparePixels(test, golden, diff, 0, 0, minWidth, 0, minHeight)
 | 
| -                + compareSizes(diff, minWidth, maxWidth, minHeight, maxHeight);
 | 
| -
 | 
| -        if (diffPixelsCount > PIXEL_DIFF_THRESHOLD) {
 | 
| -            return ComparisonResult.MISMATCH;
 | 
| -        }
 | 
| -        return ComparisonResult.MATCH;
 | 
| -    }
 | 
| -
 | 
| -    /**
 | 
| -     * Compares two bitmaps pixel-wise.
 | 
| -     *
 | 
| -     * @param testImage Bitmap of test image.
 | 
| -     *
 | 
| -     * @param goldenImage Bitmap of golden image.
 | 
| -     *
 | 
| -     * @param diffImage This is an output argument. Function will set pixels in the |diffImage| to
 | 
| -     * either transparent or red depending on whether that pixel differed in the golden and test
 | 
| -     * bitmaps. diffImage should have its width and height be the max width and height of the
 | 
| -     * golden and test bitmaps.
 | 
| -     *
 | 
| -     * @param diffThreshold Threshold for when to consider two color values as different. These
 | 
| -     * values are 8 bit (256) so this threshold value should be in range 0-256.
 | 
| -     *
 | 
| -     * @param startWidth Start x-coord to start diffing the Bitmaps.
 | 
| -     *
 | 
| -     * @param endWidth End x-coord to start diffing the Bitmaps.
 | 
| -     *
 | 
| -     * @param startHeight Start y-coord to start diffing the Bitmaps.
 | 
| -     *
 | 
| -     * @param endHeight End x-coord to start diffing the Bitmaps.
 | 
| -     *
 | 
| -     * @return Returns number of pixels that differ between |goldenImage| and |testImage|
 | 
| -     */
 | 
| -    private static int comparePixels(Bitmap testImage, Bitmap goldenImage, Bitmap diffImage,
 | 
| -            int diffThreshold, int startWidth, int endWidth, int startHeight, int endHeight) {
 | 
| -        int diffPixels = 0;
 | 
| -
 | 
| -        for (int x = startWidth; x < endWidth; x++) {
 | 
| -            for (int y = startHeight; y < endHeight; y++) {
 | 
| -                int goldenImageColor = goldenImage.getPixel(x, y);
 | 
| -                int testImageColor = testImage.getPixel(x, y);
 | 
| -
 | 
| -                int redDiff = Math.abs(Color.red(goldenImageColor) - Color.red(testImageColor));
 | 
| -                int blueDiff =
 | 
| -                        Math.abs(Color.green(goldenImageColor) - Color.green(testImageColor));
 | 
| -                int greenDiff = Math.abs(Color.blue(goldenImageColor) - Color.blue(testImageColor));
 | 
| -                int alphaDiff =
 | 
| -                        Math.abs(Color.alpha(goldenImageColor) - Color.alpha(testImageColor));
 | 
| -
 | 
| -                if (redDiff > diffThreshold || blueDiff > diffThreshold || greenDiff > diffThreshold
 | 
| -                        || alphaDiff > diffThreshold) {
 | 
| -                    diffPixels++;
 | 
| -                    diffImage.setPixel(x, y, Color.RED);
 | 
| -                } else {
 | 
| -                    diffImage.setPixel(x, y, Color.TRANSPARENT);
 | 
| -                }
 | 
| -            }
 | 
| -        }
 | 
| -        return diffPixels;
 | 
| -    }
 | 
| -
 | 
| -    /**
 | 
| -     * Compares two bitmaps size.
 | 
| -     *
 | 
| -     * @param diffImage This is an output argument. Function will set pixels in the |diffImage| to
 | 
| -     * either transparent or red depending on whether that pixel coordinate occurs in the
 | 
| -     * dimensions of the golden and not the test bitmap or vice-versa.
 | 
| -     *
 | 
| -     * @param minWidth Min width of golden and test bitmaps.
 | 
| -     *
 | 
| -     * @param maxWidth Max width of golden and test bitmaps.
 | 
| -     *
 | 
| -     * @param minHeight Min height of golden and test bitmaps.
 | 
| -     *
 | 
| -     * @param maxHeight Max height of golden and test bitmaps.
 | 
| -     *
 | 
| -     * @return Returns number of pixels that differ between |goldenImage| and |testImage| due to
 | 
| -     * their size.
 | 
| -     */
 | 
| -    private static int compareSizes(
 | 
| -            Bitmap diffImage, int minWidth, int maxWidth, int minHeight, int maxHeight) {
 | 
| -        int diffPixels = 0;
 | 
| -        if (maxWidth > minWidth) {
 | 
| -            for (int x = minWidth; x < maxWidth; x++) {
 | 
| -                for (int y = 0; y < maxHeight; y++) {
 | 
| -                    diffImage.setPixel(x, y, Color.RED);
 | 
| -                }
 | 
| -            }
 | 
| -            diffPixels += (maxWidth - minWidth) * maxHeight;
 | 
| -        }
 | 
| -        if (maxHeight > minHeight) {
 | 
| -            for (int x = 0; x < maxWidth; x++) {
 | 
| -                for (int y = minHeight; y < maxHeight; y++) {
 | 
| -                    diffImage.setPixel(x, y, Color.RED);
 | 
| -                }
 | 
| -            }
 | 
| -            diffPixels += (maxHeight - minHeight) * minWidth;
 | 
| -        }
 | 
| -        return diffPixels;
 | 
| -    }
 | 
| -}
 | 
| 
 |