1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 package org.mozilla.gecko.db; 5 6 import android.content.ContentValues; 7 import android.database.Cursor; 8 import android.net.Uri; 9 10 import org.junit.Test; 11 import org.junit.runner.RunWith; 12 import org.mozilla.gecko.background.testhelpers.TestRunner; 13 14 import org.mozilla.gecko.db.BrowserContract.History; 15 16 import static org.junit.Assert.*; 17 18 @RunWith(TestRunner.class) 19 /** 20 * Testing insertion/deletion of visits as by-product of updating history records through BrowserProvider 21 */ 22 public class BrowserProviderHistoryVisitsTest extends BrowserProviderHistoryVisitsTestBase { 23 @Test 24 /** 25 * Testing updating history records without affecting visits 26 */ testUpdateNoVisit()27 public void testUpdateNoVisit() throws Exception { 28 insertHistoryItem("https://www.mozilla.org", "testGUID"); 29 30 Cursor cursor = visitsClient.query(visitsTestUri, null, null, null, null); 31 assertNotNull(cursor); 32 assertEquals(0, cursor.getCount()); 33 cursor.close(); 34 35 ContentValues historyUpdate = new ContentValues(); 36 historyUpdate.put(History.TITLE, "Mozilla!"); 37 assertEquals(1, 38 historyClient.update( 39 historyTestUri, historyUpdate, History.URL + " = ?", new String[] {"https://www.mozilla.org"} 40 ) 41 ); 42 43 cursor = visitsClient.query(visitsTestUri, null, null, null, null); 44 assertNotNull(cursor); 45 assertEquals(0, cursor.getCount()); 46 cursor.close(); 47 48 ContentValues historyToInsert = new ContentValues(); 49 historyToInsert.put(History.URL, "https://www.eff.org"); 50 assertEquals(1, 51 historyClient.update( 52 historyTestUri.buildUpon() 53 .appendQueryParameter(BrowserContract.PARAM_INSERT_IF_NEEDED, "true").build(), 54 historyToInsert, null, null 55 ) 56 ); 57 58 cursor = visitsClient.query(visitsTestUri, null, null, null, null); 59 assertNotNull(cursor); 60 assertEquals(0, cursor.getCount()); 61 cursor.close(); 62 } 63 64 @Test 65 /** 66 * Testing INCREMENT_VISITS flag for multiple history records at once 67 */ testUpdateMultipleHistoryIncrementVisit()68 public void testUpdateMultipleHistoryIncrementVisit() throws Exception { 69 insertHistoryItem("https://www.mozilla.org", "testGUID"); 70 insertHistoryItem("https://www.mozilla.org", "testGUID2"); 71 72 // test that visits get inserted when updating existing history records 73 assertEquals(2, historyClient.update( 74 historyTestUri.buildUpon() 75 .appendQueryParameter(BrowserContract.PARAM_INCREMENT_VISITS, "true").build(), 76 new ContentValues(), History.URL + " = ?", new String[] {"https://www.mozilla.org"} 77 )); 78 79 Cursor cursor = visitsClient.query( 80 visitsTestUri, new String[] {BrowserContract.Visits.HISTORY_GUID}, null, null, null); 81 assertNotNull(cursor); 82 assertEquals(2, cursor.getCount()); 83 assertTrue(cursor.moveToFirst()); 84 85 String guid1 = cursor.getString(cursor.getColumnIndex(BrowserContract.Visits.HISTORY_GUID)); 86 cursor.moveToNext(); 87 String guid2 = cursor.getString(cursor.getColumnIndex(BrowserContract.Visits.HISTORY_GUID)); 88 cursor.close(); 89 90 assertNotEquals(guid1, guid2); 91 92 assertTrue(guid1.equals("testGUID") || guid1.equals("testGUID2")); 93 } 94 95 @Test 96 /** 97 * Testing INCREMENT_VISITS flag and its interplay with INSERT_IF_NEEDED 98 */ testUpdateHistoryIncrementVisit()99 public void testUpdateHistoryIncrementVisit() throws Exception { 100 insertHistoryItem("https://www.mozilla.org", "testGUID"); 101 102 // test that visit gets inserted when updating an existing histor record 103 assertEquals(1, historyClient.update( 104 historyTestUri.buildUpon() 105 .appendQueryParameter(BrowserContract.PARAM_INCREMENT_VISITS, "true").build(), 106 new ContentValues(), History.URL + " = ?", new String[] {"https://www.mozilla.org"} 107 )); 108 109 Cursor cursor = visitsClient.query( 110 visitsTestUri, new String[] {BrowserContract.Visits.HISTORY_GUID}, null, null, null); 111 assertNotNull(cursor); 112 assertEquals(1, cursor.getCount()); 113 assertTrue(cursor.moveToFirst()); 114 assertEquals( 115 "testGUID", 116 cursor.getString(cursor.getColumnIndex(BrowserContract.Visits.HISTORY_GUID)) 117 ); 118 cursor.close(); 119 120 // test that visit gets inserted when updatingOrInserting a new history record 121 ContentValues historyItem = new ContentValues(); 122 historyItem.put(History.URL, "https://www.eff.org"); 123 124 assertEquals(1, historyClient.update( 125 historyTestUri.buildUpon() 126 .appendQueryParameter(BrowserContract.PARAM_INCREMENT_VISITS, "true") 127 .appendQueryParameter(BrowserContract.PARAM_INSERT_IF_NEEDED, "true").build(), 128 historyItem, null, null 129 )); 130 131 cursor = historyClient.query( 132 historyTestUri, 133 new String[] {History.GUID}, History.URL + " = ?", new String[] {"https://www.eff.org"}, null 134 ); 135 assertNotNull(cursor); 136 assertEquals(1, cursor.getCount()); 137 assertTrue(cursor.moveToFirst()); 138 String insertedGUID = cursor.getString(cursor.getColumnIndex(History.GUID)); 139 cursor.close(); 140 141 cursor = visitsClient.query( 142 visitsTestUri, new String[] {BrowserContract.Visits.HISTORY_GUID}, null, null, null); 143 assertNotNull(cursor); 144 assertEquals(2, cursor.getCount()); 145 assertTrue(cursor.moveToFirst()); 146 assertEquals(insertedGUID, 147 cursor.getString(cursor.getColumnIndex(BrowserContract.Visits.HISTORY_GUID)) 148 ); 149 cursor.close(); 150 } 151 152 @Test 153 /** 154 * Test that for locally generated visits, we store their timestamps in microseconds, and not in 155 * milliseconds like history does. 156 */ testTimestampConversionOnInsertion()157 public void testTimestampConversionOnInsertion() throws Exception { 158 insertHistoryItem("https://www.mozilla.org", "testGUID"); 159 160 Long lastVisited = System.currentTimeMillis(); 161 ContentValues updatedVisitedTime = new ContentValues(); 162 updatedVisitedTime.put(History.DATE_LAST_VISITED, lastVisited); 163 164 // test with last visited date passed in 165 assertEquals(1, historyClient.update( 166 historyTestUri.buildUpon() 167 .appendQueryParameter(BrowserContract.PARAM_INCREMENT_VISITS, "true").build(), 168 updatedVisitedTime, History.URL + " = ?", new String[] {"https://www.mozilla.org"} 169 )); 170 171 Cursor cursor = visitsClient.query(visitsTestUri, new String[] {BrowserContract.Visits.DATE_VISITED}, null, null, null); 172 assertNotNull(cursor); 173 assertEquals(1, cursor.getCount()); 174 assertTrue(cursor.moveToFirst()); 175 176 assertEquals(lastVisited * 1000, cursor.getLong(cursor.getColumnIndex(BrowserContract.Visits.DATE_VISITED))); 177 cursor.close(); 178 179 // test without last visited date 180 assertEquals(1, historyClient.update( 181 historyTestUri.buildUpon() 182 .appendQueryParameter(BrowserContract.PARAM_INCREMENT_VISITS, "true").build(), 183 new ContentValues(), History.URL + " = ?", new String[] {"https://www.mozilla.org"} 184 )); 185 186 cursor = visitsClient.query(visitsTestUri, new String[] {BrowserContract.Visits.DATE_VISITED}, null, null, null); 187 assertNotNull(cursor); 188 assertEquals(2, cursor.getCount()); 189 assertTrue(cursor.moveToFirst()); 190 191 // CP should generate time off of current time upon insertion and convert to microseconds. 192 // This also tests correct ordering (DESC on date). 193 assertTrue(lastVisited * 1000 < cursor.getLong(cursor.getColumnIndex(BrowserContract.Visits.DATE_VISITED))); 194 cursor.close(); 195 } 196 197 @Test 198 /** 199 * This should perform `DELETE FROM visits WHERE history_guid in IN (?, ?, ?, ..., ?)` sort of statement 200 * SQLite has a variable count limit (999 by default), so we're testing here that our deletion 201 * code does the right thing and chunks deletes to account for this limitation. 202 */ testDeletingLotsOfHistory()203 public void testDeletingLotsOfHistory() throws Exception { 204 Uri incrementUri = historyTestUri.buildUpon() 205 .appendQueryParameter(BrowserContract.PARAM_INCREMENT_VISITS, "true").build(); 206 207 // insert bunch of history records, and for each insert a visit 208 for (int i = 0; i < 2100; i++) { 209 final String url = "https://www.mozilla" + i + ".org"; 210 insertHistoryItem(url, "testGUID" + i); 211 assertEquals(1, historyClient.update(incrementUri, new ContentValues(), History.URL + " = ?", new String[] {url})); 212 } 213 214 // sanity check 215 Cursor cursor = visitsClient.query(visitsTestUri, null, null, null, null); 216 assertNotNull(cursor); 217 assertEquals(2100, cursor.getCount()); 218 cursor.close(); 219 220 // delete all of the history items - this will trigger chunked deletion of visits as well 221 assertEquals(2100, 222 historyClient.delete(historyTestUri, null, null) 223 ); 224 225 // check that all visits where deleted 226 cursor = visitsClient.query(visitsTestUri, null, null, null, null); 227 assertNotNull(cursor); 228 assertEquals(0, cursor.getCount()); 229 cursor.close(); 230 } 231 232 @Test 233 /** 234 * Test visit deletion as by-product of history deletion - both explicit (from outside of Sync), 235 * and implicit (cascaded, from Sync). 236 */ testDeletingHistory()237 public void testDeletingHistory() throws Exception { 238 insertHistoryItem("https://www.mozilla.org", "testGUID"); 239 insertHistoryItem("https://www.eff.org", "testGUID2"); 240 241 // insert some visits 242 assertEquals(1, historyClient.update( 243 historyTestUri.buildUpon() 244 .appendQueryParameter(BrowserContract.PARAM_INCREMENT_VISITS, "true").build(), 245 new ContentValues(), History.URL + " = ?", new String[] {"https://www.mozilla.org"} 246 )); 247 assertEquals(1, historyClient.update( 248 historyTestUri.buildUpon() 249 .appendQueryParameter(BrowserContract.PARAM_INCREMENT_VISITS, "true").build(), 250 new ContentValues(), History.URL + " = ?", new String[] {"https://www.mozilla.org"} 251 )); 252 assertEquals(1, historyClient.update( 253 historyTestUri.buildUpon() 254 .appendQueryParameter(BrowserContract.PARAM_INCREMENT_VISITS, "true").build(), 255 new ContentValues(), History.URL + " = ?", new String[] {"https://www.eff.org"} 256 )); 257 258 Cursor cursor = visitsClient.query(visitsTestUri, null, null, null, null); 259 assertNotNull(cursor); 260 assertEquals(3, cursor.getCount()); 261 cursor.close(); 262 263 // test that corresponding visit records are deleted if Sync isn't involved 264 assertEquals(1, 265 historyClient.delete(historyTestUri, History.URL + " = ?", new String[] {"https://www.mozilla.org"}) 266 ); 267 268 cursor = visitsClient.query(visitsTestUri, null, null, null, null); 269 assertNotNull(cursor); 270 assertEquals(1, cursor.getCount()); 271 cursor.close(); 272 273 // test that corresponding visit records are deleted if Sync is involved 274 // insert some more visits 275 ContentValues moz = new ContentValues(); 276 moz.put(History.URL, "https://www.mozilla.org"); 277 moz.put(History.GUID, "testGUID3"); 278 assertEquals(1, historyClient.update( 279 historyTestUri.buildUpon() 280 .appendQueryParameter(BrowserContract.PARAM_INCREMENT_VISITS, "true") 281 .appendQueryParameter(BrowserContract.PARAM_INSERT_IF_NEEDED, "true").build(), 282 moz, History.URL + " = ?", new String[] {"https://www.mozilla.org"} 283 )); 284 assertEquals(1, historyClient.update( 285 historyTestUri.buildUpon() 286 .appendQueryParameter(BrowserContract.PARAM_INCREMENT_VISITS, "true") 287 .appendQueryParameter(BrowserContract.PARAM_INSERT_IF_NEEDED, "true").build(), 288 new ContentValues(), History.URL + " = ?", new String[] {"https://www.eff.org"} 289 )); 290 291 assertEquals(1, 292 historyClient.delete( 293 historyTestUri.buildUpon().appendQueryParameter(BrowserContract.PARAM_IS_SYNC, "true").build(), 294 History.URL + " = ?", new String[] {"https://www.eff.org"}) 295 ); 296 297 cursor = visitsClient.query(visitsTestUri, new String[] {BrowserContract.Visits.HISTORY_GUID}, null, null, null); 298 assertNotNull(cursor); 299 assertEquals(1, cursor.getCount()); 300 assertTrue(cursor.moveToFirst()); 301 assertEquals("testGUID3", cursor.getString(cursor.getColumnIndex(BrowserContract.Visits.HISTORY_GUID))); 302 cursor.close(); 303 } 304 305 @Test 306 /** 307 * Test that changes to History GUID are cascaded to individual visits. 308 * See UPDATE CASCADED on Visit's HISTORY_GUID foreign key. 309 */ testHistoryGUIDUpdate()310 public void testHistoryGUIDUpdate() throws Exception { 311 insertHistoryItem("https://www.mozilla.org", "testGUID"); 312 insertHistoryItem("https://www.eff.org", "testGUID2"); 313 314 // insert some visits 315 assertEquals(1, historyClient.update( 316 historyTestUri.buildUpon() 317 .appendQueryParameter(BrowserContract.PARAM_INCREMENT_VISITS, "true").build(), 318 new ContentValues(), History.URL + " = ?", new String[] {"https://www.mozilla.org"} 319 )); 320 assertEquals(1, historyClient.update( 321 historyTestUri.buildUpon() 322 .appendQueryParameter(BrowserContract.PARAM_INCREMENT_VISITS, "true").build(), 323 new ContentValues(), History.URL + " = ?", new String[] {"https://www.mozilla.org"} 324 )); 325 326 // change testGUID -> testGUIDNew 327 ContentValues newGuid = new ContentValues(); 328 newGuid.put(History.GUID, "testGUIDNew"); 329 assertEquals(1, historyClient.update( 330 historyTestUri, newGuid, History.URL + " = ?", new String[] {"https://www.mozilla.org"} 331 )); 332 333 Cursor cursor = visitsClient.query(visitsTestUri, null, BrowserContract.Visits.HISTORY_GUID + " = ?", new String[] {"testGUIDNew"}, null); 334 assertNotNull(cursor); 335 assertEquals(2, cursor.getCount()); 336 cursor.close(); 337 } 338 }