1 /*
2  * Copyright (C) 2018 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 static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.fail;
21 import static org.mockito.Mockito.when;
22 
23 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
24 import com.android.tradefed.build.BuildInfo;
25 import com.android.tradefed.build.IBuildInfo;
26 import com.android.tradefed.config.OptionSetter;
27 import com.android.tradefed.device.ITestDevice;
28 import com.android.tradefed.util.FileUtil;
29 
30 import org.junit.After;
31 import org.junit.Before;
32 import org.junit.Test;
33 import org.junit.runner.RunWith;
34 import org.junit.runners.JUnit4;
35 import org.mockito.Mockito;
36 
37 import java.io.File;
38 import java.io.FileOutputStream;
39 import java.io.IOException;
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.HashMap;
43 import java.util.HashSet;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Set;
47 
48 
49 /**
50  * Unit test for {@link BusinessLogicPreparer}.
51  */
52 
53 @RunWith(JUnit4.class)
54 public class BusinessLogicPreparerTest {
55     private ITestDevice mMockDevice;
56     private IBuildInfo mMockBuildInfo;
57     private BusinessLogicPreparer mPreparer;
58     private Set<String> mPackages;
59     private File mTmpDir;
60 
61     private static final String MEMORY_DEVICE_INFO_JSON =
62             "{\n" +
63             "    \"low_ram_device\": false,\n" +
64             "    \"memory_class\": 192,\n" +
65             "    \"large_memory_class\": 512,\n" +
66             "    \"total_memory\": 1902936064\n" +
67             "}\n";
68     private static final String MANUFACTURER_PROPERTY = "ro.product.manufacturer";
69     private static final String CONFIG_VERSION = "DYNAMIC_CONFIG_FILE:";
70     private static final String CONFIG_FILE_CONTENT =
71             "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
72                     "<dynamicConfig>\n" +
73                     "    <entry key=\"remote_config_required\">\n" +
74                     "        <value>false</value>\n" +
75                     "    </entry>\n" +
76                     "    <entry key=\"business_logic_device_features\">\n" +
77                     "        <value>android.hardware.type.automotive</value>\n" +
78                     "        <value>android.hardware.type.television</value>\n" +
79                     "        <value>android.hardware.type.watch</value>\n" +
80                     "        <value>android.hardware.type.embedded</value>\n" +
81                     "        <value>android.hardware.type.pc</value>\n" +
82                     "        <value>android.software.leanback</value>\n" +
83                     "    </entry>\n" +
84                     "    <entry key=\"business_logic_device_properties\">\n" +
85                     "        <value>ro.product.brand</value>\n" +
86                     "        <value>ro.product.first_api_level</value>\n" +
87                     "        <value>ro.product.manufacturer</value>\n" +
88                     "        <value>ro.product.model</value>\n" +
89                     "        <value>ro.product.name</value>\n" +
90                     "    </entry>\n" +
91                     "    <entry key=\"business_logic_device_packages\">\n" +
92                     "        <value>com.google.android.gms</value>\n" +
93                     "        <value>com.android.vending</value>\n" +
94                     "    </entry>\n" +
95                     "    <entry key=\"business_logic_extended_device_info\">\n" +
96                     "        <value>MemoryDeviceInfo:total_memory</value>\n" +
97                     "        <value>MemoryDeviceInfo:low_ram_device</value>\n" +
98                     "    </entry>\n" +
99                     "</dynamicConfig>\n";
100     private static final String LIST_FEATURE_QUERY = "pm list features";
101     private static final String FEATURE_WATCH = "android.hardware.type.watch";
102     private static final String FEATURE_LEANBACK = "android.software.leanback";
103     private static final String FEATURES =
104             "feature:" + FEATURE_WATCH + "\n" +
105             "feature:android.hardware.audio.low_latency\n" +
106             "feature:android.hardware.camera\n" +
107             "feature:android.hardware.microphone\n" +
108             "feature:android.hardware.nfc\n" +
109             "feature:android.hardware.telephony\n" +
110             "feature:android.hardware.wifi\n" +
111             "feature:" + FEATURE_LEANBACK + "\n";
112     private static final String GOOGLE_SETTINGS_QUERY =
113             "content query --uri content://com.google.settings/partner";
114     private static final String PARTNER_CONTENT =
115             "Row: 0 _id=35, name=use_location_for_services, value=1\n" +
116             "Row: 1 _id=57, name=network_location_opt_in, value=1\n" +
117             "Row: 2 _id=162, name=data_store_version, value=3\n" +
118             "Row: 3 _id=163, name=client_id, value=android-google\n" +
119             "Row: 4 _id=164, name=search_client_id, value=ms-android-google\n";
120     private static final String RO_BRAND = "ro.product.brand";
121     private static final String RO_FIRST_API_LEVEL = "ro.product.first_api_level";
122     private static final String RO_MANIFACTURER = "ro.product.manufacturer";
123     private static final String RO_MODEL = "ro.product.model";
124     private static final String RO_NAME = "ro.product.name";
125     private static final long MEMORY_SIZE = 1902936064;
126 
127     private String serviceUrl = "https://androidpartner.googleapis.com/v1/dynamicconfig/" +
128             "suites/{suite-name}/modules/{module}/version/{version}?key=123";
129 
130     @Before
setUp()131     public void setUp() throws Exception {
132         mTmpDir = FileUtil.createTempDir("business-logic-unit-tests");
133         mMockDevice = Mockito.mock(ITestDevice.class);
134         mMockBuildInfo = new BuildInfo();
135         mPreparer = new BusinessLogicPreparer() {
136             @Override
137             String getSuiteName() {
138                 return "cts";
139             }
140         };
141 
142         OptionSetter setter = new OptionSetter(mPreparer);
143         setter.setOptionValue("business-logic-url", serviceUrl);
144         setter.setOptionValue("business-logic-api-key", "fakeApiKey");
145         setter.setOptionValue("version", "fakeVersion");
146     }
147 
148     @After
tearDown()149     public void tearDown() throws Exception {
150         FileUtil.recursiveDelete(mTmpDir);
151     }
152 
153     @Test
testBuildRequestString_success()154     public void testBuildRequestString_success() throws Exception {
155         Map<String, String> attributes = new HashMap<>();
156         // Create a memory device info JSON file for test.
157         File jsonPath = createTestDeviceInfoJSONFile("MemoryDeviceInfo", MEMORY_DEVICE_INFO_JSON);
158         mMockBuildInfo.setFile(DeviceInfoCollector.DEVICE_INFO_DIR, jsonPath, "v1");
159         // Setup BuildInfo attributes.
160         mMockBuildInfo.addBuildAttribute(CompatibilityBuildHelper.SUITE_VERSION, "v1");
161         testBuildRequestString(16, attributes);
162     }
163 
164     @Test
testBuildRequestString_deviceDirDoesntExists()165     public void testBuildRequestString_deviceDirDoesntExists() throws Exception {
166         Map<String, String> attributes = new HashMap<>();
167         // Setup BuildInfo attributes.
168         attributes.put(CompatibilityBuildHelper.SUITE_VERSION, "v1");
169         testBuildRequestString(14, attributes);
170     }
171 
172     @Test
testBuildRequestString_noDeviceInfoJSONFileExists()173     public void testBuildRequestString_noDeviceInfoJSONFileExists() throws Exception {
174         Map<String, String> attributes = new HashMap<>();
175         // Create a memory device info JSON file for test.
176         File jsonPath = createTestDeviceInfoTextFile("MemoryDeviceInfo");
177         mMockBuildInfo.setFile(DeviceInfoCollector.DEVICE_INFO_DIR, jsonPath, "v1");
178         // Setup BuildInfo attributes.
179         mMockBuildInfo.addBuildAttribute(CompatibilityBuildHelper.SUITE_VERSION, "v1");
180         testBuildRequestString(14, attributes);
181     }
182 
testBuildRequestString(int expectedParams, Map<String, String> attributes)183     private void testBuildRequestString(int expectedParams, Map<String, String> attributes) throws Exception {
184         for (String key: attributes.keySet()) {
185             mMockBuildInfo.addBuildAttribute(key, attributes.get(key));
186         }
187         when(mMockDevice.getProperty(MANUFACTURER_PROPERTY)).thenReturn("MANUFACTURER_NAME");
188         // In getBusinessLogicFeatures
189         File configFile = createFileFromStr(CONFIG_FILE_CONTENT);
190         // BusinessLogicPreparer.getSuiteName() calls TestSuiteInfo.getName()
191         // That returns "tests" on local machine.
192         // That returns "gts" in presumit.
193         mMockBuildInfo.setFile(CONFIG_VERSION + "tests", configFile, CONFIG_VERSION + "tests");
194         mMockBuildInfo.setFile(CONFIG_VERSION + "gts", configFile, CONFIG_VERSION + "gts");
195         mMockBuildInfo.setFile(CONFIG_VERSION + "cts", configFile, CONFIG_VERSION + "cts");
196         mMockBuildInfo.setFile(CONFIG_VERSION + "ats", configFile, CONFIG_VERSION + "ats");
197         when(mMockDevice.executeShellCommand(LIST_FEATURE_QUERY)).thenReturn(FEATURES);
198         // In getBusinessLogicProperties.
199         when(mMockDevice.executeShellCommand(GOOGLE_SETTINGS_QUERY)).thenReturn(PARTNER_CONTENT);
200         when(mMockDevice.getProperty(RO_BRAND)).thenReturn("BRAND_NAME");
201         when(mMockDevice.getProperty(RO_FIRST_API_LEVEL)).thenReturn("26");
202         when(mMockDevice.getProperty(RO_MANIFACTURER)).thenReturn("MANUFACTURER_NAME");
203         when(mMockDevice.getProperty(RO_MODEL)).thenReturn("fake_model");
204         when(mMockDevice.getProperty(RO_NAME)).thenReturn("fake_name");
205         // Extra info
206         List<String> pkgNameList = Arrays.asList("com.android.vending", "com.google.android.gms",
207             "com.google.android.youtube", "com.google.android.apps.photos");
208         mPackages = new HashSet<>(pkgNameList);
209         when(mMockDevice.getInstalledPackageNames()).thenReturn(mPackages);
210         when(mMockDevice.getTotalMemory()).thenReturn(MEMORY_SIZE);
211 
212         ArrayList<String> expectFeatures = new ArrayList<String>(
213             Arrays.asList(FEATURE_WATCH, FEATURE_LEANBACK));
214         ArrayList<String> expectProperties = new ArrayList<String>(
215             Arrays.asList("search_client_id", "client_id", RO_BRAND, RO_FIRST_API_LEVEL,
216                 RO_MANIFACTURER, RO_MODEL, RO_NAME));
217         ArrayList<String> expectPropertyValues = new ArrayList<String>(
218             Arrays.asList("ms-android-google", "android-google", "BRAND_NAME", "26", "MANUFACTURER_NAME",
219                 "fake_model", "fake_name"));
220         ArrayList<String> expectPackages = new ArrayList<String>(pkgNameList);
221         ArrayList<String> expectDeviceInfos = new ArrayList<String>(
222             Arrays.asList("MemoryDeviceInfo%3Atotal_memory%3A1902936064",
223             "MemoryDeviceInfo%3Alow_ram_device%3Afalse"));
224 
225         String[] params = mPreparer.buildRequestParams(mMockDevice, mMockBuildInfo).split("&");
226         assertEquals(expectedParams, params.length);
227 
228         for (String param: params){
229             String keyVal[] = param.split("=");
230 
231             if(keyVal[0].startsWith("features")) {
232                 if(expectFeatures.contains(keyVal[1])) {
233                     expectFeatures.remove(keyVal[1]);
234                 } else {
235                     fail("Found unknown Feature string");
236                 }
237             }
238             if (keyVal[0].startsWith("properties")) {
239                 String property[] = keyVal[1].split("%3A");
240                 if(expectProperties.contains(property[0])) {
241                     assertEquals(expectPropertyValues.get(expectProperties.indexOf(property[0])),
242                             property[1]);
243                     expectProperties.remove(property[0]);
244                     expectPropertyValues.remove(property[1]);
245                 } else {
246                     fail("Found unknown Property string");
247                 }
248             }
249             if (keyVal[0].startsWith("package")) {
250                 if(expectPackages.contains(keyVal[1])) {
251                     expectPackages.remove(keyVal[1]);
252                 } else {
253                     fail("Found unknown Package string");
254                 }
255             }
256             if (keyVal[0].startsWith("device_info")) {
257                 if(expectDeviceInfos.contains(keyVal[1])) {
258                     expectDeviceInfos.remove(keyVal[1]);
259                 } else {
260                     fail("Found unknown Feature string");
261                 }
262             }
263         }
264         assertEquals(expectFeatures.size(), 0);
265         assertEquals(expectProperties.size(), 0);
266         assertEquals(expectPackages.size(), 2);
267     }
268 
createTestDeviceInfoJSONFile(String DeviceInfoClassName, String jsonStr)269     private File createTestDeviceInfoJSONFile(String DeviceInfoClassName, String jsonStr)
270         throws IOException {
271         File file = new File(mTmpDir, DeviceInfoClassName + ".deviceinfo.json");
272         FileOutputStream stream = new FileOutputStream(file);
273         stream.write(jsonStr.getBytes());
274         stream.flush();
275         stream.close();
276         return mTmpDir;
277     }
278 
createTestDeviceInfoTextFile(String DeviceInfoClassName)279     private File createTestDeviceInfoTextFile(String DeviceInfoClassName)
280         throws IOException {
281         new File(mTmpDir, DeviceInfoClassName + ".deviceinfo.text");
282         return mTmpDir;
283     }
284 
createFileFromStr(String configStr)285     private File createFileFromStr(String configStr) throws IOException {
286         File file = File.createTempFile("test", "dynamic");
287         FileOutputStream stream = new FileOutputStream(file);
288         stream.write(configStr.getBytes());
289         stream.flush();
290         stream.close();
291         return file;
292     }
293 }
294