Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
michael@0 | 1 | /* Any copyright is dedicated to the Public Domain. |
michael@0 | 2 | http://creativecommons.org/publicdomain/zero/1.0/ */ |
michael@0 | 3 | |
michael@0 | 4 | package org.mozilla.gecko.background.db; |
michael@0 | 5 | |
michael@0 | 6 | import java.util.ArrayList; |
michael@0 | 7 | |
michael@0 | 8 | import org.json.simple.JSONObject; |
michael@0 | 9 | import org.mozilla.gecko.background.sync.helpers.ExpectFetchDelegate; |
michael@0 | 10 | import org.mozilla.gecko.background.sync.helpers.ExpectFinishDelegate; |
michael@0 | 11 | import org.mozilla.gecko.background.sync.helpers.HistoryHelpers; |
michael@0 | 12 | import org.mozilla.gecko.db.BrowserContract; |
michael@0 | 13 | import org.mozilla.gecko.sync.Utils; |
michael@0 | 14 | import org.mozilla.gecko.sync.repositories.InactiveSessionException; |
michael@0 | 15 | import org.mozilla.gecko.sync.repositories.NullCursorException; |
michael@0 | 16 | import org.mozilla.gecko.sync.repositories.Repository; |
michael@0 | 17 | import org.mozilla.gecko.sync.repositories.RepositorySession; |
michael@0 | 18 | import org.mozilla.gecko.sync.repositories.android.AndroidBrowserHistoryDataAccessor; |
michael@0 | 19 | import org.mozilla.gecko.sync.repositories.android.AndroidBrowserHistoryRepository; |
michael@0 | 20 | import org.mozilla.gecko.sync.repositories.android.AndroidBrowserHistoryRepositorySession; |
michael@0 | 21 | import org.mozilla.gecko.sync.repositories.android.AndroidBrowserRepository; |
michael@0 | 22 | import org.mozilla.gecko.sync.repositories.android.AndroidBrowserRepositoryDataAccessor; |
michael@0 | 23 | import org.mozilla.gecko.sync.repositories.android.AndroidBrowserRepositorySession; |
michael@0 | 24 | import org.mozilla.gecko.sync.repositories.android.BrowserContractHelpers; |
michael@0 | 25 | import org.mozilla.gecko.sync.repositories.android.RepoUtils; |
michael@0 | 26 | import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionCreationDelegate; |
michael@0 | 27 | import org.mozilla.gecko.sync.repositories.domain.HistoryRecord; |
michael@0 | 28 | import org.mozilla.gecko.sync.repositories.domain.Record; |
michael@0 | 29 | |
michael@0 | 30 | import android.content.ContentValues; |
michael@0 | 31 | import android.content.Context; |
michael@0 | 32 | import android.database.Cursor; |
michael@0 | 33 | import android.net.Uri; |
michael@0 | 34 | |
michael@0 | 35 | public class TestAndroidBrowserHistoryRepository extends AndroidBrowserRepositoryTestCase { |
michael@0 | 36 | |
michael@0 | 37 | @Override |
michael@0 | 38 | protected AndroidBrowserRepository getRepository() { |
michael@0 | 39 | |
michael@0 | 40 | /** |
michael@0 | 41 | * Override this chain in order to avoid our test code having to create two |
michael@0 | 42 | * sessions all the time. |
michael@0 | 43 | */ |
michael@0 | 44 | return new AndroidBrowserHistoryRepository() { |
michael@0 | 45 | @Override |
michael@0 | 46 | protected void sessionCreator(RepositorySessionCreationDelegate delegate, Context context) { |
michael@0 | 47 | AndroidBrowserHistoryRepositorySession session; |
michael@0 | 48 | session = new AndroidBrowserHistoryRepositorySession(this, context) { |
michael@0 | 49 | @Override |
michael@0 | 50 | protected synchronized void trackGUID(String guid) { |
michael@0 | 51 | System.out.println("Ignoring trackGUID call: this is a test!"); |
michael@0 | 52 | } |
michael@0 | 53 | }; |
michael@0 | 54 | delegate.onSessionCreated(session); |
michael@0 | 55 | } |
michael@0 | 56 | }; |
michael@0 | 57 | } |
michael@0 | 58 | |
michael@0 | 59 | @Override |
michael@0 | 60 | protected AndroidBrowserRepositoryDataAccessor getDataAccessor() { |
michael@0 | 61 | return new AndroidBrowserHistoryDataAccessor(getApplicationContext()); |
michael@0 | 62 | } |
michael@0 | 63 | |
michael@0 | 64 | @Override |
michael@0 | 65 | protected void closeDataAccessor(AndroidBrowserRepositoryDataAccessor dataAccessor) { |
michael@0 | 66 | if (!(dataAccessor instanceof AndroidBrowserHistoryDataAccessor)) { |
michael@0 | 67 | throw new IllegalArgumentException("Only expecting a history data accessor."); |
michael@0 | 68 | } |
michael@0 | 69 | ((AndroidBrowserHistoryDataAccessor) dataAccessor).closeExtender(); |
michael@0 | 70 | } |
michael@0 | 71 | |
michael@0 | 72 | @Override |
michael@0 | 73 | public void testFetchAll() { |
michael@0 | 74 | Record[] expected = new Record[2]; |
michael@0 | 75 | expected[0] = HistoryHelpers.createHistory3(); |
michael@0 | 76 | expected[1] = HistoryHelpers.createHistory2(); |
michael@0 | 77 | basicFetchAllTest(expected); |
michael@0 | 78 | } |
michael@0 | 79 | |
michael@0 | 80 | /* |
michael@0 | 81 | * Test storing identical records with different guids. |
michael@0 | 82 | * For bookmarks identical is defined by the following fields |
michael@0 | 83 | * being the same: title, uri, type, parentName |
michael@0 | 84 | */ |
michael@0 | 85 | @Override |
michael@0 | 86 | public void testStoreIdenticalExceptGuid() { |
michael@0 | 87 | storeIdenticalExceptGuid(HistoryHelpers.createHistory1()); |
michael@0 | 88 | } |
michael@0 | 89 | |
michael@0 | 90 | @Override |
michael@0 | 91 | public void testCleanMultipleRecords() { |
michael@0 | 92 | cleanMultipleRecords( |
michael@0 | 93 | HistoryHelpers.createHistory1(), |
michael@0 | 94 | HistoryHelpers.createHistory2(), |
michael@0 | 95 | HistoryHelpers.createHistory3(), |
michael@0 | 96 | HistoryHelpers.createHistory4(), |
michael@0 | 97 | HistoryHelpers.createHistory5() |
michael@0 | 98 | ); |
michael@0 | 99 | } |
michael@0 | 100 | |
michael@0 | 101 | @Override |
michael@0 | 102 | public void testGuidsSinceReturnMultipleRecords() { |
michael@0 | 103 | HistoryRecord record0 = HistoryHelpers.createHistory1(); |
michael@0 | 104 | HistoryRecord record1 = HistoryHelpers.createHistory2(); |
michael@0 | 105 | guidsSinceReturnMultipleRecords(record0, record1); |
michael@0 | 106 | } |
michael@0 | 107 | |
michael@0 | 108 | @Override |
michael@0 | 109 | public void testGuidsSinceReturnNoRecords() { |
michael@0 | 110 | guidsSinceReturnNoRecords(HistoryHelpers.createHistory3()); |
michael@0 | 111 | } |
michael@0 | 112 | |
michael@0 | 113 | @Override |
michael@0 | 114 | public void testFetchSinceOneRecord() { |
michael@0 | 115 | fetchSinceOneRecord(HistoryHelpers.createHistory1(), |
michael@0 | 116 | HistoryHelpers.createHistory2()); |
michael@0 | 117 | } |
michael@0 | 118 | |
michael@0 | 119 | @Override |
michael@0 | 120 | public void testFetchSinceReturnNoRecords() { |
michael@0 | 121 | fetchSinceReturnNoRecords(HistoryHelpers.createHistory3()); |
michael@0 | 122 | } |
michael@0 | 123 | |
michael@0 | 124 | @Override |
michael@0 | 125 | public void testFetchOneRecordByGuid() { |
michael@0 | 126 | fetchOneRecordByGuid(HistoryHelpers.createHistory1(), |
michael@0 | 127 | HistoryHelpers.createHistory2()); |
michael@0 | 128 | } |
michael@0 | 129 | |
michael@0 | 130 | @Override |
michael@0 | 131 | public void testFetchMultipleRecordsByGuids() { |
michael@0 | 132 | HistoryRecord record0 = HistoryHelpers.createHistory1(); |
michael@0 | 133 | HistoryRecord record1 = HistoryHelpers.createHistory2(); |
michael@0 | 134 | HistoryRecord record2 = HistoryHelpers.createHistory3(); |
michael@0 | 135 | fetchMultipleRecordsByGuids(record0, record1, record2); |
michael@0 | 136 | } |
michael@0 | 137 | |
michael@0 | 138 | @Override |
michael@0 | 139 | public void testFetchNoRecordByGuid() { |
michael@0 | 140 | fetchNoRecordByGuid(HistoryHelpers.createHistory1()); |
michael@0 | 141 | } |
michael@0 | 142 | |
michael@0 | 143 | @Override |
michael@0 | 144 | public void testWipe() { |
michael@0 | 145 | doWipe(HistoryHelpers.createHistory2(), HistoryHelpers.createHistory3()); |
michael@0 | 146 | } |
michael@0 | 147 | |
michael@0 | 148 | @Override |
michael@0 | 149 | public void testStore() { |
michael@0 | 150 | basicStoreTest(HistoryHelpers.createHistory1()); |
michael@0 | 151 | } |
michael@0 | 152 | |
michael@0 | 153 | @Override |
michael@0 | 154 | public void testRemoteNewerTimeStamp() { |
michael@0 | 155 | HistoryRecord local = HistoryHelpers.createHistory1(); |
michael@0 | 156 | HistoryRecord remote = HistoryHelpers.createHistory2(); |
michael@0 | 157 | remoteNewerTimeStamp(local, remote); |
michael@0 | 158 | } |
michael@0 | 159 | |
michael@0 | 160 | @Override |
michael@0 | 161 | public void testLocalNewerTimeStamp() { |
michael@0 | 162 | HistoryRecord local = HistoryHelpers.createHistory1(); |
michael@0 | 163 | HistoryRecord remote = HistoryHelpers.createHistory2(); |
michael@0 | 164 | localNewerTimeStamp(local, remote); |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | @Override |
michael@0 | 168 | public void testDeleteRemoteNewer() { |
michael@0 | 169 | HistoryRecord local = HistoryHelpers.createHistory1(); |
michael@0 | 170 | HistoryRecord remote = HistoryHelpers.createHistory2(); |
michael@0 | 171 | deleteRemoteNewer(local, remote); |
michael@0 | 172 | } |
michael@0 | 173 | |
michael@0 | 174 | @Override |
michael@0 | 175 | public void testDeleteLocalNewer() { |
michael@0 | 176 | HistoryRecord local = HistoryHelpers.createHistory1(); |
michael@0 | 177 | HistoryRecord remote = HistoryHelpers.createHistory2(); |
michael@0 | 178 | deleteLocalNewer(local, remote); |
michael@0 | 179 | } |
michael@0 | 180 | |
michael@0 | 181 | @Override |
michael@0 | 182 | public void testDeleteRemoteLocalNonexistent() { |
michael@0 | 183 | deleteRemoteLocalNonexistent(HistoryHelpers.createHistory2()); |
michael@0 | 184 | } |
michael@0 | 185 | |
michael@0 | 186 | /** |
michael@0 | 187 | * Exists to provide access to record string logic. |
michael@0 | 188 | */ |
michael@0 | 189 | protected class HelperHistorySession extends AndroidBrowserHistoryRepositorySession { |
michael@0 | 190 | public HelperHistorySession(Repository repository, Context context) { |
michael@0 | 191 | super(repository, context); |
michael@0 | 192 | } |
michael@0 | 193 | |
michael@0 | 194 | public boolean sameRecordString(HistoryRecord r1, HistoryRecord r2) { |
michael@0 | 195 | return buildRecordString(r1).equals(buildRecordString(r2)); |
michael@0 | 196 | } |
michael@0 | 197 | } |
michael@0 | 198 | |
michael@0 | 199 | /** |
michael@0 | 200 | * Verifies that two history records with the same URI but different |
michael@0 | 201 | * titles will be reconciled locally. |
michael@0 | 202 | */ |
michael@0 | 203 | public void testRecordStringCollisionAndEquality() { |
michael@0 | 204 | final AndroidBrowserHistoryRepository repo = new AndroidBrowserHistoryRepository(); |
michael@0 | 205 | final HelperHistorySession testSession = new HelperHistorySession(repo, getApplicationContext()); |
michael@0 | 206 | |
michael@0 | 207 | final long now = RepositorySession.now(); |
michael@0 | 208 | |
michael@0 | 209 | final HistoryRecord record0 = new HistoryRecord(null, "history", now + 1, false); |
michael@0 | 210 | final HistoryRecord record1 = new HistoryRecord(null, "history", now + 2, false); |
michael@0 | 211 | final HistoryRecord record2 = new HistoryRecord(null, "history", now + 3, false); |
michael@0 | 212 | |
michael@0 | 213 | record0.histURI = "http://example.com/foo"; |
michael@0 | 214 | record1.histURI = "http://example.com/foo"; |
michael@0 | 215 | record2.histURI = "http://example.com/bar"; |
michael@0 | 216 | record0.title = "Foo 0"; |
michael@0 | 217 | record1.title = "Foo 1"; |
michael@0 | 218 | record2.title = "Foo 2"; |
michael@0 | 219 | |
michael@0 | 220 | // Ensure that two records with the same URI produce the same record string, |
michael@0 | 221 | // and two records with different URIs do not. |
michael@0 | 222 | assertTrue(testSession.sameRecordString(record0, record1)); |
michael@0 | 223 | assertFalse(testSession.sameRecordString(record0, record2)); |
michael@0 | 224 | |
michael@0 | 225 | // Two records are congruent if they have the same URI and their |
michael@0 | 226 | // identifiers match (which is why these all have null GUIDs). |
michael@0 | 227 | assertTrue(record0.congruentWith(record0)); |
michael@0 | 228 | assertTrue(record0.congruentWith(record1)); |
michael@0 | 229 | assertTrue(record1.congruentWith(record0)); |
michael@0 | 230 | assertFalse(record0.congruentWith(record2)); |
michael@0 | 231 | assertFalse(record1.congruentWith(record2)); |
michael@0 | 232 | assertFalse(record2.congruentWith(record1)); |
michael@0 | 233 | assertFalse(record2.congruentWith(record0)); |
michael@0 | 234 | |
michael@0 | 235 | // None of these records are equal, because they have different titles. |
michael@0 | 236 | // (Except for being equal to themselves, of course.) |
michael@0 | 237 | assertTrue(record0.equalPayloads(record0)); |
michael@0 | 238 | assertTrue(record1.equalPayloads(record1)); |
michael@0 | 239 | assertTrue(record2.equalPayloads(record2)); |
michael@0 | 240 | assertFalse(record0.equalPayloads(record1)); |
michael@0 | 241 | assertFalse(record1.equalPayloads(record0)); |
michael@0 | 242 | assertFalse(record1.equalPayloads(record2)); |
michael@0 | 243 | } |
michael@0 | 244 | |
michael@0 | 245 | /* |
michael@0 | 246 | * Tests for adding some visits to a history record |
michael@0 | 247 | * and doing a fetch. |
michael@0 | 248 | */ |
michael@0 | 249 | @SuppressWarnings("unchecked") |
michael@0 | 250 | public void testAddOneVisit() { |
michael@0 | 251 | final RepositorySession session = createAndBeginSession(); |
michael@0 | 252 | |
michael@0 | 253 | HistoryRecord record0 = HistoryHelpers.createHistory3(); |
michael@0 | 254 | performWait(storeRunnable(session, record0)); |
michael@0 | 255 | |
michael@0 | 256 | // Add one visit to the count and put in a new |
michael@0 | 257 | // last visited date. |
michael@0 | 258 | ContentValues cv = new ContentValues(); |
michael@0 | 259 | int visits = record0.visits.size() + 1; |
michael@0 | 260 | long newVisitTime = System.currentTimeMillis(); |
michael@0 | 261 | cv.put(BrowserContract.History.VISITS, visits); |
michael@0 | 262 | cv.put(BrowserContract.History.DATE_LAST_VISITED, newVisitTime); |
michael@0 | 263 | final AndroidBrowserRepositoryDataAccessor dataAccessor = getDataAccessor(); |
michael@0 | 264 | dataAccessor.updateByGuid(record0.guid, cv); |
michael@0 | 265 | |
michael@0 | 266 | // Add expected visit to record for verification. |
michael@0 | 267 | JSONObject expectedVisit = new JSONObject(); |
michael@0 | 268 | expectedVisit.put("date", newVisitTime * 1000); // Microseconds. |
michael@0 | 269 | expectedVisit.put("type", 1L); |
michael@0 | 270 | record0.visits.add(expectedVisit); |
michael@0 | 271 | |
michael@0 | 272 | performWait(fetchRunnable(session, new String[] { record0.guid }, new ExpectFetchDelegate(new Record[] { record0 }))); |
michael@0 | 273 | closeDataAccessor(dataAccessor); |
michael@0 | 274 | } |
michael@0 | 275 | |
michael@0 | 276 | @SuppressWarnings("unchecked") |
michael@0 | 277 | public void testAddMultipleVisits() { |
michael@0 | 278 | final RepositorySession session = createAndBeginSession(); |
michael@0 | 279 | |
michael@0 | 280 | HistoryRecord record0 = HistoryHelpers.createHistory4(); |
michael@0 | 281 | performWait(storeRunnable(session, record0)); |
michael@0 | 282 | |
michael@0 | 283 | // Add three visits to the count and put in a new |
michael@0 | 284 | // last visited date. |
michael@0 | 285 | ContentValues cv = new ContentValues(); |
michael@0 | 286 | int visits = record0.visits.size() + 3; |
michael@0 | 287 | long newVisitTime = System.currentTimeMillis(); |
michael@0 | 288 | cv.put(BrowserContract.History.VISITS, visits); |
michael@0 | 289 | cv.put(BrowserContract.History.DATE_LAST_VISITED, newVisitTime); |
michael@0 | 290 | final AndroidBrowserRepositoryDataAccessor dataAccessor = getDataAccessor(); |
michael@0 | 291 | dataAccessor.updateByGuid(record0.guid, cv); |
michael@0 | 292 | |
michael@0 | 293 | // Now shift to microsecond timing for visits. |
michael@0 | 294 | long newMicroVisitTime = newVisitTime * 1000; |
michael@0 | 295 | |
michael@0 | 296 | // Add expected visits to record for verification |
michael@0 | 297 | JSONObject expectedVisit = new JSONObject(); |
michael@0 | 298 | expectedVisit.put("date", newMicroVisitTime); |
michael@0 | 299 | expectedVisit.put("type", 1L); |
michael@0 | 300 | record0.visits.add(expectedVisit); |
michael@0 | 301 | expectedVisit = new JSONObject(); |
michael@0 | 302 | expectedVisit.put("date", newMicroVisitTime - 1000); |
michael@0 | 303 | expectedVisit.put("type", 1L); |
michael@0 | 304 | record0.visits.add(expectedVisit); |
michael@0 | 305 | expectedVisit = new JSONObject(); |
michael@0 | 306 | expectedVisit.put("date", newMicroVisitTime - 2000); |
michael@0 | 307 | expectedVisit.put("type", 1L); |
michael@0 | 308 | record0.visits.add(expectedVisit); |
michael@0 | 309 | |
michael@0 | 310 | ExpectFetchDelegate delegate = new ExpectFetchDelegate(new Record[] { record0 }); |
michael@0 | 311 | performWait(fetchRunnable(session, new String[] { record0.guid }, delegate)); |
michael@0 | 312 | |
michael@0 | 313 | Record fetched = delegate.records.get(0); |
michael@0 | 314 | assertTrue(record0.equalPayloads(fetched)); |
michael@0 | 315 | closeDataAccessor(dataAccessor); |
michael@0 | 316 | } |
michael@0 | 317 | |
michael@0 | 318 | public void testInvalidHistoryItemIsSkipped() throws NullCursorException { |
michael@0 | 319 | final AndroidBrowserHistoryRepositorySession session = (AndroidBrowserHistoryRepositorySession) createAndBeginSession(); |
michael@0 | 320 | final AndroidBrowserRepositoryDataAccessor dbHelper = session.getDBHelper(); |
michael@0 | 321 | |
michael@0 | 322 | final long now = System.currentTimeMillis(); |
michael@0 | 323 | final HistoryRecord emptyURL = new HistoryRecord(Utils.generateGuid(), "history", now, false); |
michael@0 | 324 | final HistoryRecord noVisits = new HistoryRecord(Utils.generateGuid(), "history", now, false); |
michael@0 | 325 | final HistoryRecord aboutURL = new HistoryRecord(Utils.generateGuid(), "history", now, false); |
michael@0 | 326 | |
michael@0 | 327 | emptyURL.fennecDateVisited = now; |
michael@0 | 328 | emptyURL.fennecVisitCount = 1; |
michael@0 | 329 | emptyURL.histURI = ""; |
michael@0 | 330 | emptyURL.title = "Something"; |
michael@0 | 331 | |
michael@0 | 332 | noVisits.fennecDateVisited = now; |
michael@0 | 333 | noVisits.fennecVisitCount = 0; |
michael@0 | 334 | noVisits.histURI = "http://example.org/novisits"; |
michael@0 | 335 | noVisits.title = "Something Else"; |
michael@0 | 336 | |
michael@0 | 337 | aboutURL.fennecDateVisited = now; |
michael@0 | 338 | aboutURL.fennecVisitCount = 1; |
michael@0 | 339 | aboutURL.histURI = "about:home"; |
michael@0 | 340 | aboutURL.title = "Fennec Home"; |
michael@0 | 341 | |
michael@0 | 342 | Uri one = dbHelper.insert(emptyURL); |
michael@0 | 343 | Uri two = dbHelper.insert(noVisits); |
michael@0 | 344 | Uri tre = dbHelper.insert(aboutURL); |
michael@0 | 345 | assertNotNull(one); |
michael@0 | 346 | assertNotNull(two); |
michael@0 | 347 | assertNotNull(tre); |
michael@0 | 348 | |
michael@0 | 349 | // The records are in the DB. |
michael@0 | 350 | final Cursor all = dbHelper.fetchAll(); |
michael@0 | 351 | assertEquals(3, all.getCount()); |
michael@0 | 352 | all.close(); |
michael@0 | 353 | |
michael@0 | 354 | // But aren't returned by fetching. |
michael@0 | 355 | performWait(fetchAllRunnable(session, new Record[] {})); |
michael@0 | 356 | |
michael@0 | 357 | // And we'd ignore about:home if we downloaded it. |
michael@0 | 358 | assertTrue(session.shouldIgnore(aboutURL)); |
michael@0 | 359 | |
michael@0 | 360 | session.abort(); |
michael@0 | 361 | } |
michael@0 | 362 | |
michael@0 | 363 | public void testSqlInjectPurgeDelete() { |
michael@0 | 364 | // Some setup. |
michael@0 | 365 | RepositorySession session = createAndBeginSession(); |
michael@0 | 366 | final AndroidBrowserRepositoryDataAccessor db = getDataAccessor(); |
michael@0 | 367 | |
michael@0 | 368 | try { |
michael@0 | 369 | ContentValues cv = new ContentValues(); |
michael@0 | 370 | cv.put(BrowserContract.SyncColumns.IS_DELETED, 1); |
michael@0 | 371 | |
michael@0 | 372 | // Create and insert 2 history entries, 2nd one is evil (attempts injection). |
michael@0 | 373 | HistoryRecord h1 = HistoryHelpers.createHistory1(); |
michael@0 | 374 | HistoryRecord h2 = HistoryHelpers.createHistory2(); |
michael@0 | 375 | h2.guid = "' or '1'='1"; |
michael@0 | 376 | |
michael@0 | 377 | db.insert(h1); |
michael@0 | 378 | db.insert(h2); |
michael@0 | 379 | |
michael@0 | 380 | // Test 1 - updateByGuid() handles evil history entries correctly. |
michael@0 | 381 | db.updateByGuid(h2.guid, cv); |
michael@0 | 382 | |
michael@0 | 383 | // Query history table. |
michael@0 | 384 | Cursor cur = getAllHistory(); |
michael@0 | 385 | int numHistory = cur.getCount(); |
michael@0 | 386 | |
michael@0 | 387 | // Ensure only the evil history entry is marked for deletion. |
michael@0 | 388 | try { |
michael@0 | 389 | cur.moveToFirst(); |
michael@0 | 390 | while (!cur.isAfterLast()) { |
michael@0 | 391 | String guid = RepoUtils.getStringFromCursor(cur, BrowserContract.SyncColumns.GUID); |
michael@0 | 392 | boolean deleted = RepoUtils.getLongFromCursor(cur, BrowserContract.SyncColumns.IS_DELETED) == 1; |
michael@0 | 393 | |
michael@0 | 394 | if (guid.equals(h2.guid)) { |
michael@0 | 395 | assertTrue(deleted); |
michael@0 | 396 | } else { |
michael@0 | 397 | assertFalse(deleted); |
michael@0 | 398 | } |
michael@0 | 399 | cur.moveToNext(); |
michael@0 | 400 | } |
michael@0 | 401 | } finally { |
michael@0 | 402 | cur.close(); |
michael@0 | 403 | } |
michael@0 | 404 | |
michael@0 | 405 | // Test 2 - Ensure purgeDelete()'s call to delete() deletes only 1 record. |
michael@0 | 406 | try { |
michael@0 | 407 | db.purgeDeleted(); |
michael@0 | 408 | } catch (NullCursorException e) { |
michael@0 | 409 | e.printStackTrace(); |
michael@0 | 410 | } |
michael@0 | 411 | |
michael@0 | 412 | cur = getAllHistory(); |
michael@0 | 413 | int numHistoryAfterDeletion = cur.getCount(); |
michael@0 | 414 | |
michael@0 | 415 | // Ensure we have only 1 deleted row. |
michael@0 | 416 | assertEquals(numHistoryAfterDeletion, numHistory - 1); |
michael@0 | 417 | |
michael@0 | 418 | // Ensure only the evil history is deleted. |
michael@0 | 419 | try { |
michael@0 | 420 | cur.moveToFirst(); |
michael@0 | 421 | while (!cur.isAfterLast()) { |
michael@0 | 422 | String guid = RepoUtils.getStringFromCursor(cur, BrowserContract.SyncColumns.GUID); |
michael@0 | 423 | boolean deleted = RepoUtils.getLongFromCursor(cur, BrowserContract.SyncColumns.IS_DELETED) == 1; |
michael@0 | 424 | |
michael@0 | 425 | if (guid.equals(h2.guid)) { |
michael@0 | 426 | fail("Evil guid was not deleted!"); |
michael@0 | 427 | } else { |
michael@0 | 428 | assertFalse(deleted); |
michael@0 | 429 | } |
michael@0 | 430 | cur.moveToNext(); |
michael@0 | 431 | } |
michael@0 | 432 | } finally { |
michael@0 | 433 | cur.close(); |
michael@0 | 434 | } |
michael@0 | 435 | } finally { |
michael@0 | 436 | closeDataAccessor(db); |
michael@0 | 437 | session.abort(); |
michael@0 | 438 | } |
michael@0 | 439 | } |
michael@0 | 440 | |
michael@0 | 441 | protected Cursor getAllHistory() { |
michael@0 | 442 | Context context = getApplicationContext(); |
michael@0 | 443 | Cursor cur = context.getContentResolver().query(BrowserContractHelpers.HISTORY_CONTENT_URI, |
michael@0 | 444 | BrowserContractHelpers.HistoryColumns, null, null, null); |
michael@0 | 445 | return cur; |
michael@0 | 446 | } |
michael@0 | 447 | |
michael@0 | 448 | public void testDataAccessorBulkInsert() throws NullCursorException { |
michael@0 | 449 | final AndroidBrowserHistoryRepositorySession session = (AndroidBrowserHistoryRepositorySession) createAndBeginSession(); |
michael@0 | 450 | AndroidBrowserHistoryDataAccessor db = (AndroidBrowserHistoryDataAccessor) session.getDBHelper(); |
michael@0 | 451 | |
michael@0 | 452 | ArrayList<HistoryRecord> records = new ArrayList<HistoryRecord>(); |
michael@0 | 453 | records.add(HistoryHelpers.createHistory1()); |
michael@0 | 454 | records.add(HistoryHelpers.createHistory2()); |
michael@0 | 455 | records.add(HistoryHelpers.createHistory3()); |
michael@0 | 456 | db.bulkInsert(records); |
michael@0 | 457 | |
michael@0 | 458 | performWait(fetchAllRunnable(session, preparedExpectFetchDelegate(records.toArray(new Record[records.size()])))); |
michael@0 | 459 | session.abort(); |
michael@0 | 460 | } |
michael@0 | 461 | |
michael@0 | 462 | public void testDataExtenderIsClosedBeforeBegin() { |
michael@0 | 463 | // Create a session but don't begin() it. |
michael@0 | 464 | final AndroidBrowserRepositorySession session = (AndroidBrowserRepositorySession) createSession(); |
michael@0 | 465 | AndroidBrowserHistoryDataAccessor db = (AndroidBrowserHistoryDataAccessor) session.getDBHelper(); |
michael@0 | 466 | |
michael@0 | 467 | // Confirm dataExtender is closed before beginning session. |
michael@0 | 468 | assertTrue(db.getHistoryDataExtender().isClosed()); |
michael@0 | 469 | } |
michael@0 | 470 | |
michael@0 | 471 | public void testDataExtenderIsClosedAfterFinish() throws InactiveSessionException { |
michael@0 | 472 | final AndroidBrowserHistoryRepositorySession session = (AndroidBrowserHistoryRepositorySession) createAndBeginSession(); |
michael@0 | 473 | AndroidBrowserHistoryDataAccessor db = (AndroidBrowserHistoryDataAccessor) session.getDBHelper(); |
michael@0 | 474 | |
michael@0 | 475 | // Perform an action that opens the dataExtender. |
michael@0 | 476 | HistoryRecord h1 = HistoryHelpers.createHistory1(); |
michael@0 | 477 | db.insert(h1); |
michael@0 | 478 | assertFalse(db.getHistoryDataExtender().isClosed()); |
michael@0 | 479 | |
michael@0 | 480 | // Check dataExtender is closed upon finish. |
michael@0 | 481 | performWait(finishRunnable(session, new ExpectFinishDelegate())); |
michael@0 | 482 | assertTrue(db.getHistoryDataExtender().isClosed()); |
michael@0 | 483 | } |
michael@0 | 484 | |
michael@0 | 485 | public void testDataExtenderIsClosedAfterAbort() throws InactiveSessionException { |
michael@0 | 486 | final AndroidBrowserHistoryRepositorySession session = (AndroidBrowserHistoryRepositorySession) createAndBeginSession(); |
michael@0 | 487 | AndroidBrowserHistoryDataAccessor db = (AndroidBrowserHistoryDataAccessor) session.getDBHelper(); |
michael@0 | 488 | |
michael@0 | 489 | // Perform an action that opens the dataExtender. |
michael@0 | 490 | HistoryRecord h1 = HistoryHelpers.createHistory1(); |
michael@0 | 491 | db.insert(h1); |
michael@0 | 492 | assertFalse(db.getHistoryDataExtender().isClosed()); |
michael@0 | 493 | |
michael@0 | 494 | // Check dataExtender is closed upon abort. |
michael@0 | 495 | session.abort(); |
michael@0 | 496 | assertTrue(db.getHistoryDataExtender().isClosed()); |
michael@0 | 497 | } |
michael@0 | 498 | } |