Home | History | Annotate | Download | only in testinfrastructure
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package android.uirendering.cts.testinfrastructure;
     17 
     18 import android.annotation.Nullable;
     19 import android.graphics.Bitmap;
     20 import android.renderscript.Allocation;
     21 import android.renderscript.RenderScript;
     22 import android.test.ActivityInstrumentationTestCase2;
     23 import android.uirendering.cts.bitmapcomparers.BitmapComparer;
     24 import android.uirendering.cts.bitmapverifiers.BitmapVerifier;
     25 import android.uirendering.cts.differencevisualizers.DifferenceVisualizer;
     26 import android.uirendering.cts.differencevisualizers.PassFailVisualizer;
     27 import android.uirendering.cts.util.BitmapDumper;
     28 import android.util.Log;
     29 
     30 import java.util.ArrayList;
     31 import java.util.Arrays;
     32 import java.util.List;
     33 
     34 /**
     35  * This class contains the basis for the graphics hardware test classes. Contained within this class
     36  * are several methods that help with the execution of tests, and should be extended to gain the
     37  * functionality built in.
     38  */
     39 public abstract class ActivityTestBase extends
     40         ActivityInstrumentationTestCase2<DrawActivity> {
     41     public static final String TAG_NAME = "ActivityTestBase";
     42     public static final boolean DEBUG = false;
     43     public static final boolean USE_RS = false;
     44     public static final int TEST_WIDTH = 180;
     45     public static final int TEST_HEIGHT = 180; //The minimum height and width of a device
     46     public static final int MAX_SCREEN_SHOTS = 100;
     47 
     48     private int[] mHardwareArray = new int[TEST_HEIGHT * TEST_WIDTH];
     49     private int[] mSoftwareArray = new int[TEST_HEIGHT * TEST_WIDTH];
     50     private DifferenceVisualizer mDifferenceVisualizer;
     51     private Allocation mIdealAllocation;
     52     private Allocation mGivenAllocation;
     53     private RenderScript mRenderScript;
     54     private TestCaseBuilder mTestCaseBuilder;
     55 
     56     /**
     57      * The default constructor creates the package name and sets the DrawActivity as the class that
     58      * we would use.
     59      */
     60     public ActivityTestBase() {
     61         super(DrawActivity.class);
     62         mDifferenceVisualizer = new PassFailVisualizer();
     63 
     64         // Create a location for the files to be held, if it doesn't exist already
     65         BitmapDumper.createSubDirectory(this.getClass().getSimpleName());
     66 
     67         // If we have a test currently, let's remove the older files if they exist
     68         if (getName() != null) {
     69             BitmapDumper.deleteFileInClassFolder(this.getClass().getSimpleName(), getName());
     70         }
     71     }
     72 
     73     /**
     74      * This method is called before each test case and should be called from the test class that
     75      * extends this class.
     76      */
     77     @Override
     78     public void setUp() {
     79         mDifferenceVisualizer = new PassFailVisualizer();
     80         if (USE_RS) {
     81             mRenderScript = RenderScript.create(getActivity().getApplicationContext());
     82         }
     83     }
     84 
     85     /**
     86      * This method will kill the activity so that it can be reset depending on the test.
     87      */
     88     @Override
     89     public void tearDown() {
     90         if (mTestCaseBuilder != null) {
     91             List<TestCase> testCases = mTestCaseBuilder.getTestCases();
     92 
     93             if (testCases.size() == 0) {
     94                 throw new IllegalStateException("Must have at least one test case");
     95             }
     96 
     97 
     98             for (TestCase testCase : testCases) {
     99                 if (!testCase.wasTestRan) {
    100                     Log.w(TAG_NAME, getName() + " not all of the tests were ran");
    101                     break;
    102                 }
    103             }
    104             mTestCaseBuilder = null;
    105         }
    106 
    107         Runnable finishRunnable = new Runnable() {
    108 
    109             @Override
    110             public void run() {
    111                 getActivity().finish();
    112             }
    113         };
    114 
    115         getActivity().runOnUiThread(finishRunnable);
    116     }
    117 
    118     public Bitmap takeScreenshot() {
    119         getInstrumentation().waitForIdleSync();
    120         Bitmap bitmap1 = getInstrumentation().getUiAutomation().takeScreenshot();
    121         Bitmap bitmap2;
    122         int count = 0;
    123         do  {
    124             bitmap2 = bitmap1;
    125             bitmap1 = getInstrumentation().getUiAutomation().takeScreenshot();
    126             count++;
    127         } while (count < MAX_SCREEN_SHOTS && !Arrays.equals(bitmap2.mBuffer, bitmap1.mBuffer));
    128         return bitmap1;
    129     }
    130 
    131     /**
    132      * Sets the current DifferenceVisualizer for use in current test.
    133      */
    134     public void setDifferenceVisualizer(DifferenceVisualizer differenceVisualizer) {
    135         mDifferenceVisualizer = differenceVisualizer;
    136     }
    137 
    138     /**
    139      * Used to execute a specific part of a test and get the resultant bitmap
    140      */
    141     protected Bitmap captureRenderSpec(TestCase testCase) {
    142         getActivity().enqueueRenderSpecAndWait(testCase.layoutID, testCase.canvasClient,
    143                 testCase.webViewUrl, testCase.viewInitializer, testCase.useHardware);
    144         testCase.wasTestRan = true;
    145         return takeScreenshot();
    146     }
    147 
    148     /**
    149      * Compares the two bitmaps saved using the given test. If they fail, the files are saved using
    150      * the test name.
    151      */
    152     protected void assertBitmapsAreSimilar(Bitmap bitmap1, Bitmap bitmap2,
    153             BitmapComparer comparer, String debugMessage) {
    154         boolean success;
    155 
    156         if (USE_RS && comparer.supportsRenderScript()) {
    157             mIdealAllocation = Allocation.createFromBitmap(mRenderScript, bitmap1,
    158                     Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
    159             mGivenAllocation = Allocation.createFromBitmap(mRenderScript, bitmap2,
    160                     Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
    161             success = comparer.verifySameRS(getActivity().getResources(), mIdealAllocation,
    162                     mGivenAllocation, 0, TEST_WIDTH, TEST_WIDTH, TEST_HEIGHT, mRenderScript);
    163         } else {
    164             bitmap1.getPixels(mSoftwareArray, 0, TEST_WIDTH, 0, 0, TEST_WIDTH, TEST_HEIGHT);
    165             bitmap2.getPixels(mHardwareArray, 0, TEST_WIDTH, 0, 0, TEST_WIDTH, TEST_HEIGHT);
    166             success = comparer.verifySame(mSoftwareArray, mHardwareArray, 0, TEST_WIDTH, TEST_WIDTH,
    167                     TEST_HEIGHT);
    168         }
    169 
    170         if (!success) {
    171             BitmapDumper.dumpBitmaps(bitmap1, bitmap2, getName(), this.getClass().getSimpleName(),
    172                     mDifferenceVisualizer);
    173         }
    174 
    175         assertTrue(debugMessage, success);
    176     }
    177 
    178     /**
    179      * Tests to see if a bitmap passes a verifier's test. If it doesn't the bitmap is saved to the
    180      * sdcard.
    181      */
    182     protected void assertBitmapIsVerified(Bitmap bitmap, BitmapVerifier bitmapVerifier,
    183             String debugMessage) {
    184         bitmap.getPixels(mSoftwareArray, 0, TEST_WIDTH, 0, 0,
    185                 TEST_WIDTH, TEST_HEIGHT);
    186         boolean success = bitmapVerifier.verify(mSoftwareArray, 0, TEST_WIDTH, TEST_WIDTH, TEST_HEIGHT);
    187         if (!success) {
    188             Bitmap croppedBitmap = Bitmap.createBitmap(bitmap, 0, 0, TEST_WIDTH, TEST_HEIGHT);
    189             BitmapDumper.dumpBitmap(croppedBitmap, getName(), this.getClass().getSimpleName());
    190             BitmapDumper.dumpBitmap(bitmapVerifier.getDifferenceBitmap(), getName() + "_verifier",
    191                     this.getClass().getSimpleName());
    192         }
    193         assertTrue(debugMessage, success);
    194     }
    195 
    196     protected TestCaseBuilder createTest() {
    197         mTestCaseBuilder = new TestCaseBuilder();
    198         return mTestCaseBuilder;
    199     }
    200 
    201     /**
    202      * Defines a group of CanvasClients, XML layouts, and WebView html files for testing.
    203      */
    204     protected class TestCaseBuilder {
    205         private List<TestCase> mTestCases;
    206 
    207         private TestCaseBuilder() {
    208             mTestCases = new ArrayList<TestCase>();
    209         }
    210 
    211         /**
    212          * Runs a test where the first test case is considered the "ideal" image and from there,
    213          * every test case is tested against it.
    214          */
    215         public void runWithComparer(BitmapComparer bitmapComparer) {
    216             if (mTestCases.size() == 0) {
    217                 throw new IllegalStateException("Need at least one test to run");
    218             }
    219 
    220             Bitmap idealBitmap = captureRenderSpec(mTestCases.remove(0));
    221 
    222             for (TestCase testCase : mTestCases) {
    223                 Bitmap testCaseBitmap = captureRenderSpec(testCase);
    224                 assertBitmapsAreSimilar(idealBitmap, testCaseBitmap, bitmapComparer,
    225                         testCase.getDebugString());
    226             }
    227         }
    228 
    229         /**
    230          * Runs a test where each testcase is independent of the others and each is checked against
    231          * the verifier given.
    232          */
    233         public void runWithVerifier(BitmapVerifier bitmapVerifier) {
    234             if (mTestCases.size() == 0) {
    235                 throw new IllegalStateException("Need at least one test to run");
    236             }
    237 
    238             for (TestCase testCase : mTestCases) {
    239                 Bitmap testCaseBitmap = captureRenderSpec(testCase);
    240                 assertBitmapIsVerified(testCaseBitmap, bitmapVerifier, testCase.getDebugString());
    241             }
    242         }
    243 
    244         public TestCaseBuilder addWebView(String webViewUrl,
    245                 @Nullable ViewInitializer viewInitializer) {
    246             return addWebView(webViewUrl, viewInitializer, false)
    247                     .addWebView(webViewUrl, viewInitializer, true);
    248         }
    249 
    250         public TestCaseBuilder addLayout(int layoutId, @Nullable ViewInitializer viewInitializer) {
    251             return addLayout(layoutId, viewInitializer, false)
    252                     .addLayout(layoutId, viewInitializer, true);
    253         }
    254 
    255         public TestCaseBuilder addCanvasClient(CanvasClient canvasClient) {
    256             return addCanvasClient(canvasClient, false)
    257                     .addCanvasClient(canvasClient, true);
    258         }
    259 
    260         public TestCaseBuilder addWebView(String webViewUrl,
    261                 @Nullable ViewInitializer viewInitializer, boolean useHardware) {
    262             mTestCases.add(new TestCase(null, 0, webViewUrl, viewInitializer, useHardware));
    263             return this;
    264         }
    265 
    266         public TestCaseBuilder addLayout(int layoutId, @Nullable ViewInitializer viewInitializer,
    267                 boolean useHardware) {
    268             mTestCases.add(new TestCase(null, layoutId, null, viewInitializer, useHardware));
    269             return this;
    270         }
    271 
    272         public TestCaseBuilder addCanvasClient(CanvasClient canvasClient, boolean useHardware) {
    273             mTestCases.add(new TestCase(canvasClient, 0, null, null, useHardware));
    274             return this;
    275         }
    276 
    277         private List<TestCase> getTestCases() {
    278             return mTestCases;
    279         }
    280     }
    281 
    282     private class TestCase {
    283         public int layoutID;
    284         public CanvasClient canvasClient;
    285         public String webViewUrl;
    286         public ViewInitializer viewInitializer;
    287         public boolean useHardware;
    288         public boolean wasTestRan;
    289 
    290         public TestCase(CanvasClient client, int id, String viewUrl,
    291                 ViewInitializer viewInitializer, boolean useHardware) {
    292             int count = 0;
    293             count += (client == null ? 0 : 1);
    294             count += (viewUrl == null ? 0 : 1);
    295             count += (id == 0 ? 0 : 1);
    296             assert(count == 1);
    297             assert(client == null || viewInitializer == null);
    298             this.layoutID = id;
    299             this.canvasClient = client;
    300             this.webViewUrl = viewUrl;
    301             this.viewInitializer = viewInitializer;
    302             this.useHardware = useHardware;
    303             this.wasTestRan = false;
    304         }
    305 
    306         public String getDebugString() {
    307             String debug = "";
    308             if (canvasClient != null) {
    309                 debug += "CanvasClient : ";
    310                 if (canvasClient.getDebugString() != null) {
    311                     debug += canvasClient.getDebugString();
    312                 } else {
    313                     debug += "no debug string given";
    314                 }
    315             } else if (webViewUrl != null) {
    316                 debug += "WebView URL : " + webViewUrl;
    317             } else {
    318                 debug += "Layout resource : " +
    319                         getActivity().getResources().getResourceName(layoutID);
    320             }
    321             debug += "\nTest ran in " + (useHardware ? "hardware" : "software") + "\n";
    322             return debug;
    323         }
    324     }
    325 }
    326