michael@0: package org.mozilla.gecko.tests; michael@0: michael@0: import java.io.File; michael@0: import java.io.FileOutputStream; michael@0: import java.io.InputStream; michael@0: import java.io.OutputStream; michael@0: michael@0: import org.json.JSONArray; michael@0: import org.json.JSONException; michael@0: import org.json.JSONObject; michael@0: import org.mozilla.gecko.Actions; michael@0: import org.mozilla.gecko.Distribution; michael@0: import org.mozilla.gecko.db.BrowserContract; michael@0: import org.mozilla.gecko.util.ThreadUtils; michael@0: michael@0: import android.app.Activity; michael@0: import android.content.SharedPreferences; michael@0: michael@0: /** michael@0: * Tests distribution customization. michael@0: * mock-package.zip should contain the following directory structure: michael@0: * michael@0: * distribution/ michael@0: * preferences.json michael@0: * bookmarks.json michael@0: * searchplugins/ michael@0: * common/ michael@0: * engine.xml michael@0: */ michael@0: public class testDistribution extends ContentProviderTest { michael@0: private static final String MOCK_PACKAGE = "mock-package.zip"; michael@0: private static final int PREF_REQUEST_ID = 0x7357; michael@0: michael@0: private Activity mActivity; michael@0: michael@0: /** michael@0: * This is a hack. michael@0: * michael@0: * Startup results in us writing prefs -- we fetch the Distribution, which michael@0: * caches its state. Our tests try to wipe those prefs, but apparently michael@0: * sometimes race with startup, which leads to us not getting one of our michael@0: * expected messages. The test fails. michael@0: * michael@0: * This hack waits for any existing background tasks -- such as the one that michael@0: * writes prefs -- to finish before we begin the test. michael@0: */ michael@0: private void waitForBackgroundHappiness() { michael@0: final Object signal = new Object(); michael@0: final Runnable done = new Runnable() { michael@0: @Override michael@0: public void run() { michael@0: synchronized (signal) { michael@0: signal.notify(); michael@0: } michael@0: } michael@0: }; michael@0: synchronized (signal) { michael@0: ThreadUtils.postToBackgroundThread(done); michael@0: try { michael@0: signal.wait(); michael@0: } catch (InterruptedException e) { michael@0: mAsserter.ok(false, "InterruptedException waiting on background thread.", e.toString()); michael@0: } michael@0: } michael@0: mAsserter.dumpLog("Background task completed. Proceeding."); michael@0: } michael@0: michael@0: public void testDistribution() { michael@0: mActivity = getActivity(); michael@0: michael@0: String mockPackagePath = getMockPackagePath(); michael@0: michael@0: // Wait for any startup-related background distribution shenanigans to michael@0: // finish. This reduces the chance of us racing with startup pref writes. michael@0: waitForBackgroundHappiness(); michael@0: michael@0: // Pre-clear distribution pref, run basic preferences and en-US localized preferences Tests michael@0: clearDistributionPref(); michael@0: setTestLocale("en-US"); michael@0: initDistribution(mockPackagePath); michael@0: checkPreferences(); michael@0: checkLocalizedPreferences("en-US"); michael@0: checkSearchPlugin(); michael@0: michael@0: // Pre-clear distribution pref, and run es-MX localized preferences Test michael@0: clearDistributionPref(); michael@0: setTestLocale("es-MX"); michael@0: initDistribution(mockPackagePath); michael@0: checkLocalizedPreferences("es-MX"); michael@0: } michael@0: michael@0: // Initialize the distribution from the mock package. michael@0: private void initDistribution(String aPackagePath) { michael@0: // Call Distribution.init with the mock package. michael@0: Actions.EventExpecter distributionSetExpecter = mActions.expectGeckoEvent("Distribution:Set:OK"); michael@0: Distribution.init(mActivity, aPackagePath, "prefs-" + System.currentTimeMillis()); michael@0: distributionSetExpecter.blockForEvent(); michael@0: distributionSetExpecter.unregisterListener(); michael@0: } michael@0: michael@0: // Test distribution and preferences values stored in preferences.json michael@0: private void checkPreferences() { michael@0: String prefID = "distribution.id"; michael@0: String prefAbout = "distribution.about"; michael@0: String prefVersion = "distribution.version"; michael@0: String prefTestBoolean = "distribution.test.boolean"; michael@0: String prefTestString = "distribution.test.string"; michael@0: String prefTestInt = "distribution.test.int"; michael@0: michael@0: try { michael@0: final String[] prefNames = { prefID, michael@0: prefAbout, michael@0: prefVersion, michael@0: prefTestBoolean, michael@0: prefTestString, michael@0: prefTestInt }; michael@0: michael@0: Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("Preferences:Data"); michael@0: mActions.sendPreferencesGetEvent(PREF_REQUEST_ID, prefNames); michael@0: michael@0: JSONObject data = null; michael@0: int requestId = -1; michael@0: michael@0: // Wait until we get the correct "Preferences:Data" event michael@0: while (requestId != PREF_REQUEST_ID) { michael@0: data = new JSONObject(eventExpecter.blockForEventData()); michael@0: requestId = data.getInt("requestId"); michael@0: } michael@0: eventExpecter.unregisterListener(); michael@0: michael@0: JSONArray preferences = data.getJSONArray("preferences"); michael@0: for (int i = 0; i < preferences.length(); i++) { michael@0: JSONObject pref = (JSONObject) preferences.get(i); michael@0: String name = pref.getString("name"); michael@0: michael@0: if (name.equals(prefID)) { michael@0: mAsserter.is(pref.getString("value"), "test-partner", "check " + prefID); michael@0: } else if (name.equals(prefAbout)) { michael@0: mAsserter.is(pref.getString("value"), "Test Partner", "check " + prefAbout); michael@0: } else if (name.equals(prefVersion)) { michael@0: mAsserter.is(pref.getInt("value"), 1, "check " + prefVersion); michael@0: } else if (name.equals(prefTestBoolean)) { michael@0: mAsserter.is(pref.getBoolean("value"), true, "check " + prefTestBoolean); michael@0: } else if (name.equals(prefTestString)) { michael@0: mAsserter.is(pref.getString("value"), "test", "check " + prefTestString); michael@0: } else if (name.equals(prefTestInt)) { michael@0: mAsserter.is(pref.getInt("value"), 5, "check " + prefTestInt); michael@0: } michael@0: } michael@0: michael@0: } catch (JSONException e) { michael@0: mAsserter.ok(false, "exception getting preferences", e.toString()); michael@0: } michael@0: } michael@0: michael@0: private void checkSearchPlugin() { michael@0: Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("SearchEngines:Data"); michael@0: mActions.sendGeckoEvent("SearchEngines:GetVisible", null); michael@0: michael@0: try { michael@0: JSONObject data = new JSONObject(eventExpecter.blockForEventData()); michael@0: eventExpecter.unregisterListener(); michael@0: JSONArray searchEngines = data.getJSONArray("searchEngines"); michael@0: boolean foundEngine = false; michael@0: for (int i = 0; i < searchEngines.length(); i++) { michael@0: JSONObject engine = (JSONObject) searchEngines.get(i); michael@0: String name = engine.getString("name"); michael@0: if (name.equals("Test search engine")) { michael@0: foundEngine = true; michael@0: break; michael@0: } michael@0: } michael@0: mAsserter.ok(foundEngine, "check search plugin", "found test search plugin"); michael@0: } catch (JSONException e) { michael@0: mAsserter.ok(false, "exception getting search plugins", e.toString()); michael@0: } michael@0: } michael@0: michael@0: // Sets the distribution locale preference for the test michael@0: private void setTestLocale(String aLocale) { michael@0: String prefUseragentLocale = "general.useragent.locale"; michael@0: michael@0: JSONObject jsonPref = new JSONObject(); michael@0: try { michael@0: // Request the pref change to the locale. michael@0: jsonPref.put("name", prefUseragentLocale); michael@0: jsonPref.put("type", "string"); michael@0: jsonPref.put("value", aLocale); michael@0: mActions.sendGeckoEvent("Preferences:Set", jsonPref.toString()); michael@0: michael@0: // Wait for confirmation of the pref change. michael@0: final String[] prefNames = { prefUseragentLocale }; michael@0: michael@0: Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("Preferences:Data"); michael@0: mActions.sendPreferencesGetEvent(PREF_REQUEST_ID, prefNames); michael@0: michael@0: JSONObject data = null; michael@0: int requestId = -1; michael@0: michael@0: // Wait until we get the correct "Preferences:Data" event michael@0: while (requestId != PREF_REQUEST_ID) { michael@0: data = new JSONObject(eventExpecter.blockForEventData()); michael@0: requestId = data.getInt("requestId"); michael@0: } michael@0: eventExpecter.unregisterListener(); michael@0: michael@0: } catch (Exception e) { michael@0: mAsserter.ok(false, "exception setting test locale", e.toString()); michael@0: } michael@0: } michael@0: michael@0: // Test localized distribution and preferences values stored in preferences.json michael@0: private void checkLocalizedPreferences(String aLocale) { michael@0: String prefAbout = "distribution.about"; michael@0: String prefLocalizeable = "distribution.test.localizeable"; michael@0: String prefLocalizeableOverride = "distribution.test.localizeable-override"; michael@0: michael@0: try { michael@0: final String[] prefNames = { prefAbout, prefLocalizeable, prefLocalizeableOverride }; michael@0: michael@0: Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("Preferences:Data"); michael@0: mActions.sendPreferencesGetEvent(PREF_REQUEST_ID, prefNames); michael@0: michael@0: JSONObject data = null; michael@0: int requestId = -1; michael@0: michael@0: // Wait until we get the correct "Preferences:Data" event michael@0: while (requestId != PREF_REQUEST_ID) { michael@0: data = new JSONObject(eventExpecter.blockForEventData()); michael@0: requestId = data.getInt("requestId"); michael@0: } michael@0: eventExpecter.unregisterListener(); michael@0: michael@0: JSONArray preferences = data.getJSONArray("preferences"); michael@0: for (int i = 0; i < preferences.length(); i++) { michael@0: JSONObject pref = (JSONObject) preferences.get(i); michael@0: String name = pref.getString("name"); michael@0: michael@0: if (name.equals(prefAbout)) { michael@0: if (aLocale.equals("en-US")) { michael@0: mAsserter.is(pref.getString("value"), "Test Partner", "check " + prefAbout); michael@0: } else if (aLocale.equals("es-MX")) { michael@0: mAsserter.is(pref.getString("value"), "Afiliado de Prueba", "check " + prefAbout); michael@0: } michael@0: } else if (name.equals(prefLocalizeable)) { michael@0: if (aLocale.equals("en-US")) { michael@0: mAsserter.is(pref.getString("value"), "http://test.org/en-US/en-US/", "check " + prefLocalizeable); michael@0: } else if (aLocale.equals("es-MX")) { michael@0: mAsserter.is(pref.getString("value"), "http://test.org/es-MX/es-MX/", "check " + prefLocalizeable); michael@0: } michael@0: } else if (name.equals(prefLocalizeableOverride)) { michael@0: if (aLocale.equals("en-US")) { michael@0: mAsserter.is(pref.getString("value"), "http://cheese.com", "check " + prefLocalizeableOverride); michael@0: } else if (aLocale.equals("es-MX")) { michael@0: mAsserter.is(pref.getString("value"), "http://test.org/es-MX/", "check " + prefLocalizeableOverride); michael@0: } michael@0: } michael@0: } michael@0: michael@0: } catch (JSONException e) { michael@0: mAsserter.ok(false, "exception getting preferences", e.toString()); michael@0: } michael@0: } michael@0: michael@0: // Copies the mock package to the data directory and returns the file path to it. michael@0: private String getMockPackagePath() { michael@0: String mockPackagePath = ""; michael@0: michael@0: try { michael@0: InputStream inStream = getAsset(MOCK_PACKAGE); michael@0: File dataDir = new File(mActivity.getApplicationInfo().dataDir); michael@0: File outFile = new File(dataDir, MOCK_PACKAGE); michael@0: michael@0: OutputStream outStream = new FileOutputStream(outFile); michael@0: int b; michael@0: while ((b = inStream.read()) != -1) { michael@0: outStream.write(b); michael@0: } michael@0: inStream.close(); michael@0: outStream.close(); michael@0: michael@0: mockPackagePath = outFile.getPath(); michael@0: michael@0: } catch (Exception e) { michael@0: mAsserter.ok(false, "exception copying mock distribution package to data directory", e.toString()); michael@0: } michael@0: michael@0: return mockPackagePath; michael@0: } michael@0: michael@0: // Clears the distribution pref to return distribution state to STATE_UNKNOWN michael@0: private void clearDistributionPref() { michael@0: mAsserter.dumpLog("Clearing distribution pref."); michael@0: SharedPreferences settings = mActivity.getSharedPreferences("GeckoApp", Activity.MODE_PRIVATE); michael@0: String keyName = mActivity.getPackageName() + ".distribution_state"; michael@0: settings.edit().remove(keyName).commit(); michael@0: } michael@0: michael@0: @Override michael@0: public void setUp() throws Exception { michael@0: // TODO: Set up the content provider after setting the distribution. michael@0: super.setUp(sBrowserProviderCallable, BrowserContract.AUTHORITY, "browser.db"); michael@0: } michael@0: michael@0: private void delete(File file) throws Exception { michael@0: if (file.isDirectory()) { michael@0: File[] files = file.listFiles(); michael@0: for (File f : files) { michael@0: delete(f); michael@0: } michael@0: } michael@0: mAsserter.ok(file.delete(), "clean up distribution files", "deleted " + file.getPath()); michael@0: } michael@0: michael@0: @Override michael@0: public void tearDown() throws Exception { michael@0: File dataDir = new File(mActivity.getApplicationInfo().dataDir); michael@0: michael@0: // Delete mock package from data directory. michael@0: File mockPackage = new File(dataDir, MOCK_PACKAGE); michael@0: mAsserter.ok(mockPackage.delete(), "clean up mock package", "deleted " + mockPackage.getPath()); michael@0: michael@0: // Recursively delete distribution files that Distribution.init copied to data directory. michael@0: File distDir = new File(dataDir, "distribution"); michael@0: delete(distDir); michael@0: michael@0: clearDistributionPref(); michael@0: michael@0: super.tearDown(); michael@0: } michael@0: }