1 /* 2 * Copyright (C) 2017 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.compatibility.common.tradefed.targetprep; 18 19 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; 20 import com.android.tradefed.build.IBuildInfo; 21 import com.android.tradefed.config.Option; 22 import com.android.tradefed.config.OptionClass; 23 import com.android.tradefed.device.DeviceNotAvailableException; 24 import com.android.tradefed.device.ITestDevice; 25 import com.android.tradefed.invoker.TestInformation; 26 import com.android.tradefed.log.LogUtil.CLog; 27 28 import java.io.File; 29 import java.io.FileNotFoundException; 30 import java.nio.file.Paths; 31 import java.util.HashMap; 32 import java.util.Map; 33 34 /** An {@link PreconditionPreparer} that collects one device file. */ 35 @OptionClass(alias = "device-file-collector") 36 public class DeviceFileCollector extends PreconditionPreparer { 37 38 @Option( 39 name = DeviceInfoCollector.SKIP_DEVICE_INFO_OPTION, 40 shortName = 'd', 41 description = "Whether device info collection should be skipped" 42 ) 43 private boolean mSkipDeviceInfo = false; 44 45 @Option(name = "src-file", description = "The file path to copy to the results dir") 46 private String mSrcFile; 47 48 @Option(name = "dest-file", description = "The destination file path under the result") 49 private String mDestFile; 50 51 /** 52 * If device.getProperty(key) is not value for all properties listed, the collection will be 53 * skipped. If this map is empty, mSrcFile will always be collected. 54 * 55 * This can be specified by the following option: 56 * {@code <option name="property" key="foo" value="bar"/>} 57 */ 58 @Option(name = "property", description = "run this test on device with this property value") 59 private Map<String, String> mPropertyMap = new HashMap<>(); 60 61 private File mResultFile; 62 63 /** {@inheritDoc} */ 64 @Override run(TestInformation testInfo)65 public void run(TestInformation testInfo) { 66 if (mSkipDeviceInfo) 67 return; 68 69 ITestDevice device = testInfo.getDevice(); 70 if (!matchProperties(device)) 71 return; 72 73 createResultDir(testInfo.getBuildInfo()); 74 if (mResultFile != null && !mResultFile.isDirectory() && 75 mSrcFile != null && !mSrcFile.isEmpty()) { 76 77 try { 78 if (device.doesFileExist(mSrcFile)) { 79 device.pullFile(mSrcFile, mResultFile); 80 } else { 81 CLog.w(String.format("File does not exist on device: \"%s\"", mSrcFile)); 82 } 83 } catch (DeviceNotAvailableException e) { 84 CLog.e("Caught exception during pull."); 85 CLog.e(e); 86 } 87 } 88 } 89 createResultDir(IBuildInfo buildInfo)90 private void createResultDir(IBuildInfo buildInfo) { 91 CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(buildInfo); 92 try { 93 File resultDir = buildHelper.getResultDir(); 94 if (mDestFile == null || mDestFile.isEmpty()) { 95 mDestFile = Paths.get(mSrcFile).getFileName().toString(); 96 } 97 mResultFile = Paths.get(resultDir.getAbsolutePath(), mDestFile).toFile(); 98 resultDir = mResultFile.getParentFile(); 99 resultDir.mkdirs(); 100 if (!resultDir.isDirectory()) { 101 CLog.e("%s is not a directory", resultDir.getAbsolutePath()); 102 return; 103 } 104 } catch (FileNotFoundException fnfe) { 105 CLog.e(fnfe); 106 } 107 } 108 matchProperties(ITestDevice device)109 private boolean matchProperties(ITestDevice device) { 110 for (Map.Entry<String, String> propEntry : mPropertyMap.entrySet()) { 111 try { 112 String actualValue = device.getProperty(propEntry.getKey()); 113 if (!propEntry.getValue().equals(actualValue)) { 114 CLog.i("Skipping '%s' because property doesn't match. " 115 + "(key=%s, expected=%s, actual=%s)", 116 mSrcFile, propEntry.getKey(), propEntry.getValue(), actualValue); 117 return false; 118 } 119 } catch (DeviceNotAvailableException e) { 120 CLog.e("Caught exception during property check."); 121 CLog.e(e); 122 return false; 123 } 124 } 125 return true; 126 } 127 } 128