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 import org.chromium.base.PathUtils; | |
16 | |
17 import java.io.File; | |
18 import java.io.FileOutputStream; | |
19 import java.io.IOException; | |
20 import java.io.OutputStreamWriter; | |
21 import java.io.Writer; | |
22 import java.lang.annotation.Annotation; | |
23 import java.lang.reflect.Method; | |
24 import java.util.Arrays; | |
25 import java.util.Collection; | |
26 import java.util.HashMap; | |
27 import java.util.HashSet; | |
28 import java.util.LinkedList; | |
29 import java.util.List; | |
30 import java.util.Map; | |
31 import java.util.Set; | |
32 | |
33 /** | |
34 * A RunListener that list out all the test information into a json file. | |
35 */ | |
36 public class TestListInstrumentationRunListener extends InstrumentationRunListen er { | |
37 private static final String TAG = "TestListRunListener"; | |
38 private static final Set<String> SKIP_METHODS = new HashSet<>( | |
39 Arrays.asList(new String[] {"toString", "hashCode", "annotationType" , "equals"})); | |
40 | |
41 private final Map<Class<?>, List<JSONObject>> mTestClassJSONObjects = new Ha shMap<>(); | |
42 private final String mOutputPath; | |
43 | |
44 public TestListInstrumentationRunListener(String outputPath) { | |
45 super(); | |
46 mOutputPath = outputPath; | |
47 } | |
48 | |
49 /** | |
50 * Store the test method description to a Map at the beginning of a test run . | |
51 */ | |
52 @Override | |
53 public void testStarted(Description desc) throws Exception { | |
54 if (!mTestClassJSONObjects.containsKey(desc.getTestClass())) { | |
55 mTestClassJSONObjects.put(desc.getTestClass(), new LinkedList<>()); | |
56 } | |
57 mTestClassJSONObjects.get(desc.getTestClass()).add(getTestClassJSON(desc )); | |
58 } | |
59 | |
60 /** | |
61 * Create a JSONArray with all the test class JSONObjects and save it to lis ted output path. | |
62 */ | |
63 @Override | |
64 public void testRunFinished(Result result) throws Exception { | |
65 List<JSONObject> allTestClasses = new LinkedList<>(); | |
66 for (Map.Entry<Class<?>, List<JSONObject>> entry : mTestClassJSONObjects .entrySet()) { | |
67 Class<?> testClass = entry.getKey(); | |
68 JSONObject classObject = | |
69 new JSONObject() | |
70 .put("class", testClass.getName()) | |
71 .put("superclass", testClass.getSuperclass().getName ()) | |
72 .put("annotations", | |
73 getAnnotationJSON(Arrays.asList(testClass.ge tAnnotations()))) | |
74 .put("methods", new JSONArray(entry.getValue())); | |
75 allTestClasses.add(classObject); | |
76 } | |
77 Writer writer = null; | |
78 File file = new File( | |
79 PathUtils.getExternalStorageDirectory() + "/chromium_tests_root/ " + mOutputPath); | |
jbudorick
2017/07/14 18:43:55
This should be provided with an absolute path, not
the real yoland
2017/07/18 04:20:21
Done
| |
80 try { | |
81 writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8") ; | |
82 JSONArray allTestClassesJSON = new JSONArray(allTestClasses); | |
83 writer.write(allTestClassesJSON.toString()); | |
84 } catch (Exception e) { | |
85 Log.e(TAG, "failed to write json to file", e); | |
86 } finally { | |
87 if (writer != null) { | |
88 try { | |
89 writer.close(); | |
90 } catch (IOException e) { | |
91 } | |
92 } | |
93 } | |
94 } | |
95 | |
96 /** | |
97 * Return a JSONOject that represent a Description of a method". | |
98 */ | |
99 private static JSONObject getTestClassJSON(Description desc) throws Exceptio n { | |
100 return new JSONObject() | |
101 .put("method", desc.getMethodName()) | |
102 .put("annotations", getAnnotationJSON(desc.getAnnotations())); | |
103 } | |
104 | |
105 /** | |
106 * Create a JSONObject that represent a collection of anntations. | |
107 * | |
108 * For example, for the following group of annotations for ExampleClass | |
109 * <code> | |
110 * @A | |
111 * @B(message = "hello", level = 3) | |
112 * public class ExampleClass() {} | |
113 * </code> | |
114 * | |
115 * This method would return a JSONObject as such: | |
116 * <code> | |
117 * { | |
118 * "A": {}, | |
119 * "B": { | |
120 * "message": "hello", | |
121 * "level": "3" | |
122 * } | |
123 * } | |
124 * </code> | |
125 * | |
126 * The method accomplish this by though through each annotation and reflecti vely call the | |
127 * annotation's method to get the element value, with exceptions to methods like "equal()" | |
128 * or "hashCode". | |
129 */ | |
130 private static JSONObject getAnnotationJSON(Collection<Annotation> annotatio ns) | |
131 throws Exception { | |
132 JSONObject annotaionsJsons = new JSONObject(); | |
133 for (Annotation a : annotations) { | |
134 JSONObject elementJsonObject = new JSONObject(); | |
135 for (Method method : a.annotationType().getMethods()) { | |
136 if (SKIP_METHODS.contains(method.getName())) { | |
137 continue; | |
138 } | |
139 try { | |
140 Object value = method.invoke(a); | |
141 if (value == null) { | |
142 elementJsonObject.put(method.getName(), null); | |
143 } else { | |
144 elementJsonObject.put(method.getName(), | |
145 value.getClass().isArray() | |
146 ? new JSONArray(Arrays.asList((Object[]) value)) | |
147 : value.toString()); | |
148 } | |
149 } catch (IllegalArgumentException e) { | |
150 } | |
151 } | |
152 annotaionsJsons.put(a.annotationType().getSimpleName(), elementJsonO bject); | |
153 } | |
154 return annotaionsJsons; | |
155 } | |
156 } | |
OLD | NEW |