/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ package org.mozilla.android.sync.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.util.ArrayList; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import org.junit.Test; import org.mozilla.gecko.sync.CryptoRecord; import org.mozilla.gecko.sync.ExtendedJSONObject; import org.mozilla.gecko.sync.NonArrayJSONException; import org.mozilla.gecko.sync.NonObjectJSONException; import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord; import org.mozilla.gecko.sync.repositories.domain.ClientRecord; import org.mozilla.gecko.sync.repositories.domain.HistoryRecord; import org.mozilla.gecko.sync.repositories.domain.Record; import org.mozilla.gecko.sync.repositories.domain.TabsRecord; import org.mozilla.gecko.sync.repositories.domain.TabsRecord.Tab; public class TestRecord { @SuppressWarnings("static-method") @Test public void testQueryRecord() throws NonObjectJSONException, IOException, ParseException { final String expectedGUID = "Bl3n3gpKag3s"; final String testRecord = "{\"id\":\"" + expectedGUID + "\"," + " \"type\":\"query\"," + " \"title\":\"Downloads\"," + " \"parentName\":\"\"," + " \"bmkUri\":\"place:transition=7&sort=4\"," + " \"tags\":[]," + " \"keyword\":null," + " \"description\":null," + " \"loadInSidebar\":false," + " \"parentid\":\"BxfRgGiNeITG\"}"; final ExtendedJSONObject o = new ExtendedJSONObject(testRecord); final CryptoRecord cr = new CryptoRecord(o); cr.guid = expectedGUID; cr.lastModified = System.currentTimeMillis(); cr.collection = "bookmarks"; final BookmarkRecord r = new BookmarkRecord("Bl3n3gpKag3s", "bookmarks"); r.initFromEnvelope(cr); assertEquals(expectedGUID, r.guid); assertEquals("query", r.type); assertEquals("places:uri=place%3Atransition%3D7%26sort%3D4", r.bookmarkURI); // Check that we get the same bookmark URI out the other end, // once we've parsed it into a CryptoRecord, a BookmarkRecord, then // back into a CryptoRecord. assertEquals("place:transition=7&sort=4", r.getEnvelope().payload.getString("bmkUri")); } @SuppressWarnings("static-method") @Test public void testRecordGUIDs() { for (int i = 0; i < 50; ++i) { CryptoRecord cryptoRecord = new HistoryRecord().getEnvelope(); assertEquals(12, cryptoRecord.guid.length()); } } @Test public void testRecordEquality() { long now = System.currentTimeMillis(); BookmarkRecord bOne = new BookmarkRecord("abcdefghijkl", "bookmarks", now , false); BookmarkRecord bTwo = new BookmarkRecord("abcdefghijkl", "bookmarks", now , false); HistoryRecord hOne = new HistoryRecord("mbcdefghijkm", "history", now , false); HistoryRecord hTwo = new HistoryRecord("mbcdefghijkm", "history", now , false); // Identical records. assertFalse(bOne == bTwo); assertTrue(bOne.equals(bTwo)); assertTrue(bOne.equalPayloads(bTwo)); assertTrue(bOne.congruentWith(bTwo)); assertTrue(bTwo.equals(bOne)); assertTrue(bTwo.equalPayloads(bOne)); assertTrue(bTwo.congruentWith(bOne)); // Null checking. assertFalse(bOne.equals(null)); assertFalse(bOne.equalPayloads(null)); assertFalse(bOne.congruentWith(null)); // Different types. hOne.guid = bOne.guid; assertFalse(bOne.equals(hOne)); assertFalse(bOne.equalPayloads(hOne)); assertFalse(bOne.congruentWith(hOne)); hOne.guid = hTwo.guid; // Congruent androidID. bOne.androidID = 1; assertFalse(bOne.equals(bTwo)); assertTrue(bOne.equalPayloads(bTwo)); assertTrue(bOne.congruentWith(bTwo)); assertFalse(bTwo.equals(bOne)); assertTrue(bTwo.equalPayloads(bOne)); assertTrue(bTwo.congruentWith(bOne)); // Non-congruent androidID. bTwo.androidID = 2; assertFalse(bOne.equals(bTwo)); assertTrue(bOne.equalPayloads(bTwo)); assertFalse(bOne.congruentWith(bTwo)); assertFalse(bTwo.equals(bOne)); assertTrue(bTwo.equalPayloads(bOne)); assertFalse(bTwo.congruentWith(bOne)); // Identical androidID. bOne.androidID = 2; assertTrue(bOne.equals(bTwo)); assertTrue(bOne.equalPayloads(bTwo)); assertTrue(bOne.congruentWith(bTwo)); assertTrue(bTwo.equals(bOne)); assertTrue(bTwo.equalPayloads(bOne)); assertTrue(bTwo.congruentWith(bOne)); // Different times. bTwo.lastModified += 1000; assertFalse(bOne.equals(bTwo)); assertTrue(bOne.equalPayloads(bTwo)); assertTrue(bOne.congruentWith(bTwo)); assertFalse(bTwo.equals(bOne)); assertTrue(bTwo.equalPayloads(bOne)); assertTrue(bTwo.congruentWith(bOne)); // Add some visits. JSONObject v1 = fakeVisit(now - 1000); JSONObject v2 = fakeVisit(now - 500); hOne.fennecDateVisited = now + 2000; hOne.fennecVisitCount = 1; assertFalse(hOne.equals(hTwo)); assertTrue(hOne.equalPayloads(hTwo)); assertTrue(hOne.congruentWith(hTwo)); addVisit(hOne, v1); assertFalse(hOne.equals(hTwo)); assertFalse(hOne.equalPayloads(hTwo)); assertTrue(hOne.congruentWith(hTwo)); addVisit(hTwo, v2); assertFalse(hOne.equals(hTwo)); assertFalse(hOne.equalPayloads(hTwo)); assertTrue(hOne.congruentWith(hTwo)); // Now merge the visits. addVisit(hTwo, v1); addVisit(hOne, v2); assertFalse(hOne.equals(hTwo)); assertTrue(hOne.equalPayloads(hTwo)); assertTrue(hOne.congruentWith(hTwo)); hTwo.fennecDateVisited = hOne.fennecDateVisited; hTwo.fennecVisitCount = hOne.fennecVisitCount = 2; assertTrue(hOne.equals(hTwo)); assertTrue(hOne.equalPayloads(hTwo)); assertTrue(hOne.congruentWith(hTwo)); } @SuppressWarnings("unchecked") private void addVisit(HistoryRecord r, JSONObject visit) { if (r.visits == null) { r.visits = new JSONArray(); } r.visits.add(visit); } @SuppressWarnings("unchecked") private JSONObject fakeVisit(long time) { JSONObject object = new JSONObject(); object.put("type", 1L); object.put("date", time * 1000); return object; } @SuppressWarnings("static-method") @Test public void testTabParsing() throws ParseException, NonArrayJSONException { String json = "{\"title\":\"mozilla-central mozilla/browser/base/content/syncSetup.js\"," + " \"urlHistory\":[\"http://mxr.mozilla.org/mozilla-central/source/browser/base/content/syncSetup.js#72\"]," + " \"icon\":\"http://mxr.mozilla.org/mxr.png\"," + " \"lastUsed\":\"1306374531\"}"; JSONParser p = new JSONParser(); Tab tab = Tab.fromJSONObject((JSONObject) p.parse(json)); assertEquals("mozilla-central mozilla/browser/base/content/syncSetup.js", tab.title); assertEquals("http://mxr.mozilla.org/mxr.png", tab.icon); assertEquals("http://mxr.mozilla.org/mozilla-central/source/browser/base/content/syncSetup.js#72", tab.history.get(0)); assertEquals(1306374531000L, tab.lastUsed); String zeroJSON = "{\"title\":\"a\"," + " \"urlHistory\":[\"http://example.com\"]," + " \"icon\":\"\"," + " \"lastUsed\":0}"; Tab zero = Tab.fromJSONObject((JSONObject) p.parse(zeroJSON)); assertEquals("a", zero.title); assertEquals("", zero.icon); assertEquals("http://example.com", zero.history.get(0)); assertEquals(0L, zero.lastUsed); } @SuppressWarnings({ "unchecked", "static-method" }) @Test public void testTabsRecordCreation() throws Exception { final TabsRecord record = new TabsRecord("testGuid"); record.clientName = "test client name"; final JSONArray history1 = new JSONArray(); history1.add("http://test.com/test1.html"); final Tab tab1 = 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"); final Tab tab2 = new Tab("test title 2", "http://test.com/test2.png", history2, 2000); record.tabs = new ArrayList<Tab>(); record.tabs.add(tab1); record.tabs.add(tab2); final TabsRecord parsed = new TabsRecord(); parsed.initFromEnvelope(CryptoRecord.fromJSONRecord(record.getEnvelope().toJSONString())); assertEquals(record.guid, parsed.guid); assertEquals(record.clientName, parsed.clientName); assertEquals(record.tabs, parsed.tabs); // Verify that equality test doesn't always return true. parsed.tabs.get(0).history.add("http://test.com/different.html"); assertFalse(record.tabs.equals(parsed.tabs)); } public static class URITestBookmarkRecord extends BookmarkRecord { public static void doTest() { assertEquals("places:uri=abc%26def+baz&p1=123&p2=bar+baz", encodeUnsupportedTypeURI("abc&def baz", "p1", "123", "p2", "bar baz")); assertEquals("places:uri=abc%26def+baz&p1=123", encodeUnsupportedTypeURI("abc&def baz", "p1", "123", null, "bar baz")); assertEquals("places:p1=123", encodeUnsupportedTypeURI(null, "p1", "123", "p2", null)); } } @SuppressWarnings("static-method") @Test public void testEncodeURI() { URITestBookmarkRecord.doTest(); } private static final String payload = "{\"id\":\"M5bwUKK8hPyF\"," + "\"type\":\"livemark\"," + "\"siteUri\":\"http://www.bbc.co.uk/go/rss/int/news/-/news/\"," + "\"feedUri\":\"http://fxfeeds.mozilla.com/en-US/firefox/headlines.xml\"," + "\"parentName\":\"Bookmarks Toolbar\"," + "\"parentid\":\"toolbar\"," + "\"title\":\"Latest Headlines\"," + "\"description\":\"\"," + "\"children\":" + "[\"7oBdEZB-8BMO\", \"SUd1wktMNCTB\", \"eZe4QWzo1BcY\", \"YNBhGwhVnQsN\"," + "\"mNTdpgoRZMbW\", \"-L8Vci6CbkJY\", \"bVzudKSQERc1\", \"Gxl9lb4DXsmL\"," + "\"3Qr13GucOtEh\"]}"; public class PayloadBookmarkRecord extends BookmarkRecord { public PayloadBookmarkRecord() { super("abcdefghijkl", "bookmarks", 1234, false); } public void doTest() throws NonObjectJSONException, IOException, ParseException { this.initFromPayload(new ExtendedJSONObject(payload)); assertEquals("abcdefghijkl", this.guid); // Ignores payload. assertEquals("livemark", this.type); assertEquals("Bookmarks Toolbar", this.parentName); assertEquals("toolbar", this.parentID); assertEquals("", this.description); assertEquals(null, this.children); final String encodedSite = "http%3A%2F%2Fwww.bbc.co.uk%2Fgo%2Frss%2Fint%2Fnews%2F-%2Fnews%2F"; final String encodedFeed = "http%3A%2F%2Ffxfeeds.mozilla.com%2Fen-US%2Ffirefox%2Fheadlines.xml"; final String expectedURI = "places:siteUri=" + encodedSite + "&feedUri=" + encodedFeed; assertEquals(expectedURI, this.bookmarkURI); } } @Test public void testUnusualBookmarkRecords() throws NonObjectJSONException, IOException, ParseException { PayloadBookmarkRecord record = new PayloadBookmarkRecord(); record.doTest(); } @SuppressWarnings("static-method") @Test public void testTTL() { Record record = new HistoryRecord(); assertEquals(HistoryRecord.HISTORY_TTL, record.ttl); // ClientRecords are transient, HistoryRecords are not. Record clientRecord = new ClientRecord(); assertTrue(clientRecord.ttl < record.ttl); CryptoRecord cryptoRecord = record.getEnvelope(); assertEquals(record.ttl, cryptoRecord.ttl); } }