1 /* 2 * Copyright (C) 2018 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 com.android.tradefed.result.proto; 17 18 import com.android.tradefed.config.Option; 19 import com.android.tradefed.invoker.IInvocationContext; 20 import com.android.tradefed.log.LogUtil.CLog; 21 import com.android.tradefed.result.proto.TestRecordProto.ChildReference; 22 import com.android.tradefed.result.proto.TestRecordProto.TestRecord; 23 import com.android.tradefed.util.FileUtil; 24 import com.android.tradefed.util.StreamUtil; 25 26 import java.io.File; 27 import java.io.FileOutputStream; 28 import java.io.IOException; 29 30 /** Proto reporter that dumps the {@link TestRecord} into a file. */ 31 public class FileProtoResultReporter extends ProtoResultReporter { 32 33 public static final String USE_DELIMITED_API = "use-delimited-api"; 34 35 @Option( 36 name = USE_DELIMITED_API, 37 description = "Use Proto.useDelimitedApi to save proto, otherwise use default api.") 38 private boolean mUseDelimitedApi = true; 39 40 public static final String PROTO_OUTPUT_FILE = "proto-output-file"; 41 42 @Option( 43 name = PROTO_OUTPUT_FILE, 44 description = "File where the proto output will be saved. If unset, reporter will be inop." 45 ) 46 private File mOutputFile = null; 47 48 public static final String PERIODIC_PROTO_WRITING_OPTION = "periodic-proto-writing"; 49 50 @Option( 51 name = PERIODIC_PROTO_WRITING_OPTION, 52 description = 53 "Whether or not to output intermediate proto per module following a numbered " 54 + "sequence." 55 ) 56 private boolean mPeriodicWriting = false; 57 58 // Current index of the sequence of proto output 59 private int mIndex = 0; 60 61 @Override processStartInvocation( TestRecord invocationStartRecord, IInvocationContext invocationContext)62 public void processStartInvocation( 63 TestRecord invocationStartRecord, IInvocationContext invocationContext) { 64 writeProto(invocationStartRecord); 65 } 66 67 @Override processTestModuleEnd(TestRecord moduleRecord)68 public void processTestModuleEnd(TestRecord moduleRecord) { 69 writeProto(moduleRecord); 70 } 71 72 @Override processTestRunEnded(TestRecord runRecord, boolean moduleInProgress)73 public void processTestRunEnded(TestRecord runRecord, boolean moduleInProgress) { 74 if (!moduleInProgress) { 75 // If it's a testRun outside of the module scope, output it to ensure we support 76 // non-module use cases. 77 writeProto(runRecord); 78 } 79 } 80 81 @Override processFinalProto(TestRecord finalRecord)82 public void processFinalProto(TestRecord finalRecord) { 83 writeProto(finalRecord); 84 } 85 86 @Override createModuleChildReference(TestRecord record)87 protected ChildReference createModuleChildReference(TestRecord record) { 88 // Do not keep a copy of module record in invocation level 89 if (isPeriodicWriting()) { 90 return null; 91 } 92 return super.createModuleChildReference(record); 93 } 94 95 /** Enable writing each module individualy to a file. */ setPeriodicWriting(boolean enabled)96 public void setPeriodicWriting(boolean enabled) { 97 mPeriodicWriting = enabled; 98 } 99 100 /** Whether or not periodic writing is enabled. */ isPeriodicWriting()101 public boolean isPeriodicWriting() { 102 return mPeriodicWriting; 103 } 104 writeProto(TestRecord record)105 private void writeProto(TestRecord record) { 106 if (mOutputFile == null) { 107 return; 108 } 109 FileOutputStream output = null; 110 File tmpFile = null; 111 try { 112 tmpFile = FileUtil.createTempFile("tmp-proto", "", mOutputFile.getParentFile()); 113 File outputFile = mOutputFile; 114 if (mPeriodicWriting) { 115 outputFile = new File(mOutputFile.getAbsolutePath() + mIndex); 116 } 117 // Write to the tmp file 118 output = new FileOutputStream(tmpFile); 119 if (mUseDelimitedApi) { 120 record.writeDelimitedTo(output); 121 } else { 122 record.writeTo(output); 123 } 124 if (mPeriodicWriting) { 125 nextOutputFile(); 126 } 127 // Move the tmp file to the new name when done writing. 128 tmpFile.renameTo(outputFile); 129 } catch (IOException e) { 130 CLog.e(e); 131 throw new RuntimeException(e); 132 } finally { 133 StreamUtil.close(output); 134 } 135 } 136 nextOutputFile()137 private void nextOutputFile() { 138 mIndex++; 139 } 140 setOutputFile(File outputFile)141 public void setOutputFile(File outputFile) { 142 mOutputFile = outputFile; 143 } 144 getOutputFile()145 public File getOutputFile() { 146 return mOutputFile; 147 } 148 setDelimitedOutput(boolean delimitedOutput)149 public void setDelimitedOutput(boolean delimitedOutput) { 150 mUseDelimitedApi = delimitedOutput; 151 } 152 } 153