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 android.deviceconfig.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertNull;
21 import static org.junit.Assert.assertTrue;
22 import static org.junit.Assert.fail;
23 import static org.junit.Assume.assumeTrue;
24 
25 import android.content.Context;
26 import android.os.SystemClock;
27 import android.os.UserHandle;
28 import android.provider.DeviceConfig;
29 import android.provider.DeviceConfig.OnPropertiesChangedListener;
30 import android.provider.DeviceConfig.Properties;
31 
32 import androidx.test.InstrumentationRegistry;
33 import androidx.test.runner.AndroidJUnit4;
34 
35 import com.android.modules.utils.build.SdkLevel;
36 
37 import org.junit.After;
38 import org.junit.AfterClass;
39 import org.junit.Before;
40 import org.junit.BeforeClass;
41 import org.junit.Test;
42 import org.junit.runner.RunWith;
43 
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.HashMap;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.concurrent.CountDownLatch;
50 import java.util.concurrent.Executor;
51 import java.util.concurrent.Executors;
52 import java.util.concurrent.TimeUnit;
53 
54 @RunWith(AndroidJUnit4.class)
55 public final class DeviceConfigApiTests {
56     private static final String NAMESPACE1 = "namespace1";
57     private static final String NAMESPACE2 = "namespace2";
58     private static final String NAMESPACE3 = "namespace3";
59     private static final String NAMESPACE4 = "namespace4";
60     private static final String EMPTY_NAMESPACE = "empty_namespace";
61     private static final String KEY1 = "key1";
62     private static final String KEY2 = "key2";
63     private static final String KEY3 = "key3";
64     private static final String KEY4 = "key4";
65     private static final String VALUE1 = "value1";
66     private static final String VALUE2 = "value2";
67     private static final String VALUE3 = "value3";
68     private static final String VALUE4 = "value4";
69     private static final String DEFAULT_VALUE = "default_value";
70 
71     private static final String KEY_NON_EXISTING = "key_non_existing";
72 
73     private static final boolean DEFAULT_BOOLEAN_TRUE = true;
74     private static final boolean DEFAULT_BOOLEAN_FALSE = false;
75     private static final boolean BOOLEAN_TRUE = true;
76     private static final boolean BOOLEAN_FALSE = false;
77     private static final String INVALID_BOOLEAN = "TR_UE";
78 
79     private static final int DEFAULT_INT = 999;
80     private static final int VALID_INT = 123;
81     private static final String INVALID_INT = "12E";
82 
83     private static final long DEFAULT_LONG = 123456;
84     private static final long VALID_LONG = 278724287;
85     private static final String INVALID_LONG = "23232R42";
86 
87     private static final float DEFAULT_FLOAT = 123.456f;
88     private static final float VALID_FLOAT = 456.789f;
89     private static final String INVALID_FLOAT = "34343et";
90 
91     private static final int RESET_MODE_PACKAGE_DEFAULTS = 1;
92     private static final int SYNC_DISABLED_MODE_NONE = 0;
93 
94     private static final long OPERATION_TIMEOUT_MS = 5000;
95 
96     private static final Context CONTEXT = InstrumentationRegistry.getContext();
97 
98     private static final Executor EXECUTOR = CONTEXT.getMainExecutor();
99 
100 
101     private static final long WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS = 2000; // 2 sec
102     private final Object mLock = new Object();
103 
104 
105     private static final String WRITE_DEVICE_CONFIG_PERMISSION =
106             "android.permission.WRITE_DEVICE_CONFIG";
107 
108     private static final String READ_DEVICE_CONFIG_PERMISSION =
109             "android.permission.READ_DEVICE_CONFIG";
110 
111     private static final String MONITOR_DEVICE_CONFIG_ACCESS =
112             "android.permission.MONITOR_DEVICE_CONFIG_ACCESS";
113 
114     // String used to skip tests if not support.
115     // TODO: ideally it would be simpler to just use assumeTrue() in the @BeforeClass method, but
116     // then the test would crash - it might be an issue on atest / AndroidJUnit4
117     private static String sUnsupportedReason;
118 
119     private int mInitialSyncDisabledMode;
120 
121     /**
122      * Get necessary permissions to access and modify properties through DeviceConfig API.
123      */
124     @BeforeClass
setUp()125     public static void setUp() throws Exception {
126         assumeTrue(SdkLevel.isAtLeastU());
127         if (CONTEXT.getUserId() != UserHandle.USER_SYSTEM
128                 && CONTEXT.getPackageManager().isInstantApp()) {
129             sUnsupportedReason = "cannot run test as instant app on secondary user "
130                     + CONTEXT.getUserId();
131             return;
132         }
133 
134         InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
135                 WRITE_DEVICE_CONFIG_PERMISSION, READ_DEVICE_CONFIG_PERMISSION,
136                 MONITOR_DEVICE_CONFIG_ACCESS);
137     }
138 
139     @Before
assumeSupported()140     public void assumeSupported() {
141         assumeTrue(sUnsupportedReason, isSupported());
142     }
143 
144     @Before
setUpSyncDisabledMode()145     public void setUpSyncDisabledMode() {
146         mInitialSyncDisabledMode = DeviceConfig.getSyncDisabledMode();
147         DeviceConfig.setSyncDisabledMode(SYNC_DISABLED_MODE_NONE);
148     }
149 
150     /**
151      * Nullify properties in DeviceConfig API after completion of every test.
152      */
153     @After
cleanUp()154     public void cleanUp() throws Exception {
155         if (!isSupported()) return;
156 
157         // first wait to make sure callbacks for SetProperties/SetProperty
158         // invoked in the test methods got emitted. So that the callbacks
159         // won't interfere with setPropertiesAndAssertSuccessfulChange invoked
160         // in nullifyProperty.
161         if (SdkLevel.isAtLeastV()) {
162             DeviceConfig.clearAllLocalOverrides();
163         }
164         TimeUnit.MILLISECONDS.sleep(WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS);
165         nullifyProperty(NAMESPACE1, KEY1);
166         nullifyProperty(NAMESPACE2, KEY1);
167         nullifyProperty(NAMESPACE1, KEY2);
168         nullifyProperty(NAMESPACE2, KEY2);
169         nullifyProperty(NAMESPACE3, KEY3);
170         nullifyProperty(NAMESPACE4, KEY3);
171         nullifyProperty(NAMESPACE3, KEY4);
172         nullifyProperty(NAMESPACE4, KEY4);
173         DeviceConfig.setSyncDisabledMode(mInitialSyncDisabledMode);
174     }
175 
176     /**
177      * Delete properties in DeviceConfig API after completion of all tests and drop shell
178      * permissions.
179      */
180     @AfterClass
cleanUpAfterAllTests()181     public static void cleanUpAfterAllTests() {
182         if (!isSupported()) return;
183 
184         deletePropertyThrowShell(NAMESPACE1, KEY1);
185         deletePropertyThrowShell(NAMESPACE2, KEY1);
186         deletePropertyThrowShell(NAMESPACE1, KEY2);
187         deletePropertyThrowShell(NAMESPACE2, KEY2);
188         InstrumentationRegistry.getInstrumentation().getUiAutomation()
189                 .dropShellPermissionIdentity();
190     }
191 
192     @Test
testPropertiesIncludedInGetAllProperties()193     public void testPropertiesIncludedInGetAllProperties() {
194         if (!SdkLevel.isAtLeastV()) {
195             return;
196         }
197 
198         DeviceConfig.setProperty(NAMESPACE3, KEY3, VALUE3, false);
199         DeviceConfig.setProperty(NAMESPACE4, KEY4, VALUE4, false);
200 
201         boolean containsNamespace3 = false;
202         boolean containsNamespace4 = false;
203         for (Properties properties : DeviceConfig.getAllProperties()) {
204             System.out.println(properties);
205             boolean hasNamespace3 = properties.getNamespace().equals(NAMESPACE3);
206             boolean hasValue3 = VALUE3.equals(properties.getString(KEY3, null));
207             if (hasNamespace3 && hasValue3) {
208                 containsNamespace3 = true;
209                 assertEquals(VALUE3, properties.getString(KEY3, null));
210             }
211 
212             boolean hasNamespace4 = properties.getNamespace().equals(NAMESPACE4);
213             boolean hasValue4 = VALUE4.equals(properties.getString(KEY4, null));
214             if (hasNamespace4 && hasValue4) {
215                 containsNamespace4 = true;
216             }
217         }
218 
219         assertTrue(containsNamespace3);
220         assertTrue(containsNamespace4);
221     }
222 
223     /**
224      * Test that creating a sticky local override for a flag prevents further writes to that flag.
225      */
226     @Test
testAddStickyLocalOverridePreventsWrites()227     public void testAddStickyLocalOverridePreventsWrites() {
228         if (!SdkLevel.isAtLeastV()) {
229             return;
230         }
231 
232         DeviceConfig.setLocalOverride(NAMESPACE3, KEY3, VALUE3);
233 
234         String key3Value = DeviceConfig.getProperty(NAMESPACE3, KEY3);
235         assertEquals(VALUE3, key3Value);
236 
237         DeviceConfig.setProperty(NAMESPACE3, KEY3, VALUE4, /* makeDefault= */ false);
238         key3Value = DeviceConfig.getProperty(NAMESPACE3, KEY3);
239         assertEquals(VALUE3, key3Value);
240     }
241 
242     /**
243      * Test that when we locally override a flag, we can still write other flags.
244      */
245     @Test
testAddStickyLocalOverrideDoesNotAffectOtherFlags()246     public void testAddStickyLocalOverrideDoesNotAffectOtherFlags() {
247         if (!SdkLevel.isAtLeastV()) {
248             return;
249         }
250 
251         DeviceConfig.setLocalOverride(NAMESPACE3, KEY3, VALUE3);
252         DeviceConfig.setProperty(NAMESPACE3, KEY4, VALUE4, /* makeDefault= */ false);
253         String key4Value = DeviceConfig.getProperty(NAMESPACE3, KEY4);
254         assertEquals(VALUE4, key4Value);
255     }
256 
257     /**
258      * Test that when we apply some overrides, they show up in the override list.
259      */
260     @Test
testGetStickyLocalOverrides()261     public void testGetStickyLocalOverrides() {
262         if (!SdkLevel.isAtLeastV()) {
263             return;
264         }
265 
266         DeviceConfig.setProperty(NAMESPACE3, KEY3, VALUE4, false);
267         DeviceConfig.setProperty(NAMESPACE3, KEY4, VALUE3, false);
268         DeviceConfig.setLocalOverride(NAMESPACE3, KEY3, VALUE3);
269         DeviceConfig.setLocalOverride(NAMESPACE3, KEY4, VALUE4);
270 
271         Map<String, Map<String, String>> expectedOverrides = new HashMap<>();
272         Map<String, String> expectedInnerMap = new HashMap<>();
273         expectedInnerMap.put(KEY3, VALUE4);
274         expectedInnerMap.put(KEY4, VALUE3);
275         expectedOverrides.put(NAMESPACE3, expectedInnerMap);
276 
277         assertEquals(expectedOverrides, DeviceConfig.getUnderlyingValuesForOverriddenFlags());
278     }
279 
280     /**
281      * Test that when we clear all overrides, the override list is empty.
282      */
283     @Test
testClearStickyLocalOverrides()284     public void testClearStickyLocalOverrides() {
285         if (!SdkLevel.isAtLeastV()) {
286             return;
287         }
288 
289         DeviceConfig.setLocalOverride(NAMESPACE4, KEY3, VALUE3);
290         DeviceConfig.setLocalOverride(NAMESPACE4, KEY4, VALUE4);
291 
292         DeviceConfig.clearAllLocalOverrides();
293 
294         Map<String, Map<String, String>> overrides =
295                 DeviceConfig.getUnderlyingValuesForOverriddenFlags();
296         assertTrue(overrides.isEmpty());
297     }
298 
299     /**
300      * Test that when we clear a single override, it doesn't appear in the list.
301      */
302     @Test
testClearStickyLocalOverride()303     public void testClearStickyLocalOverride() {
304         if (!SdkLevel.isAtLeastV()) {
305             return;
306         }
307 
308         DeviceConfig.setProperty(NAMESPACE3, KEY3, VALUE4, false);
309         DeviceConfig.setProperty(NAMESPACE4, KEY4, VALUE3, false);
310         DeviceConfig.setLocalOverride(NAMESPACE3, KEY3, VALUE3);
311         DeviceConfig.setLocalOverride(NAMESPACE4, KEY4, VALUE4);
312 
313         DeviceConfig.clearLocalOverride(NAMESPACE3, KEY3);
314 
315         Map<String, Map<String, String>> expectedOverrides = new HashMap<>();
316         Map<String, String> expectedInnerMap = new HashMap<>();
317         expectedInnerMap.put(KEY4, VALUE3);
318         expectedOverrides.put(NAMESPACE4, expectedInnerMap);
319 
320         assertEquals(expectedOverrides, DeviceConfig.getUnderlyingValuesForOverriddenFlags());
321     }
322 
323     /**
324      * Checks that getting property which does not exist returns null.
325      */
326     @Test
testGetProperty_empty()327     public void testGetProperty_empty() {
328         String result = DeviceConfig.getProperty(EMPTY_NAMESPACE, KEY1);
329         assertNull("Request for non existant flag name in DeviceConfig API should return null "
330                 + "while " + result + " was returned", result);
331     }
332 
333     /**
334      * Checks that setting and getting property from the same namespace return correct value.
335      */
336     @Test
testSetAndGetProperty_sameNamespace()337     public void testSetAndGetProperty_sameNamespace() {
338         DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE1, /*makeDefault=*/false);
339         String result = DeviceConfig.getProperty(NAMESPACE1, KEY1);
340         assertEquals("Value read from DeviceConfig API does not match written value.", VALUE1,
341                 result);
342     }
343 
344     /**
345      * Checks that setting and getting properties from the same namespace return correct values.
346      */
347     @Test
testSetAndGetProperties_sameNamespace()348     public void testSetAndGetProperties_sameNamespace() throws Exception {
349         Properties properties = new Properties.Builder(NAMESPACE1)
350                 .setString(KEY1, VALUE1).setInt(KEY2, VALID_INT).build();
351         assertEquals(DEFAULT_VALUE, DeviceConfig.getString(NAMESPACE1, KEY1, DEFAULT_VALUE));
352         assertEquals(DEFAULT_INT, DeviceConfig.getInt(NAMESPACE1, KEY2, DEFAULT_INT));
353         DeviceConfig.setProperties(properties);
354 
355         assertEquals(VALUE1, DeviceConfig.getString(NAMESPACE1, KEY1, DEFAULT_VALUE));
356         assertEquals(VALID_INT, DeviceConfig.getInt(NAMESPACE1, KEY2, DEFAULT_INT));
357     }
358 
359     /**
360      * Checks that setting a property in one namespace does not set the same property in a different
361      * namespace.
362      */
363     @Test
testSetAndGetProperty_differentNamespace()364     public void testSetAndGetProperty_differentNamespace() {
365         DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE1, /*makeDefault=*/false);
366         String result = DeviceConfig.getProperty(NAMESPACE2, KEY1);
367         assertNull("Value for same keys written to different namespaces must not clash", result);
368     }
369 
370     /**
371      * Checks that setting properties in one namespace does not set the same properties in a
372      * different namespace.
373      */
374     @Test
testSetAndGetProperties_differentNamespace()375     public void testSetAndGetProperties_differentNamespace() throws Exception {
376         Properties properties = new Properties.Builder(NAMESPACE1)
377                 .setString(KEY1, VALUE1).setInt(KEY2, VALID_INT).build();
378         DeviceConfig.setProperties(properties);
379 
380         assertEquals(DEFAULT_VALUE, DeviceConfig.getString(NAMESPACE2, KEY1, DEFAULT_VALUE));
381         assertEquals(DEFAULT_INT, DeviceConfig.getInt(NAMESPACE2, KEY2, DEFAULT_INT));
382     }
383 
384     /**
385      * Checks that different namespaces can keep different values for the same key.
386      */
387     @Test
testSetAndGetProperty_multipleNamespaces()388     public void testSetAndGetProperty_multipleNamespaces() {
389         DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE1, /*makeDefault=*/false);
390         DeviceConfig.setProperty(NAMESPACE2, KEY1, VALUE2, /*makeDefault=*/false);
391         String result = DeviceConfig.getProperty(NAMESPACE1, KEY1);
392         assertEquals("Value read from DeviceConfig API does not match written value.", VALUE1,
393                 result);
394         result = DeviceConfig.getProperty(NAMESPACE2, KEY1);
395         assertEquals("Value read from DeviceConfig API does not match written value.", VALUE2,
396                 result);
397     }
398 
399     /**
400      * Checks that different namespaces can keep different values for the same keys.
401      */
402     @Test
testSetAndGetProperties_multipleNamespaces()403     public void testSetAndGetProperties_multipleNamespaces() throws Exception {
404         int VALID_INT2 = VALID_INT + 2;
405         Properties properties1 = new Properties.Builder(NAMESPACE1)
406                 .setString(KEY1, VALUE1).setInt(KEY2, VALID_INT).build();
407         Properties properties2 = new Properties.Builder(NAMESPACE2)
408                 .setString(KEY1, VALUE2).setInt(KEY2, VALID_INT2).build();
409         DeviceConfig.setProperties(properties1);
410         DeviceConfig.setProperties(properties2);
411 
412         assertEquals(VALUE1, DeviceConfig.getString(NAMESPACE1, KEY1, DEFAULT_VALUE));
413         assertEquals(VALID_INT, DeviceConfig.getInt(NAMESPACE1, KEY2, DEFAULT_INT));
414         assertEquals(VALUE2, DeviceConfig.getString(NAMESPACE2, KEY1, DEFAULT_VALUE));
415         assertEquals(VALID_INT2, DeviceConfig.getInt(NAMESPACE2, KEY2, DEFAULT_INT));
416     }
417 
418     /**
419      * Checks that saving value twice keeps the last value.
420      */
421     @Test
testSetAndGetProperty_overrideValue()422     public void testSetAndGetProperty_overrideValue() {
423         DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE1, /*makeDefault=*/false);
424         DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE2, /*makeDefault=*/false);
425         String result = DeviceConfig.getProperty(NAMESPACE1, KEY1);
426         assertEquals("New value written to the same namespace/key did not override previous"
427                 + " value.", VALUE2, result);
428     }
429 
430     /**
431      * Checks that saving values twice keeps the last values.
432      */
433     @Test
testSetAndGetProperties_overrideValue()434     public void testSetAndGetProperties_overrideValue() throws Exception {
435         int VALID_INT2 = VALID_INT + 2;
436         Properties properties1 = new Properties.Builder(NAMESPACE1)
437                 .setString(KEY1, VALUE1).setInt(KEY2, VALID_INT).build();
438         Properties properties2 = new Properties.Builder(NAMESPACE1)
439                 .setString(KEY1, VALUE2).setInt(KEY2, VALID_INT2).build();
440         DeviceConfig.setProperties(properties1);
441         DeviceConfig.setProperties(properties2);
442 
443         assertEquals(VALUE2, DeviceConfig.getString(NAMESPACE1, KEY1, DEFAULT_VALUE));
444         assertEquals(VALID_INT2, DeviceConfig.getInt(NAMESPACE1, KEY2, DEFAULT_INT));
445     }
446 
447     /**
448      * Checks that getString() for null property returns default value.
449      */
450     @Test
testGetString_empty()451     public void testGetString_empty() {
452         final String result = DeviceConfig.getString(NAMESPACE1, KEY1, DEFAULT_VALUE);
453         assertEquals("DeviceConfig.getString() must return default value if property is null",
454                 DEFAULT_VALUE, result);
455     }
456 
457     /**
458      * Checks that getString() for null property returns default value even if it is null.
459      */
460     @Test
testGetString_nullDefault()461     public void testGetString_nullDefault() {
462         final String result = DeviceConfig.getString(NAMESPACE1, KEY1, null);
463         assertEquals("DeviceConfig.getString() must return default value if property is null",
464                 null, result);
465     }
466 
467     /**
468      * Checks that getString() returns string saved in property.
469      */
470     @Test
testGetString_nonEmpty()471     public void testGetString_nonEmpty() {
472         DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE1, /*makeDefault=*/false);
473 
474         final String result = DeviceConfig.getString(NAMESPACE1, KEY1, DEFAULT_VALUE);
475         assertEquals("DeviceConfig.getString() must return same value as getProperty() when " +
476                 "property is not null", VALUE1, result);
477     }
478 
479     /**
480      * Checks that getString() fails with NullPointerException when called with null namespace.
481      */
482     @Test
testGetString_nullNamespace()483     public void testGetString_nullNamespace() {
484         try {
485             DeviceConfig.getString(null, KEY1, DEFAULT_VALUE);
486             fail("DeviceConfig.getString() with null namespace must result in "
487                     + "NullPointerException");
488         } catch (NullPointerException e) {
489             // expected
490         }
491     }
492 
493     /**
494      * Checks that getString() fails with NullPointerException when called with null key.
495      */
496     @Test
testGetString_nullName()497     public void testGetString_nullName() {
498         try {
499             DeviceConfig.getString(NAMESPACE1, null, DEFAULT_VALUE);
500             fail("DeviceConfig.getString() with null name must result in NullPointerException");
501         } catch (NullPointerException e) {
502             // expected
503         }
504     }
505 
506     /**
507      * Checks that getBoolean() for null property returns default value.
508      */
509     @Test
testGetBoolean_empty()510     public void testGetBoolean_empty() {
511         final boolean result = DeviceConfig.getBoolean(NAMESPACE1, KEY1, DEFAULT_BOOLEAN_TRUE);
512         assertEquals("DeviceConfig.getBoolean() must return default value if property is null",
513                 DEFAULT_BOOLEAN_TRUE, result);
514     }
515 
516     /**
517      * Checks that getBoolean() returns boolean representation of string saved in property.
518      */
519     @Test
testGetBoolean_valid()520     public void testGetBoolean_valid() {
521         DeviceConfig.setProperty(NAMESPACE1, KEY1, String.valueOf(BOOLEAN_TRUE),
522                 /*makeDefault=*/false);
523 
524         final boolean result = DeviceConfig.getBoolean(NAMESPACE1, KEY1, DEFAULT_BOOLEAN_FALSE);
525         assertEquals("DeviceConfig.getString() must return boolean equivalent value of"
526                 + " getProperty() when property is not null", BOOLEAN_TRUE, result);
527     }
528 
529     /**
530      * Checks that getBoolean() returns false for any invalid property value.
531      */
532     @Test
testGetBoolean_invalid()533     public void testGetBoolean_invalid() {
534         DeviceConfig.setProperty(NAMESPACE1, KEY1, INVALID_BOOLEAN, /*makeDefault=*/false);
535 
536         final boolean result = DeviceConfig.getBoolean(NAMESPACE1, KEY1, DEFAULT_BOOLEAN_TRUE);
537         // Anything non-null other than case insensitive "true" parses to false.
538         assertEquals("DeviceConfig.getBoolean() must return boolean equivalent value of"
539                 + " getProperty() when property is not null", BOOLEAN_FALSE, result);
540     }
541 
542     /**
543      * Checks that getBoolean() fails with NullPointerException when called with null namespace.
544      */
545     @Test
testGetBoolean_nullNamespace()546     public void testGetBoolean_nullNamespace() {
547         try {
548             DeviceConfig.getBoolean(null, KEY1, DEFAULT_BOOLEAN_TRUE);
549             fail("DeviceConfig.getBoolean() with null namespace must result in "
550                     + "NullPointerException");
551         } catch (NullPointerException e) {
552             // expected
553         }
554     }
555 
556     /**
557      * Checks that getBoolean() fails with NullPointerException when called with null name.
558      */
559     @Test
testGetBoolean_nullName()560     public void testGetBoolean_nullName() {
561         try {
562             DeviceConfig.getBoolean(NAMESPACE1, null, DEFAULT_BOOLEAN_TRUE);
563             fail("DeviceConfig.getBoolean() with null name must result in NullPointerException");
564         } catch (NullPointerException e) {
565             // expected
566         }
567     }
568 
569     /**
570      * Checks that getInt() for null property returns default value.
571      */
572     @Test
testGetInt_empty()573     public void testGetInt_empty() {
574         final int result = DeviceConfig.getInt(NAMESPACE1, KEY1, DEFAULT_INT);
575         assertEquals("DeviceConfig.getInt() must return default value if property is null",
576                 DEFAULT_INT, result);
577     }
578 
579     /**
580      * Checks that getInt() returns integer representation of string saved in property.
581      */
582     @Test
testGetInt_valid()583     public void testGetInt_valid() {
584         DeviceConfig.setProperty(NAMESPACE1, KEY1, String.valueOf(VALID_INT),
585                 /*makeDefault=*/false);
586 
587         final int result = DeviceConfig.getInt(NAMESPACE1, KEY1, DEFAULT_INT);
588         assertEquals("DeviceConfig.getInt() must return integer equivalent value of"
589                 + " getProperty() when property is not null", VALID_INT, result);
590     }
591 
592     /**
593      * Checks that getInt() returns default value if property is not well-formed integer value.
594      */
595     @Test
testGetInt_invalid()596     public void testGetInt_invalid() {
597         DeviceConfig.setProperty(NAMESPACE1, KEY1, INVALID_INT, /*makeDefault=*/false);
598 
599         final int result = DeviceConfig.getInt(NAMESPACE1, KEY1, DEFAULT_INT);
600         // Failure to parse results in using the default value
601         assertEquals("DeviceConfig.getInt() must return integer equivalent value of"
602                 + " getProperty() when property is not null", DEFAULT_INT, result);
603     }
604 
605     /**
606      * Checks that getInt() fails with NullPointerException when called with null namespace.
607      */
608     @Test
testGetInt_nullNamespace()609     public void testGetInt_nullNamespace() {
610         try {
611             DeviceConfig.getInt(null, KEY1, VALID_INT);
612             fail("DeviceConfig.getInt() with null namespace must result in NullPointerException");
613         } catch (NullPointerException e) {
614             // expected
615         }
616     }
617 
618     /**
619      * Checks that getInt() fails with NullPointerException when called with null name.
620      */
621     @Test
testGetInt_nullName()622     public void testGetInt_nullName() {
623         try {
624             DeviceConfig.getInt(NAMESPACE1, null, VALID_INT);
625             fail("DeviceConfig.getInt() with null name must result in NullPointerException");
626         } catch (NullPointerException e) {
627             // expected
628         }
629     }
630 
631     /**
632      * Checks that getLong() for null property returns default value.
633      */
634     @Test
testGetLong_empty()635     public void testGetLong_empty() {
636         final long result = DeviceConfig.getLong(NAMESPACE1, KEY1, DEFAULT_LONG);
637         assertEquals("DeviceConfig.getLong() must return default value if property is null",
638                 DEFAULT_LONG, result);
639     }
640 
641     /**
642      * Checks that getLong() returns long representation of string saved in property.
643      */
644     @Test
testGetLong_valid()645     public void testGetLong_valid() {
646         DeviceConfig.setProperty(NAMESPACE1, KEY1, String.valueOf(VALID_LONG),
647                 /*makeDefault=*/false);
648 
649         final long result = DeviceConfig.getLong(NAMESPACE1, KEY1, DEFAULT_LONG);
650         assertEquals("DeviceConfig.getLong() must return long equivalent value of"
651                 + " getProperty() when property is not null", VALID_LONG, result);
652     }
653 
654     /**
655      * Checks that getLong() returns default value if property is not well-formed long value.
656      */
657     @Test
testGetLong_invalid()658     public void testGetLong_invalid() {
659         DeviceConfig.setProperty(NAMESPACE1, KEY1, INVALID_LONG, /*makeDefault=*/false);
660 
661         final long result = DeviceConfig.getLong(NAMESPACE1, KEY1, DEFAULT_LONG);
662         // Failure to parse results in using the default value
663         assertEquals("DeviceConfig.getLong() must return long equivalent value of"
664                 + " getProperty() when property is not null", DEFAULT_LONG, result);
665     }
666 
667     /**
668      * Checks that getLong() fails with NullPointerException when called with null namespace.
669      */
670     @Test
testGetLong_nullNamespace()671     public void testGetLong_nullNamespace() {
672         try {
673             DeviceConfig.getLong(null, KEY1, DEFAULT_LONG);
674             fail("DeviceConfig.getLong() with null namespace must result in "
675                     + "NullPointerException");
676         } catch (NullPointerException e) {
677             // expected
678         }
679     }
680 
681     /**
682      * Checks that getLong() fails with NullPointerException when called with null name.
683      */
684     @Test
testGetLong_nullName()685     public void testGetLong_nullName() {
686         try {
687             DeviceConfig.getLong(NAMESPACE1, null, 0);
688             fail("DeviceConfig.getLong() with null name must result in NullPointerException");
689         } catch (NullPointerException e) {
690             // expected
691         }
692     }
693 
694     /**
695      * Checks that getFloat() for null property returns default value.
696      */
697     @Test
testGetFloat_empty()698     public void testGetFloat_empty() {
699         final float result = DeviceConfig.getFloat(NAMESPACE1, KEY1, DEFAULT_FLOAT);
700         assertEquals("DeviceConfig.getFloat() must return default value if property is null",
701                 DEFAULT_FLOAT, result, 0.0);
702     }
703 
704     /**
705      * Checks that getFloat() returns float representation of string saved in property.
706      */
707     @Test
testGetFloat_valid()708     public void testGetFloat_valid() {
709         DeviceConfig.setProperty(NAMESPACE1, KEY1, String.valueOf(VALID_FLOAT),
710                 /*makeDefault=*/false);
711 
712         final float result = DeviceConfig.getFloat(NAMESPACE1, KEY1, DEFAULT_FLOAT);
713         assertEquals("DeviceConfig.getFloat() must return float equivalent value of"
714                 + " getProperty() when property is not null", VALID_FLOAT, result, 0.0);
715     }
716 
717     /**
718      * Checks that getFloat() returns default value if property is not well-formed float value.
719      */
720     @Test
testGetFloat_invalid()721     public void testGetFloat_invalid() {
722         DeviceConfig.setProperty(NAMESPACE1, KEY1, INVALID_FLOAT, /*makeDefault=*/false);
723 
724         final float result = DeviceConfig.getFloat(NAMESPACE1, KEY1, DEFAULT_FLOAT);
725         // Failure to parse results in using the default value
726         assertEquals("DeviceConfig.getFloat() must return float equivalent value of"
727                 + " getProperty() when property is not null", DEFAULT_FLOAT, result, 0.0f);
728     }
729 
730     /**
731      * Checks that getFloat() fails with NullPointerException when called with null namespace.
732      */
733     @Test
testGetFloat_nullNamespace()734     public void testGetFloat_nullNamespace() {
735         try {
736             DeviceConfig.getFloat(null, KEY1, DEFAULT_FLOAT);
737             fail("DeviceConfig.getFloat() with null namespace must result in "
738                     + "NullPointerException");
739         } catch (NullPointerException e) {
740             // expected
741         }
742     }
743 
744     /**
745      * Checks that getFloat() fails with NullPointerException when called with null name.
746      */
747     @Test
testGetFloat_nullName()748     public void testGetFloat_nullName() {
749         try {
750             DeviceConfig.getFloat(NAMESPACE1, null, DEFAULT_FLOAT);
751             fail("DeviceConfig.getFloat() with null name must result in NullPointerException");
752         } catch (NullPointerException e) {
753             // expected
754         }
755     }
756 
757     /**
758      * Checks that setProperty() fails with NullPointerException when called with null namespace.
759      */
760     @Test
testSetProperty_nullNamespace()761     public void testSetProperty_nullNamespace() {
762         try {
763             DeviceConfig.setProperty(null, KEY1, DEFAULT_VALUE, /*makeDefault=*/false);
764             fail("DeviceConfig.setProperty() with null namespace must result in "
765                     + "NullPointerException");
766         } catch (NullPointerException e) {
767             // expected
768         }
769     }
770 
771     /**
772      * Checks that setProperty() fails with NullPointerException when called with null name.
773      */
774     @Test
testSetProperty_nullName()775     public void testSetProperty_nullName() {
776         try {
777             DeviceConfig.setProperty(NAMESPACE1, null, DEFAULT_VALUE, /*makeDefault=*/false);
778             fail("DeviceConfig.setProperty() with null name must result in NullPointerException");
779         } catch (NullPointerException e) {
780             // expected
781         }
782     }
783 
784     /**
785      * Checks that Properties.getString() for null property returns default value.
786      */
787     @Test
testGetPropertiesString_empty()788     public void testGetPropertiesString_empty() {
789         setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, VALUE1);
790         final Properties properties =
791                 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, null);
792         final String result = properties.getString(KEY1, DEFAULT_VALUE);
793         assertEquals("DeviceConfig.Properties.getString() must return default value if property "
794                         + "is null", DEFAULT_VALUE, result);
795     }
796 
797     /**
798      * Checks that Properties.getString() for null property returns default value even if it is
799      * null.
800      */
801     @Test
testGetPropertiesString_nullDefault()802     public void testGetPropertiesString_nullDefault() {
803         setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, DEFAULT_VALUE);
804         final Properties properties =
805                 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, null);
806         final String result = properties.getString(KEY1, null);
807         assertEquals("DeviceConfig.Properties.getString() must return default value if property is "
808                         + "null", null, result);
809     }
810 
811     /**
812      * Checks that Properties.getString() returns string saved in property.
813      */
814     @Test
testGetPropertiesString_nonEmpty()815     public void testGetPropertiesString_nonEmpty() {
816         final Properties properties =
817                 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, VALUE1);
818 
819         final String result = properties.getString(KEY1, DEFAULT_VALUE);
820         assertEquals("DeviceConfig.Properties.getString() must return same value as getProperty() "
821                 + "when property is not null", VALUE1, result);
822     }
823 
824     /**
825      * Checks that Properties.getBoolean() for null property returns default value.
826      */
827     @Test
testGetPropertiesBoolean_empty()828     public void testGetPropertiesBoolean_empty() {
829         setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, String.valueOf(BOOLEAN_TRUE));
830         final Properties properties =
831                 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, null);
832         final boolean result = properties.getBoolean(KEY1, DEFAULT_BOOLEAN_TRUE);
833         assertEquals("DeviceConfig.Properties.getBoolean() must return default value if property "
834                         + "is null", DEFAULT_BOOLEAN_TRUE, result);
835     }
836 
837     /**
838      * Checks that Properties.getBoolean() returns boolean representation of string saved in
839      * property.
840      */
841     @Test
testGetPropertiesBoolean_valid()842     public void testGetPropertiesBoolean_valid() {
843         final Properties properties = setPropertiesAndAssertSuccessfulChange(
844                 NAMESPACE1, KEY1, String.valueOf(BOOLEAN_TRUE));
845         final boolean result = properties.getBoolean(KEY1, DEFAULT_BOOLEAN_FALSE);
846         assertEquals("DeviceConfig.Properties.getString() must return boolean equivalent value of"
847                 + " getProperty() when property is not null", BOOLEAN_TRUE, result);
848     }
849 
850     /**
851      * Checks that Properties.getBoolean() returns false for any invalid (non parselable) property
852      * value.
853      */
854     @Test
testGetPropertiesBoolean_invalid()855     public void testGetPropertiesBoolean_invalid() {
856         final Properties properties =
857                 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, INVALID_BOOLEAN);
858 
859         final boolean result = properties.getBoolean(KEY1, DEFAULT_BOOLEAN_TRUE);
860         // Anything non-null other than case insensitive "true" parses to false.
861         assertEquals("DeviceConfig.Properties.getBoolean() must return boolean equivalent value of"
862                 + " getProperty() when property is not null", BOOLEAN_FALSE, result);
863     }
864 
865     /**
866      * Checks that Properties.getInt() for null property returns default value.
867      */
868     @Test
testGetPropertiesInt_empty()869     public void testGetPropertiesInt_empty() {
870         setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, String.valueOf(VALID_INT));
871         final Properties properties =
872                 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, null);
873 
874         final int result = properties.getInt(KEY1, DEFAULT_INT);
875         assertEquals("DeviceConfig.Properties.getInt() must return default value if property is "
876                         + "null", DEFAULT_INT, result);
877     }
878 
879     /**
880      * Checks that Properties.getInt() returns integer representation of string saved in property.
881      */
882     @Test
testGetPropertiesInt_valid()883     public void testGetPropertiesInt_valid() {
884         final Properties properties =
885                 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, String.valueOf(VALID_INT));
886 
887         final int result = properties.getInt(KEY1, DEFAULT_INT);
888         assertEquals("DeviceConfig.Properties.getInt() must return integer equivalent value of"
889                 + " getProperty() when property is not null", VALID_INT, result);
890     }
891 
892     /**
893      * Checks that Properties.getInt() returns default value if property is not well-formed integer
894      * value.
895      */
896     @Test
testGetPropertiesInt_invalid()897     public void testGetPropertiesInt_invalid() {
898         final Properties properties =
899                 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, INVALID_INT);
900 
901         final int result = properties.getInt(KEY1, DEFAULT_INT);
902         // Failure to parse results in using the default value
903         assertEquals("DeviceConfig.Properties.getInt() must return integer equivalent value of"
904                 + " getProperty() when property is not null", DEFAULT_INT, result);
905     }
906 
907     /**
908      * Checks that Properties.getLong() for null property returns default value.
909      */
910     @Test
testGetPropertiesLong_empty()911     public void testGetPropertiesLong_empty() {
912         setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, String.valueOf(VALID_LONG));
913         final Properties properties =
914                 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, null);
915 
916         final long result = properties.getLong(KEY1, DEFAULT_LONG);
917         assertEquals("DeviceConfig.Properties.getLong() must return default value if property is "
918                         + "null", DEFAULT_LONG, result);
919     }
920 
921     /**
922      * Checks that Properties.getLong() returns long representation of string saved in property.
923      */
924     @Test
testGetPropertiesLong_valid()925     public void testGetPropertiesLong_valid() {
926         final Properties properties =
927                 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, String.valueOf(VALID_LONG));
928 
929         final long result = properties.getLong(KEY1, DEFAULT_LONG);
930         assertEquals("DeviceConfig.Properties.getLong() must return long equivalent value of"
931                 + " getProperty() when property is not null", VALID_LONG, result);
932     }
933 
934     /**
935      * Checks that Properties.getLong() returns default value if property is not well-formed long
936      * value.
937      */
938     @Test
testGetPropertiesLong_invalid()939     public void testGetPropertiesLong_invalid() {
940         final Properties properties =
941                 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, INVALID_LONG);
942 
943         final long result = properties.getLong(KEY1, DEFAULT_LONG);
944         // Failure to parse results in using the default value
945         assertEquals("DeviceConfig.Properties.getLong() must return long equivalent value of"
946                 + " getProperty() when property is not null", DEFAULT_LONG, result);
947     }
948 
949     /**
950      * Checks that Properties.getFloat() for null property returns default value.
951      */
952     @Test
testGetPropertiesFloat_empty()953     public void testGetPropertiesFloat_empty() {
954         setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, String.valueOf(VALID_FLOAT));
955         final Properties properties =
956                 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, null);
957         final float result = properties.getFloat(KEY1, DEFAULT_FLOAT);
958         assertEquals("DeviceConfig.Properties.getFloat() must return default value if property is "
959                         + "null", DEFAULT_FLOAT, result, 0.0f);
960     }
961 
962     /**
963      * Checks that Properties.getFloat() returns float representation of string saved in property.
964      */
965     @Test
testGetPropertiesFloat_valid()966     public void testGetPropertiesFloat_valid() {
967         final Properties properties = setPropertiesAndAssertSuccessfulChange(
968                 NAMESPACE1, KEY1, String.valueOf(VALID_FLOAT));
969 
970         final float result = properties.getFloat(KEY1, DEFAULT_FLOAT);
971         assertEquals("DeviceConfig.Properties.getFloat() must return float equivalent value of"
972                 + " getProperty() when property is not null", VALID_FLOAT, result, 0.0f);
973     }
974 
975     /**
976      * Checks that Properties.getFloat() returns default value if property is not well-formed float
977      * value.
978      */
979     @Test
testGetPropertiesFloat_invalid()980     public void testGetPropertiesFloat_invalid() {
981         final Properties properties = setPropertiesAndAssertSuccessfulChange(
982                 NAMESPACE1, KEY1, INVALID_FLOAT);
983 
984         final float result = properties.getFloat(KEY1, DEFAULT_FLOAT);
985         // Failure to parse results in using the default value
986         assertEquals("DeviceConfig.Properties.getFloat() must return float equivalent value of"
987                 + " getProperty() when property is not null", DEFAULT_FLOAT, result, 0.0f);
988     }
989 
990     @Test
testDeleteProperty_nullNamespace()991     public void testDeleteProperty_nullNamespace() {
992         try {
993             DeviceConfig.deleteProperty(null, KEY1);
994             fail("DeviceConfig.deleteProperty() with null namespace must result in "
995                     + "NullPointerException");
996         } catch (NullPointerException e) {
997             // expected
998         }
999     }
1000 
1001     @Test
testDeleteProperty_nullName()1002     public void testDeleteProperty_nullName() {
1003         try {
1004             DeviceConfig.deleteProperty(NAMESPACE1, null);
1005             fail("DeviceConfig.deleteProperty() with null name must result in "
1006                     + "NullPointerException");
1007         } catch (NullPointerException e) {
1008             // expected
1009         }
1010     }
1011 
1012     @Test
testDeletePropertyString()1013     public void testDeletePropertyString() {
1014         setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, VALUE1);
1015         deletePropertyAndAssertSuccessfulChange(NAMESPACE1, KEY1);
1016         assertEquals("DeviceConfig.Properties.getString() must return default value if "
1017                 + "property is deleted", DEFAULT_VALUE,
1018                 DeviceConfig.getProperties(NAMESPACE1, KEY1).getString(KEY1, DEFAULT_VALUE));
1019     }
1020 
1021     @Test
testDeletePropertyBoolean()1022     public void testDeletePropertyBoolean() {
1023         setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, String.valueOf(BOOLEAN_TRUE));
1024         deletePropertyAndAssertSuccessfulChange(NAMESPACE1, KEY1);
1025         assertEquals("DeviceConfig.Properties.getBoolean() must return default value if "
1026                         + "property is deleted", BOOLEAN_FALSE,
1027                 DeviceConfig.getProperties(NAMESPACE1, KEY1).getBoolean(KEY1,
1028                         DEFAULT_BOOLEAN_FALSE));
1029     }
1030 
1031     @Test
testDeletePropertyInt()1032     public void testDeletePropertyInt() {
1033         setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, String.valueOf(VALID_INT));
1034         deletePropertyAndAssertSuccessfulChange(NAMESPACE1, KEY1);
1035         assertEquals("DeviceConfig.Properties.getInt() must return default value if "
1036                         + "property is deleted", DEFAULT_INT,
1037                 DeviceConfig.getProperties(NAMESPACE1, KEY1).getInt(KEY1, DEFAULT_INT));
1038     }
1039 
1040     @Test
testDeletePropertyLong()1041     public void testDeletePropertyLong() {
1042         setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, String.valueOf(VALID_LONG));
1043         deletePropertyAndAssertSuccessfulChange(NAMESPACE1, KEY1);
1044         assertEquals("DeviceConfig.Properties.getLong() must return default value if "
1045                         + "property is deleted", DEFAULT_LONG,
1046                 DeviceConfig.getProperties(NAMESPACE1, KEY1).getLong(KEY1, DEFAULT_LONG));
1047     }
1048 
1049     @Test
testDeletePropertyFloat()1050     public void testDeletePropertyFloat() {
1051         setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, String.valueOf(VALID_FLOAT));
1052         deletePropertyAndAssertSuccessfulChange(NAMESPACE1, KEY1);
1053         assertEquals("DeviceConfig.Properties.getString() must return default value if "
1054                         + "property is deleted", DEFAULT_FLOAT,
1055                 DeviceConfig.getProperties(NAMESPACE1, KEY1).getFloat(KEY1, DEFAULT_FLOAT), 0.0f);
1056     }
1057 
1058     @Test
testDeleteProperty_withNonExistingProperty()1059     public void testDeleteProperty_withNonExistingProperty() {
1060         assertNull(DeviceConfig.getProperty(NAMESPACE1, KEY_NON_EXISTING));
1061         // Test that deletion returns true when the key doesn't exist
1062         deletePropertyAndAssertNoChange(NAMESPACE1, KEY_NON_EXISTING);
1063     }
1064 
1065     @Test
testDeleteProperty_withUndeletedProperty()1066     public void testDeleteProperty_withUndeletedProperty() {
1067         setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, VALUE1);
1068         setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY2, VALUE2);
1069         assertEquals(VALUE1, DeviceConfig.getProperty(NAMESPACE1, KEY1));
1070         assertEquals(VALUE2, DeviceConfig.getProperty(NAMESPACE1, KEY2));
1071         final Properties propertiesBeforeDeletion = DeviceConfig.getProperties(
1072                 NAMESPACE1, KEY1, KEY2);
1073         assertEquals(VALUE1, propertiesBeforeDeletion.getString(KEY1, DEFAULT_VALUE));
1074         assertEquals(VALUE2, propertiesBeforeDeletion.getString(KEY2, DEFAULT_VALUE));
1075         // Only delete one property, leaving another one undeleted
1076         final Properties propertiesAfterDeletion = deletePropertyAndAssertSuccessfulChange(
1077                 NAMESPACE1, KEY1);
1078         final String result = DeviceConfig.getString(NAMESPACE1, KEY1, DEFAULT_VALUE);
1079         assertEquals("DeviceConfig.getString() must return default value if property is "
1080                 + "deleted", DEFAULT_VALUE, result);
1081         assertNull("DeviceConfig.getProperty() must return null if property is deleted",
1082                 DeviceConfig.getProperty(NAMESPACE1, KEY1));
1083         assertEquals(VALUE2, DeviceConfig.getProperty(NAMESPACE1, KEY2));
1084         assertEquals("DeviceConfig.Properties.getString() must return default value if "
1085                 + "property is deleted", DEFAULT_VALUE, propertiesAfterDeletion.getString(KEY1,
1086                 DEFAULT_VALUE));
1087         assertEquals(VALUE2, propertiesBeforeDeletion.getString(KEY2, DEFAULT_VALUE));
1088     }
1089 
1090     /**
1091      * Test that properties listener is successfully registered and provides callbacks on value
1092      * change when DeviceConfig.setProperty is called.
1093      */
1094     @Test
testPropertiesListener_setProperty()1095     public void testPropertiesListener_setProperty() {
1096         setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, VALUE1);
1097     }
1098 
1099     /**
1100      * Test that properties listener is successfully registered and provides callbacks on value
1101      * change when DeviceConfig.setProperties is called.
1102      */
1103     @Test
testPropertiesListener_setProperties()1104     public void testPropertiesListener_setProperties() throws Exception {
1105         Properties properties = new Properties.Builder(NAMESPACE1)
1106                 .setString(KEY1, VALUE1).setInt(KEY2, VALID_INT).build();
1107         setPropertiesAndAssertSuccessfulChange(properties);
1108     }
1109 
1110     /**
1111      * Test that properties listener is successfully registered and provides callbacks on value
1112      * change when DeviceConfig.deleteProperty is called.
1113      */
1114     @Test
testPropertiesListener_deleteProperty()1115     public void testPropertiesListener_deleteProperty() {
1116         setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, VALUE1);
1117         deletePropertyAndAssertSuccessfulChange(NAMESPACE1, KEY1);
1118     }
1119 
1120     /**
1121      * Test that two properties listeners subscribed to the same namespace are successfully
1122      * registered and unregistered while receiving correct updates in all states.
1123      */
1124     @Test
testTwoPropertiesListenersSameNamespace()1125     public void testTwoPropertiesListenersSameNamespace() {
1126         final List<PropertyUpdate> receivedUpdates1 = new ArrayList<>();
1127         final List<PropertyUpdate> receivedUpdates2 = new ArrayList<>();
1128 
1129         OnPropertiesChangedListener listener1 = createOnPropertiesChangedListener(receivedUpdates1);
1130         OnPropertiesChangedListener listener2 = createOnPropertiesChangedListener(receivedUpdates2);
1131 
1132         try {
1133             DeviceConfig.addOnPropertiesChangedListener(NAMESPACE1, EXECUTOR, listener1);
1134             DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE1, /*makeDefault=*/false);
1135 
1136             waitForListenerUpdateOrTimeout(receivedUpdates1, /*expectedTotalUpdatesCount=*/1);
1137             waitForListenerUpdateOrTimeout(receivedUpdates2, /*expectedTotalUpdatesCount=*/0);
1138 
1139             assertEquals("OnPropertiesListener did not receive expected update",
1140                     receivedUpdates1.size(), /*expectedTotalUpdatesCount=*/1);
1141             assertEquals("OnPropertiesListener received unexpected update",
1142                     receivedUpdates2.size(), /*expectedTotalUpdatesCount=*/0);
1143             receivedUpdates1.get(0).assertEqual(NAMESPACE1, KEY1, VALUE1);
1144 
1145             DeviceConfig.addOnPropertiesChangedListener(NAMESPACE1, EXECUTOR, listener2);
1146             DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE2, /*makeDefault=*/false);
1147 
1148             waitForListenerUpdateOrTimeout(receivedUpdates1, /*expectedTotalUpdatesCount=*/2);
1149             waitForListenerUpdateOrTimeout(receivedUpdates2, /*expectedTotalUpdatesCount=*/1);
1150 
1151             assertEquals("OnPropertiesListener did not receive expected update",
1152                     receivedUpdates1.size(), 2);
1153             assertEquals("OnPropertiesListener did not receive expected update",
1154                     receivedUpdates2.size(), 1);
1155             receivedUpdates1.get(1).assertEqual(NAMESPACE1, KEY1, VALUE2);
1156             receivedUpdates2.get(0).assertEqual(NAMESPACE1, KEY1, VALUE2);
1157 
1158             DeviceConfig.removeOnPropertiesChangedListener(listener1);
1159             DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE1, /*makeDefault=*/false);
1160 
1161             waitForListenerUpdateOrTimeout(receivedUpdates2, /*expectedTotalUpdatesCount=*/2);
1162             waitForListenerUpdateOrTimeout(receivedUpdates1, /*expectedTotalUpdatesCount=*/2);
1163 
1164             assertEquals("OnPropertiesListener received unexpected update",
1165                     receivedUpdates1.size(), 2);
1166             assertEquals("OnPropertiesListener did not receive expected update",
1167                     receivedUpdates2.size(), 2);
1168 
1169             receivedUpdates2.get(1).assertEqual(NAMESPACE1, KEY1, VALUE1);
1170 
1171             DeviceConfig.removeOnPropertiesChangedListener(listener2);
1172             DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE2, /*makeDefault=*/false);
1173 
1174             waitForListenerUpdateOrTimeout(receivedUpdates2, /*expectedTotalUpdatesCount=*/2);
1175             waitForListenerUpdateOrTimeout(receivedUpdates1, /*expectedTotalUpdatesCount=*/2);
1176 
1177             assertEquals("OnPropertiesListener received unexpected update",
1178                     receivedUpdates1.size(), 2);
1179             assertEquals("OnPropertiesListener received unexpected update",
1180                     receivedUpdates2.size(), 2);
1181         } finally {
1182             DeviceConfig.removeOnPropertiesChangedListener(listener1);
1183             DeviceConfig.removeOnPropertiesChangedListener(listener2);
1184         }
1185     }
1186 
1187     /**
1188      * Test that two properties listeners subscribed to different namespaces are successfully
1189      * registered and unregistered while receiving correct updates in all states.
1190      */
1191     @Test
testTwoPropertiesListenersDifferentNamespace()1192     public void testTwoPropertiesListenersDifferentNamespace() {
1193         final List<PropertyUpdate> receivedUpdates1 = new ArrayList<>();
1194         final List<PropertyUpdate> receivedUpdates2 = new ArrayList<>();
1195 
1196         OnPropertiesChangedListener listener1 = createOnPropertiesChangedListener(receivedUpdates1);
1197         OnPropertiesChangedListener listener2 = createOnPropertiesChangedListener(receivedUpdates2);
1198 
1199         try {
1200             DeviceConfig.addOnPropertiesChangedListener(NAMESPACE1, EXECUTOR, listener1);
1201             DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE1, /*makeDefault=*/false);
1202 
1203             waitForListenerUpdateOrTimeout(receivedUpdates1, /*expectedTotalUpdatesCount=*/1);
1204             waitForListenerUpdateOrTimeout(receivedUpdates2, /*expectedTotalUpdatesCount=*/0);
1205 
1206             assertEquals("OnPropertiesListener did not receive expected update",
1207                     receivedUpdates1.size(), 1);
1208             assertEquals("OnPropertiesListener received unexpected update",
1209                     receivedUpdates2.size(), 0);
1210             receivedUpdates1.get(0).assertEqual(NAMESPACE1, KEY1, VALUE1);
1211 
1212             DeviceConfig.addOnPropertiesChangedListener(NAMESPACE2, EXECUTOR, listener2);
1213             DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE2, /*makeDefault=*/false);
1214 
1215             waitForListenerUpdateOrTimeout(receivedUpdates1, /*expectedTotalUpdatesCount=*/2);
1216             waitForListenerUpdateOrTimeout(receivedUpdates2, /*expectedTotalUpdatesCount=*/0);
1217 
1218             assertEquals("OnPropertiesListener did not receive expected update",
1219                     receivedUpdates1.size(), 2);
1220             assertEquals("OnPropertiesListener received unexpected update",
1221                     receivedUpdates2.size(), 0);
1222             receivedUpdates1.get(1).assertEqual(NAMESPACE1, KEY1, VALUE2);
1223 
1224             DeviceConfig.setProperty(NAMESPACE2, KEY1, VALUE1, /*makeDefault=*/false);
1225             waitForListenerUpdateOrTimeout(receivedUpdates2, /*expectedTotalUpdatesCount=*/1);
1226             waitForListenerUpdateOrTimeout(receivedUpdates1, /*expectedTotalUpdatesCount=*/2);
1227 
1228             assertEquals("OnPropertiesListener received unexpected update",
1229                     receivedUpdates1.size(), 2);
1230             assertEquals("OnPropertiesListener did not receive expected update",
1231                     receivedUpdates2.size(), 1);
1232 
1233             receivedUpdates2.get(0).assertEqual(NAMESPACE2, KEY1, VALUE1);
1234 
1235             DeviceConfig.removeOnPropertiesChangedListener(listener1);
1236             DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE1, /*makeDefault=*/false);
1237 
1238             waitForListenerUpdateOrTimeout(receivedUpdates2, /*expectedTotalUpdatesCount=*/1);
1239             waitForListenerUpdateOrTimeout(receivedUpdates1, /*expectedTotalUpdatesCount=*/2);
1240 
1241             assertEquals("OnPropertiesListener received unexpected update",
1242                     receivedUpdates1.size(), 2);
1243             assertEquals("OnPropertiesListener received unexpected update",
1244                     receivedUpdates2.size(), 1);
1245 
1246             DeviceConfig.setProperty(NAMESPACE2, KEY1, VALUE2, /*makeDefault=*/false);
1247             waitForListenerUpdateOrTimeout(receivedUpdates2, /*expectedTotalUpdatesCount=*/2);
1248             waitForListenerUpdateOrTimeout(receivedUpdates1, /*expectedTotalUpdatesCount=*/2);
1249 
1250             assertEquals("OnPropertiesListener received unexpected update",
1251                     receivedUpdates1.size(), 2);
1252             assertEquals("OnPropertiesListener did not receive expected update",
1253                     receivedUpdates2.size(), 2);
1254 
1255             receivedUpdates2.get(1).assertEqual(NAMESPACE2, KEY1, VALUE2);
1256             DeviceConfig.removeOnPropertiesChangedListener(listener2);
1257 
1258             waitForListenerUpdateOrTimeout(receivedUpdates2, /*expectedTotalUpdatesCount=*/2);
1259             waitForListenerUpdateOrTimeout(receivedUpdates1, /*expectedTotalUpdatesCount=*/2);
1260 
1261             assertEquals("OnPropertiesListener received unexpected update",
1262                     receivedUpdates1.size(), 2);
1263             assertEquals("OnPropertiesListener received unexpected update",
1264                     receivedUpdates2.size(), 2);
1265 
1266         } catch(Exception e) {
1267             throw e;
1268         } finally {
1269             DeviceConfig.removeOnPropertiesChangedListener(listener1);
1270             DeviceConfig.removeOnPropertiesChangedListener(listener2);
1271         }
1272     }
1273 
1274     /**
1275      * Test that reset to package default successfully resets values.
1276      */
1277     @Test
testResetToPackageDefaults()1278     public void testResetToPackageDefaults() {
1279         DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE1, /*makeDefault=*/true);
1280         DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE2, /*makeDefault=*/false);
1281 
1282         assertEquals(DeviceConfig.getProperty(NAMESPACE1, KEY1), VALUE2);
1283 
1284         DeviceConfig.resetToDefaults(RESET_MODE_PACKAGE_DEFAULTS, NAMESPACE1);
1285 
1286         assertEquals(DeviceConfig.getProperty(NAMESPACE1, KEY1), VALUE1);
1287     }
1288 
1289     /**
1290      * Test updating syncDisabledMode.
1291      */
1292     @Test
testSetSyncDisabledMode()1293     public void testSetSyncDisabledMode() {
1294         DeviceConfig.setSyncDisabledMode(SYNC_DISABLED_MODE_NONE);
1295         assertEquals(SYNC_DISABLED_MODE_NONE, DeviceConfig.getSyncDisabledMode());
1296         DeviceConfig.setSyncDisabledMode(RESET_MODE_PACKAGE_DEFAULTS);
1297         assertEquals(RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.getSyncDisabledMode());
1298     }
1299 
1300 
1301     /**
1302      * Test getting public name spaces.
1303      */
1304     @Test
testGetPublicNameSpace()1305     public void testGetPublicNameSpace() {
1306         List<String> publicNameSpaces = Arrays.asList("textclassifier", "runtime", "statsd_java",
1307                 "statsd_java_boot", "selection_toolbar", "autofill",
1308                 "device_policy_manager", "content_capture");
1309 
1310         assertTrue(DeviceConfig.getPublicNamespaces().containsAll(publicNameSpaces));
1311     }
1312 
1313     /**
1314      * Test set monitor callback.
1315      */
1316     @Test
testSetMonitorCallback()1317     public void testSetMonitorCallback() {
1318         final CountDownLatch latch = new CountDownLatch(2);
1319         final TestMonitorCallback callback = new TestMonitorCallback(latch);
1320 
1321         DeviceConfig.setMonitorCallback(CONTEXT.getContentResolver(),
1322                 Executors.newSingleThreadExecutor(), callback);
1323         try {
1324             DeviceConfig.setProperties(new Properties.Builder(NAMESPACE1)
1325                     .setString(KEY1, VALUE1).setString(KEY2, VALUE2).build());
1326         } catch (DeviceConfig.BadConfigException e) {
1327             fail("Callback set strings" + e.toString());
1328         }
1329 
1330         // Reading properties triggers the monitor callback function.
1331         DeviceConfig.getString(NAMESPACE1, KEY1, null);
1332 
1333         try {
1334             if (!latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
1335                 fail("Callback function was not called");
1336             }
1337         } catch (InterruptedException e) {
1338             // this part is executed when an exception (in this example InterruptedException) occurs
1339             fail("Callback function was not called due to interruption" + e.toString());
1340         }
1341         assertEquals(callback.onNamespaceUpdateCalls, 1);
1342         assertEquals(callback.onDeviceConfigAccessCalls, 1);
1343         DeviceConfig.clearMonitorCallback(CONTEXT.getContentResolver());
1344     }
1345 
1346     /**
1347      * Test clear monitor callback.
1348      */
1349     @Test
testClearMonitorCallback()1350     public void testClearMonitorCallback() {
1351         final CountDownLatch latch = new CountDownLatch(2);
1352         final TestMonitorCallback callback = new TestMonitorCallback(latch);
1353 
1354         DeviceConfig.setMonitorCallback(CONTEXT.getContentResolver(),
1355                 Executors.newSingleThreadExecutor(), callback);
1356         DeviceConfig.clearMonitorCallback(CONTEXT.getContentResolver());
1357         // Reading properties triggers the monitor callback function.
1358         DeviceConfig.getString(NAMESPACE1, KEY1, null);
1359         try {
1360             DeviceConfig.setProperties(new Properties.Builder(NAMESPACE1)
1361                     .setString(KEY1, VALUE1).setString(KEY2, VALUE2).build());
1362         } catch (DeviceConfig.BadConfigException e) {
1363             fail("Callback set strings" + e.toString());
1364         }
1365 
1366         try {
1367             if (latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
1368                 fail("Callback function was called while it has been cleared");
1369             }
1370         } catch (InterruptedException e) {
1371             // this part is executed when an exception (in this example InterruptedException) occurs
1372             fail("un expected interruption occur" + e.toString());
1373         }
1374         assertEquals(callback.onNamespaceUpdateCalls, 0);
1375         assertEquals(callback.onDeviceConfigAccessCalls, 0);
1376     }
1377 
1378     private class TestMonitorCallback implements DeviceConfig.MonitorCallback {
1379         public int onNamespaceUpdateCalls = 0;
1380         public int onDeviceConfigAccessCalls = 0;
1381         public CountDownLatch latch;
1382 
TestMonitorCallback(CountDownLatch latch)1383         TestMonitorCallback(CountDownLatch latch) {
1384             this.latch = latch;
1385         }
1386 
onNamespaceUpdate(String updatedNamespace)1387         public void onNamespaceUpdate(String updatedNamespace) {
1388             onNamespaceUpdateCalls++;
1389             latch.countDown();
1390         }
1391 
onDeviceConfigAccess(String callingPackage, String namespace)1392         public void onDeviceConfigAccess(String callingPackage, String namespace) {
1393             onDeviceConfigAccessCalls++;
1394             latch.countDown();
1395         }
1396     }
1397 
createOnPropertiesChangedListener( List<PropertyUpdate> receivedUpdates)1398     private OnPropertiesChangedListener createOnPropertiesChangedListener(
1399             List<PropertyUpdate> receivedUpdates) {
1400         OnPropertiesChangedListener changeListener = new OnPropertiesChangedListener() {
1401             @Override
1402             public void onPropertiesChanged(Properties properties) {
1403                 synchronized (mLock) {
1404                     receivedUpdates.add(new PropertyUpdate(properties));
1405                     mLock.notifyAll();
1406                 }
1407             }
1408         };
1409         return changeListener;
1410     }
1411 
waitForListenerUpdateOrTimeout( List<PropertyUpdate> receivedUpdates, int expectedTotalUpdatesCount)1412     private void waitForListenerUpdateOrTimeout(
1413             List<PropertyUpdate> receivedUpdates, int expectedTotalUpdatesCount) {
1414 
1415         final long startTimeMillis = SystemClock.uptimeMillis();
1416         synchronized (mLock) {
1417             while (true) {
1418                 if (receivedUpdates.size() >= expectedTotalUpdatesCount) {
1419                     return;
1420                 }
1421                 final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
1422                 if (elapsedTimeMillis >= WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS) {
1423                     return;
1424                 }
1425                 final long remainingTimeMillis = WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS
1426                         - elapsedTimeMillis;
1427                 try {
1428                     mLock.wait(remainingTimeMillis);
1429                 } catch (InterruptedException ie) {
1430                     /* ignore */
1431                 }
1432             }
1433         }
1434     }
1435 
setPropertiesAndAssertSuccessfulChange(String setNamespace, String setName, String setValue)1436     private Properties setPropertiesAndAssertSuccessfulChange(String setNamespace, String setName,
1437             String setValue) {
1438         final List<PropertyUpdate> receivedUpdates = new ArrayList<>();
1439         OnPropertiesChangedListener changeListener = createOnPropertiesChangedListener(receivedUpdates);
1440 
1441         DeviceConfig.addOnPropertiesChangedListener(setNamespace, EXECUTOR, changeListener);
1442 
1443         DeviceConfig.setProperty(setNamespace, setName, setValue, /*makeDefault=*/false);
1444         waitForListenerUpdateOrTimeout(receivedUpdates, 1);
1445         DeviceConfig.removeOnPropertiesChangedListener(changeListener);
1446 
1447         assertEquals("Failed to receive update to OnPropertiesChangedListener",
1448                 receivedUpdates.size(), 1);
1449         PropertyUpdate propertiesUpdate = receivedUpdates.get(0);
1450         propertiesUpdate.assertEqual(setNamespace, setName, setValue);
1451 
1452         return propertiesUpdate.properties;
1453     }
1454 
setPropertiesAndAssertSuccessfulChange(Properties properties)1455     private Properties setPropertiesAndAssertSuccessfulChange(Properties properties)
1456             throws Exception {
1457         final List<PropertyUpdate> receivedUpdates = new ArrayList<>();
1458         OnPropertiesChangedListener changeListener
1459                 = createOnPropertiesChangedListener(receivedUpdates);
1460         DeviceConfig.addOnPropertiesChangedListener(
1461                 properties.getNamespace(), EXECUTOR, changeListener);
1462 
1463         DeviceConfig.setProperties(properties);
1464         waitForListenerUpdateOrTimeout(receivedUpdates, 1);
1465         DeviceConfig.removeOnPropertiesChangedListener(changeListener);
1466 
1467         assertEquals("Failed to receive update to OnPropertiesChangedListener",
1468                 1, receivedUpdates.size());
1469         PropertyUpdate propertiesUpdate = receivedUpdates.get(0);
1470         propertiesUpdate.assertEqual(properties);
1471 
1472         return propertiesUpdate.properties;
1473     }
1474 
deletePropertyAndAssertSuccessfulChange(String namespace, String name)1475     private Properties deletePropertyAndAssertSuccessfulChange(String namespace, String name) {
1476         final List<PropertyUpdate> receivedUpdates = new ArrayList<>();
1477         OnPropertiesChangedListener changeListener = createOnPropertiesChangedListener(receivedUpdates);
1478 
1479         DeviceConfig.addOnPropertiesChangedListener(namespace, EXECUTOR, changeListener);
1480 
1481         assertTrue(DeviceConfig.deleteProperty(namespace, name));
1482         assertNull("DeviceConfig.getProperty() must return null if property is deleted",
1483                 DeviceConfig.getProperty(namespace, name));
1484         waitForListenerUpdateOrTimeout(receivedUpdates, 1);
1485         DeviceConfig.removeOnPropertiesChangedListener(changeListener);
1486 
1487         assertEquals("Failed to receive update to OnPropertiesChangedListener",
1488                 receivedUpdates.size(), 1);
1489         PropertyUpdate propertiesUpdate = receivedUpdates.get(0);
1490         propertiesUpdate.assertEqual(namespace, name, null);
1491 
1492         return propertiesUpdate.properties;
1493     }
1494 
deletePropertyAndAssertNoChange(String namespace, String name)1495     private Properties deletePropertyAndAssertNoChange(String namespace, String name) {
1496         final List<PropertyUpdate> receivedUpdates = new ArrayList<>();
1497         OnPropertiesChangedListener changeListener = createOnPropertiesChangedListener(
1498                 receivedUpdates);
1499 
1500         DeviceConfig.addOnPropertiesChangedListener(namespace, EXECUTOR, changeListener);
1501 
1502         assertTrue(DeviceConfig.deleteProperty(namespace, name));
1503         assertNull("DeviceConfig.getProperty() must return null if property is deleted",
1504                 DeviceConfig.getProperty(namespace, name));
1505         waitForListenerUpdateOrTimeout(receivedUpdates, 1);
1506         DeviceConfig.removeOnPropertiesChangedListener(changeListener);
1507 
1508         assertEquals("Received unexpected update to OnPropertiesChangedListener",
1509                 receivedUpdates.size(), 0);
1510         PropertyUpdate propertiesUpdate = new PropertyUpdate(DeviceConfig.getProperties(
1511                 namespace, ""));
1512         propertiesUpdate.assertEqual(namespace, name, null);
1513 
1514         return propertiesUpdate.properties;
1515     }
1516 
nullifyProperty(String namespace, String key)1517     private void nullifyProperty(String namespace, String key) {
1518         if (DeviceConfig.getString(namespace, key, null) != null) {
1519             setPropertiesAndAssertSuccessfulChange(namespace, key, null);
1520         }
1521     }
1522 
deletePropertyThrowShell(String namespace, String key)1523     private static void deletePropertyThrowShell(String namespace, String key) {
1524         InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
1525                 "device_config delete " + namespace + " " + key);
1526     }
1527 
isSupported()1528     private static boolean isSupported() {
1529         return sUnsupportedReason == null;
1530     }
1531 
1532     private static class PropertyUpdate {
1533         Properties properties;
1534 
PropertyUpdate(Properties properties)1535         PropertyUpdate(Properties properties) {
1536             this.properties = properties;
1537         }
1538 
assertEqual(String namespace, String name, String value)1539         void assertEqual(String namespace, String name, String value) {
1540             Properties properties =
1541                     new Properties.Builder(namespace).setString(name, value).build();
1542             assertEqual(properties);
1543         }
1544 
assertEqual(Properties expected)1545         void assertEqual(Properties expected) {
1546             assertEquals(expected.getNamespace(), properties.getNamespace());
1547             assertEquals(expected.getKeyset().size(), properties.getKeyset().size());
1548             for (String key : properties.getKeyset()) {
1549                 assertEquals(expected.getString(key, DEFAULT_VALUE),
1550                         properties.getString(key, DEFAULT_VALUE));
1551                 assertEquals(expected.getBoolean(key, DEFAULT_BOOLEAN_FALSE),
1552                         properties.getBoolean(key, DEFAULT_BOOLEAN_FALSE));
1553                 assertEquals(expected.getInt(key, DEFAULT_INT),
1554                         properties.getInt(key, DEFAULT_INT));
1555                 assertEquals(expected.getFloat(key, DEFAULT_FLOAT),
1556                         properties.getFloat(key, DEFAULT_FLOAT), 0);
1557                 assertEquals(expected.getLong(key, DEFAULT_LONG),
1558                         properties.getLong(key, DEFAULT_LONG));
1559             }
1560         }
1561     }
1562 }
1563