|
1 package org.mozilla.gecko.tests; |
|
2 |
|
3 import java.io.File; |
|
4 import java.io.FileOutputStream; |
|
5 import java.io.InputStream; |
|
6 import java.io.OutputStream; |
|
7 |
|
8 import org.json.JSONArray; |
|
9 import org.json.JSONException; |
|
10 import org.json.JSONObject; |
|
11 import org.mozilla.gecko.Actions; |
|
12 import org.mozilla.gecko.Distribution; |
|
13 import org.mozilla.gecko.db.BrowserContract; |
|
14 import org.mozilla.gecko.util.ThreadUtils; |
|
15 |
|
16 import android.app.Activity; |
|
17 import android.content.SharedPreferences; |
|
18 |
|
19 /** |
|
20 * Tests distribution customization. |
|
21 * mock-package.zip should contain the following directory structure: |
|
22 * |
|
23 * distribution/ |
|
24 * preferences.json |
|
25 * bookmarks.json |
|
26 * searchplugins/ |
|
27 * common/ |
|
28 * engine.xml |
|
29 */ |
|
30 public class testDistribution extends ContentProviderTest { |
|
31 private static final String MOCK_PACKAGE = "mock-package.zip"; |
|
32 private static final int PREF_REQUEST_ID = 0x7357; |
|
33 |
|
34 private Activity mActivity; |
|
35 |
|
36 /** |
|
37 * This is a hack. |
|
38 * |
|
39 * Startup results in us writing prefs -- we fetch the Distribution, which |
|
40 * caches its state. Our tests try to wipe those prefs, but apparently |
|
41 * sometimes race with startup, which leads to us not getting one of our |
|
42 * expected messages. The test fails. |
|
43 * |
|
44 * This hack waits for any existing background tasks -- such as the one that |
|
45 * writes prefs -- to finish before we begin the test. |
|
46 */ |
|
47 private void waitForBackgroundHappiness() { |
|
48 final Object signal = new Object(); |
|
49 final Runnable done = new Runnable() { |
|
50 @Override |
|
51 public void run() { |
|
52 synchronized (signal) { |
|
53 signal.notify(); |
|
54 } |
|
55 } |
|
56 }; |
|
57 synchronized (signal) { |
|
58 ThreadUtils.postToBackgroundThread(done); |
|
59 try { |
|
60 signal.wait(); |
|
61 } catch (InterruptedException e) { |
|
62 mAsserter.ok(false, "InterruptedException waiting on background thread.", e.toString()); |
|
63 } |
|
64 } |
|
65 mAsserter.dumpLog("Background task completed. Proceeding."); |
|
66 } |
|
67 |
|
68 public void testDistribution() { |
|
69 mActivity = getActivity(); |
|
70 |
|
71 String mockPackagePath = getMockPackagePath(); |
|
72 |
|
73 // Wait for any startup-related background distribution shenanigans to |
|
74 // finish. This reduces the chance of us racing with startup pref writes. |
|
75 waitForBackgroundHappiness(); |
|
76 |
|
77 // Pre-clear distribution pref, run basic preferences and en-US localized preferences Tests |
|
78 clearDistributionPref(); |
|
79 setTestLocale("en-US"); |
|
80 initDistribution(mockPackagePath); |
|
81 checkPreferences(); |
|
82 checkLocalizedPreferences("en-US"); |
|
83 checkSearchPlugin(); |
|
84 |
|
85 // Pre-clear distribution pref, and run es-MX localized preferences Test |
|
86 clearDistributionPref(); |
|
87 setTestLocale("es-MX"); |
|
88 initDistribution(mockPackagePath); |
|
89 checkLocalizedPreferences("es-MX"); |
|
90 } |
|
91 |
|
92 // Initialize the distribution from the mock package. |
|
93 private void initDistribution(String aPackagePath) { |
|
94 // Call Distribution.init with the mock package. |
|
95 Actions.EventExpecter distributionSetExpecter = mActions.expectGeckoEvent("Distribution:Set:OK"); |
|
96 Distribution.init(mActivity, aPackagePath, "prefs-" + System.currentTimeMillis()); |
|
97 distributionSetExpecter.blockForEvent(); |
|
98 distributionSetExpecter.unregisterListener(); |
|
99 } |
|
100 |
|
101 // Test distribution and preferences values stored in preferences.json |
|
102 private void checkPreferences() { |
|
103 String prefID = "distribution.id"; |
|
104 String prefAbout = "distribution.about"; |
|
105 String prefVersion = "distribution.version"; |
|
106 String prefTestBoolean = "distribution.test.boolean"; |
|
107 String prefTestString = "distribution.test.string"; |
|
108 String prefTestInt = "distribution.test.int"; |
|
109 |
|
110 try { |
|
111 final String[] prefNames = { prefID, |
|
112 prefAbout, |
|
113 prefVersion, |
|
114 prefTestBoolean, |
|
115 prefTestString, |
|
116 prefTestInt }; |
|
117 |
|
118 Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("Preferences:Data"); |
|
119 mActions.sendPreferencesGetEvent(PREF_REQUEST_ID, prefNames); |
|
120 |
|
121 JSONObject data = null; |
|
122 int requestId = -1; |
|
123 |
|
124 // Wait until we get the correct "Preferences:Data" event |
|
125 while (requestId != PREF_REQUEST_ID) { |
|
126 data = new JSONObject(eventExpecter.blockForEventData()); |
|
127 requestId = data.getInt("requestId"); |
|
128 } |
|
129 eventExpecter.unregisterListener(); |
|
130 |
|
131 JSONArray preferences = data.getJSONArray("preferences"); |
|
132 for (int i = 0; i < preferences.length(); i++) { |
|
133 JSONObject pref = (JSONObject) preferences.get(i); |
|
134 String name = pref.getString("name"); |
|
135 |
|
136 if (name.equals(prefID)) { |
|
137 mAsserter.is(pref.getString("value"), "test-partner", "check " + prefID); |
|
138 } else if (name.equals(prefAbout)) { |
|
139 mAsserter.is(pref.getString("value"), "Test Partner", "check " + prefAbout); |
|
140 } else if (name.equals(prefVersion)) { |
|
141 mAsserter.is(pref.getInt("value"), 1, "check " + prefVersion); |
|
142 } else if (name.equals(prefTestBoolean)) { |
|
143 mAsserter.is(pref.getBoolean("value"), true, "check " + prefTestBoolean); |
|
144 } else if (name.equals(prefTestString)) { |
|
145 mAsserter.is(pref.getString("value"), "test", "check " + prefTestString); |
|
146 } else if (name.equals(prefTestInt)) { |
|
147 mAsserter.is(pref.getInt("value"), 5, "check " + prefTestInt); |
|
148 } |
|
149 } |
|
150 |
|
151 } catch (JSONException e) { |
|
152 mAsserter.ok(false, "exception getting preferences", e.toString()); |
|
153 } |
|
154 } |
|
155 |
|
156 private void checkSearchPlugin() { |
|
157 Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("SearchEngines:Data"); |
|
158 mActions.sendGeckoEvent("SearchEngines:GetVisible", null); |
|
159 |
|
160 try { |
|
161 JSONObject data = new JSONObject(eventExpecter.blockForEventData()); |
|
162 eventExpecter.unregisterListener(); |
|
163 JSONArray searchEngines = data.getJSONArray("searchEngines"); |
|
164 boolean foundEngine = false; |
|
165 for (int i = 0; i < searchEngines.length(); i++) { |
|
166 JSONObject engine = (JSONObject) searchEngines.get(i); |
|
167 String name = engine.getString("name"); |
|
168 if (name.equals("Test search engine")) { |
|
169 foundEngine = true; |
|
170 break; |
|
171 } |
|
172 } |
|
173 mAsserter.ok(foundEngine, "check search plugin", "found test search plugin"); |
|
174 } catch (JSONException e) { |
|
175 mAsserter.ok(false, "exception getting search plugins", e.toString()); |
|
176 } |
|
177 } |
|
178 |
|
179 // Sets the distribution locale preference for the test |
|
180 private void setTestLocale(String aLocale) { |
|
181 String prefUseragentLocale = "general.useragent.locale"; |
|
182 |
|
183 JSONObject jsonPref = new JSONObject(); |
|
184 try { |
|
185 // Request the pref change to the locale. |
|
186 jsonPref.put("name", prefUseragentLocale); |
|
187 jsonPref.put("type", "string"); |
|
188 jsonPref.put("value", aLocale); |
|
189 mActions.sendGeckoEvent("Preferences:Set", jsonPref.toString()); |
|
190 |
|
191 // Wait for confirmation of the pref change. |
|
192 final String[] prefNames = { prefUseragentLocale }; |
|
193 |
|
194 Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("Preferences:Data"); |
|
195 mActions.sendPreferencesGetEvent(PREF_REQUEST_ID, prefNames); |
|
196 |
|
197 JSONObject data = null; |
|
198 int requestId = -1; |
|
199 |
|
200 // Wait until we get the correct "Preferences:Data" event |
|
201 while (requestId != PREF_REQUEST_ID) { |
|
202 data = new JSONObject(eventExpecter.blockForEventData()); |
|
203 requestId = data.getInt("requestId"); |
|
204 } |
|
205 eventExpecter.unregisterListener(); |
|
206 |
|
207 } catch (Exception e) { |
|
208 mAsserter.ok(false, "exception setting test locale", e.toString()); |
|
209 } |
|
210 } |
|
211 |
|
212 // Test localized distribution and preferences values stored in preferences.json |
|
213 private void checkLocalizedPreferences(String aLocale) { |
|
214 String prefAbout = "distribution.about"; |
|
215 String prefLocalizeable = "distribution.test.localizeable"; |
|
216 String prefLocalizeableOverride = "distribution.test.localizeable-override"; |
|
217 |
|
218 try { |
|
219 final String[] prefNames = { prefAbout, prefLocalizeable, prefLocalizeableOverride }; |
|
220 |
|
221 Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("Preferences:Data"); |
|
222 mActions.sendPreferencesGetEvent(PREF_REQUEST_ID, prefNames); |
|
223 |
|
224 JSONObject data = null; |
|
225 int requestId = -1; |
|
226 |
|
227 // Wait until we get the correct "Preferences:Data" event |
|
228 while (requestId != PREF_REQUEST_ID) { |
|
229 data = new JSONObject(eventExpecter.blockForEventData()); |
|
230 requestId = data.getInt("requestId"); |
|
231 } |
|
232 eventExpecter.unregisterListener(); |
|
233 |
|
234 JSONArray preferences = data.getJSONArray("preferences"); |
|
235 for (int i = 0; i < preferences.length(); i++) { |
|
236 JSONObject pref = (JSONObject) preferences.get(i); |
|
237 String name = pref.getString("name"); |
|
238 |
|
239 if (name.equals(prefAbout)) { |
|
240 if (aLocale.equals("en-US")) { |
|
241 mAsserter.is(pref.getString("value"), "Test Partner", "check " + prefAbout); |
|
242 } else if (aLocale.equals("es-MX")) { |
|
243 mAsserter.is(pref.getString("value"), "Afiliado de Prueba", "check " + prefAbout); |
|
244 } |
|
245 } else if (name.equals(prefLocalizeable)) { |
|
246 if (aLocale.equals("en-US")) { |
|
247 mAsserter.is(pref.getString("value"), "http://test.org/en-US/en-US/", "check " + prefLocalizeable); |
|
248 } else if (aLocale.equals("es-MX")) { |
|
249 mAsserter.is(pref.getString("value"), "http://test.org/es-MX/es-MX/", "check " + prefLocalizeable); |
|
250 } |
|
251 } else if (name.equals(prefLocalizeableOverride)) { |
|
252 if (aLocale.equals("en-US")) { |
|
253 mAsserter.is(pref.getString("value"), "http://cheese.com", "check " + prefLocalizeableOverride); |
|
254 } else if (aLocale.equals("es-MX")) { |
|
255 mAsserter.is(pref.getString("value"), "http://test.org/es-MX/", "check " + prefLocalizeableOverride); |
|
256 } |
|
257 } |
|
258 } |
|
259 |
|
260 } catch (JSONException e) { |
|
261 mAsserter.ok(false, "exception getting preferences", e.toString()); |
|
262 } |
|
263 } |
|
264 |
|
265 // Copies the mock package to the data directory and returns the file path to it. |
|
266 private String getMockPackagePath() { |
|
267 String mockPackagePath = ""; |
|
268 |
|
269 try { |
|
270 InputStream inStream = getAsset(MOCK_PACKAGE); |
|
271 File dataDir = new File(mActivity.getApplicationInfo().dataDir); |
|
272 File outFile = new File(dataDir, MOCK_PACKAGE); |
|
273 |
|
274 OutputStream outStream = new FileOutputStream(outFile); |
|
275 int b; |
|
276 while ((b = inStream.read()) != -1) { |
|
277 outStream.write(b); |
|
278 } |
|
279 inStream.close(); |
|
280 outStream.close(); |
|
281 |
|
282 mockPackagePath = outFile.getPath(); |
|
283 |
|
284 } catch (Exception e) { |
|
285 mAsserter.ok(false, "exception copying mock distribution package to data directory", e.toString()); |
|
286 } |
|
287 |
|
288 return mockPackagePath; |
|
289 } |
|
290 |
|
291 // Clears the distribution pref to return distribution state to STATE_UNKNOWN |
|
292 private void clearDistributionPref() { |
|
293 mAsserter.dumpLog("Clearing distribution pref."); |
|
294 SharedPreferences settings = mActivity.getSharedPreferences("GeckoApp", Activity.MODE_PRIVATE); |
|
295 String keyName = mActivity.getPackageName() + ".distribution_state"; |
|
296 settings.edit().remove(keyName).commit(); |
|
297 } |
|
298 |
|
299 @Override |
|
300 public void setUp() throws Exception { |
|
301 // TODO: Set up the content provider after setting the distribution. |
|
302 super.setUp(sBrowserProviderCallable, BrowserContract.AUTHORITY, "browser.db"); |
|
303 } |
|
304 |
|
305 private void delete(File file) throws Exception { |
|
306 if (file.isDirectory()) { |
|
307 File[] files = file.listFiles(); |
|
308 for (File f : files) { |
|
309 delete(f); |
|
310 } |
|
311 } |
|
312 mAsserter.ok(file.delete(), "clean up distribution files", "deleted " + file.getPath()); |
|
313 } |
|
314 |
|
315 @Override |
|
316 public void tearDown() throws Exception { |
|
317 File dataDir = new File(mActivity.getApplicationInfo().dataDir); |
|
318 |
|
319 // Delete mock package from data directory. |
|
320 File mockPackage = new File(dataDir, MOCK_PACKAGE); |
|
321 mAsserter.ok(mockPackage.delete(), "clean up mock package", "deleted " + mockPackage.getPath()); |
|
322 |
|
323 // Recursively delete distribution files that Distribution.init copied to data directory. |
|
324 File distDir = new File(dataDir, "distribution"); |
|
325 delete(distDir); |
|
326 |
|
327 clearDistributionPref(); |
|
328 |
|
329 super.tearDown(); |
|
330 } |
|
331 } |