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 package com.android.tradefed.testtype.binary; 17 18 import com.android.tradefed.config.Option; 19 import com.android.tradefed.config.OptionCopier; 20 import com.android.tradefed.device.DeviceNotAvailableException; 21 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric; 22 import com.android.tradefed.result.ITestInvocationListener; 23 import com.android.tradefed.result.TestDescription; 24 import com.android.tradefed.testtype.IAbi; 25 import com.android.tradefed.testtype.IAbiReceiver; 26 import com.android.tradefed.testtype.IRemoteTest; 27 import com.android.tradefed.testtype.IRuntimeHintProvider; 28 import com.android.tradefed.testtype.IShardableTest; 29 import com.android.tradefed.testtype.ITestCollector; 30 31 import java.io.File; 32 import java.util.ArrayList; 33 import java.util.Collection; 34 import java.util.HashMap; 35 import java.util.List; 36 37 /** Base class for executable style of tests. For example: binaries, shell scripts. */ 38 public abstract class ExecutableBaseTest 39 implements IRemoteTest, IRuntimeHintProvider, ITestCollector, IShardableTest, IAbiReceiver { 40 41 public static final String NO_BINARY_ERROR = "Binary %s does not exist."; 42 43 @Option(name = "binary", description = "Path to the binary to be run. Can be repeated.") 44 private List<String> mBinaryPaths = new ArrayList<>(); 45 46 @Option( 47 name = "collect-tests-only", 48 description = "Only dry-run through the tests, do not actually run them." 49 ) 50 private boolean mCollectTestsOnly = false; 51 52 @Option( 53 name = "runtime-hint", 54 description = "The hint about the test's runtime.", 55 isTimeVal = true 56 ) 57 private long mRuntimeHintMs = 60000L; // 1 minute 58 59 private IAbi mAbi; 60 61 @Override run(ITestInvocationListener listener)62 public final void run(ITestInvocationListener listener) throws DeviceNotAvailableException { 63 for (String binary : mBinaryPaths) { 64 String path = findBinary(binary); 65 if (path == null) { 66 listener.testRunStarted(new File(binary).getName(), 0); 67 listener.testRunFailed(String.format(NO_BINARY_ERROR, binary)); 68 listener.testRunEnded(0L, new HashMap<String, Metric>()); 69 } else { 70 listener.testRunStarted(new File(path).getName(), 1); 71 long startTimeMs = System.currentTimeMillis(); 72 TestDescription description = 73 new TestDescription(new File(path).getName(), new File(path).getName()); 74 listener.testStarted(description); 75 try { 76 if (!mCollectTestsOnly) { 77 // Do not actually run the test if we are dry running it. 78 runBinary(path, listener, description); 79 } 80 } finally { 81 listener.testEnded(description, new HashMap<String, Metric>()); 82 listener.testRunEnded( 83 System.currentTimeMillis() - startTimeMs, 84 new HashMap<String, Metric>()); 85 } 86 } 87 } 88 } 89 90 /** 91 * Search for the binary to be able to run it. 92 * 93 * @param binary the path of the binary or simply the binary name. 94 * @return The path to the binary, or null if not found. 95 */ findBinary(String binary)96 public abstract String findBinary(String binary); 97 98 /** 99 * Actually run the binary at the given path. 100 * 101 * @param binaryPath The path of the binary. 102 * @param listener The listener where to report the results. 103 * @param description The test in progress. 104 */ runBinary( String binaryPath, ITestInvocationListener listener, TestDescription description)105 public abstract void runBinary( 106 String binaryPath, ITestInvocationListener listener, TestDescription description) 107 throws DeviceNotAvailableException; 108 109 /** {@inheritDoc} */ 110 @Override setCollectTestsOnly(boolean shouldCollectTest)111 public final void setCollectTestsOnly(boolean shouldCollectTest) { 112 mCollectTestsOnly = shouldCollectTest; 113 } 114 115 /** {@inheritDoc} */ 116 @Override getRuntimeHint()117 public final long getRuntimeHint() { 118 return mRuntimeHintMs; 119 } 120 121 /** {@inheritDoc} */ 122 @Override setAbi(IAbi abi)123 public final void setAbi(IAbi abi) { 124 mAbi = abi; 125 } 126 127 /** {@inheritDoc} */ 128 @Override getAbi()129 public IAbi getAbi() { 130 return mAbi; 131 } 132 133 /** {@inheritDoc} */ 134 @Override split()135 public final Collection<IRemoteTest> split() { 136 if (mBinaryPaths.size() <= 2) { 137 return null; 138 } 139 Collection<IRemoteTest> tests = new ArrayList<>(); 140 for (String path : mBinaryPaths) { 141 tests.add(getTestShard(path)); 142 } 143 return tests; 144 } 145 getTestShard(String path)146 private IRemoteTest getTestShard(String path) { 147 ExecutableBaseTest shard = null; 148 try { 149 shard = this.getClass().newInstance(); 150 OptionCopier.copyOptionsNoThrow(this, shard); 151 // We approximate the runtime of each shard to be equal since we can't know. 152 shard.mRuntimeHintMs = mRuntimeHintMs / shard.mBinaryPaths.size(); 153 // Set one binary per shard 154 shard.mBinaryPaths.clear(); 155 shard.mBinaryPaths.add(path); 156 } catch (InstantiationException | IllegalAccessException e) { 157 // This cannot happen because the class was already created once at that point. 158 throw new RuntimeException( 159 String.format( 160 "%s (%s) when attempting to create shard object", 161 e.getClass().getSimpleName(), e.getMessage())); 162 } 163 return shard; 164 } 165 } 166