/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ package org.mozilla.android.sync.test; import org.json.simple.JSONArray; import org.mozilla.gecko.db.BrowserContract; import org.mozilla.gecko.sync.repositories.android.FennecTabsRepository; import org.mozilla.gecko.sync.repositories.android.RepoUtils; import org.mozilla.gecko.sync.repositories.domain.TabsRecord; import org.mozilla.gecko.sync.repositories.domain.TabsRecord.Tab; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import android.os.RemoteException; /** * Exercise Fennec's tabs provider. * * @author rnewman * */ public class TestFennecTabsStorage extends AndroidSyncTestCase { public static final String TEST_CLIENT_GUID = "test guid"; // Real GUIDs never contain spaces. public static final String TEST_CLIENT_NAME = "test client name"; public static final String TABS_CLIENT_GUID_IS = BrowserContract.Tabs.CLIENT_GUID + " = ?"; protected Tab testTab1; protected Tab testTab2; protected Tab testTab3; private ContentProviderClient getClientsClient() { final ContentResolver cr = getApplicationContext().getContentResolver(); return cr.acquireContentProviderClient(BrowserContract.Clients.CONTENT_URI); } private ContentProviderClient getTabsClient() { final ContentResolver cr = getApplicationContext().getContentResolver(); return cr.acquireContentProviderClient(BrowserContract.Tabs.CONTENT_URI); } protected int deleteAllTestTabs(final ContentProviderClient tabsClient) throws RemoteException { if (tabsClient == null) { return -1; } return tabsClient.delete(BrowserContract.Tabs.CONTENT_URI, TABS_CLIENT_GUID_IS, new String[] { TEST_CLIENT_GUID }); } protected void tearDown() throws Exception { deleteAllTestTabs(getTabsClient()); } @SuppressWarnings("unchecked") private void insertSomeTestTabs(ContentProviderClient tabsClient) throws RemoteException { final JSONArray history1 = new JSONArray(); history1.add("http://test.com/test1.html"); testTab1 = new Tab("test title 1", "http://test.com/test1.png", history1, 1000); final JSONArray history2 = new JSONArray(); history2.add("http://test.com/test2.html#1"); history2.add("http://test.com/test2.html#2"); history2.add("http://test.com/test2.html#3"); testTab2 = new Tab("test title 2", "http://test.com/test2.png", history2, 2000); final JSONArray history3 = new JSONArray(); history3.add("http://test.com/test3.html#1"); history3.add("http://test.com/test3.html#2"); testTab3 = new Tab("test title 3", "http://test.com/test3.png", history3, 3000); tabsClient.insert(BrowserContract.Tabs.CONTENT_URI, testTab1.toContentValues(TEST_CLIENT_GUID, 0)); tabsClient.insert(BrowserContract.Tabs.CONTENT_URI, testTab2.toContentValues(TEST_CLIENT_GUID, 1)); tabsClient.insert(BrowserContract.Tabs.CONTENT_URI, testTab3.toContentValues(TEST_CLIENT_GUID, 2)); } // Sanity. public void testObtainCP() { final ContentProviderClient clientsClient = getClientsClient(); assertNotNull(clientsClient); clientsClient.release(); final ContentProviderClient tabsClient = getTabsClient(); assertNotNull(tabsClient); tabsClient.release(); } public void testWipeClients() throws RemoteException { final Uri uri = BrowserContract.Clients.CONTENT_URI; final ContentProviderClient clientsClient = getClientsClient(); // Have to ensure that it's empty… clientsClient.delete(uri, null, null); int deleted = clientsClient.delete(uri, null, null); assertEquals(0, deleted); } public void testWipeTabs() throws RemoteException { final ContentProviderClient tabsClient = getTabsClient(); // Have to ensure that it's empty… deleteAllTestTabs(tabsClient); int deleted = deleteAllTestTabs(tabsClient); assertEquals(0, deleted); } public void testStoreAndRetrieveClients() throws RemoteException { final Uri uri = BrowserContract.Clients.CONTENT_URI; final ContentProviderClient clientsClient = getClientsClient(); // Have to ensure that it's empty… clientsClient.delete(uri, null, null); final long now = System.currentTimeMillis(); final ContentValues first = new ContentValues(); final ContentValues second = new ContentValues(); first.put(BrowserContract.Clients.GUID, "abcdefghijkl"); first.put(BrowserContract.Clients.NAME, "Frist Psot"); first.put(BrowserContract.Clients.LAST_MODIFIED, now + 1); second.put(BrowserContract.Clients.GUID, "mnopqrstuvwx"); second.put(BrowserContract.Clients.NAME, "Second!!1!"); second.put(BrowserContract.Clients.LAST_MODIFIED, now + 2); ContentValues[] values = new ContentValues[] { first, second }; final int inserted = clientsClient.bulkInsert(uri, values); assertEquals(2, inserted); final String since = BrowserContract.Clients.LAST_MODIFIED + " >= ?"; final String[] nowArg = new String[] { String.valueOf(now) }; final String guidAscending = BrowserContract.Clients.GUID + " ASC"; Cursor cursor = clientsClient.query(uri, null, since, nowArg, guidAscending); assertNotNull(cursor); try { assertTrue(cursor.moveToFirst()); assertEquals(2, cursor.getCount()); final String g1 = cursor.getString(cursor.getColumnIndexOrThrow(BrowserContract.Clients.GUID)); final String n1 = cursor.getString(cursor.getColumnIndexOrThrow(BrowserContract.Clients.NAME)); final long m1 = cursor.getLong(cursor.getColumnIndexOrThrow(BrowserContract.Clients.LAST_MODIFIED)); assertEquals(first.get(BrowserContract.Clients.GUID), g1); assertEquals(first.get(BrowserContract.Clients.NAME), n1); assertEquals(now + 1, m1); assertTrue(cursor.moveToNext()); final String g2 = cursor.getString(cursor.getColumnIndexOrThrow(BrowserContract.Clients.GUID)); final String n2 = cursor.getString(cursor.getColumnIndexOrThrow(BrowserContract.Clients.NAME)); final long m2 = cursor.getLong(cursor.getColumnIndexOrThrow(BrowserContract.Clients.LAST_MODIFIED)); assertEquals(second.get(BrowserContract.Clients.GUID), g2); assertEquals(second.get(BrowserContract.Clients.NAME), n2); assertEquals(now + 2, m2); assertFalse(cursor.moveToNext()); } finally { cursor.close(); } int deleted = clientsClient.delete(uri, null, null); assertEquals(2, deleted); } public void testTabFromCursor() throws Exception { final ContentProviderClient tabsClient = getTabsClient(); deleteAllTestTabs(tabsClient); insertSomeTestTabs(tabsClient); final String positionAscending = BrowserContract.Tabs.POSITION + " ASC"; Cursor cursor = null; try { cursor = tabsClient.query(BrowserContract.Tabs.CONTENT_URI, null, TABS_CLIENT_GUID_IS, new String[] { TEST_CLIENT_GUID }, positionAscending); assertEquals(3, cursor.getCount()); cursor.moveToFirst(); final Tab parsed1 = FennecTabsRepository.tabFromCursor(cursor); assertEquals(testTab1, parsed1); cursor.moveToNext(); final Tab parsed2 = FennecTabsRepository.tabFromCursor(cursor); assertEquals(testTab2, parsed2); cursor.moveToPosition(2); final Tab parsed3 = FennecTabsRepository.tabFromCursor(cursor); assertEquals(testTab3, parsed3); } finally { cursor.close(); } } public void testTabsRecordFromCursor() throws Exception { final ContentProviderClient tabsClient = getTabsClient(); deleteAllTestTabs(tabsClient); insertSomeTestTabs(tabsClient); final String positionAscending = BrowserContract.Tabs.POSITION + " ASC"; Cursor cursor = null; try { cursor = tabsClient.query(BrowserContract.Tabs.CONTENT_URI, null, TABS_CLIENT_GUID_IS, new String[] { TEST_CLIENT_GUID }, positionAscending); assertEquals(3, cursor.getCount()); cursor.moveToPosition(1); final TabsRecord tabsRecord = FennecTabsRepository.tabsRecordFromCursor(cursor, TEST_CLIENT_GUID, TEST_CLIENT_NAME); // Make sure we clean up after ourselves. assertEquals(1, cursor.getPosition()); assertEquals(TEST_CLIENT_GUID, tabsRecord.guid); assertEquals(TEST_CLIENT_NAME, tabsRecord.clientName); assertEquals(3, tabsRecord.tabs.size()); assertEquals(testTab1, tabsRecord.tabs.get(0)); assertEquals(testTab2, tabsRecord.tabs.get(1)); assertEquals(testTab3, tabsRecord.tabs.get(2)); assertEquals(Math.max(Math.max(testTab1.lastUsed, testTab2.lastUsed), testTab3.lastUsed), tabsRecord.lastModified); } finally { cursor.close(); } } // Verify that we can fetch a record when there are no local tabs at all. public void testEmptyTabsRecordFromCursor() throws Exception { final ContentProviderClient tabsClient = getTabsClient(); deleteAllTestTabs(tabsClient); final String positionAscending = BrowserContract.Tabs.POSITION + " ASC"; Cursor cursor = null; try { cursor = tabsClient.query(BrowserContract.Tabs.CONTENT_URI, null, TABS_CLIENT_GUID_IS, new String[] { TEST_CLIENT_GUID }, positionAscending); assertEquals(0, cursor.getCount()); final TabsRecord tabsRecord = FennecTabsRepository.tabsRecordFromCursor(cursor, TEST_CLIENT_GUID, TEST_CLIENT_NAME); assertEquals(TEST_CLIENT_GUID, tabsRecord.guid); assertEquals(TEST_CLIENT_NAME, tabsRecord.clientName); assertNotNull(tabsRecord.tabs); assertEquals(0, tabsRecord.tabs.size()); assertEquals(0, tabsRecord.lastModified); } finally { cursor.close(); } } // Not much of a test, but verifies the tabs record at least agrees with the // disk data and doubles as a database inspector. public void testLocalTabs() throws Exception { final ContentProviderClient tabsClient = getTabsClient(); final String positionAscending = BrowserContract.Tabs.POSITION + " ASC"; Cursor cursor = null; try { // Keep this in sync with the Fennec schema. cursor = tabsClient.query(BrowserContract.Tabs.CONTENT_URI, null, BrowserContract.Tabs.CLIENT_GUID + " IS NULL", null, positionAscending); RepoUtils.dumpCursor(cursor); final TabsRecord tabsRecord = FennecTabsRepository.tabsRecordFromCursor(cursor, TEST_CLIENT_GUID, TEST_CLIENT_NAME); assertEquals(TEST_CLIENT_GUID, tabsRecord.guid); assertEquals(TEST_CLIENT_NAME, tabsRecord.clientName); assertNotNull(tabsRecord.tabs); assertEquals(cursor.getCount(), tabsRecord.tabs.size()); } finally { cursor.close(); } } }