1 /*
2  * Copyright (C) 2019 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.server.am;
18 
19 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
20 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
21 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
22 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
23 
24 import android.content.ContentResolver;
25 import android.os.SystemProperties;
26 import android.provider.Settings;
27 import android.text.TextUtils;
28 
29 import com.android.dx.mockito.inline.extended.ExtendedMockito;
30 
31 import org.junit.After;
32 import org.junit.Assert;
33 import org.junit.Before;
34 import org.junit.Test;
35 import org.mockito.Answers;
36 import org.mockito.Mock;
37 import org.mockito.MockitoSession;
38 import org.mockito.quality.Strictness;
39 import org.mockito.stubbing.Answer;
40 
41 import java.util.Arrays;
42 import java.util.HashMap;
43 import java.util.HashSet;
44 import java.util.List;
45 
46 /**
47  * Test SettingsToPropertiesMapper.
48  */
49 public class SettingsToPropertiesMapperTest {
50     private static final String NAME_VALID_CHARACTERS_REGEX = "^[\\w\\-@:]*$";
51     private static final String[] TEST_MAPPING = new String[] {
52             Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS
53     };
54 
55     private MockitoSession mSession;
56 
57     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
58     private ContentResolver mMockContentResolver;
59 
60     private SettingsToPropertiesMapper mTestMapper;
61 
62     private HashMap<String, String> mSystemSettingsMap;
63     private HashMap<String, String> mGlobalSettingsMap;
64 
65     @Before
setUp()66     public void setUp() throws Exception {
67         mSession =
68                 ExtendedMockito.mockitoSession().initMocks(
69                         this)
70                         .strictness(Strictness.LENIENT)
71                         .spyStatic(SystemProperties.class)
72                         .spyStatic(Settings.Global.class)
73                         .spyStatic(SettingsToPropertiesMapper.class)
74                         .startMocking();
75         mSystemSettingsMap = new HashMap<>();
76         mGlobalSettingsMap = new HashMap<>();
77 
78         // Mock SystemProperties setter and various getters
79         doAnswer((Answer<Void>) invocationOnMock -> {
80                     String key = invocationOnMock.getArgument(0);
81                     String value = invocationOnMock.getArgument(1);
82 
83                     mSystemSettingsMap.put(key, value);
84                     return null;
85                 }
86         ).when(() -> SystemProperties.set(anyString(), anyString()));
87 
88         doAnswer((Answer<String>) invocationOnMock -> {
89                     String key = invocationOnMock.getArgument(0);
90 
91                     String storedValue = mSystemSettingsMap.get(key);
92                     return storedValue == null ? "" : storedValue;
93                 }
94         ).when(() -> SystemProperties.get(anyString()));
95 
96         // Mock Settings.Global methods
97         doAnswer((Answer<String>) invocationOnMock -> {
98                     String key = invocationOnMock.getArgument(1);
99 
100                     return mGlobalSettingsMap.get(key);
101                 }
102         ).when(() -> Settings.Global.getString(any(), anyString()));
103 
104         mTestMapper = new SettingsToPropertiesMapper(
105             mMockContentResolver, TEST_MAPPING, new String[] {});
106     }
107 
108     @After
tearDown()109     public void tearDown() throws Exception {
110         mSession.finishMocking();
111     }
112 
113     @Test
validateRegisteredGlobalSettings()114     public void validateRegisteredGlobalSettings() {
115         HashSet<String> hashSet = new HashSet<>();
116         for (String globalSetting : SettingsToPropertiesMapper.sGlobalSettings) {
117             if (hashSet.contains(globalSetting)) {
118                 Assert.fail("globalSetting "
119                         + globalSetting
120                         + " is registered more than once in "
121                         + "SettingsToPropertiesMapper.sGlobalSettings.");
122             }
123             hashSet.add(globalSetting);
124             if (TextUtils.isEmpty(globalSetting)) {
125                 Assert.fail("empty globalSetting registered.");
126             }
127             if (!globalSetting.matches(NAME_VALID_CHARACTERS_REGEX)) {
128                 Assert.fail(globalSetting + " contains invalid characters. "
129                         + "Only alphanumeric characters, '-', '@', ':' and '_' are valid.");
130             }
131         }
132     }
133 
134     @Test
validateRegisteredDeviceConfigScopes()135     public void validateRegisteredDeviceConfigScopes() {
136         HashSet<String> hashSet = new HashSet<>();
137         for (String deviceConfigScope : SettingsToPropertiesMapper.sDeviceConfigScopes) {
138             if (hashSet.contains(deviceConfigScope)) {
139                 Assert.fail("deviceConfigScope "
140                         + deviceConfigScope
141                         + " is registered more than once in "
142                         + "SettingsToPropertiesMapper.sDeviceConfigScopes.");
143             }
144             hashSet.add(deviceConfigScope);
145             if (TextUtils.isEmpty(deviceConfigScope)) {
146                 Assert.fail("empty deviceConfigScope registered.");
147             }
148             if (!deviceConfigScope.matches(NAME_VALID_CHARACTERS_REGEX)) {
149                 Assert.fail(deviceConfigScope + " contains invalid characters. "
150                         + "Only alphanumeric characters, '-', '@', ':' and '_' are valid.");
151             }
152         }
153     }
154 
155     @Test
testUpdatePropertiesFromSettings()156     public void testUpdatePropertiesFromSettings() {
157         mGlobalSettingsMap.put(Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue");
158 
159         String systemPropertyName = "persist.device_config.global_settings."
160                 + "sqlite_compatibility_wal_flags";
161 
162         mTestMapper.updatePropertiesFromSettings();
163         String propValue = mSystemSettingsMap.get(systemPropertyName);
164         Assert.assertEquals("testValue", propValue);
165 
166         mGlobalSettingsMap.put(Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue2");
167         mTestMapper.updatePropertyFromSetting(
168                 Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
169                 systemPropertyName);
170         propValue = mSystemSettingsMap.get(systemPropertyName);
171         Assert.assertEquals("testValue2", propValue);
172 
173         mGlobalSettingsMap.put(Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, null);
174         mTestMapper.updatePropertyFromSetting(
175                 Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
176                 systemPropertyName);
177         propValue = mSystemSettingsMap.get(systemPropertyName);
178         Assert.assertEquals("", propValue);
179     }
180 
181     @Test
testMakePropertyName()182     public void testMakePropertyName() {
183         try {
184             Assert.assertEquals("persist.device_config.test_category.test_flag",
185                     SettingsToPropertiesMapper.makePropertyName("test_category", "test_flag"));
186         } catch (Exception e) {
187             Assert.fail("Unexpected exception: " + e.getMessage());
188         }
189 
190         try {
191             Assert.assertEquals(null,
192                     SettingsToPropertiesMapper.makePropertyName("test_category!!!", "test_flag"));
193         } catch (Exception e) {
194             Assert.fail("Unexpected exception: " + e.getMessage());
195         }
196 
197         try {
198             Assert.assertEquals(null,
199                     SettingsToPropertiesMapper.makePropertyName("test_category", ".test_flag"));
200         } catch (Exception e) {
201             Assert.fail("Unexpected exception: " + e.getMessage());
202         }
203     }
204 
205     @Test
testUpdatePropertiesFromSettings_PropertyAndSettingNotPresent()206     public void testUpdatePropertiesFromSettings_PropertyAndSettingNotPresent() {
207         // Test that empty property will not be set if setting is not set
208         mTestMapper.updatePropertiesFromSettings();
209         String propValue = mSystemSettingsMap.get("TestProperty");
210         Assert.assertNull("Property should not be set if setting is null", propValue);
211     }
212 
213     @Test
testIsNativeFlagsResetPerformed()214     public void testIsNativeFlagsResetPerformed() {
215         mSystemSettingsMap.put("device_config.reset_performed", "true");
216         Assert.assertTrue(mTestMapper.isNativeFlagsResetPerformed());
217 
218         mSystemSettingsMap.put("device_config.reset_performed", "false");
219         Assert.assertFalse(mTestMapper.isNativeFlagsResetPerformed());
220 
221         mSystemSettingsMap.put("device_config.reset_performed", "");
222         Assert.assertFalse(mTestMapper.isNativeFlagsResetPerformed());
223     }
224 
225     @Test
testGetResetNativeCategories()226     public void testGetResetNativeCategories() {
227         doReturn("persist.device_config.category1.flag;"
228                 + "persist.device_config.category2.flag;persist.device_config.category3.flag;"
229                 + "persist.device_config.category3.flag2")
230             .when(() -> SettingsToPropertiesMapper.getResetFlagsFileContent());
231 
232         mSystemSettingsMap.put("device_config.reset_performed", "");
233         Assert.assertEquals(mTestMapper.getResetNativeCategories().length, 0);
234 
235         mSystemSettingsMap.put("device_config.reset_performed", "true");
236         List<String> categories = Arrays.asList(mTestMapper.getResetNativeCategories());
237         Assert.assertEquals(3, categories.size());
238         Assert.assertTrue(categories.contains("category1"));
239         Assert.assertTrue(categories.contains("category2"));
240         Assert.assertTrue(categories.contains("category3"));
241     }
242 }
243