1 /* 2 * Copyright 2017 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 package org.skia.skqp; 9 10 import android.content.Context; 11 import android.content.res.AssetManager; 12 import android.content.res.Resources; 13 import android.support.test.InstrumentationRegistry; 14 import android.util.Log; 15 import java.io.File; 16 import java.io.IOException; 17 import org.junit.runner.Description; 18 import org.junit.runner.RunWith; 19 import org.junit.runner.Runner; 20 import org.junit.runner.manipulation.Filter; 21 import org.junit.runner.manipulation.Filterable; 22 import org.junit.runner.manipulation.NoTestsRemainException; 23 import org.junit.runner.notification.Failure; 24 import org.junit.runner.notification.RunNotifier; 25 26 @RunWith(SkQPRunner.class) 27 public class SkQPRunner extends Runner implements Filterable { 28 private int mShouldRunTestCount; 29 private Description[] mTests; 30 private boolean[] mShouldSkipTest; 31 private SkQP impl; 32 private static final String TAG = SkQP.LOG_PREFIX; 33 34 private static void Fail(Description desc, RunNotifier notifier, String failure) { 35 notifier.fireTestFailure(new Failure(desc, new Throwable(failure))); 36 } 37 38 private static File GetOutputDir() { 39 Context c = InstrumentationRegistry.getTargetContext(); 40 // File f = c.getFilesDir(); 41 File f = c.getExternalFilesDir(null); 42 return new File(f, "output"); 43 } 44 45 //////////////////////////////////////////////////////////////////////////// 46 47 public SkQPRunner(Class testClass) { 48 impl = new SkQP(); 49 File filesDir = SkQPRunner.GetOutputDir(); 50 try { 51 SkQP.ensureEmtpyDirectory(filesDir); 52 } catch (IOException e) { 53 Log.w(TAG, "ensureEmtpyDirectory: " + e.getMessage()); 54 } 55 Log.i(TAG, String.format("output written to \"%s\"", filesDir.getAbsolutePath())); 56 57 Resources resources = InstrumentationRegistry.getTargetContext().getResources(); 58 AssetManager mAssetManager = resources.getAssets(); 59 impl.nInit(mAssetManager, filesDir.getAbsolutePath(), false); 60 61 mTests = new Description[this.testCount()]; 62 mShouldSkipTest = new boolean[mTests.length]; // = {false, false, ....}; 63 int index = 0; 64 for (int backend = 0; backend < impl.mBackends.length; backend++) { 65 for (int gm = 0; gm < impl.mGMs.length; gm++) { 66 mTests[index++] = Description.createTestDescription(SkQPRunner.class, 67 impl.mBackends[backend] + "/" + impl.mGMs[gm]); 68 } 69 } 70 for (int unitTest = 0; unitTest < impl.mUnitTests.length; unitTest++) { 71 mTests[index++] = Description.createTestDescription(SkQPRunner.class, 72 "unitTest/" + impl.mUnitTests[unitTest]); 73 } 74 assert(index == mTests.length); 75 mShouldRunTestCount = mTests.length; 76 } 77 78 @Override 79 public void filter(Filter filter) throws NoTestsRemainException { 80 int count = 0; 81 for (int i = 0; i < mTests.length; ++i) { 82 mShouldSkipTest[i] = !filter.shouldRun(mTests[i]); 83 if (!mShouldSkipTest[i]) { 84 ++count; 85 } 86 } 87 mShouldRunTestCount = count; 88 if (0 == count) { 89 throw new NoTestsRemainException(); 90 } 91 } 92 93 @Override 94 public Description getDescription() { 95 Description d = Description.createSuiteDescription(SkQP.class); 96 for (int i = 0; i < mTests.length; ++i) { 97 d.addChild(mTests[i]); 98 } 99 return d; 100 } 101 102 @Override 103 public int testCount() { 104 return impl.mUnitTests.length + impl.mGMs.length * impl.mBackends.length; 105 } 106 107 @Override 108 public void run(RunNotifier notifier) { 109 int testNumber = 1; // out of number of actually run tests. 110 int testIndex = 0; // out of potential tests. 111 for (int backend = 0; backend < impl.mBackends.length; backend++) { 112 for (int gm = 0; gm < impl.mGMs.length; gm++, testIndex++) { 113 Description desc = mTests[testIndex]; 114 String name = desc.getMethodName(); 115 if (mShouldSkipTest[testIndex]) { 116 continue; 117 } 118 Log.v(TAG, String.format("Rendering Test '%s' started (%d/%d).", 119 name, testNumber++, mShouldRunTestCount)); 120 notifier.fireTestStarted(desc); 121 float value = java.lang.Float.MAX_VALUE; 122 String error = null; 123 try { 124 value = impl.nExecuteGM(gm, backend); 125 } catch (SkQPException exept) { 126 error = exept.getMessage(); 127 } 128 if (error != null) { 129 SkQPRunner.Fail(desc, notifier, String.format("Exception: %s", error)); 130 Log.w(TAG, String.format("[ERROR] '%s': %s", name, error)); 131 } else if (value != 0) { 132 SkQPRunner.Fail(desc, notifier, String.format( 133 "Image mismatch: max channel diff = %f", value)); 134 Log.w(TAG, String.format("[FAIL] '%s': %f > 0", name, value)); 135 } else { 136 Log.i(TAG, String.format("Rendering Test '%s' passed", name)); 137 } 138 notifier.fireTestFinished(desc); 139 } 140 } 141 for (int unitTest = 0; unitTest < impl.mUnitTests.length; unitTest++, testIndex++) { 142 Description desc = mTests[testIndex]; 143 String name = desc.getMethodName(); 144 if (mShouldSkipTest[testIndex]) { 145 continue; 146 } 147 148 Log.v(TAG, String.format("Test '%s' started (%d/%d).", 149 name, testNumber++, mShouldRunTestCount)); 150 notifier.fireTestStarted(desc); 151 String[] errors = impl.nExecuteUnitTest(unitTest); 152 if (errors != null && errors.length > 0) { 153 Log.w(TAG, String.format("[FAIL] Test '%s' had %d failures.", name, errors.length)); 154 for (String error : errors) { 155 SkQPRunner.Fail(desc, notifier, error); 156 Log.w(TAG, String.format("[FAIL] '%s': %s", name, error)); 157 } 158 } else { 159 Log.i(TAG, String.format("Test '%s' passed.", name)); 160 } 161 notifier.fireTestFinished(desc); 162 } 163 impl.nMakeReport(); 164 Log.i(TAG, String.format("output written to \"%s\"", GetOutputDir().getAbsolutePath())); 165 } 166 } 167