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