Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2010 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.content.cts;
     18 
     19 import android.app.QueuedWork;
     20 import android.content.Context;
     21 import android.content.SharedPreferences;
     22 import android.content.pm.ApplicationInfo;
     23 import android.content.pm.PackageManager;
     24 import android.os.StrictMode;
     25 import android.preference.PreferenceManager;
     26 import android.test.AndroidTestCase;
     27 import android.util.Log;
     28 import java.io.File;
     29 import java.io.FileInputStream;
     30 import java.io.FileOutputStream;
     31 import java.io.IOException;
     32 import java.util.HashMap;
     33 import java.util.Map;
     34 import java.util.Random;
     35 
     36 /**
     37  * Test {@link SharedPreferences}.
     38  */
     39 public class SharedPreferencesTest extends AndroidTestCase {
     40     private static final String TAG = "SharedPreferencesTest";
     41 
     42     private Context mContext;
     43 
     44     private File mPrefsFile;
     45 
     46     @Override
     47     protected void setUp() throws Exception {
     48         super.setUp();
     49         mContext = getContext();
     50 
     51         SharedPreferences prefs = getPrefs();
     52         prefs.edit().clear().commit();
     53         try {
     54             ApplicationInfo applicationInfo = mContext.getPackageManager().getApplicationInfo(
     55                     mContext.getPackageName(), 0);
     56             mPrefsFile = new File(applicationInfo.dataDir,
     57                     "shared_prefs/android.content.cts_preferences.xml");
     58         } catch (PackageManager.NameNotFoundException e) {
     59             throw new IllegalStateException(e);
     60         }
     61         mPrefsFile.delete();
     62     }
     63 
     64     private SharedPreferences getPrefs() {
     65         return PreferenceManager.getDefaultSharedPreferences(mContext);
     66     }
     67 
     68     public void testNoFileInitially() {
     69         assertFalse(mPrefsFile.exists());
     70     }
     71 
     72     public void testCommitCreatesFiles() {
     73         SharedPreferences prefs = getPrefs();
     74         assertFalse(mPrefsFile.exists());
     75         prefs.edit().putString("foo", "bar").commit();
     76         assertTrue(mPrefsFile.exists());
     77     }
     78 
     79     public void testDefaults() {
     80         SharedPreferences prefs = getPrefs();
     81         String key = "not-set";
     82         assertFalse(prefs.contains(key));
     83         assertEquals(0, prefs.getAll().size());
     84         assertTrue(prefs.getAll().isEmpty());
     85         assertEquals(false, prefs.getBoolean(key, false));
     86         assertEquals(true, prefs.getBoolean(key, true));
     87         assertEquals(0.5f, prefs.getFloat(key, 0.5f));
     88         assertEquals(123, prefs.getInt(key, 123));
     89         assertEquals(999L, prefs.getLong(key, 999L));
     90         assertEquals("default", prefs.getString(key, "default"));
     91     }
     92 
     93     public void testPutNullRemovesKey() {
     94         SharedPreferences prefs = getPrefs();
     95         prefs.edit().putString("test-key", "test-value").commit();
     96         assertEquals("test-value", prefs.getString("test-key", null));
     97 
     98         SharedPreferences.Editor editor = prefs.edit().putString("test-key", null);
     99         assertEquals("test-value", prefs.getString("test-key", null));
    100         editor.commit();
    101 
    102         assertNull(prefs.getString("test-key", null));
    103         assertFalse(prefs.contains("test-key"));
    104     }
    105 
    106     private abstract class RedundantWriteTest {
    107         // Do some initial operation on editor.  No commit needed.
    108         public abstract void setUp(SharedPreferences.Editor editor);
    109 
    110         // Do some later operation on editor (e.g. a redundant edit).
    111         // No commit needed.
    112         public abstract void subsequentEdit(SharedPreferences.Editor editor);
    113 
    114         public boolean expectingMutation() {
    115             return false;
    116         }
    117 
    118         // Tests that a redundant edit after an initital setup doesn't
    119         // result in a duplicate write-out to disk.
    120         public final void test() throws Exception {
    121             SharedPreferences prefs = getPrefs();
    122             SharedPreferences.Editor editor;
    123 
    124             assertFalse(mPrefsFile.exists());
    125             prefs.edit().commit();
    126             assertTrue(mPrefsFile.exists());
    127 
    128             editor = prefs.edit();
    129             setUp(editor);
    130             editor.commit();
    131             long modtimeMillis1 = mPrefsFile.lastModified();
    132 
    133             // Wait a second and modify the preferences in a dummy,
    134             // redundant way.  Wish I could inject a clock or disk mock
    135             // here, but can't.  Instead relying on checking modtime of
    136             // file on disk.
    137             Thread.sleep(1000); // ms
    138 
    139             editor = prefs.edit();
    140             subsequentEdit(editor);
    141             editor.commit();
    142 
    143             long modtimeMillis2 = mPrefsFile.lastModified();
    144             assertEquals(expectingMutation(), modtimeMillis1 != modtimeMillis2);
    145         }
    146     };
    147 
    148     public void testRedundantBoolean() throws Exception {
    149         new RedundantWriteTest() {
    150             public void setUp(SharedPreferences.Editor editor) {
    151                 editor.putBoolean("foo", true);
    152             }
    153             public void subsequentEdit(SharedPreferences.Editor editor) {
    154                 editor.putBoolean("foo", true);
    155             }
    156         }.test();
    157     }
    158 
    159     public void testRedundantString() throws Exception {
    160         new RedundantWriteTest() {
    161             public void setUp(SharedPreferences.Editor editor) {
    162                 editor.putString("foo", "bar");
    163             }
    164             public void subsequentEdit(SharedPreferences.Editor editor) {
    165                 editor.putString("foo", "bar");
    166             }
    167         }.test();
    168     }
    169 
    170     public void testNonRedundantString() throws Exception {
    171         new RedundantWriteTest() {
    172             public void setUp(SharedPreferences.Editor editor) {
    173                 editor.putString("foo", "bar");
    174             }
    175             public void subsequentEdit(SharedPreferences.Editor editor) {
    176                 editor.putString("foo", "baz");
    177             }
    178             public boolean expectingMutation() {
    179                 return true;
    180             }
    181         }.test();
    182     }
    183 
    184     public void testRedundantClear() throws Exception {
    185         new RedundantWriteTest() {
    186             public void setUp(SharedPreferences.Editor editor) {
    187                 editor.clear();
    188             }
    189             public void subsequentEdit(SharedPreferences.Editor editor) {
    190                 editor.clear();
    191             }
    192         }.test();
    193     }
    194 
    195     public void testNonRedundantClear() throws Exception {
    196         new RedundantWriteTest() {
    197             public void setUp(SharedPreferences.Editor editor) {
    198                 editor.putString("foo", "bar");
    199             }
    200             public void subsequentEdit(SharedPreferences.Editor editor) {
    201                 editor.clear();
    202             }
    203             public boolean expectingMutation() {
    204                 return true;
    205             }
    206         }.test();
    207     }
    208 
    209     public void testRedundantRemove() throws Exception {
    210         new RedundantWriteTest() {
    211             public void setUp(SharedPreferences.Editor editor) {
    212                 editor.putString("foo", "bar");
    213             }
    214             public void subsequentEdit(SharedPreferences.Editor editor) {
    215                 editor.remove("not-exist-key");
    216             }
    217         }.test();
    218     }
    219 
    220     public void testRedundantCommitWritesFileIfNotAlreadyExisting() {
    221         SharedPreferences prefs = getPrefs();
    222         assertFalse(mPrefsFile.exists());
    223         prefs.edit().putString("foo", "bar").commit();
    224         assertTrue(mPrefsFile.exists());
    225 
    226         // Delete the file out from under it.  (not sure why this
    227         // would happen in practice, but perhaps the app did it for
    228         // some reason...)
    229         mPrefsFile.delete();
    230 
    231         // And verify that a redundant edit (which would otherwise not
    232         // write do disk), still does write to disk if the file isn't
    233         // there.
    234         prefs.edit().putString("foo", "bar").commit();
    235         assertTrue(mPrefsFile.exists());
    236     }
    237 
    238     public void testTorture() {
    239         Map<String, String> expectedMap = new HashMap<String, String>();
    240         Random rand = new Random();
    241 
    242         SharedPreferences prefs = mContext.getSharedPreferences("torture", Context.MODE_PRIVATE);
    243         prefs.edit().clear().commit();
    244 
    245         for (int i = 0; i < 100; i++) {
    246             prefs = mContext.getSharedPreferences("torture", Context.MODE_PRIVATE);
    247             assertEquals(expectedMap, prefs.getAll());
    248 
    249             String key = new Integer(rand.nextInt(25)).toString();
    250             String value = new Integer(i).toString();
    251             SharedPreferences.Editor editor = prefs.edit();
    252 
    253             if (rand.nextInt(100) < 85) {
    254                 Log.d(TAG, "Setting " + key + "=" + value);
    255                 editor.putString(key, value);
    256                 expectedMap.put(key, value);
    257             } else {
    258                 Log.d(TAG, "Removing " + key);
    259                 editor.remove(key);
    260                 expectedMap.remove(key);
    261             }
    262 
    263             // Use apply on most, but commit some too.
    264             if (rand.nextInt(100) < 85) {
    265                 Log.d(TAG, "apply.");
    266                 editor.apply();
    267             } else {
    268                 Log.d(TAG, "commit.");
    269                 editor.commit();
    270             }
    271 
    272             assertEquals(expectedMap, prefs.getAll());
    273         }
    274     }
    275 
    276     // Checks that an in-memory commit doesn't mutate a data structure
    277     // still being used while writing out to disk.
    278     public void testTorture2() {
    279         Random rand = new Random();
    280 
    281         for (int fi = 0; fi < 100; fi++) {
    282             String prefsName = "torture_" + fi;
    283             SharedPreferences prefs = mContext.getSharedPreferences(prefsName, Context.MODE_PRIVATE);
    284             prefs.edit().clear().commit();
    285             Map<String, String> expectedMap = new HashMap<String, String>();
    286 
    287             for (int applies = 0; applies < 3; applies++) {
    288                 SharedPreferences.Editor editor = prefs.edit();
    289                 for (int n = 0; n < 1000; n++) {
    290                     String key = new Integer(rand.nextInt(25)).toString();
    291                     String value = new Integer(n).toString();
    292                     editor.putString(key, value);
    293                     expectedMap.put(key, value);
    294                 }
    295                 editor.apply();
    296             }
    297             QueuedWork.waitToFinish();
    298 
    299             String clonePrefsName = prefsName + "_clone";
    300             File prefsFile = mContext.getSharedPrefsFile(prefsName);
    301             File prefsFileClone = mContext.getSharedPrefsFile(clonePrefsName);
    302             prefsFileClone.delete();
    303             try {
    304                 FileOutputStream fos = new FileOutputStream(prefsFileClone);
    305                 FileInputStream fis = new FileInputStream(prefsFile);
    306                 byte buf[] = new byte[1024];
    307                 int n;
    308                 while ((n = fis.read(buf)) > 0) {
    309                     fos.write(buf, 0, n);
    310                 }
    311                 fis.close();
    312                 fos.close();
    313             } catch (IOException e) {
    314             }
    315 
    316             SharedPreferences clonePrefs = mContext.getSharedPreferences(
    317                 clonePrefsName, Context.MODE_PRIVATE);
    318             assertEquals(expectedMap, clonePrefs.getAll());
    319 
    320             prefsFile.delete();
    321             prefsFileClone.delete();
    322         }
    323     }
    324 
    325     public void testModeMultiProcess() {
    326         // Pre-load it.
    327         mContext.getSharedPreferences("multiprocessTest", 0);
    328 
    329         final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
    330         try {
    331             StrictMode.ThreadPolicy diskReadDeath =
    332                     new StrictMode.ThreadPolicy.Builder().detectDiskReads().penaltyDeath().build();
    333             StrictMode.setThreadPolicy(diskReadDeath);
    334 
    335             // This shouldn't hit disk.  (it was already pre-loaded above)
    336             mContext.getSharedPreferences("multiprocessTest", 0);
    337 
    338             boolean didRead = false;
    339             // This SHOULD hit disk.  (multi-process flag is set)
    340             try {
    341                 mContext.getSharedPreferences("multiprocessTest", Context.MODE_MULTI_PROCESS);
    342                 fail();  // we shouldn't get here.
    343             } catch (StrictMode.StrictModeViolation e) {
    344                 didRead = true;
    345             }
    346             assertTrue(didRead);
    347         } finally {
    348             StrictMode.setThreadPolicy(oldPolicy);
    349         }
    350     }
    351 }
    352