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