1 /* 2 * Copyright (C) 2019 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 17 package com.android.junitxml; 18 19 import org.junit.runner.Description; 20 import org.junit.runner.Result; 21 import org.junit.runner.notification.Failure; 22 import org.junit.runner.notification.RunListener; 23 24 import java.io.File; 25 import java.io.FileWriter; 26 import java.io.IOException; 27 import java.util.HashMap; 28 import java.util.Map; 29 30 /** 31 * {@link RunListener} to write JUnit4 test status to File, then atest could use this file to get 32 * real time status}. 33 */ 34 public class AtestRunListener extends RunListener { 35 36 private static final String CLASSNAME_KEY = "className"; 37 private static final String TESTNAME_KEY = "testName"; 38 private static final String TRACE_KEY = "trace"; 39 private static final String RUNNAME_KEY = "runName"; 40 private static final String TESTCOUNT_KEY = "testCount"; 41 private static final String ATTEMPT_KEY = "runAttempt"; 42 private static final String TIME_KEY = "time"; 43 private static final String START_TIME_KEY = "start_time"; 44 private static final String END_TIME_KEY = "end_time"; 45 private static final String MODULE_NAME_KEY = "moduleName"; 46 47 /** Relevant test status keys. */ 48 public static class StatusKeys { 49 public static final String TEST_ENDED = "TEST_ENDED"; 50 public static final String TEST_FAILED = "TEST_FAILED"; 51 public static final String TEST_IGNORED = "TEST_IGNORED"; 52 public static final String TEST_STARTED = "TEST_STARTED"; 53 public static final String TEST_RUN_ENDED = "TEST_RUN_ENDED"; 54 public static final String TEST_RUN_STARTED = "TEST_RUN_STARTED"; 55 public static final String TEST_MODULE_STARTED = "TEST_MODULE_STARTED"; 56 public static final String TEST_MODULE_ENDED = "TEST_MODULE_ENDED"; 57 } 58 59 private final int mTotalCount; 60 61 // the file where to log the events. 62 private final File mReportFile; 63 64 private final String mSuiteName; 65 AtestRunListener(String suiteName, File reportFile, int totalCount)66 public AtestRunListener(String suiteName, File reportFile, int totalCount) { 67 mSuiteName = suiteName; 68 mReportFile = reportFile; 69 mTotalCount = totalCount; 70 } 71 72 @Override testRunStarted(Description description)73 public void testRunStarted(Description description) { 74 Map<String, Object> moduleStartEventData = new HashMap<>(); 75 moduleStartEventData.put(MODULE_NAME_KEY, mSuiteName); 76 printEvent(StatusKeys.TEST_MODULE_STARTED, moduleStartEventData); 77 78 Map<String, Object> testRunEventData = new HashMap<>(); 79 testRunEventData.put(TESTCOUNT_KEY, mTotalCount); 80 testRunEventData.put(ATTEMPT_KEY, 0); 81 testRunEventData.put(RUNNAME_KEY, mSuiteName); 82 printEvent(StatusKeys.TEST_RUN_STARTED, testRunEventData); 83 } 84 85 @Override testRunFinished(Result result)86 public void testRunFinished(Result result) { 87 Map<String, Object> eventData = new HashMap<>(); 88 eventData.put(TIME_KEY, result.getRunTime()); 89 printEvent(StatusKeys.TEST_RUN_ENDED, eventData); 90 printEvent(StatusKeys.TEST_MODULE_ENDED, new HashMap<>()); 91 } 92 93 @Override testFailure(Failure failure)94 public void testFailure(Failure failure) { 95 Description description = failure.getDescription(); 96 Map<String, Object> eventData = new HashMap<>(); 97 eventData.put(CLASSNAME_KEY, description.getClassName()); 98 eventData.put(TESTNAME_KEY, description.getMethodName()); 99 eventData.put(TRACE_KEY, failure.getTrace()); 100 printEvent(StatusKeys.TEST_FAILED, eventData); 101 } 102 103 @Override testStarted(Description description)104 public void testStarted(Description description) { 105 Map<String, Object> eventData = new HashMap<>(); 106 eventData.put(START_TIME_KEY, System.currentTimeMillis()); 107 eventData.put(CLASSNAME_KEY, description.getClassName()); 108 eventData.put(TESTNAME_KEY, description.getMethodName()); 109 printEvent(StatusKeys.TEST_STARTED, eventData); 110 } 111 112 @Override testFinished(Description description)113 public void testFinished(Description description) { 114 Map<String, Object> eventData = new HashMap<>(); 115 eventData.put(END_TIME_KEY, System.currentTimeMillis()); 116 eventData.put(CLASSNAME_KEY, description.getClassName()); 117 eventData.put(TESTNAME_KEY, description.getMethodName()); 118 printEvent(StatusKeys.TEST_ENDED, eventData); 119 } 120 121 @Override testIgnored(Description description)122 public void testIgnored(Description description) { 123 Map<String, Object> eventData = new HashMap<>(); 124 eventData.put(TESTNAME_KEY, description.getMethodName()); 125 eventData.put(CLASSNAME_KEY, description.getClassName()); 126 eventData.put(START_TIME_KEY, System.currentTimeMillis()); 127 eventData.put(END_TIME_KEY, System.currentTimeMillis()); 128 printEvent(StatusKeys.TEST_STARTED, eventData); 129 printEvent(StatusKeys.TEST_IGNORED, eventData); 130 printEvent(StatusKeys.TEST_ENDED, eventData); 131 } 132 printEvent(String key, Map<String, Object> event)133 private void printEvent(String key, Map<String, Object> event) { 134 if (mReportFile.canWrite()) { 135 try { 136 try (FileWriter fw = new FileWriter(mReportFile, true)) { 137 String eventLog = String.format("%s %s\n\n", key, toJson(event)); 138 fw.append(eventLog); 139 } 140 } catch (IOException e) { 141 throw new RuntimeException(e); 142 } 143 } else { 144 throw new RuntimeException( 145 String.format( 146 "report file: %s is not writable", mReportFile.getAbsolutePath())); 147 } 148 } 149 toJson(Map<String, Object> data)150 private String toJson(Map<String, Object> data) { 151 StringBuilder sb = new StringBuilder(); 152 for (Map.Entry<String, Object> entry : data.entrySet()) { 153 sb.append(","); 154 sb.append("\"").append(entry.getKey()).append("\":"); 155 if (entry.getValue() instanceof Number) { 156 sb.append(entry.getValue().toString()); 157 } else { 158 sb.append("\"").append(entry.getValue()).append("\""); 159 } 160 } 161 sb.replace(0, 1, "{").append("}"); 162 return sb.toString(); 163 } 164 } 165