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