Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
1 package org.mozilla.gecko.tests;
3 import java.io.File;
4 import java.io.FileReader;
5 import java.io.FileWriter;
6 import java.io.IOException;
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.Assert;
13 import org.mozilla.gecko.FennecMochitestAssert;
15 public abstract class SessionTest extends BaseTest {
16 protected Navigation mNavigation;
18 @Override
19 public void setUp() throws Exception {
20 super.setUp();
22 mNavigation = new Navigation(mDevice);
23 }
25 /**
26 * A generic session object representing a collection of items that has a
27 * selected index.
28 */
29 protected abstract class SessionObject<T> {
30 private final int mIndex;
31 private final T[] mItems;
33 public SessionObject(int index, T... items) {
34 mIndex = index;
35 mItems = items;
36 }
38 public int getIndex() {
39 return mIndex;
40 }
42 public T[] getItems() {
43 return mItems;
44 }
45 }
47 protected class PageInfo {
48 private String url;
49 private String title;
51 public PageInfo(String key) {
52 if (key.startsWith("about:")) {
53 url = key;
54 } else {
55 url = getPage(key);
56 }
57 title = key;
58 }
59 }
61 protected class SessionTab extends SessionObject<PageInfo> {
62 public SessionTab(int index, PageInfo... items) {
63 super(index, items);
64 }
65 }
67 protected class Session extends SessionObject<SessionTab> {
68 public Session(int index, SessionTab... items) {
69 super(index, items);
70 }
71 }
73 /**
74 * Walker for visiting items in a browser-like navigation order.
75 */
76 protected abstract class NavigationWalker<T> {
77 private final T[] mItems;
78 private final int mIndex;
80 public NavigationWalker(SessionObject<T> obj) {
81 mItems = obj.getItems();
82 mIndex = obj.getIndex();
83 }
85 /**
86 * Walks over the list of items, calling the onItem() callback for each.
87 *
88 * The selected item is the first item visited. Each item after the
89 * selected item is then visited in ascending index order. Finally, the
90 * list is iterated in reverse, and each item before the selected item
91 * is visited in descending index order.
92 */
93 public void walk() {
94 onItem(mItems[mIndex], mIndex);
95 for (int i = mIndex + 1; i < mItems.length; i++) {
96 goForward();
97 onItem(mItems[i], i);
98 }
99 if (mIndex > 0) {
100 for (int i = mItems.length - 2; i >= 0; i--) {
101 goBack();
102 if (i < mIndex) {
103 onItem(mItems[i], i);
104 }
105 }
106 }
107 }
109 /**
110 * Callback when an item is visited during a walk.
111 *
112 * Only one callback is executed per item.
113 */
114 public abstract void onItem(T item, int currentIndex);
116 /**
117 * Callback executed for each back step of the walk.
118 */
119 public void goBack() {}
121 /**
122 * Callback executed for each forward step of the walk.
123 */
124 public void goForward() {}
125 }
127 /**
128 * Loads a set of tabs in the browser specified by the given session.
129 *
130 * @param session Session to load
131 */
132 protected void loadSessionTabs(Session session) {
133 // Verify initial about:home tab
134 verifyTabCount(1);
135 verifyUrl("about:home");
137 SessionTab[] tabs = session.getItems();
138 for (int i = 0; i < tabs.length; i++) {
139 final SessionTab tab = tabs[i];
140 final PageInfo[] pages = tab.getItems();
142 // New tabs always start with about:home, so make sure about:home
143 // is always the first entry.
144 mAsserter.is(pages[0].url, "about:home", "first page in tab is about:home");
146 // If this is the first tab, the tab already exists, so no need to
147 // create a new one. Otherwise, create a new tab if we're loading
148 // the first the first page in the set.
149 if (i > 0) {
150 addTab();
151 }
153 for (int j = 1; j < pages.length; j++) {
154 Actions.EventExpecter pageShowExpecter = mActions.expectGeckoEvent("Content:PageShow");
156 loadUrl(pages[j].url);
158 pageShowExpecter.blockForEvent();
159 pageShowExpecter.unregisterListener();
160 }
162 final int index = tab.getIndex();
163 for (int j = pages.length - 1; j > index; j--) {
164 mNavigation.back();
165 }
166 }
168 selectTabAt(session.getIndex());
169 }
171 /**
172 * Verifies that the set of open tabs matches the given session.
173 *
174 * @param session Session to verify
175 */
176 protected void verifySessionTabs(Session session) {
177 verifyTabCount(session.getItems().length);
179 (new NavigationWalker<SessionTab>(session) {
180 boolean mFirstTabVisited;
182 @Override
183 public void onItem(SessionTab tab, int currentIndex) {
184 // The first tab to check should already be selected at startup
185 if (mFirstTabVisited) {
186 selectTabAt(currentIndex);
187 } else {
188 mFirstTabVisited = true;
189 }
191 (new NavigationWalker<PageInfo>(tab) {
192 @Override
193 public void onItem(PageInfo page, int currentIndex) {
194 if (page.url.equals("about:home")) {
195 waitForText("Enter Search or Address");
196 verifyUrl(page.url);
197 } else {
198 waitForText(page.title);
199 verifyPageTitle(page.title);
200 }
201 }
203 @Override
204 public void goBack() {
205 mNavigation.back();
206 }
208 @Override
209 public void goForward() {
210 mNavigation.forward();
211 }
212 }).walk();
213 }
214 }).walk();
215 }
217 /**
218 * Gets session restore JSON corresponding to the open session.
219 *
220 * The JSON format follows the format used in Gecko for session restore and
221 * should be interchangeable with the Gecko's generated sessionstore.js.
222 *
223 * @param session Session to serialize
224 * @return JSON string of session
225 */
226 protected String buildSessionJSON(Session session) {
227 final SessionTab[] sessionTabs = session.getItems();
228 String sessionString = null;
230 try {
231 final JSONArray tabs = new JSONArray();
233 for (int i = 0; i < sessionTabs.length; i++) {
234 final JSONObject tab = new JSONObject();
235 final JSONArray entries = new JSONArray();
236 final SessionTab sessionTab = sessionTabs[i];
237 final PageInfo[] pages = sessionTab.getItems();
239 for (int j = 0; j < pages.length; j++) {
240 final PageInfo page = pages[j];
241 final JSONObject entry = new JSONObject();
242 entry.put("url", page.url);
243 entry.put("title", page.title);
244 entries.put(entry);
245 }
247 tab.put("entries", entries);
248 tab.put("index", sessionTab.getIndex() + 1);
249 tabs.put(tab);
250 }
252 JSONObject window = new JSONObject();
253 window.put("tabs", tabs);
254 window.put("selected", session.getIndex() + 1);
255 sessionString = new JSONObject().put("windows", new JSONArray().put(window)).toString();
256 } catch (JSONException e) {
257 mAsserter.ok(false, "JSON exception", getStackTraceString(e));
258 }
260 return sessionString;
261 }
263 /**
264 * @see SessionTest#verifySessionJSON(Session, String, Assert)
265 */
266 protected void verifySessionJSON(Session session, String sessionString) {
267 verifySessionJSON(session, sessionString, mAsserter);
268 }
270 /**
271 * Verifies a session JSON string against the given session.
272 *
273 * @param session Session to verify against
274 * @param sessionString JSON string to verify
275 * @param asserter Assert class to use during verification
276 */
277 protected void verifySessionJSON(Session session, String sessionString, Assert asserter) {
278 final SessionTab[] sessionTabs = session.getItems();
280 try {
281 final JSONObject window = new JSONObject(sessionString).getJSONArray("windows").getJSONObject(0);
282 final JSONArray tabs = window.getJSONArray("tabs");
283 final int optSelected = window.optInt("selected", -1);
285 asserter.is(optSelected, session.getIndex() + 1, "selected tab matches");
287 for (int i = 0; i < tabs.length(); i++) {
288 final JSONObject tab = tabs.getJSONObject(i);
289 final int index = tab.getInt("index");
290 final JSONArray entries = tab.getJSONArray("entries");
291 final SessionTab sessionTab = sessionTabs[i];
292 final PageInfo[] pages = sessionTab.getItems();
294 asserter.is(index, sessionTab.getIndex() + 1, "selected page index matches");
296 for (int j = 0; j < entries.length(); j++) {
297 final JSONObject entry = entries.getJSONObject(j);
298 final String url = entry.getString("url");
299 final String title = entry.optString("title");
300 final PageInfo page = pages[j];
302 asserter.is(url, page.url, "URL in JSON matches session URL");
303 if (!page.url.startsWith("about:")) {
304 asserter.is(title, page.title, "title in JSON matches session title");
305 }
306 }
307 }
308 } catch (JSONException e) {
309 asserter.ok(false, "JSON exception", getStackTraceString(e));
310 }
311 }
313 /**
314 * Exception thrown by NonFatalAsserter for assertion failures.
315 */
316 public static class AssertException extends RuntimeException {
317 public AssertException(String msg) {
318 super(msg);
319 }
320 }
322 /**
323 * Asserter that throws an AssertException on failure instead of aborting
324 * the test.
325 *
326 * This can be used in methods called via waitForCondition() where an assertion
327 * might not immediately succeed.
328 */
329 public class NonFatalAsserter extends FennecMochitestAssert {
330 @Override
331 public void ok(boolean condition, String name, String diag) {
332 if (!condition) {
333 String details = (diag == null ? "" : " | " + diag);
334 throw new AssertException("Assertion failed: " + name + details);
335 }
336 mAsserter.ok(condition, name, diag);
337 }
338 }
340 /**
341 * Gets a URL for a dynamically-generated page.
342 *
343 * The page will have a URL unique to the given ID, and the page's title
344 * will match the given ID.
345 *
346 * @param id ID used to generate page URL
347 * @return URL of the page
348 */
349 protected String getPage(String id) {
350 return getAbsoluteUrl("/robocop/robocop_dynamic.sjs?id=" + id);
351 }
353 protected String readProfileFile(String filename) {
354 try {
355 return readFile(new File(mProfile, filename));
356 } catch (IOException e) {
357 mAsserter.ok(false, "Error reading" + filename, getStackTraceString(e));
358 }
359 return null;
360 }
362 protected void writeProfileFile(String filename, String data) {
363 try {
364 writeFile(new File(mProfile, filename), data);
365 } catch (IOException e) {
366 mAsserter.ok(false, "Error writing to " + filename, getStackTraceString(e));
367 }
368 }
370 private String readFile(File target) throws IOException {
371 if (!target.exists()) {
372 return null;
373 }
375 FileReader fr = new FileReader(target);
376 try {
377 StringBuffer sb = new StringBuffer();
378 char[] buf = new char[8192];
379 int read = fr.read(buf);
380 while (read >= 0) {
381 sb.append(buf, 0, read);
382 read = fr.read(buf);
383 }
384 return sb.toString();
385 } finally {
386 fr.close();
387 }
388 }
390 private void writeFile(File target, String data) throws IOException {
391 FileWriter writer = new FileWriter(target);
392 try {
393 writer.write(data);
394 } finally {
395 writer.close();
396 }
397 }
398 }