• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.compatibility.common.tradefed.build;
17 
18 import com.android.annotations.VisibleForTesting;
19 import com.android.tradefed.build.BuildRetrievalError;
20 import com.android.tradefed.build.DeviceBuildInfo;
21 import com.android.tradefed.build.IBuildInfo;
22 import com.android.tradefed.build.IBuildInfo.BuildInfoProperties;
23 import com.android.tradefed.build.IBuildProvider;
24 import com.android.tradefed.build.IDeviceBuildInfo;
25 import com.android.tradefed.build.IDeviceBuildProvider;
26 import com.android.tradefed.config.Option;
27 import com.android.tradefed.config.Option.Importance;
28 import com.android.tradefed.config.OptionClass;
29 import com.android.tradefed.device.DeviceNotAvailableException;
30 import com.android.tradefed.device.ITestDevice;
31 import com.android.tradefed.invoker.IInvocationContext;
32 import com.android.tradefed.testtype.IInvocationContextReceiver;
33 import com.android.tradefed.testtype.suite.TestSuiteInfo;
34 import com.android.tradefed.util.FileUtil;
35 import com.android.tradefed.util.VersionParser;
36 
37 import java.io.File;
38 import java.io.IOException;
39 import java.text.SimpleDateFormat;
40 import java.util.ArrayList;
41 import java.util.Date;
42 import java.util.HashMap;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.regex.Pattern;
46 /**
47  * A simple {@link IBuildProvider} that uses a pre-existing Compatibility install.
48  */
49 @OptionClass(alias="compatibility-build-provider")
50 public class CompatibilityBuildProvider implements IDeviceBuildProvider, IInvocationContextReceiver {
51 
52     private static final Pattern RELEASE_BUILD = Pattern.compile("^[A-Z]{3}\\d{2}[A-Z]{0,1}$");
53     private static final String ROOT_DIR = "ROOT_DIR";
54     private static final String SUITE_BUILD = "SUITE_BUILD";
55     private static final String SUITE_NAME = "SUITE_NAME";
56     private static final String SUITE_FULL_NAME = "SUITE_FULL_NAME";
57     private static final String SUITE_VERSION = "SUITE_VERSION";
58     private static final String SUITE_PLAN = "SUITE_PLAN";
59     private static final String RESULT_DIR = "RESULT_DIR";
60     private static final String START_TIME_MS = "START_TIME_MS";
61     public static final String DYNAMIC_CONFIG_OVERRIDE_URL = "DYNAMIC_CONFIG_OVERRIDE_URL";
62 
63     /* API Key for compatibility test project, used for dynamic configuration */
64     private static final String API_KEY = "AIzaSyAbwX5JRlmsLeygY2WWihpIJPXFLueOQ3U";
65 
66     @Option(name="branch", description="build branch name to supply.")
67     private String mBranch = null;
68 
69     @Option(name = "build-id",
70             description =
71                     "build version number to supply. Override the default cts version number.")
72     private String mBuildId = null;
73 
74     @Option(name="build-flavor", description="build flavor name to supply.")
75     private String mBuildFlavor = null;
76 
77     @Option(
78         name = "build-flavor-prefix",
79         description = "allow for a prefix to be inserted into build flavor."
80     )
81     private String mBuildFlavorPrefix = null;
82 
83     @Option(name="build-target", description="build target name to supply.")
84     private String mBuildTarget = null;
85 
86     @Option(name="build-attribute", description="build attributes to supply.")
87     private Map<String, String> mBuildAttributes = new HashMap<String,String>();
88 
89     @Option(name="use-device-build-info", description="Bootstrap build info from device")
90     private boolean mUseDeviceBuildInfo = false;
91 
92     @Option(name = "dynamic-config-url",
93             description = "Specify the url for override config")
94     private String mURL = "https://androidpartner.googleapis.com/v1/dynamicconfig/"
95             + "suites/{suite-name}/modules/{module}/version/{version}?key=" + API_KEY;
96 
97     @Option(name = "url-suite-name-override",
98             description = "Override the name that should used to replace the {suite-name} "
99                     + "pattern in the dynamic-config-url.")
100     private String mUrlSuiteNameOverride = null;
101 
102     @Option(name = "plan",
103             description = "the test suite plan to run, such as \"everything\" or \"cts\"",
104             importance = Importance.ALWAYS)
105     private String mSuitePlan;
106 
107     private String mTestTag;
108     private File mArtificialRootDir;
109 
110     /**
111      * Util method to inject build attributes into supplied {@link IBuildInfo}
112      * @param buildInfo
113      */
injectBuildAttributes(IBuildInfo buildInfo)114     private void injectBuildAttributes(IBuildInfo buildInfo) {
115         for (Map.Entry<String, String> entry : mBuildAttributes.entrySet()) {
116             buildInfo.addBuildAttribute(entry.getKey(), entry.getValue());
117         }
118         if (mTestTag != null) {
119             buildInfo.setTestTag(mTestTag);
120         }
121     }
122 
123     /**
124      * {@inheritDoc}
125      */
126     @Override
setInvocationContext(IInvocationContext invocationContext)127     public void setInvocationContext(IInvocationContext invocationContext) {
128         mTestTag = invocationContext.getTestTag();
129     }
130 
131     /**
132      * {@inheritDoc}
133      */
134     @Override
getBuild()135     public IBuildInfo getBuild() throws BuildRetrievalError {
136         // Create a blank BuildInfo which will get populated later.
137         String version = null;
138         if (mBuildId != null) {
139             version = mBuildId;
140         } else {
141             version = getSuiteInfoBuildNumber();
142             if (version == null) {
143                 version = IBuildInfo.UNKNOWN_BUILD_ID;
144             }
145         }
146         IBuildInfo ctsBuild = new DeviceBuildInfo(version, mBuildTarget);
147         if (mBranch  != null) {
148             ctsBuild.setBuildBranch(mBranch);
149         }
150         if (mBuildFlavor != null) {
151             String buildFlavor = mBuildFlavor;
152             if (mBuildFlavorPrefix != null) {
153                 buildFlavor = mBuildFlavorPrefix + buildFlavor;
154             }
155             ctsBuild.setBuildFlavor(buildFlavor);
156         }
157         injectBuildAttributes(ctsBuild);
158         addCompatibilitySuiteInfo(ctsBuild);
159         return ctsBuild;
160     }
161 
162     /**
163      * {@inheritDoc}
164      */
165     @Override
getBuild(ITestDevice device)166     public IBuildInfo getBuild(ITestDevice device)
167             throws BuildRetrievalError, DeviceNotAvailableException {
168         if (!mUseDeviceBuildInfo) {
169             // return a regular build info without extracting device attributes into standard
170             // build info fields
171             return getBuild();
172         } else {
173             if (mBuildId == null) {
174                 mBuildId = device.getBuildId();
175             }
176             String buildFlavor = mBuildFlavor;
177             if (buildFlavor == null) {
178                 buildFlavor = device.getBuildFlavor();
179             }
180             if (mBuildFlavorPrefix != null) {
181                 buildFlavor = mBuildFlavorPrefix + buildFlavor;
182             }
183             if (mBuildTarget == null) {
184                 String name = device.getProperty("ro.product.name");
185                 String variant = device.getProperty("ro.build.type");
186                 mBuildTarget = name + "-" + variant;
187             }
188             IBuildInfo info = new DeviceBuildInfo(mBuildId, mBuildTarget);
189             if (mBranch == null) {
190                 // if branch is not specified via param, make a pseudo branch name based on platform
191                 // version and product info from device
192                 mBranch = String.format("%s-%s-%s-%s",
193                         device.getProperty("ro.product.brand"),
194                         device.getProperty("ro.product.name"),
195                         device.getProductVariant(),
196                         device.getProperty("ro.build.version.release"));
197             }
198             info.setBuildBranch(mBranch);
199             info.setBuildFlavor(buildFlavor);
200             String buildAlias = device.getBuildAlias();
201             if (RELEASE_BUILD.matcher(buildAlias).matches()) {
202                 info.addBuildAttribute("build_alias", buildAlias);
203             }
204             injectBuildAttributes(info);
205             addCompatibilitySuiteInfo(info);
206             return info;
207         }
208     }
209 
210     /**
211      * {@inheritDoc}
212      */
213     @Override
buildNotTested(IBuildInfo info)214     public void buildNotTested(IBuildInfo info) {
215         // ignore
216     }
217 
218     /**
219      * {@inheritDoc}
220      */
221     @Override
cleanUp(IBuildInfo info)222     public void cleanUp(IBuildInfo info) {
223         // Everything should have been copied properly to result folder, we clean up
224         if (info instanceof IDeviceBuildInfo) {
225             List<File> doNotDelete = new ArrayList<>();
226             // Clean up everything except the tests dir
227             doNotDelete.add(((IDeviceBuildInfo) info).getTestsDir());
228             info.cleanUp(doNotDelete);
229         } else {
230             info.cleanUp();
231         }
232         FileUtil.recursiveDelete(mArtificialRootDir);
233     }
234 
addCompatibilitySuiteInfo(IBuildInfo info)235     private void addCompatibilitySuiteInfo(IBuildInfo info) {
236         long startTimeMs = System.currentTimeMillis();
237         info.addBuildAttribute(SUITE_BUILD, getSuiteInfoBuildNumber());
238         info.addBuildAttribute(SUITE_NAME, getSuiteInfoName());
239         info.addBuildAttribute(SUITE_FULL_NAME, getSuiteInfoFullname());
240         info.addBuildAttribute(SUITE_VERSION, getSuiteInfoVersion());
241         info.addBuildAttribute(SUITE_PLAN, mSuitePlan);
242         info.addBuildAttribute(START_TIME_MS, Long.toString(startTimeMs));
243         info.addBuildAttribute(RESULT_DIR, getDirSuffix(startTimeMs));
244         String rootDirPath = getRootDirPath();
245         if (rootDirPath == null || rootDirPath.trim().equals("")) {
246             throw new IllegalArgumentException(
247                     String.format("Missing install path property %s_ROOT", getSuiteInfoName()));
248         }
249         File rootDir = new File(rootDirPath);
250         if (!rootDir.exists()) {
251             throw new IllegalArgumentException(
252                     String.format("Root directory doesn't exist %s", rootDir.getAbsolutePath()));
253         }
254         info.addBuildAttribute(ROOT_DIR, rootDir.getAbsolutePath());
255         // For DeviceBuildInfo we populate the testsDir folder of the build info.
256         if (info instanceof IDeviceBuildInfo) {
257             if (mArtificialRootDir == null) {
258                 // If the real CTS directory is used, do not copy it again.
259                 info.setProperties(
260                         BuildInfoProperties.DO_NOT_LINK_TESTS_DIR,
261                         BuildInfoProperties.DO_NOT_COPY_ON_SHARDING);
262             } else {
263                 info.setProperties(BuildInfoProperties.DO_NOT_COPY_ON_SHARDING);
264             }
265             File testDir = new File(rootDir, String.format("android-%s/testcases/",
266                     getSuiteInfoName().toLowerCase()));
267             ((IDeviceBuildInfo) info).setTestsDir(testDir, "0");
268         }
269         if (mURL != null && !mURL.isEmpty()) {
270             String suiteName = mUrlSuiteNameOverride;
271             if (suiteName == null) {
272                 suiteName = getSuiteInfoName();
273             }
274             info.addBuildAttribute(DYNAMIC_CONFIG_OVERRIDE_URL,
275                     mURL.replace("{suite-name}", suiteName));
276         }
277     }
278 
279     /**
280      * Returns the CTS_ROOT variable that the harness was started with.
281      */
282     @VisibleForTesting
getRootDirPath()283     String getRootDirPath() {
284         String varName = String.format("%s_ROOT", getSuiteInfoName());
285         String rootDirVariable = System.getProperty(varName);
286         if (rootDirVariable != null) {
287             return rootDirVariable;
288         }
289         // Create an artificial root dir, we are most likely running from Tradefed directly.
290         try {
291             mArtificialRootDir = FileUtil.createTempDir(
292                     String.format("%s-root-dir", getSuiteInfoName()));
293             new File(mArtificialRootDir, String.format("android-%s/testcases",
294                     getSuiteInfoName().toLowerCase())).mkdirs();
295             return mArtificialRootDir.getAbsolutePath();
296         } catch (IOException e) {
297             throw new RuntimeException(
298                     String.format("%s was not set, and couldn't create an artificial one.",
299                             varName));
300         }
301     }
302 
303     /**
304      * Return the SuiteInfo name generated at build time. Exposed for testing.
305      */
getSuiteInfoName()306     protected String getSuiteInfoName() {
307         return TestSuiteInfo.getInstance().getName();
308     }
309 
310     /**
311      * Return the SuiteInfo build number generated at build time. Exposed for testing.
312      */
getSuiteInfoBuildNumber()313     protected String getSuiteInfoBuildNumber() {
314         String buildNumber = TestSuiteInfo.getInstance().getBuildNumber();
315         String versionFile = VersionParser.fetchVersion();
316         if (versionFile != null) {
317             buildNumber = versionFile;
318         }
319         return buildNumber;
320     }
321 
322     /**
323      * Return the SuiteInfo fullname generated at build time. Exposed for testing.
324      */
getSuiteInfoFullname()325     protected String getSuiteInfoFullname() {
326         return TestSuiteInfo.getInstance().getFullName();
327     }
328 
329     /**
330      * Return the SuiteInfo version generated at build time. Exposed for testing.
331      */
getSuiteInfoVersion()332     protected String getSuiteInfoVersion() {
333         return TestSuiteInfo.getInstance().getVersion();
334     }
335 
336     /**
337      * @return a {@link String} to use for directory suffixes created from the given time.
338      */
getDirSuffix(long millis)339     private String getDirSuffix(long millis) {
340         return new SimpleDateFormat("yyyy.MM.dd_HH.mm.ss").format(new Date(millis));
341     }
342 }
343