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