OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.base.test; |
| 6 |
| 7 import android.support.test.internal.runner.listener.InstrumentationRunListener; |
| 8 |
| 9 import org.json.JSONArray; |
| 10 import org.json.JSONObject; |
| 11 import org.junit.runner.Description; |
| 12 import org.junit.runner.Result; |
| 13 |
| 14 import org.chromium.base.Log; |
| 15 |
| 16 import java.io.File; |
| 17 import java.io.FileOutputStream; |
| 18 import java.io.IOException; |
| 19 import java.io.OutputStreamWriter; |
| 20 import java.io.Writer; |
| 21 import java.lang.annotation.Annotation; |
| 22 import java.lang.reflect.Method; |
| 23 import java.util.Arrays; |
| 24 import java.util.Collection; |
| 25 import java.util.HashMap; |
| 26 import java.util.HashSet; |
| 27 import java.util.Map; |
| 28 import java.util.Set; |
| 29 |
| 30 /** |
| 31 * A RunListener that list out all the test information into a json file. |
| 32 */ |
| 33 public class TestListInstrumentationRunListener extends InstrumentationRunListen
er { |
| 34 private static final String TAG = "TestListRunListener"; |
| 35 private static final Set<String> SKIP_METHODS = new HashSet<>( |
| 36 Arrays.asList(new String[] {"toString", "hashCode", "annotationType"
, "equals"})); |
| 37 |
| 38 private final Map<Class<?>, JSONObject> mTestClassJsonMap = new HashMap<>(); |
| 39 private final String mOutputPath; |
| 40 |
| 41 public TestListInstrumentationRunListener(String outputPath) { |
| 42 super(); |
| 43 mOutputPath = outputPath; |
| 44 } |
| 45 |
| 46 /** |
| 47 * Store the test method description to a Map at the beginning of a test run
. |
| 48 */ |
| 49 @Override |
| 50 public void testStarted(Description desc) throws Exception { |
| 51 if (mTestClassJsonMap.containsKey(desc.getTestClass())) { |
| 52 ((JSONArray) mTestClassJsonMap.get(desc.getTestClass()).get("methods
")) |
| 53 .put(getTestMethodJSON(desc)); |
| 54 } else { |
| 55 Class<?> testClass = desc.getTestClass(); |
| 56 mTestClassJsonMap.put(desc.getTestClass(), new JSONObject() |
| 57 .put("class", testClass.getName()) |
| 58 .put("superclass", testClass.getSuperclass().getName()) |
| 59 .put("annotations", |
| 60 getAnnotationJSON(Arrays.asList(testClass.getAnnotat
ions()))) |
| 61 .put("methods", new JSONArray().put(getTestMethodJSON(desc))
)); |
| 62 } |
| 63 } |
| 64 |
| 65 /** |
| 66 * Create a JSONArray with all the test class JSONObjects and save it to lis
ted output path. |
| 67 */ |
| 68 @Override |
| 69 public void testRunFinished(Result result) throws IOException { |
| 70 Writer writer = null; |
| 71 File file = new File(mOutputPath); |
| 72 try { |
| 73 writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8")
; |
| 74 JSONArray allTestClassesJSON = new JSONArray(mTestClassJsonMap.value
s()); |
| 75 writer.write(allTestClassesJSON.toString()); |
| 76 } catch (IOException e) { |
| 77 Log.e(TAG, "failed to write json to file", e); |
| 78 throw e; |
| 79 } finally { |
| 80 if (writer != null) { |
| 81 try { |
| 82 writer.close(); |
| 83 } catch (IOException e) { |
| 84 // Intentionally ignore IOException when closing writer |
| 85 } |
| 86 } |
| 87 } |
| 88 } |
| 89 |
| 90 /** |
| 91 * Return a JSONOject that represent a Description of a method". |
| 92 */ |
| 93 static JSONObject getTestMethodJSON(Description desc) throws Exception { |
| 94 return new JSONObject() |
| 95 .put("method", desc.getMethodName()) |
| 96 .put("annotations", getAnnotationJSON(desc.getAnnotations())); |
| 97 } |
| 98 |
| 99 /** |
| 100 * Create a JSONObject that represent a collection of anntations. |
| 101 * |
| 102 * For example, for the following group of annotations for ExampleClass |
| 103 * <code> |
| 104 * @A |
| 105 * @B(message = "hello", level = 3) |
| 106 * public class ExampleClass() {} |
| 107 * </code> |
| 108 * |
| 109 * This method would return a JSONObject as such: |
| 110 * <code> |
| 111 * { |
| 112 * "A": {}, |
| 113 * "B": { |
| 114 * "message": "hello", |
| 115 * "level": "3" |
| 116 * } |
| 117 * } |
| 118 * </code> |
| 119 * |
| 120 * The method accomplish this by though through each annotation and reflecti
vely call the |
| 121 * annotation's method to get the element value, with exceptions to methods
like "equals()" |
| 122 * or "hashCode". |
| 123 */ |
| 124 static JSONObject getAnnotationJSON(Collection<Annotation> annotations) |
| 125 throws Exception { |
| 126 JSONObject annotationsJsons = new JSONObject(); |
| 127 for (Annotation a : annotations) { |
| 128 JSONObject elementJsonObject = new JSONObject(); |
| 129 for (Method method : a.annotationType().getMethods()) { |
| 130 if (SKIP_METHODS.contains(method.getName())) { |
| 131 continue; |
| 132 } |
| 133 try { |
| 134 Object value = method.invoke(a); |
| 135 if (value == null) { |
| 136 elementJsonObject.put(method.getName(), null); |
| 137 } else { |
| 138 elementJsonObject.put(method.getName(), |
| 139 value.getClass().isArray() |
| 140 ? new JSONArray(Arrays.asList((Object[])
value)) |
| 141 : value.toString()); |
| 142 } |
| 143 } catch (IllegalArgumentException e) { |
| 144 } |
| 145 } |
| 146 annotationsJsons.put(a.annotationType().getSimpleName(), elementJson
Object); |
| 147 } |
| 148 return annotationsJsons; |
| 149 } |
| 150 } |
OLD | NEW |