Bug 1274029 - Part 5: Unit tests for aggregate updates r=sebastian draft
authorGrigory Kruglov <gkruglov@mozilla.com>
Wed, 01 Jun 2016 10:21:14 -0700
changeset 374084 36e3f906395b66a3dfea013dc3e1a253031dbb55
parent 374083 2a26b46931e2211ca4c5f2dfcd1e9fd9c8f9116a
child 522542 20f2ab4417ce2dd74c5f46768946359693c102bd
push id19919
push usergkruglov@mozilla.com
push dateWed, 01 Jun 2016 19:30:11 +0000
reviewerssebastian
bugs1274029
milestone49.0a1
Bug 1274029 - Part 5: Unit tests for aggregate updates r=sebastian MozReview-Commit-ID: 9EljLhpySAj
mobile/android/tests/background/junit4/src/org/mozilla/gecko/db/BrowserProviderHistoryTest.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/db/BrowserProviderHistoryVisitsTestBase.java
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/db/BrowserProviderHistoryTest.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/db/BrowserProviderHistoryTest.java
@@ -136,32 +136,174 @@ public class BrowserProviderHistoryTest 
         /**
          * Normal expiration should leave 15 thumbnails
          * See {@link BrowserProvider.DEFAULT_EXPIRY_THUMBNAIL_COUNT}
          */
         assertRowCount(thumbnailClient, thumbnailTestUri, 15);
     }
 
     /**
+     * Test that we update aggregates at the appropriate times. Local visit aggregates are only updated
+     * when updating history record with PARAM_INCREMENT_VISITS=true. Remote aggregate values are updated
+     * only if set directly. Aggregate values are not set when inserting a new history record via insertHistory.
+     * Local aggregate values are set when inserting a new history record via update.
+     * @throws Exception
+     */
+    @Test
+    public void testHistoryVisitAggregates() throws Exception {
+        final long baseDate = System.currentTimeMillis();
+        final String url = "https://www.mozilla.org";
+        final Uri historyIncrementVisitsUri = historyTestUri.buildUpon()
+                .appendQueryParameter(BrowserContract.PARAM_INCREMENT_VISITS, "true")
+                .appendQueryParameter(BrowserContract.PARAM_INSERT_IF_NEEDED, "true").build();
+
+        // Test default values
+        insertHistoryItem(url, null, baseDate, null);
+        assertHistoryAggregates(BrowserContract.History.URL + " = ?", new String[] {url},
+                0, 0, 0, 0, 0);
+
+        // Test setting visit count on new history item creation
+        final String url2 = "https://www.eff.org";
+        insertHistoryItem(url2, null, baseDate, 17);
+        assertHistoryAggregates(BrowserContract.History.URL + " = ?", new String[] {url2},
+                17, 0, 0, 0, 0);
+
+        // Test setting visit count on new history item creation via .update
+        final String url3 = "https://www.torproject.org";
+        final ContentValues cv = new ContentValues();
+        cv.put(BrowserContract.History.URL, url3);
+        cv.put(BrowserContract.History.VISITS, 13);
+        cv.put(BrowserContract.History.DATE_LAST_VISITED, baseDate);
+        historyClient.update(historyIncrementVisitsUri, cv, BrowserContract.History.URL + " = ?", new String[] {url3});
+        assertHistoryAggregates(BrowserContract.History.URL + " = ?", new String[] {url3},
+                13, 13, baseDate, 0, 0);
+
+        // Test that updating meta doesn't touch aggregates
+        cv.clear();
+        cv.put(BrowserContract.History.TITLE, "New title");
+        historyClient.update(historyTestUri, cv, BrowserContract.History.URL + " = ?", new String[] {url});
+        assertHistoryAggregates(BrowserContract.History.URL + " = ?", new String[] {url},
+                0, 0, 0, 0, 0);
+
+        // Test that incrementing visits without specifying visit count updates local aggregate values
+        final long lastVisited = System.currentTimeMillis();
+        cv.clear();
+        cv.put(BrowserContract.History.DATE_LAST_VISITED, lastVisited);
+        historyClient.update(historyIncrementVisitsUri,
+                cv, BrowserContract.History.URL + " = ?", new String[] {url});
+        assertHistoryAggregates(BrowserContract.History.URL + " = ?", new String[] {url},
+                1, 1, lastVisited, 0, 0);
+
+        // Test that incrementing visits by a specified visit count updates local aggregate values
+        // We don't support bumping visit count by more than 1. This doesn't make sense when we keep
+        // detailed information about our individual visits.
+        final long lastVisited2 = System.currentTimeMillis();
+        cv.clear();
+        cv.put(BrowserContract.History.DATE_LAST_VISITED, lastVisited2);
+        cv.put(BrowserContract.History.VISITS, 10);
+        historyClient.update(
+                historyTestUri.buildUpon()
+                        .appendQueryParameter(BrowserContract.PARAM_INCREMENT_VISITS, "true").build(),
+                cv, BrowserContract.History.URL + " = ?", new String[] {url});
+        assertHistoryAggregates(BrowserContract.History.URL + " = ?", new String[] {url},
+                2, 2, lastVisited2, 0, 0);
+
+        // Test that we can directly update aggregate values
+        // NB: visits is unchanged (2)
+        final long lastVisited3 = System.currentTimeMillis();
+        cv.clear();
+        cv.put(BrowserContract.History.LOCAL_DATE_LAST_VISITED, lastVisited3);
+        cv.put(BrowserContract.History.LOCAL_VISITS, 19);
+        cv.put(BrowserContract.History.REMOTE_DATE_LAST_VISITED, lastVisited3 - 100);
+        cv.put(BrowserContract.History.REMOTE_VISITS, 3);
+        historyClient.update(historyTestUri, cv, BrowserContract.History.URL + " = ?", new String[] {url});
+        assertHistoryAggregates(BrowserContract.History.URL + " = ?", new String[] {url},
+                2, 19, lastVisited3, 3, lastVisited3 - 100);
+
+        // Test that we can set remote aggregate count to a specific value
+        cv.clear();
+        cv.put(BrowserContract.History.REMOTE_VISITS, 5);
+        historyClient.update(historyTestUri, cv, BrowserContract.History.URL + " = ?", new String[] {url});
+        assertHistoryAggregates(BrowserContract.History.URL + " = ?", new String[] {url},
+                2, 19, lastVisited3, 5, lastVisited3 - 100);
+
+        // Test that we can increment remote aggregate value by setting a query param in the URI
+        final Uri historyIncrementRemoteAggregateUri = historyTestUri.buildUpon()
+                .appendQueryParameter(BrowserContract.PARAM_INCREMENT_REMOTE_AGGREGATES, "true")
+                .build();
+        cv.clear();
+        cv.put(BrowserContract.History.REMOTE_DATE_LAST_VISITED, lastVisited3);
+        cv.put(BrowserContract.History.REMOTE_VISITS, 3);
+        historyClient.update(historyIncrementRemoteAggregateUri, cv, BrowserContract.History.URL + " = ?", new String[] {url});
+        // NB: remoteVisits=8. Previous value was 5, and we're incrementing by 3.
+        assertHistoryAggregates(BrowserContract.History.URL + " = ?", new String[] {url},
+                2, 19, lastVisited3, 8, lastVisited3);
+
+        // Test that we throw when trying to increment REMOTE_VISITS without passing in "increment by" value
+        cv.clear();
+        try {
+            historyClient.update(historyIncrementRemoteAggregateUri, cv, BrowserContract.History.URL + " = ?", new String[]{url});
+            assertTrue("Expected to throw IllegalArgumentException", false);
+        } catch (IllegalArgumentException e) {
+            assertTrue(true);
+
+            // NB: same values as above, to ensure throwing update didn't actually change anything.
+            assertHistoryAggregates(BrowserContract.History.URL + " = ?", new String[] {url},
+                    2, 19, lastVisited3, 8, lastVisited3);
+        }
+    }
+
+    private void assertHistoryAggregates(String selection, String[] selectionArg, int visits, int localVisits, long localLastVisited, int remoteVisits, long remoteLastVisited) throws Exception {
+        final Cursor c = historyClient.query(historyTestUri, new String[] {
+                BrowserContract.History.VISITS,
+                BrowserContract.History.LOCAL_VISITS,
+                BrowserContract.History.REMOTE_VISITS,
+                BrowserContract.History.LOCAL_DATE_LAST_VISITED,
+                BrowserContract.History.REMOTE_DATE_LAST_VISITED
+        }, selection, selectionArg, null);
+
+        assertNotNull(c);
+        try {
+            assertTrue(c.moveToFirst());
+
+            final int visitsCol = c.getColumnIndexOrThrow(BrowserContract.History.VISITS);
+            final int localVisitsCol = c.getColumnIndexOrThrow(BrowserContract.History.LOCAL_VISITS);
+            final int remoteVisitsCol = c.getColumnIndexOrThrow(BrowserContract.History.REMOTE_VISITS);
+            final int localDateLastVisitedCol = c.getColumnIndexOrThrow(BrowserContract.History.LOCAL_DATE_LAST_VISITED);
+            final int remoteDateLastVisitedCol = c.getColumnIndexOrThrow(BrowserContract.History.REMOTE_DATE_LAST_VISITED);
+
+            assertEquals(visits, c.getInt(visitsCol));
+
+            assertEquals(localVisits, c.getInt(localVisitsCol));
+            assertEquals(localLastVisited, c.getLong(localDateLastVisitedCol));
+
+            assertEquals(remoteVisits, c.getInt(remoteVisitsCol));
+            assertEquals(remoteLastVisited, c.getLong(remoteDateLastVisitedCol));
+        } finally {
+            c.close();
+        }
+    }
+
+    /**
      * Insert <code>count</code> history records with thumbnails, and for a third of records insert a visit.
      * Inserting visits only for some of the history records is in order to ensure we're correctly JOIN-ing
      * History and Visits tables in the Combined view.
      * Will ensure that date_created and date_modified for new records are the same as last visited date.
      *
      * @param count number of history records to insert
      * @param baseTime timestamp which will be used as a basis for last visited date
      * @throws RemoteException
      */
     private void insertHistory(int count, long baseTime) throws RemoteException {
         Uri incrementUri = historyTestUri.buildUpon()
                 .appendQueryParameter(BrowserContract.PARAM_INCREMENT_VISITS, "true").build();
 
         for (int i = 0; i < count; i++) {
             final String url = "https://www.mozilla" + i + ".org";
-            insertHistoryItem(url, "testGUID" + i, baseTime - i);
+            insertHistoryItem(url, "testGUID" + i, baseTime - i, null);
             if (i % 3 == 0) {
                 assertEquals(1, historyClient.update(incrementUri, new ContentValues(), BrowserContract.History.URL + " = ?", new String[]{url}));
             }
 
             // inserting a new entry sets the date created and modified automatically, so let's reset them
             ContentValues cv = new ContentValues();
             cv.put(BrowserContract.History.DATE_CREATED, baseTime - i);
             cv.put(BrowserContract.History.DATE_MODIFIED, baseTime - i);
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/db/BrowserProviderHistoryVisitsTestBase.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/db/BrowserProviderHistoryVisitsTestBase.java
@@ -41,20 +41,25 @@ public class BrowserProviderHistoryVisit
         provider.shutdown();
     }
 
     protected Uri testUri(Uri baseUri) {
         return baseUri.buildUpon().appendQueryParameter(BrowserContract.PARAM_IS_TEST, "1").build();
     }
 
     protected Uri insertHistoryItem(String url, String guid) throws RemoteException {
-        return insertHistoryItem(url, guid, System.currentTimeMillis());
+        return insertHistoryItem(url, guid, System.currentTimeMillis(), null);
     }
 
-    protected Uri insertHistoryItem(String url, String guid, Long lastVisited) throws RemoteException {
+    protected Uri insertHistoryItem(String url, String guid, Long lastVisited, Integer visitCount) throws RemoteException {
         ContentValues historyItem = new ContentValues();
         historyItem.put(BrowserContract.History.URL, url);
-        historyItem.put(BrowserContract.History.GUID, guid);
+        if (guid != null) {
+            historyItem.put(BrowserContract.History.GUID, guid);
+        }
+        if (visitCount != null) {
+            historyItem.put(BrowserContract.History.VISITS, visitCount);
+        }
         historyItem.put(BrowserContract.History.DATE_LAST_VISITED, lastVisited);
 
         return historyClient.insert(historyTestUri, historyItem);
     }
 }