1 /* 2 * Copyright (C) 2012 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.targetprep; 17 18 import com.android.ddmlib.FileListingService; 19 import com.android.tradefed.build.IBuildInfo; 20 import com.android.tradefed.build.IDeviceBuildInfo; 21 import com.android.tradefed.config.Option; 22 import com.android.tradefed.config.Option.Importance; 23 import com.android.tradefed.config.OptionClass; 24 import com.android.tradefed.device.DeviceNotAvailableException; 25 import com.android.tradefed.device.ITestDevice; 26 import com.android.tradefed.log.LogUtil.CLog; 27 import com.android.tradefed.util.ArrayUtil; 28 import com.android.tradefed.util.FileUtil; 29 30 import java.io.File; 31 import java.util.ArrayList; 32 import java.util.Collection; 33 import java.util.Collections; 34 import java.util.List; 35 36 /** 37 * A {@link ITargetPreparer} that pushes one or more files/dirs from a {@link 38 * IDeviceBuildInfo#getTestsDir()} folder onto device. 39 * 40 * <p>This preparer will look in alternate directories if the tests zip does not exist or does not 41 * contain the required apk. The search will go in order from the last alternative dir specified to 42 * the first. 43 */ 44 @OptionClass(alias = "tests-zip-file") 45 public class TestFilePushSetup extends BaseTargetPreparer { 46 47 @Option(name = "test-file-name", description = 48 "the relative path of a test zip file/directory to install on device. Can be repeated.", 49 importance = Importance.IF_UNSET) 50 private Collection<String> mTestPaths = new ArrayList<String>(); 51 52 @Option(name = "throw-if-not-found", description = 53 "Throw exception if the specified file is not found.") 54 private boolean mThrowIfNoFile = true; 55 56 @Option(name = "alt-dir", 57 description = "Alternate directory to look for the apk if the apk is not in the tests " 58 + "zip file. For each alternate dir, will look in // and //DATA. Can be " 59 + "repeated. Look for apks in last alt-dir first.") 60 private List<File> mAltDirs = new ArrayList<>(); 61 62 @Option(name = "alt-dir-behavior", description = "The order of alternate directory to be used " 63 + "when searching for files to push") 64 private AltDirBehavior mAltDirBehavior = AltDirBehavior.FALLBACK; 65 66 /** 67 * Adds a file to the list of items to push 68 * 69 * @param fileName 70 */ 71 protected void addTestFileName(String fileName) { 72 mTestPaths.add(fileName); 73 } 74 75 /** 76 * Retrieves the list of files to be pushed from test zip onto device 77 */ 78 protected Collection<String> getTestFileNames() { 79 return mTestPaths; 80 } 81 82 protected void clearTestFileName() { 83 mTestPaths.clear(); 84 } 85 86 /** 87 * Resolve the host side path based on testing artifact information inside build info. 88 * 89 * @param buildInfo build artifact information 90 * @param fileName filename of artifacts to push 91 * @return a {@link File} representing the physical file/path on host 92 */ 93 protected File getLocalPathForFilename(IBuildInfo buildInfo, String fileName, 94 ITestDevice device) throws TargetSetupError { 95 List<File> dirs = new ArrayList<>(); 96 for (File dir : mAltDirs) { 97 dirs.add(dir); 98 dirs.add(FileUtil.getFileForPath(dir, "DATA")); 99 } 100 // reverse the order so ones provided via command line last can be searched first 101 Collections.reverse(dirs); 102 103 List<File> expandedTestDirs = new ArrayList<>(); 104 if (buildInfo instanceof IDeviceBuildInfo) { 105 File testsDir = ((IDeviceBuildInfo)buildInfo).getTestsDir(); 106 if (testsDir != null && testsDir.exists()) { 107 expandedTestDirs.add(FileUtil.getFileForPath(testsDir, "DATA")); 108 } 109 } 110 if (mAltDirBehavior == AltDirBehavior.FALLBACK) { 111 // alt dirs are appended after build artifact dirs 112 expandedTestDirs.addAll(dirs); 113 dirs = expandedTestDirs; 114 } else if (mAltDirBehavior == AltDirBehavior.OVERRIDE) { 115 dirs.addAll(expandedTestDirs); 116 } else { 117 throw new TargetSetupError("Missing handler for alt-dir-behavior: " + mAltDirBehavior, 118 device.getDeviceDescriptor()); 119 } 120 if (dirs.isEmpty()) { 121 throw new TargetSetupError( 122 "Provided buildInfo does not contain a valid tests directory and no " + 123 "alternative directories were provided", device.getDeviceDescriptor()); 124 } 125 126 for (File dir : dirs) { 127 File testAppFile = new File(dir, fileName); 128 if (testAppFile.exists()) { 129 return testAppFile; 130 } 131 } 132 return null; 133 } 134 135 /** 136 * {@inheritDoc} 137 */ 138 @Override 139 public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError, 140 BuildError, DeviceNotAvailableException { 141 if (!(buildInfo instanceof IDeviceBuildInfo)) { 142 throw new IllegalArgumentException(String.format("Provided buildInfo is not a %s", 143 IDeviceBuildInfo.class.getCanonicalName())); 144 } 145 if (mTestPaths.size() == 0) { 146 CLog.d("No test files to push, skipping"); 147 return; 148 } 149 int filePushed = 0; 150 for (String fileName : mTestPaths) { 151 File localFile = getLocalPathForFilename(buildInfo, fileName, device); 152 if (localFile == null) { 153 if (mThrowIfNoFile) { 154 throw new TargetSetupError(String.format( 155 "Could not find test file %s directory in extracted tests.zip", 156 fileName), device.getDeviceDescriptor()); 157 } else { 158 CLog.w(String.format( 159 "Could not find test file %s directory in extracted tests.zip, but" + 160 "will continue test setup as throw-if-not-found is set to false", 161 fileName)); 162 continue; 163 } 164 } 165 String remoteFileName = getDevicePathFromUserData(fileName); 166 CLog.d("Pushing file: %s -> %s", localFile.getAbsoluteFile(), remoteFileName); 167 if (localFile.isDirectory()) { 168 device.pushDir(localFile, remoteFileName); 169 } else if (localFile.isFile()) { 170 device.pushFile(localFile, remoteFileName); 171 } 172 // there's no recursive option for 'chown', best we can do here 173 device.executeShellCommand(String.format("chown system.system %s", remoteFileName)); 174 filePushed++; 175 } 176 if (filePushed == 0 && mThrowIfNoFile) { 177 throw new TargetSetupError("No file is pushed from tests.zip", 178 device.getDeviceDescriptor()); 179 } 180 } 181 182 protected void setThrowIfNoFile(boolean throwIfNoFile) { 183 mThrowIfNoFile = throwIfNoFile; 184 } 185 186 /** 187 * Set an alternate directory. Exposed for testing. 188 */ 189 protected void setAltDir(File altDir) { 190 mAltDirs.add(altDir); 191 } 192 193 /** 194 * Set the alternative directory search behavior. Exposed for testing. 195 */ 196 protected void setAltDirBehavior(AltDirBehavior behavior) { 197 mAltDirBehavior = behavior; 198 } 199 200 static String getDevicePathFromUserData(String path) { 201 return ArrayUtil.join(FileListingService.FILE_SEPARATOR, 202 "", FileListingService.DIRECTORY_DATA, path); 203 } 204 } 205