1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/tests/testDistribution.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,331 @@ 1.4 +package org.mozilla.gecko.tests; 1.5 + 1.6 +import java.io.File; 1.7 +import java.io.FileOutputStream; 1.8 +import java.io.InputStream; 1.9 +import java.io.OutputStream; 1.10 + 1.11 +import org.json.JSONArray; 1.12 +import org.json.JSONException; 1.13 +import org.json.JSONObject; 1.14 +import org.mozilla.gecko.Actions; 1.15 +import org.mozilla.gecko.Distribution; 1.16 +import org.mozilla.gecko.db.BrowserContract; 1.17 +import org.mozilla.gecko.util.ThreadUtils; 1.18 + 1.19 +import android.app.Activity; 1.20 +import android.content.SharedPreferences; 1.21 + 1.22 +/** 1.23 + * Tests distribution customization. 1.24 + * mock-package.zip should contain the following directory structure: 1.25 + * 1.26 + * distribution/ 1.27 + * preferences.json 1.28 + * bookmarks.json 1.29 + * searchplugins/ 1.30 + * common/ 1.31 + * engine.xml 1.32 + */ 1.33 +public class testDistribution extends ContentProviderTest { 1.34 + private static final String MOCK_PACKAGE = "mock-package.zip"; 1.35 + private static final int PREF_REQUEST_ID = 0x7357; 1.36 + 1.37 + private Activity mActivity; 1.38 + 1.39 + /** 1.40 + * This is a hack. 1.41 + * 1.42 + * Startup results in us writing prefs -- we fetch the Distribution, which 1.43 + * caches its state. Our tests try to wipe those prefs, but apparently 1.44 + * sometimes race with startup, which leads to us not getting one of our 1.45 + * expected messages. The test fails. 1.46 + * 1.47 + * This hack waits for any existing background tasks -- such as the one that 1.48 + * writes prefs -- to finish before we begin the test. 1.49 + */ 1.50 + private void waitForBackgroundHappiness() { 1.51 + final Object signal = new Object(); 1.52 + final Runnable done = new Runnable() { 1.53 + @Override 1.54 + public void run() { 1.55 + synchronized (signal) { 1.56 + signal.notify(); 1.57 + } 1.58 + } 1.59 + }; 1.60 + synchronized (signal) { 1.61 + ThreadUtils.postToBackgroundThread(done); 1.62 + try { 1.63 + signal.wait(); 1.64 + } catch (InterruptedException e) { 1.65 + mAsserter.ok(false, "InterruptedException waiting on background thread.", e.toString()); 1.66 + } 1.67 + } 1.68 + mAsserter.dumpLog("Background task completed. Proceeding."); 1.69 + } 1.70 + 1.71 + public void testDistribution() { 1.72 + mActivity = getActivity(); 1.73 + 1.74 + String mockPackagePath = getMockPackagePath(); 1.75 + 1.76 + // Wait for any startup-related background distribution shenanigans to 1.77 + // finish. This reduces the chance of us racing with startup pref writes. 1.78 + waitForBackgroundHappiness(); 1.79 + 1.80 + // Pre-clear distribution pref, run basic preferences and en-US localized preferences Tests 1.81 + clearDistributionPref(); 1.82 + setTestLocale("en-US"); 1.83 + initDistribution(mockPackagePath); 1.84 + checkPreferences(); 1.85 + checkLocalizedPreferences("en-US"); 1.86 + checkSearchPlugin(); 1.87 + 1.88 + // Pre-clear distribution pref, and run es-MX localized preferences Test 1.89 + clearDistributionPref(); 1.90 + setTestLocale("es-MX"); 1.91 + initDistribution(mockPackagePath); 1.92 + checkLocalizedPreferences("es-MX"); 1.93 + } 1.94 + 1.95 + // Initialize the distribution from the mock package. 1.96 + private void initDistribution(String aPackagePath) { 1.97 + // Call Distribution.init with the mock package. 1.98 + Actions.EventExpecter distributionSetExpecter = mActions.expectGeckoEvent("Distribution:Set:OK"); 1.99 + Distribution.init(mActivity, aPackagePath, "prefs-" + System.currentTimeMillis()); 1.100 + distributionSetExpecter.blockForEvent(); 1.101 + distributionSetExpecter.unregisterListener(); 1.102 + } 1.103 + 1.104 + // Test distribution and preferences values stored in preferences.json 1.105 + private void checkPreferences() { 1.106 + String prefID = "distribution.id"; 1.107 + String prefAbout = "distribution.about"; 1.108 + String prefVersion = "distribution.version"; 1.109 + String prefTestBoolean = "distribution.test.boolean"; 1.110 + String prefTestString = "distribution.test.string"; 1.111 + String prefTestInt = "distribution.test.int"; 1.112 + 1.113 + try { 1.114 + final String[] prefNames = { prefID, 1.115 + prefAbout, 1.116 + prefVersion, 1.117 + prefTestBoolean, 1.118 + prefTestString, 1.119 + prefTestInt }; 1.120 + 1.121 + Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("Preferences:Data"); 1.122 + mActions.sendPreferencesGetEvent(PREF_REQUEST_ID, prefNames); 1.123 + 1.124 + JSONObject data = null; 1.125 + int requestId = -1; 1.126 + 1.127 + // Wait until we get the correct "Preferences:Data" event 1.128 + while (requestId != PREF_REQUEST_ID) { 1.129 + data = new JSONObject(eventExpecter.blockForEventData()); 1.130 + requestId = data.getInt("requestId"); 1.131 + } 1.132 + eventExpecter.unregisterListener(); 1.133 + 1.134 + JSONArray preferences = data.getJSONArray("preferences"); 1.135 + for (int i = 0; i < preferences.length(); i++) { 1.136 + JSONObject pref = (JSONObject) preferences.get(i); 1.137 + String name = pref.getString("name"); 1.138 + 1.139 + if (name.equals(prefID)) { 1.140 + mAsserter.is(pref.getString("value"), "test-partner", "check " + prefID); 1.141 + } else if (name.equals(prefAbout)) { 1.142 + mAsserter.is(pref.getString("value"), "Test Partner", "check " + prefAbout); 1.143 + } else if (name.equals(prefVersion)) { 1.144 + mAsserter.is(pref.getInt("value"), 1, "check " + prefVersion); 1.145 + } else if (name.equals(prefTestBoolean)) { 1.146 + mAsserter.is(pref.getBoolean("value"), true, "check " + prefTestBoolean); 1.147 + } else if (name.equals(prefTestString)) { 1.148 + mAsserter.is(pref.getString("value"), "test", "check " + prefTestString); 1.149 + } else if (name.equals(prefTestInt)) { 1.150 + mAsserter.is(pref.getInt("value"), 5, "check " + prefTestInt); 1.151 + } 1.152 + } 1.153 + 1.154 + } catch (JSONException e) { 1.155 + mAsserter.ok(false, "exception getting preferences", e.toString()); 1.156 + } 1.157 + } 1.158 + 1.159 + private void checkSearchPlugin() { 1.160 + Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("SearchEngines:Data"); 1.161 + mActions.sendGeckoEvent("SearchEngines:GetVisible", null); 1.162 + 1.163 + try { 1.164 + JSONObject data = new JSONObject(eventExpecter.blockForEventData()); 1.165 + eventExpecter.unregisterListener(); 1.166 + JSONArray searchEngines = data.getJSONArray("searchEngines"); 1.167 + boolean foundEngine = false; 1.168 + for (int i = 0; i < searchEngines.length(); i++) { 1.169 + JSONObject engine = (JSONObject) searchEngines.get(i); 1.170 + String name = engine.getString("name"); 1.171 + if (name.equals("Test search engine")) { 1.172 + foundEngine = true; 1.173 + break; 1.174 + } 1.175 + } 1.176 + mAsserter.ok(foundEngine, "check search plugin", "found test search plugin"); 1.177 + } catch (JSONException e) { 1.178 + mAsserter.ok(false, "exception getting search plugins", e.toString()); 1.179 + } 1.180 + } 1.181 + 1.182 + // Sets the distribution locale preference for the test 1.183 + private void setTestLocale(String aLocale) { 1.184 + String prefUseragentLocale = "general.useragent.locale"; 1.185 + 1.186 + JSONObject jsonPref = new JSONObject(); 1.187 + try { 1.188 + // Request the pref change to the locale. 1.189 + jsonPref.put("name", prefUseragentLocale); 1.190 + jsonPref.put("type", "string"); 1.191 + jsonPref.put("value", aLocale); 1.192 + mActions.sendGeckoEvent("Preferences:Set", jsonPref.toString()); 1.193 + 1.194 + // Wait for confirmation of the pref change. 1.195 + final String[] prefNames = { prefUseragentLocale }; 1.196 + 1.197 + Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("Preferences:Data"); 1.198 + mActions.sendPreferencesGetEvent(PREF_REQUEST_ID, prefNames); 1.199 + 1.200 + JSONObject data = null; 1.201 + int requestId = -1; 1.202 + 1.203 + // Wait until we get the correct "Preferences:Data" event 1.204 + while (requestId != PREF_REQUEST_ID) { 1.205 + data = new JSONObject(eventExpecter.blockForEventData()); 1.206 + requestId = data.getInt("requestId"); 1.207 + } 1.208 + eventExpecter.unregisterListener(); 1.209 + 1.210 + } catch (Exception e) { 1.211 + mAsserter.ok(false, "exception setting test locale", e.toString()); 1.212 + } 1.213 + } 1.214 + 1.215 + // Test localized distribution and preferences values stored in preferences.json 1.216 + private void checkLocalizedPreferences(String aLocale) { 1.217 + String prefAbout = "distribution.about"; 1.218 + String prefLocalizeable = "distribution.test.localizeable"; 1.219 + String prefLocalizeableOverride = "distribution.test.localizeable-override"; 1.220 + 1.221 + try { 1.222 + final String[] prefNames = { prefAbout, prefLocalizeable, prefLocalizeableOverride }; 1.223 + 1.224 + Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("Preferences:Data"); 1.225 + mActions.sendPreferencesGetEvent(PREF_REQUEST_ID, prefNames); 1.226 + 1.227 + JSONObject data = null; 1.228 + int requestId = -1; 1.229 + 1.230 + // Wait until we get the correct "Preferences:Data" event 1.231 + while (requestId != PREF_REQUEST_ID) { 1.232 + data = new JSONObject(eventExpecter.blockForEventData()); 1.233 + requestId = data.getInt("requestId"); 1.234 + } 1.235 + eventExpecter.unregisterListener(); 1.236 + 1.237 + JSONArray preferences = data.getJSONArray("preferences"); 1.238 + for (int i = 0; i < preferences.length(); i++) { 1.239 + JSONObject pref = (JSONObject) preferences.get(i); 1.240 + String name = pref.getString("name"); 1.241 + 1.242 + if (name.equals(prefAbout)) { 1.243 + if (aLocale.equals("en-US")) { 1.244 + mAsserter.is(pref.getString("value"), "Test Partner", "check " + prefAbout); 1.245 + } else if (aLocale.equals("es-MX")) { 1.246 + mAsserter.is(pref.getString("value"), "Afiliado de Prueba", "check " + prefAbout); 1.247 + } 1.248 + } else if (name.equals(prefLocalizeable)) { 1.249 + if (aLocale.equals("en-US")) { 1.250 + mAsserter.is(pref.getString("value"), "http://test.org/en-US/en-US/", "check " + prefLocalizeable); 1.251 + } else if (aLocale.equals("es-MX")) { 1.252 + mAsserter.is(pref.getString("value"), "http://test.org/es-MX/es-MX/", "check " + prefLocalizeable); 1.253 + } 1.254 + } else if (name.equals(prefLocalizeableOverride)) { 1.255 + if (aLocale.equals("en-US")) { 1.256 + mAsserter.is(pref.getString("value"), "http://cheese.com", "check " + prefLocalizeableOverride); 1.257 + } else if (aLocale.equals("es-MX")) { 1.258 + mAsserter.is(pref.getString("value"), "http://test.org/es-MX/", "check " + prefLocalizeableOverride); 1.259 + } 1.260 + } 1.261 + } 1.262 + 1.263 + } catch (JSONException e) { 1.264 + mAsserter.ok(false, "exception getting preferences", e.toString()); 1.265 + } 1.266 + } 1.267 + 1.268 + // Copies the mock package to the data directory and returns the file path to it. 1.269 + private String getMockPackagePath() { 1.270 + String mockPackagePath = ""; 1.271 + 1.272 + try { 1.273 + InputStream inStream = getAsset(MOCK_PACKAGE); 1.274 + File dataDir = new File(mActivity.getApplicationInfo().dataDir); 1.275 + File outFile = new File(dataDir, MOCK_PACKAGE); 1.276 + 1.277 + OutputStream outStream = new FileOutputStream(outFile); 1.278 + int b; 1.279 + while ((b = inStream.read()) != -1) { 1.280 + outStream.write(b); 1.281 + } 1.282 + inStream.close(); 1.283 + outStream.close(); 1.284 + 1.285 + mockPackagePath = outFile.getPath(); 1.286 + 1.287 + } catch (Exception e) { 1.288 + mAsserter.ok(false, "exception copying mock distribution package to data directory", e.toString()); 1.289 + } 1.290 + 1.291 + return mockPackagePath; 1.292 + } 1.293 + 1.294 + // Clears the distribution pref to return distribution state to STATE_UNKNOWN 1.295 + private void clearDistributionPref() { 1.296 + mAsserter.dumpLog("Clearing distribution pref."); 1.297 + SharedPreferences settings = mActivity.getSharedPreferences("GeckoApp", Activity.MODE_PRIVATE); 1.298 + String keyName = mActivity.getPackageName() + ".distribution_state"; 1.299 + settings.edit().remove(keyName).commit(); 1.300 + } 1.301 + 1.302 + @Override 1.303 + public void setUp() throws Exception { 1.304 + // TODO: Set up the content provider after setting the distribution. 1.305 + super.setUp(sBrowserProviderCallable, BrowserContract.AUTHORITY, "browser.db"); 1.306 + } 1.307 + 1.308 + private void delete(File file) throws Exception { 1.309 + if (file.isDirectory()) { 1.310 + File[] files = file.listFiles(); 1.311 + for (File f : files) { 1.312 + delete(f); 1.313 + } 1.314 + } 1.315 + mAsserter.ok(file.delete(), "clean up distribution files", "deleted " + file.getPath()); 1.316 + } 1.317 + 1.318 + @Override 1.319 + public void tearDown() throws Exception { 1.320 + File dataDir = new File(mActivity.getApplicationInfo().dataDir); 1.321 + 1.322 + // Delete mock package from data directory. 1.323 + File mockPackage = new File(dataDir, MOCK_PACKAGE); 1.324 + mAsserter.ok(mockPackage.delete(), "clean up mock package", "deleted " + mockPackage.getPath()); 1.325 + 1.326 + // Recursively delete distribution files that Distribution.init copied to data directory. 1.327 + File distDir = new File(dataDir, "distribution"); 1.328 + delete(distDir); 1.329 + 1.330 + clearDistributionPref(); 1.331 + 1.332 + super.tearDown(); 1.333 + } 1.334 +}