Bug 1068425 - Add tile reporting tests. r=rnewman, a=lsblakk
authorBrian Nicholson <bnicholson@mozilla.com>
Wed, 05 Nov 2014 12:14:35 -0800
changeset 235178 3a07770613e6b3c0a63f922968cbd7c4ae9f7548
parent 235177 6cce1dc5f97e1e7295f3be0f2c1528e345787ee7
child 235179 2d9008e8bed6af1dcc35159fdc357d32249fcd98
push id611
push userraliiev@mozilla.com
push dateMon, 05 Jan 2015 23:23:16 +0000
treeherdermozilla-release@345cd3b9c445 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrnewman, lsblakk
bugs1068425
milestone35.0a2
Bug 1068425 - Add tile reporting tests. r=rnewman, a=lsblakk
mobile/android/base/tests/StringHelper.java
mobile/android/base/tests/assets/mock-package.zip
mobile/android/base/tests/robocop_tiles.sjs
mobile/android/base/tests/testDistribution.java
mobile/android/chrome/content/browser.js
testing/profiles/prefs_general.js
--- a/mobile/android/base/tests/StringHelper.java
+++ b/mobile/android/base/tests/StringHelper.java
@@ -45,16 +45,17 @@ public class StringHelper {
     public static final String CONTEXT_MENU_COPY_LINK = "Copy Link";
     public static final String CONTEXT_MENU_SHARE_LINK = "Share Link";
     public static final String CONTEXT_MENU_EDIT = "Edit";
     public static final String CONTEXT_MENU_SHARE = "Share";
     public static final String CONTEXT_MENU_REMOVE = "Remove";
     public static final String CONTEXT_MENU_COPY_ADDRESS = "Copy Address";
     public static final String CONTEXT_MENU_EDIT_SITE_SETTINGS = "Edit Site Settings";
     public static final String CONTEXT_MENU_ADD_TO_HOME_SCREEN = "Add to Home Screen";
+    public static final String CONTEXT_MENU_PIN_SITE = "Pin Site";
 
     // Context Menu menu items
     public static final String[] CONTEXT_MENU_ITEMS_IN_PRIVATE_TAB = new String[] {
         CONTEXT_MENU_OPEN_LINK_IN_PRIVATE_TAB,
         CONTEXT_MENU_COPY_LINK,
         CONTEXT_MENU_SHARE_LINK,
         CONTEXT_MENU_BOOKMARK_LINK
     };
@@ -138,16 +139,20 @@ public class StringHelper {
     public static final String ROBOCOP_LOGIN_TITLE = "Robocop Login";
     public static final String ROBOCOP_OFFLINE_STORAGE_TITLE = "Robocop offline storage";
     public static final String ROBOCOP_PICTURE_LINK_TITLE = "Picture Link";
     public static final String ROBOCOP_SEARCH_TITLE = "Robocop Search Engine";
     public static final String ROBOCOP_TEXT_PAGE_TITLE = "Robocop Text Page";
     public static final String ROBOCOP_INPUT_TITLE = "Robocop Input";
     public static final String ROBOCOP_SELECTION_HANDLER_TITLE = "Automated Text Selection tests for Mobile";
 
+    // Distribution tile labels
+    public static final String DISTRIBUTION1_LABEL = "Distribution 1";
+    public static final String DISTRIBUTION2_LABEL = "Distribution 2";
+
     // Settings menu strings
     // Section labels - ordered as found in the settings menu
     public static final String CUSTOMIZE_SECTION_LABEL = "Customize";
     public static final String DISPLAY_SECTION_LABEL = "Display";
     public static final String PRIVACY_SECTION_LABEL = "Privacy";
     public static final String MOZILLA_SECTION_LABEL = "Mozilla";
     public static final String DEVELOPER_TOOLS_SECTION_LABEL = "Developer tools";
 
index c7742ea6dbd599e6c11d9cd8663ac0699e02cafc..7c299823caeed7a27055cc121d79b5b9cbeadea2
GIT binary patch
literal 3787
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
PhH2$wr(co!vcdlc?f_G?
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tests/robocop_tiles.sjs
@@ -0,0 +1,26 @@
+/**
+ * Used with testDistribution.
+ * Responds by simply echoing back the request data.
+ */
+
+const cc = Components.Constructor;
+const BinaryInputStream = cc("@mozilla.org/binaryinputstream;1",
+                             "nsIBinaryInputStream",
+                             "setInputStream");
+
+function handleRequest(request, response) {
+  let bodyStream = new BinaryInputStream(request.bodyInputStream);
+  let avail;
+  let bytes = [];
+  while ((avail = bodyStream.available()) > 0) {
+    Array.prototype.push.apply(bytes, bodyStream.readByteArray(avail));
+  }
+  let data = String.fromCharCode.apply(null, bytes);
+
+  // Including this header will cause Gecko to broadcast the Robocop:TilesResponse event.
+  response.setHeader("X-Robocop", "true", false);
+
+  response.setHeader("Content-Type", "application/json", false);
+  response.setHeader("Cache-Control", "no-cache", false);
+  response.write(data);
+}
--- a/mobile/android/base/tests/testDistribution.java
+++ b/mobile/android/base/tests/testDistribution.java
@@ -7,19 +7,22 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.HttpURLConnection;
 import java.net.URI;
 import java.util.jar.JarInputStream;
 
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
+
 import org.mozilla.gecko.Actions;
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.db.BrowserContract;
+import org.mozilla.gecko.db.BrowserDB;
+import org.mozilla.gecko.db.SuggestedSites;
 import org.mozilla.gecko.distribution.Distribution;
 import org.mozilla.gecko.distribution.ReferrerDescriptor;
 import org.mozilla.gecko.distribution.ReferrerReceiver;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.app.Activity;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -34,16 +37,20 @@ import android.util.Log;
  * mock-package.zip should contain the following directory structure:
  *
  *   distribution/
  *     preferences.json
  *     bookmarks.json
  *     searchplugins/
  *       common/
  *         engine.xml
+ *     suggestedsites/
+ *       locales/
+ *         en-US/
+ *           suggestedsites.json
  */
 public class testDistribution extends ContentProviderTest {
     private static final String CLASS_REFERRER_RECEIVER = "org.mozilla.gecko.distribution.ReferrerReceiver";
     private static final String ACTION_INSTALL_REFERRER = "com.android.vending.INSTALL_REFERRER";
     private static final int WAIT_TIMEOUT_MSEC = 10000;
     public static final String LOGTAG = "GeckoTestDistribution";
 
     public static class TestableDistribution extends Distribution {
@@ -112,16 +119,23 @@ public class testDistribution extends Co
         mActivity = getActivity();
 
         String mockPackagePath = getMockPackagePath();
 
         // Wait for any startup-related background distribution shenanigans to
         // finish. This reduces the chance of us racing with startup pref writes.
         waitForBackgroundHappiness();
 
+        // Pre-clear distribution pref, override suggested sites and run tiles tests.
+        clearDistributionPref();
+        Distribution dist = initDistribution(mockPackagePath);
+        SuggestedSites suggestedSites = new SuggestedSites(mActivity, dist);
+        BrowserDB.setSuggestedSites(suggestedSites);
+        checkTilesReporting();
+
         // Pre-clear distribution pref, run basic preferences and en-US localized preferences Tests
         clearDistributionPref();
         setTestLocale("en-US");
         initDistribution(mockPackagePath);
         checkPreferences();
         checkLocalizedPreferences("en-US");
         checkSearchPlugin();
 
@@ -224,22 +238,23 @@ public class testDistribution extends Co
                 }
             }
         };
 
         doReferrerTest(ref, distribution, distributionReady);
     }
 
     // Initialize the distribution from the mock package.
-    private void initDistribution(String aPackagePath) {
+    private Distribution initDistribution(String aPackagePath) {
         // Call Distribution.init with the mock package.
         Actions.EventExpecter distributionSetExpecter = mActions.expectGeckoEvent("Distribution:Set:OK");
-        Distribution.init(mActivity, aPackagePath, "prefs-" + System.currentTimeMillis());
+        Distribution dist = Distribution.init(mActivity, aPackagePath, "prefs-" + System.currentTimeMillis());
         distributionSetExpecter.blockForEvent();
         distributionSetExpecter.unregisterListener();
+        return dist;
     }
 
     // Test distribution and preferences values stored in preferences.json
     private void checkPreferences() {
         String prefID = "distribution.id";
         String prefAbout = "distribution.about";
         String prefVersion = "distribution.version";
         String prefTestBoolean = "distribution.test.boolean";
@@ -434,16 +449,51 @@ public class testDistribution extends Co
     private void clearDistributionPref() {
         mAsserter.dumpLog("Clearing distribution pref.");
         SharedPreferences settings = mActivity.getSharedPreferences("GeckoApp", Activity.MODE_PRIVATE);
         String keyName = mActivity.getPackageName() + ".distribution_state";
         settings.edit().remove(keyName).commit();
         TestableDistribution.clearReferrerDescriptorForTesting();
     }
 
+    public void checkTilesReporting() throws Exception {
+        // Slight hack: Force top sites grid to reload.
+        inputAndLoadUrl(StringHelper.ABOUT_BLANK_URL);
+        inputAndLoadUrl(StringHelper.ABOUT_HOME_URL);
+
+        // Click the first tracking tile and verify the posted data.
+        JSONObject response = clickTrackingTile(StringHelper.DISTRIBUTION1_LABEL);
+        assertEquals(response.getInt("click"), 0);
+        assertEquals(response.getString("tiles"), "[{\"id\":123},{\"id\":456},{},{},{},{}]");
+
+        inputAndLoadUrl(StringHelper.ABOUT_HOME_URL);
+
+        // Pin the second tracking tile.
+        mSolo.clickLongOnText(StringHelper.DISTRIBUTION2_LABEL);
+        mSolo.waitForDialogToOpen();
+        mSolo.clickOnText(StringHelper.CONTEXT_MENU_PIN_SITE);
+
+        // Click the second tracking tile and verify the posted data.
+        response = clickTrackingTile(StringHelper.DISTRIBUTION2_LABEL);
+        assertEquals(response.getInt("click"), 1);
+        assertEquals(response.getString("tiles"), "[{\"id\":123},{\"id\":456,\"pin\":true},{},{},{},{}]");
+    }
+
+    private JSONObject clickTrackingTile(String text) throws JSONException {
+        boolean viewFound = waitForText(text);
+        assertTrue("Found text: " + text, viewFound);
+
+        Actions.EventExpecter loadExpecter = mActions.expectGeckoEvent("Robocop:TilesResponse");
+        mSolo.clickOnText(text);
+        String data = loadExpecter.blockForEventData();
+        JSONObject dataJSON = new JSONObject(data);
+        String response = dataJSON.getString("response");
+        return new JSONObject(response);
+    }
+
     @Override
     public void setUp() throws Exception {
         // TODO: Set up the content provider after setting the distribution.
         super.setUp(sBrowserProviderCallable, BrowserContract.AUTHORITY, "browser.db");
     }
 
     private void delete(File file) throws Exception {
       if (file.isDirectory()) {
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -4265,16 +4265,25 @@ Tab.prototype = {
         // Upload any pending tile click events.
         // Tiles data will be non-null for this tab only if:
         // 1) the user just clicked a suggested site with a tracking ID, and
         // 2) tiles reporting is enabled (gTilesReportURL != null).
         if (this.tilesData) {
           let xhr = new XMLHttpRequest();
           xhr.open("POST", gTilesReportURL, true);
           xhr.setRequestHeader("Content-Type", "application/json");
+          xhr.onload = function (e) {
+            // Broadcast reply if X-Robocop header is set. Used for testing only.
+            if (this.status == 200 && this.getResponseHeader("X-Robocop")) {
+              Messaging.sendRequest({
+                type: "Robocop:TilesResponse",
+                response: this.response
+              });
+            }
+          };
           xhr.send(this.tilesData);
           this.tilesData = null;
         }
 
         if (!Reader.isEnabledForParseOnLoad)
           return;
 
         // Once document is fully loaded, parse it
--- a/testing/profiles/prefs_general.js
+++ b/testing/profiles/prefs_general.js
@@ -211,16 +211,19 @@ user_pref("browser.webapps.checkForUpdat
 // en-US only uses .types.0.uri, but set all of them just to be sure.
 user_pref('browser.contentHandlers.types.0.uri', 'http://test1.example.org/rss?url=%%s')
 user_pref('browser.contentHandlers.types.1.uri', 'http://test1.example.org/rss?url=%%s')
 user_pref('browser.contentHandlers.types.2.uri', 'http://test1.example.org/rss?url=%%s')
 user_pref('browser.contentHandlers.types.3.uri', 'http://test1.example.org/rss?url=%%s')
 user_pref('browser.contentHandlers.types.4.uri', 'http://test1.example.org/rss?url=%%s')
 user_pref('browser.contentHandlers.types.5.uri', 'http://test1.example.org/rss?url=%%s')
 
+// Set dummy server for Android tiles testing.
+user_pref('browser.tiles.reportURL', 'http://%(server)s/tests/robocop/robocop_tiles.sjs')
+
 // We want to collect telemetry, but we don't want to send in the results.
 user_pref('toolkit.telemetry.server', 'https://%(server)s/telemetry-dummy/');
 
 // We don't want to hit the real Firefox Accounts server for tests.  We don't
 // actually need a functioning FxA server, so just set it to something that
 // resolves and accepts requests, even if they all fail.
 user_pref('identity.fxaccounts.auth.uri', 'https://%(server)s/fxa-dummy/');