1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/tests/background/junit3/src/db/TestPasswordsRepository.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,398 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +package org.mozilla.gecko.background.db; 1.8 + 1.9 +import java.util.HashSet; 1.10 +import java.util.Set; 1.11 + 1.12 +import org.mozilla.gecko.background.helpers.AndroidSyncTestCase; 1.13 +import org.mozilla.gecko.background.sync.helpers.ExpectFetchDelegate; 1.14 +import org.mozilla.gecko.background.sync.helpers.ExpectFetchSinceDelegate; 1.15 +import org.mozilla.gecko.background.sync.helpers.ExpectGuidsSinceDelegate; 1.16 +import org.mozilla.gecko.background.sync.helpers.ExpectStoredDelegate; 1.17 +import org.mozilla.gecko.background.sync.helpers.PasswordHelpers; 1.18 +import org.mozilla.gecko.background.sync.helpers.SessionTestHelper; 1.19 +import org.mozilla.gecko.background.testhelpers.WaitHelper; 1.20 +import org.mozilla.gecko.db.BrowserContract; 1.21 +import org.mozilla.gecko.sync.Utils; 1.22 +import org.mozilla.gecko.sync.repositories.InactiveSessionException; 1.23 +import org.mozilla.gecko.sync.repositories.NoStoreDelegateException; 1.24 +import org.mozilla.gecko.sync.repositories.Repository; 1.25 +import org.mozilla.gecko.sync.repositories.RepositorySession; 1.26 +import org.mozilla.gecko.sync.repositories.android.BrowserContractHelpers; 1.27 +import org.mozilla.gecko.sync.repositories.android.PasswordsRepositorySession; 1.28 +import org.mozilla.gecko.sync.repositories.android.RepoUtils; 1.29 +import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionCreationDelegate; 1.30 +import org.mozilla.gecko.sync.repositories.domain.PasswordRecord; 1.31 +import org.mozilla.gecko.sync.repositories.domain.Record; 1.32 + 1.33 +import android.content.ContentProviderClient; 1.34 +import android.content.Context; 1.35 +import android.database.Cursor; 1.36 +import android.os.RemoteException; 1.37 + 1.38 +public class TestPasswordsRepository extends AndroidSyncTestCase { 1.39 + private final String NEW_PASSWORD1 = "password"; 1.40 + private final String NEW_PASSWORD2 = "drowssap"; 1.41 + 1.42 + @Override 1.43 + public void setUp() { 1.44 + wipe(); 1.45 + assertTrue(WaitHelper.getTestWaiter().isIdle()); 1.46 + } 1.47 + 1.48 + public void testFetchAll() { 1.49 + RepositorySession session = createAndBeginSession(); 1.50 + Record[] expected = new Record[] { PasswordHelpers.createPassword1(), 1.51 + PasswordHelpers.createPassword2() }; 1.52 + 1.53 + performWait(storeRunnable(session, expected[0])); 1.54 + performWait(storeRunnable(session, expected[1])); 1.55 + 1.56 + performWait(fetchAllRunnable(session, expected)); 1.57 + dispose(session); 1.58 + } 1.59 + 1.60 + public void testGuidsSinceReturnMultipleRecords() { 1.61 + RepositorySession session = createAndBeginSession(); 1.62 + 1.63 + PasswordRecord record1 = PasswordHelpers.createPassword1(); 1.64 + PasswordRecord record2 = PasswordHelpers.createPassword2(); 1.65 + 1.66 + updatePassword(NEW_PASSWORD1, record1); 1.67 + long timestamp = updatePassword(NEW_PASSWORD2, record2); 1.68 + 1.69 + String[] expected = new String[] { record1.guid, record2.guid }; 1.70 + 1.71 + performWait(storeRunnable(session, record1)); 1.72 + performWait(storeRunnable(session, record2)); 1.73 + 1.74 + performWait(guidsSinceRunnable(session, timestamp, expected)); 1.75 + dispose(session); 1.76 + } 1.77 + 1.78 + public void testGuidsSinceReturnNoRecords() { 1.79 + RepositorySession session = createAndBeginSession(); 1.80 + 1.81 + // Store 1 record in the past. 1.82 + performWait(storeRunnable(session, PasswordHelpers.createPassword1())); 1.83 + 1.84 + String[] expected = {}; 1.85 + performWait(guidsSinceRunnable(session, System.currentTimeMillis() + 1000, expected)); 1.86 + dispose(session); 1.87 + } 1.88 + 1.89 + public void testFetchSinceOneRecord() { 1.90 + RepositorySession session = createAndBeginSession(); 1.91 + 1.92 + // Passwords fetchSince checks timePasswordChanged, not insertion time. 1.93 + PasswordRecord record1 = PasswordHelpers.createPassword1(); 1.94 + long timeModified1 = updatePassword(NEW_PASSWORD1, record1); 1.95 + performWait(storeRunnable(session, record1)); 1.96 + 1.97 + PasswordRecord record2 = PasswordHelpers.createPassword2(); 1.98 + long timeModified2 = updatePassword(NEW_PASSWORD2, record2); 1.99 + performWait(storeRunnable(session, record2)); 1.100 + 1.101 + String[] expectedOne = new String[] { record2.guid }; 1.102 + performWait(fetchSinceRunnable(session, timeModified2 - 10, expectedOne)); 1.103 + 1.104 + String[] expectedBoth = new String[] { record1.guid, record2.guid }; 1.105 + performWait(fetchSinceRunnable(session, timeModified1 - 10, expectedBoth)); 1.106 + 1.107 + dispose(session); 1.108 + } 1.109 + 1.110 + public void testFetchSinceReturnNoRecords() { 1.111 + RepositorySession session = createAndBeginSession(); 1.112 + 1.113 + performWait(storeRunnable(session, PasswordHelpers.createPassword2())); 1.114 + 1.115 + long timestamp = System.currentTimeMillis(); 1.116 + 1.117 + performWait(fetchSinceRunnable(session, timestamp + 2000, new String[] {})); 1.118 + dispose(session); 1.119 + } 1.120 + 1.121 + public void testFetchOneRecordByGuid() { 1.122 + RepositorySession session = createAndBeginSession(); 1.123 + Record record = PasswordHelpers.createPassword1(); 1.124 + performWait(storeRunnable(session, record)); 1.125 + performWait(storeRunnable(session, PasswordHelpers.createPassword2())); 1.126 + 1.127 + String[] guids = new String[] { record.guid }; 1.128 + Record[] expected = new Record[] { record }; 1.129 + performWait(fetchRunnable(session, guids, expected)); 1.130 + dispose(session); 1.131 + } 1.132 + 1.133 + public void testFetchMultipleRecordsByGuids() { 1.134 + RepositorySession session = createAndBeginSession(); 1.135 + PasswordRecord record1 = PasswordHelpers.createPassword1(); 1.136 + PasswordRecord record2 = PasswordHelpers.createPassword2(); 1.137 + PasswordRecord record3 = PasswordHelpers.createPassword3(); 1.138 + 1.139 + performWait(storeRunnable(session, record1)); 1.140 + performWait(storeRunnable(session, record2)); 1.141 + performWait(storeRunnable(session, record3)); 1.142 + 1.143 + String[] guids = new String[] { record1.guid, record2.guid }; 1.144 + Record[] expected = new Record[] { record1, record2 }; 1.145 + performWait(fetchRunnable(session, guids, expected)); 1.146 + dispose(session); 1.147 + } 1.148 + 1.149 + public void testFetchNoRecordByGuid() { 1.150 + RepositorySession session = createAndBeginSession(); 1.151 + Record record = PasswordHelpers.createPassword1(); 1.152 + 1.153 + performWait(storeRunnable(session, record)); 1.154 + performWait(fetchRunnable(session, 1.155 + new String[] { Utils.generateGuid() }, 1.156 + new Record[] {})); 1.157 + dispose(session); 1.158 + } 1.159 + 1.160 + public void testStore() { 1.161 + final RepositorySession session = createAndBeginSession(); 1.162 + performWait(storeRunnable(session, PasswordHelpers.createPassword1())); 1.163 + dispose(session); 1.164 + } 1.165 + 1.166 + public void testRemoteNewerTimeStamp() { 1.167 + final RepositorySession session = createAndBeginSession(); 1.168 + 1.169 + // Store updated local record. 1.170 + PasswordRecord local = PasswordHelpers.createPassword1(); 1.171 + updatePassword(NEW_PASSWORD1, local, System.currentTimeMillis() - 1000); 1.172 + performWait(storeRunnable(session, local)); 1.173 + 1.174 + // Sync a remote record version that is newer. 1.175 + PasswordRecord remote = PasswordHelpers.createPassword2(); 1.176 + remote.guid = local.guid; 1.177 + updatePassword(NEW_PASSWORD2, remote); 1.178 + performWait(storeRunnable(session, remote)); 1.179 + 1.180 + // Make a fetch, expecting only the newer (remote) record. 1.181 + performWait(fetchAllRunnable(session, new Record[] { remote })); 1.182 + dispose(session); 1.183 + } 1.184 + 1.185 + public void testLocalNewerTimeStamp() { 1.186 + final RepositorySession session = createAndBeginSession(); 1.187 + // Remote record updated before local record. 1.188 + PasswordRecord remote = PasswordHelpers.createPassword1(); 1.189 + updatePassword(NEW_PASSWORD1, remote, System.currentTimeMillis() - 1000); 1.190 + 1.191 + // Store updated local record. 1.192 + PasswordRecord local = PasswordHelpers.createPassword2(); 1.193 + updatePassword(NEW_PASSWORD2, local); 1.194 + performWait(storeRunnable(session, local)); 1.195 + 1.196 + // Sync a remote record version that is older. 1.197 + remote.guid = local.guid; 1.198 + performWait(storeRunnable(session, remote)); 1.199 + 1.200 + // Make a fetch, expecting only the newer (local) record. 1.201 + performWait(fetchAllRunnable(session, new Record[] { local })); 1.202 + dispose(session); 1.203 + } 1.204 + 1.205 + /* 1.206 + * Store two records that are identical except for guid. Expect to find the 1.207 + * remote one after reconciling. 1.208 + */ 1.209 + public void testStoreIdenticalExceptGuid() { 1.210 + RepositorySession session = createAndBeginSession(); 1.211 + PasswordRecord record = PasswordHelpers.createPassword1(); 1.212 + record.guid = "before1"; 1.213 + // Store record. 1.214 + performWait(storeRunnable(session, record)); 1.215 + 1.216 + // Store same record, but with different guid. 1.217 + record.guid = Utils.generateGuid(); 1.218 + performWait(storeRunnable(session, record)); 1.219 + 1.220 + performWait(fetchAllRunnable(session, new Record[] { record })); 1.221 + dispose(session); 1.222 + 1.223 + session = createAndBeginSession(); 1.224 + 1.225 + PasswordRecord record2 = PasswordHelpers.createPassword2(); 1.226 + record2.guid = "before2"; 1.227 + // Store record. 1.228 + performWait(storeRunnable(session, record2)); 1.229 + 1.230 + // Store same record, but with different guid. 1.231 + record2.guid = Utils.generateGuid(); 1.232 + performWait(storeRunnable(session, record2)); 1.233 + 1.234 + performWait(fetchAllRunnable(session, new Record[] { record, record2 })); 1.235 + dispose(session); 1.236 + } 1.237 + 1.238 + /* 1.239 + * Store two records that are identical except for guid when they both point 1.240 + * to the same site and there are multiple records for that site. Expect to 1.241 + * find the remote one after reconciling. 1.242 + */ 1.243 + public void testStoreIdenticalExceptGuidOnSameSite() { 1.244 + RepositorySession session = createAndBeginSession(); 1.245 + PasswordRecord record1 = PasswordHelpers.createPassword1(); 1.246 + record1.encryptedUsername = "original"; 1.247 + record1.guid = "before1"; 1.248 + PasswordRecord record2 = PasswordHelpers.createPassword1(); 1.249 + record2.encryptedUsername = "different"; 1.250 + record1.guid = "before2"; 1.251 + // Store records. 1.252 + performWait(storeRunnable(session, record1)); 1.253 + performWait(storeRunnable(session, record2)); 1.254 + performWait(fetchAllRunnable(session, new Record[] { record1, record2 })); 1.255 + 1.256 + dispose(session); 1.257 + session = createAndBeginSession(); 1.258 + 1.259 + // Store same records, but with different guids. 1.260 + record1.guid = Utils.generateGuid(); 1.261 + performWait(storeRunnable(session, record1)); 1.262 + performWait(fetchAllRunnable(session, new Record[] { record1, record2 })); 1.263 + 1.264 + record2.guid = Utils.generateGuid(); 1.265 + performWait(storeRunnable(session, record2)); 1.266 + performWait(fetchAllRunnable(session, new Record[] { record1, record2 })); 1.267 + 1.268 + dispose(session); 1.269 + } 1.270 + 1.271 + public void testRawFetch() throws RemoteException { 1.272 + RepositorySession session = createAndBeginSession(); 1.273 + Record[] expected = new Record[] { PasswordHelpers.createPassword1(), 1.274 + PasswordHelpers.createPassword2() }; 1.275 + 1.276 + performWait(storeRunnable(session, expected[0])); 1.277 + performWait(storeRunnable(session, expected[1])); 1.278 + 1.279 + ContentProviderClient client = getApplicationContext().getContentResolver().acquireContentProviderClient(BrowserContract.PASSWORDS_AUTHORITY_URI); 1.280 + Cursor cursor = client.query(BrowserContractHelpers.PASSWORDS_CONTENT_URI, null, null, null, null); 1.281 + assertEquals(2, cursor.getCount()); 1.282 + cursor.moveToFirst(); 1.283 + Set<String> guids = new HashSet<String>(); 1.284 + while (!cursor.isAfterLast()) { 1.285 + String guid = RepoUtils.getStringFromCursor(cursor, BrowserContract.Passwords.GUID); 1.286 + guids.add(guid); 1.287 + cursor.moveToNext(); 1.288 + } 1.289 + cursor.close(); 1.290 + assertEquals(2, guids.size()); 1.291 + assertTrue(guids.contains(expected[0].guid)); 1.292 + assertTrue(guids.contains(expected[1].guid)); 1.293 + dispose(session); 1.294 + } 1.295 + 1.296 + // Helper methods. 1.297 + private RepositorySession createAndBeginSession() { 1.298 + return SessionTestHelper.createAndBeginSession( 1.299 + getApplicationContext(), 1.300 + getRepository()); 1.301 + } 1.302 + 1.303 + private Repository getRepository() { 1.304 + /** 1.305 + * Override this chain in order to avoid our test code having to create two 1.306 + * sessions all the time. Don't track records, so they filtering doesn't happen. 1.307 + */ 1.308 + return new PasswordsRepositorySession.PasswordsRepository() { 1.309 + @Override 1.310 + public void createSession(RepositorySessionCreationDelegate delegate, 1.311 + Context context) { 1.312 + PasswordsRepositorySession session; 1.313 + session = new PasswordsRepositorySession(this, context) { 1.314 + @Override 1.315 + protected synchronized void trackGUID(String guid) { 1.316 + } 1.317 + }; 1.318 + delegate.onSessionCreated(session); 1.319 + } 1.320 + }; 1.321 + } 1.322 + 1.323 + private void wipe() { 1.324 + Context context = getApplicationContext(); 1.325 + context.getContentResolver().delete(BrowserContractHelpers.PASSWORDS_CONTENT_URI, null, null); 1.326 + context.getContentResolver().delete(BrowserContractHelpers.DELETED_PASSWORDS_CONTENT_URI, null, null); 1.327 + } 1.328 + 1.329 + private static void dispose(RepositorySession session) { 1.330 + if (session != null) { 1.331 + session.abort(); 1.332 + } 1.333 + } 1.334 + 1.335 + private static long updatePassword(String password, PasswordRecord record, long timestamp) { 1.336 + record.encryptedPassword = password; 1.337 + long modifiedTime = System.currentTimeMillis(); 1.338 + record.timePasswordChanged = record.lastModified = modifiedTime; 1.339 + return modifiedTime; 1.340 + } 1.341 + 1.342 + private static long updatePassword(String password, PasswordRecord record) { 1.343 + return updatePassword(password, record, System.currentTimeMillis()); 1.344 + } 1.345 + 1.346 + // Runnable Helpers. 1.347 + private static Runnable storeRunnable(final RepositorySession session, final Record record) { 1.348 + return new Runnable() { 1.349 + @Override 1.350 + public void run() { 1.351 + session.setStoreDelegate(new ExpectStoredDelegate(record.guid)); 1.352 + try { 1.353 + session.store(record); 1.354 + session.storeDone(); 1.355 + } catch (NoStoreDelegateException e) { 1.356 + fail("NoStoreDelegateException should not occur."); 1.357 + } 1.358 + } 1.359 + }; 1.360 + } 1.361 + 1.362 + private static Runnable fetchAllRunnable(final RepositorySession session, final Record[] records) { 1.363 + return new Runnable() { 1.364 + @Override 1.365 + public void run() { 1.366 + session.fetchAll(new ExpectFetchDelegate(records)); 1.367 + } 1.368 + }; 1.369 + } 1.370 + 1.371 + private static Runnable guidsSinceRunnable(final RepositorySession session, final long timestamp, final String[] expected) { 1.372 + return new Runnable() { 1.373 + @Override 1.374 + public void run() { 1.375 + session.guidsSince(timestamp, new ExpectGuidsSinceDelegate(expected)); 1.376 + } 1.377 + }; 1.378 + } 1.379 + 1.380 + private static Runnable fetchSinceRunnable(final RepositorySession session, final long timestamp, final String[] expected) { 1.381 + return new Runnable() { 1.382 + @Override 1.383 + public void run() { 1.384 + session.fetchSince(timestamp, new ExpectFetchSinceDelegate(timestamp, expected)); 1.385 + } 1.386 + }; 1.387 + } 1.388 + 1.389 + private static Runnable fetchRunnable(final RepositorySession session, final String[] guids, final Record[] expected) { 1.390 + return new Runnable() { 1.391 + @Override 1.392 + public void run() { 1.393 + try { 1.394 + session.fetch(guids, new ExpectFetchDelegate(expected)); 1.395 + } catch (InactiveSessionException e) { 1.396 + performNotify(e); 1.397 + } 1.398 + } 1.399 + }; 1.400 + } 1.401 +}