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