1 /* 2 * Copyright (C) 2016 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.VtsCompatibilityInvocationHelper; 20 import com.android.ddmlib.Log; 21 import com.android.tradefed.build.IBuildInfo; 22 import com.android.tradefed.config.Option; 23 import com.android.tradefed.config.OptionClass; 24 import com.android.tradefed.device.ITestDevice; 25 import com.android.tradefed.device.DeviceNotAvailableException; 26 import com.android.tradefed.log.LogUtil.CLog; 27 import com.android.tradefed.targetprep.BuildError; 28 import com.android.tradefed.targetprep.PushFilePreparer; 29 import com.android.tradefed.targetprep.TargetSetupError; 30 import com.android.tradefed.testtype.IAbi; 31 import com.android.tradefed.testtype.IAbiReceiver; 32 33 import java.io.File; 34 import java.util.TreeSet; 35 import java.util.Collection; 36 import java.lang.Class; 37 import java.io.BufferedReader; 38 import java.io.FileReader; 39 import java.io.FileNotFoundException; 40 41 /** 42 * Pushes specified testing artifacts from Compatibility repository. 43 */ 44 @OptionClass(alias = "vts-file-pusher") 45 public class VtsFilePusher extends PushFilePreparer implements IAbiReceiver { 46 @Option(name="push-group", description= 47 "A push group name. Must be a .push file under tools/vts-tradefed/res/push_groups/. " 48 + "May be under a relative path. May be repeated. Duplication of file specs " 49 + "is ok. Will throw a TargetSetupError if a file push fails.") 50 private Collection<String> mPushSpecGroups = new TreeSet<>(); 51 52 @Option(name="push-group-cleanup", description = "Whether files in push group " 53 + "should be cleaned up from device after test. Note that preparer does not verify " 54 + "that files/directories have been deleted. Default value: true.") 55 private boolean mPushGroupCleanup = true; 56 57 @Option(name="push-group-remount-system", description="Whether to remounts system " 58 + "partition to be writable before push or clean up default files. " 59 + "Default value: false") 60 private boolean mPushGroupRemount = false; 61 62 @Option(name = "append-bitness", description = "Append the ABI's bitness to the filename.") 63 private boolean mAppendBitness = false; 64 65 private static final String DIR_PUSH_GROUPS = "vts/tools/vts-tradefed/res/push_groups"; 66 static final String PUSH_GROUP_FILE_EXTENSION = ".push"; 67 68 private Collection<String> mFilesPushed = new TreeSet<>(); 69 private IAbi mAbi; 70 private VtsCompatibilityInvocationHelper mInvocationHelper; 71 72 /** 73 * Load file push specs from .push files as a collection of Strings 74 * @param buildInfo 75 * @return a collection of push spec strings 76 * @throws TargetSetupError if load fails 77 */ loadFilePushGroups(IBuildInfo buildInfo)78 private Collection<String> loadFilePushGroups(IBuildInfo buildInfo) throws TargetSetupError { 79 Collection<String> result = new TreeSet<>(); 80 File testDir; 81 try { 82 testDir = mInvocationHelper.getTestsDir(); 83 } catch(FileNotFoundException e) { 84 throw new TargetSetupError(e.getMessage()); 85 } 86 87 for (String group: mPushSpecGroups) { 88 TreeSet<String> stack = new TreeSet<>(); 89 result.addAll(loadFilePushGroup(group, testDir.getAbsolutePath(), stack)); 90 } 91 return result; 92 } 93 94 /** 95 * Recursively load file push specs from a push group file into a collection of strings. 96 * 97 * @param specFile, push group file name with .push extension. Can contain relative directory. 98 * @param testsDir, the directory where test data files are stored. 99 */ loadFilePushGroup(String specFileName, String testsDir, Collection<String> stack)100 private Collection<String> loadFilePushGroup(String specFileName, String testsDir, 101 Collection<String> stack) throws TargetSetupError { 102 Collection<String> result = new TreeSet<>(); 103 104 String relativePath = new File( 105 DIR_PUSH_GROUPS, specFileName).getPath(); 106 File specFile = null; 107 108 try { 109 specFile = new File(testsDir, relativePath); 110 } catch(Exception e) { 111 throw new TargetSetupError(e.getMessage()); 112 } 113 114 try (BufferedReader br = new BufferedReader(new FileReader(specFile))) { 115 String line; 116 while ((line = br.readLine()) != null) { 117 String spec = line.trim(); 118 if (spec.contains("->")) { 119 result.add(spec); 120 } else if (spec.contains(PUSH_GROUP_FILE_EXTENSION)) { 121 if (!stack.contains(spec)) { 122 stack.add(spec); 123 result.addAll(loadFilePushGroup(spec, testsDir, stack)); 124 stack.remove(spec); 125 } 126 } else if (spec.length() > 0) { 127 throw new TargetSetupError("Unknown file push spec: " + spec); 128 } 129 } 130 } catch(Exception e) { 131 throw new TargetSetupError(e.getMessage()); 132 } 133 134 return result; 135 } 136 137 /** 138 * Push file groups if configured in .xml file. 139 * @param device 140 * @param buildInfo 141 * @throws TargetSetupError, DeviceNotAvailableException 142 */ pushFileGroups(ITestDevice device, IBuildInfo buildInfo)143 private void pushFileGroups(ITestDevice device, IBuildInfo buildInfo) 144 throws TargetSetupError, DeviceNotAvailableException { 145 if (mPushGroupRemount) { 146 device.remountSystemWritable(); 147 } 148 149 for (String pushspec : loadFilePushGroups(buildInfo)) { 150 String[] pair = pushspec.split("->"); 151 152 if (pair.length != 2) { 153 throw new TargetSetupError( 154 String.format("Failed to parse push spec '%s'", pushspec)); 155 } 156 157 File src = new File(pair[0]); 158 159 if (!src.isAbsolute()) { 160 src = resolveRelativeFilePath(buildInfo, pair[0]); 161 } 162 163 Class cls = this.getClass(); 164 165 if (!src.exists()) { 166 Log.w(cls.getSimpleName(), String.format( 167 "Skipping push spec in push group whose source does not exist: %s", 168 pushspec)); 169 continue; 170 } 171 172 Log.d(cls.getSimpleName(), 173 String.format("Trying to push file from local to remote: %s", pushspec)); 174 175 if ((src.isDirectory() && !device.pushDir(src, pair[1])) || 176 (!device.pushFile(src, pair[1]))) { 177 mFilesPushed = null; 178 throw new TargetSetupError(String.format("Failed to push local '%s' to remote '%s'", 179 pair[0], pair[1])); 180 } else { 181 mFilesPushed.add(pair[1]); 182 } 183 } 184 } 185 186 /** 187 * {@inheritDoc} 188 */ 189 @Override setUp(ITestDevice device, IBuildInfo buildInfo)190 public void setUp(ITestDevice device, IBuildInfo buildInfo) 191 throws TargetSetupError, BuildError, DeviceNotAvailableException { 192 device.enableAdbRoot(); 193 mInvocationHelper = new VtsCompatibilityInvocationHelper(); 194 pushFileGroups(device, buildInfo); 195 196 super.setUp(device, buildInfo); 197 } 198 199 /** 200 * {@inheritDoc} 201 */ 202 @Override tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)203 public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e) 204 throws DeviceNotAvailableException { 205 206 if (!(e instanceof DeviceNotAvailableException) && mPushGroupCleanup && mFilesPushed != null) { 207 device.enableAdbRoot(); 208 if (mPushGroupRemount) { 209 device.remountSystemWritable(); 210 } 211 for (String devicePath : mFilesPushed) { 212 device.executeShellCommand("rm -r " + devicePath); 213 } 214 } 215 216 super.tearDown(device, buildInfo, e); 217 } 218 219 /** 220 * {@inheritDoc} 221 */ 222 @Override setAbi(IAbi abi)223 public void setAbi(IAbi abi) { 224 mAbi = abi; 225 } 226 227 /** 228 * {@inheritDoc} 229 */ 230 @Override getAbi()231 public IAbi getAbi() { 232 return mAbi; 233 } 234 235 /** 236 * {@inheritDoc} 237 */ 238 @Override resolveRelativeFilePath(IBuildInfo buildInfo, String fileName)239 public File resolveRelativeFilePath(IBuildInfo buildInfo, String fileName) { 240 File f = null; 241 try { 242 f = new File(mInvocationHelper.getTestsDir(), 243 String.format("%s%s", fileName, mAppendBitness ? mAbi.getBitness() : "")); 244 CLog.d("Copying from %s", f.getAbsolutePath()); 245 return f; 246 } catch (FileNotFoundException e) { 247 CLog.e(e); 248 CLog.e("File not found: %s", f); 249 } 250 return null; 251 } 252 } 253 254