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.compatibility.SuiteInfo;
20 import com.android.tradefed.build.BuildInfo;
21 import com.android.tradefed.build.BuildRetrievalError;
22 import com.android.tradefed.build.DeviceBuildInfo;
23 import com.android.tradefed.build.IBuildInfo;
24 import com.android.tradefed.build.IBuildProvider;
25 import com.android.tradefed.build.IDeviceBuildInfo;
26 import com.android.tradefed.build.IDeviceBuildProvider;
27 import com.android.tradefed.config.Option;
28 import com.android.tradefed.config.Option.Importance;
29 import com.android.tradefed.config.OptionClass;
30 import com.android.tradefed.device.DeviceNotAvailableException;
31 import com.android.tradefed.device.ITestDevice;
32 
33 import java.io.File;
34 import java.text.SimpleDateFormat;
35 import java.util.Date;
36 import java.util.HashMap;
37 import java.util.Map;
38 import java.util.regex.Pattern;
39 /**
40  * A simple {@link IBuildProvider} that uses a pre-existing Compatibility install.
41  */
42 @OptionClass(alias="compatibility-build-provider")
43 public class CompatibilityBuildProvider implements IDeviceBuildProvider {
44 
45     private static final Pattern RELEASE_BUILD = Pattern.compile("^[A-Z]{3}\\d{2}[A-Z]{0,1}$");
46     private static final String ROOT_DIR = "ROOT_DIR";
47     private static final String SUITE_BUILD = "SUITE_BUILD";
48     private static final String SUITE_NAME = "SUITE_NAME";
49     private static final String SUITE_FULL_NAME = "SUITE_FULL_NAME";
50     private static final String SUITE_VERSION = "SUITE_VERSION";
51     private static final String SUITE_PLAN = "SUITE_PLAN";
52     private static final String RESULT_DIR = "RESULT_DIR";
53     private static final String START_TIME_MS = "START_TIME_MS";
54     private static final String DYNAMIC_CONFIG_OVERRIDE_URL = "DYNAMIC_CONFIG_OVERRIDE_URL";
55 
56     /* API Key for compatibility test project, used for dynamic configuration */
57     private static final String API_KEY = "AIzaSyAbwX5JRlmsLeygY2WWihpIJPXFLueOQ3U";
58 
59     @Option(name="branch", description="build branch name to supply.")
60     private String mBranch = null;
61 
62     @Option(name = "build-id",
63             description =
64                     "build version number to supply. Override the default cts version number.")
65     private String mBuildId = null;
66 
67     @Option(name="build-flavor", description="build flavor name to supply.")
68     private String mBuildFlavor = null;
69 
70     @Option(name="build-attribute", description="build attributes to supply.")
71     private Map<String, String> mBuildAttributes = new HashMap<String,String>();
72 
73     @Option(name="use-device-build-info", description="Bootstrap build info from device")
74     private boolean mUseDeviceBuildInfo = false;
75 
76     @Option(name="test-tag", description="test tag name to supply.")
77     private String mTestTag = "cts";
78 
79     @Option(name = "dynamic-config-url",
80             description = "Specify the url for override config")
81     private String mURL = "https://androidpartner.googleapis.com/v1/dynamicconfig/"
82             + "suites/{suite-name}/modules/{module}/version/{version}?key=" + API_KEY;
83 
84     @Option(name = "plan",
85             description = "the test suite plan to run, such as \"everything\" or \"cts\"",
86             importance = Importance.ALWAYS)
87     private String mSuitePlan;
88 
89     /**
90      * Util method to inject build attributes into supplied {@link IBuildInfo}
91      * @param buildInfo
92      */
injectBuildAttributes(IBuildInfo buildInfo)93     private void injectBuildAttributes(IBuildInfo buildInfo) {
94         for (Map.Entry<String, String> entry : mBuildAttributes.entrySet()) {
95             buildInfo.addBuildAttribute(entry.getKey(), entry.getValue());
96         }
97     }
98 
99     /**
100      * {@inheritDoc}
101      */
102     @Override
getBuild()103     public IBuildInfo getBuild() {
104         // Create a blank BuildInfo which will get populated later.
105         String version = null;
106         if (mBuildId != null) {
107             version = mBuildId;
108         } else {
109             version = getSuiteInfoBuildNumber();
110             if (version == null) {
111                 version = IBuildInfo.UNKNOWN_BUILD_ID;
112             }
113         }
114         IBuildInfo ctsBuild = new BuildInfo(version, mTestTag);
115         if (mBranch  != null) {
116             ctsBuild.setBuildBranch(mBranch);
117         }
118         if (mBuildFlavor != null) {
119             ctsBuild.setBuildFlavor(mBuildFlavor);
120         }
121         injectBuildAttributes(ctsBuild);
122         addCompatibilitySuiteInfo(ctsBuild);
123         return ctsBuild;
124     }
125 
126     /**
127      * {@inheritDoc}
128      */
129     @Override
getBuild(ITestDevice device)130     public IBuildInfo getBuild(ITestDevice device)
131             throws BuildRetrievalError, DeviceNotAvailableException {
132         if (!mUseDeviceBuildInfo) {
133             // return a regular build info without extracting device attributes into standard
134             // build info fields
135             return getBuild();
136         } else {
137             String buildId = device.getBuildId();
138             String buildFlavor = device.getBuildFlavor();
139             IBuildInfo info = new DeviceBuildInfo(buildId, mTestTag);
140             if (mBranch == null) {
141                 // if branch is not specified via param, make a pseudo branch name based on platform
142                 // version and product info from device
143                 mBranch = String.format("%s-%s-%s-%s",
144                         device.getProperty("ro.product.brand"),
145                         device.getProperty("ro.product.name"),
146                         device.getProductVariant(),
147                         device.getProperty("ro.build.version.release"));
148             }
149             info.setBuildBranch(mBranch);
150             info.setBuildFlavor(buildFlavor);
151             String buildAlias = device.getBuildAlias();
152             if (RELEASE_BUILD.matcher(buildAlias).matches()) {
153                 info.addBuildAttribute("build_alias", buildAlias);
154             }
155             injectBuildAttributes(info);
156             addCompatibilitySuiteInfo(info);
157             return info;
158         }
159     }
160 
161     /**
162      * {@inheritDoc}
163      */
164     @Override
buildNotTested(IBuildInfo info)165     public void buildNotTested(IBuildInfo info) {
166         // ignore
167     }
168 
169     /**
170      * {@inheritDoc}
171      */
172     @Override
cleanUp(IBuildInfo info)173     public void cleanUp(IBuildInfo info) {
174         // ignore
175     }
176 
addCompatibilitySuiteInfo(IBuildInfo info)177     private void addCompatibilitySuiteInfo(IBuildInfo info) {
178         long startTimeMs = System.currentTimeMillis();
179         info.addBuildAttribute(SUITE_BUILD, getSuiteInfoBuildNumber());
180         info.addBuildAttribute(SUITE_NAME, getSuiteInfoName());
181         info.addBuildAttribute(SUITE_FULL_NAME, getSuiteInfoFullname());
182         info.addBuildAttribute(SUITE_VERSION, getSuiteInfoVersion());
183         info.addBuildAttribute(SUITE_PLAN, mSuitePlan);
184         info.addBuildAttribute(START_TIME_MS, Long.toString(startTimeMs));
185         info.addBuildAttribute(RESULT_DIR, getDirSuffix(startTimeMs));
186         String rootDirPath = getRootDirPath();
187         if (rootDirPath == null || rootDirPath.trim().equals("")) {
188             throw new IllegalArgumentException(
189                     String.format("Missing install path property %s_ROOT", getSuiteInfoName()));
190         }
191         File rootDir = new File(rootDirPath);
192         if (!rootDir.exists()) {
193             throw new IllegalArgumentException(
194                     String.format("Root directory doesn't exist %s", rootDir.getAbsolutePath()));
195         }
196         info.addBuildAttribute(ROOT_DIR, rootDir.getAbsolutePath());
197         // For DeviceBuildInfo we populate the testsDir folder of the build info.
198         if (info instanceof IDeviceBuildInfo) {
199             File testDir =  new File(rootDir, String.format("android-%s/testcases/",
200                     getSuiteInfoName().toLowerCase()));
201             ((IDeviceBuildInfo) info).setTestsDir(testDir, "0");
202         }
203         if (mURL != null && !mURL.isEmpty()) {
204             info.addBuildAttribute(DYNAMIC_CONFIG_OVERRIDE_URL,
205                     mURL.replace("{suite-name}", getSuiteInfoName()));
206         }
207     }
208 
209     /**
210      * Returns the CTS_ROOT variable that the harness was started with.
211      */
212     @VisibleForTesting
getRootDirPath()213     String getRootDirPath() {
214         return System.getProperty(String.format("%s_ROOT", getSuiteInfoName()));
215     }
216 
217     /**
218      * Return the SuiteInfo name generated at build time. Exposed for testing.
219      */
getSuiteInfoName()220     protected String getSuiteInfoName() {
221         return SuiteInfo.NAME;
222     }
223 
224     /**
225      * Return the SuiteInfo build number generated at build time. Exposed for testing.
226      */
getSuiteInfoBuildNumber()227     protected String getSuiteInfoBuildNumber() {
228         return SuiteInfo.BUILD_NUMBER;
229     }
230 
231     /**
232      * Return the SuiteInfo fullname generated at build time. Exposed for testing.
233      */
getSuiteInfoFullname()234     protected String getSuiteInfoFullname() {
235         return SuiteInfo.FULLNAME;
236     }
237 
238     /**
239      * Return the SuiteInfo version generated at build time. Exposed for testing.
240      */
getSuiteInfoVersion()241     protected String getSuiteInfoVersion() {
242         return SuiteInfo.VERSION;
243     }
244 
245     /**
246      * @return a {@link String} to use for directory suffixes created from the given time.
247      */
getDirSuffix(long millis)248     private String getDirSuffix(long millis) {
249         return new SimpleDateFormat("yyyy.MM.dd_HH.mm.ss").format(new Date(millis));
250     }
251 }
252