<lambda>null1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
2  * Any copyright is dedicated to the Public Domain.
3    http://creativecommons.org/publicdomain/zero/1.0/ */
4 
5 package org.mozilla.geckoview.test
6 
7 import android.os.Handler
8 import android.os.Looper
9 import org.mozilla.geckoview.GeckoResult
10 import org.mozilla.geckoview.GeckoSession
11 import org.mozilla.geckoview.GeckoSessionSettings
12 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.*
13 import org.mozilla.geckoview.test.util.Callbacks
14 import org.mozilla.geckoview.test.util.UiThreadUtils
15 
16 import androidx.test.filters.MediumTest
17 import androidx.test.ext.junit.runners.AndroidJUnit4
18 
19 import org.hamcrest.Matchers.*
20 import org.json.JSONArray
21 import org.json.JSONObject
22 import org.junit.Assume.assumeThat
23 import org.junit.Test
24 import org.junit.runner.RunWith
25 
26 /**
27  * Test for the GeckoSessionTestRule class, to ensure it properly sets up a session for
28  * each test, and to ensure it can properly wait for and assert delegate
29  * callbacks.
30  */
31 @RunWith(AndroidJUnit4::class)
32 @MediumTest
33 class GeckoSessionTestRuleTest : BaseSessionTest(noErrorCollector = true) {
34 
35     @Test fun getSession() {
36         assertThat("Can get session", sessionRule.session, notNullValue())
37         assertThat("Session is open",
38                    sessionRule.session.isOpen, equalTo(true))
39     }
40 
41     @ClosedSessionAtStart
42     @Test fun getSession_closedSession() {
43         assertThat("Session is closed", sessionRule.session.isOpen, equalTo(false))
44     }
45 
46     @Setting.List(Setting(key = Setting.Key.USE_PRIVATE_MODE, value = "true"),
47                   Setting(key = Setting.Key.DISPLAY_MODE, value = "DISPLAY_MODE_MINIMAL_UI"),
48                   Setting(key = Setting.Key.ALLOW_JAVASCRIPT, value = "false"))
49     @Setting(key = Setting.Key.USE_TRACKING_PROTECTION, value = "true")
50     @Test fun settingsApplied() {
51         assertThat("USE_PRIVATE_MODE should be set",
52                    sessionRule.session.settings.usePrivateMode,
53                    equalTo(true))
54         assertThat("DISPLAY_MODE should be set",
55                    sessionRule.session.settings.displayMode,
56                    equalTo(GeckoSessionSettings.DISPLAY_MODE_MINIMAL_UI))
57         assertThat("USE_TRACKING_PROTECTION should be set",
58                    sessionRule.session.settings.useTrackingProtection,
59                    equalTo(true))
60         assertThat("ALLOW_JAVASCRIPT should be set",
61                 sessionRule.session.settings.allowJavascript,
62                 equalTo(false))
63     }
64 
65     @Test(expected = UiThreadUtils.TimeoutException::class)
66     @TimeoutMillis(2000)
67     fun noPendingCallbacks() {
68         // Make sure we don't have unexpected pending callbacks at the start of a test.
69         sessionRule.waitUntilCalled(object : Callbacks.All {
70             // There may be extraneous onSessionStateChange and onHistoryStateChange calls
71             // after a test, so ignore the first received.
72             @AssertCalled(count = 2)
73             override fun onSessionStateChange(session: GeckoSession, state: GeckoSession.SessionState) {
74             }
75 
76             @AssertCalled(count = 2)
77             override fun onHistoryStateChange(session: GeckoSession, historyList: GeckoSession.HistoryDelegate.HistoryList) {
78             }
79         })
80     }
81 
82     @Test fun includesAllCallbacks() {
83         for (ifce in GeckoSession::class.java.classes) {
84             if (!ifce.isInterface || !ifce.simpleName.endsWith("Delegate")) {
85                 continue
86             }
87             assertThat("Callbacks.All should include interface " + ifce.simpleName,
88                        ifce.isInstance(Callbacks.Default), equalTo(true))
89         }
90     }
91 
92     @NullDelegate.List(NullDelegate(GeckoSession.ContentDelegate::class),
93                        NullDelegate(Callbacks.NavigationDelegate::class))
94     @NullDelegate(Callbacks.ScrollDelegate::class)
95     @Test fun nullDelegate() {
96         assertThat("Content delegate should be null",
97                    sessionRule.session.contentDelegate, nullValue())
98         assertThat("Navigation delegate should be null",
99                    sessionRule.session.navigationDelegate, nullValue())
100         assertThat("Scroll delegate should be null",
101                    sessionRule.session.scrollDelegate, nullValue())
102 
103         assertThat("Progress delegate should not be null",
104                    sessionRule.session.progressDelegate, notNullValue())
105     }
106 
107     @NullDelegate(GeckoSession.ProgressDelegate::class)
108     @ClosedSessionAtStart
109     @Test fun nullDelegate_closed() {
110         assertThat("Progress delegate should be null",
111                    sessionRule.session.progressDelegate, nullValue())
112     }
113 
114     @Test(expected = AssertionError::class)
115     @NullDelegate(GeckoSession.ProgressDelegate::class)
116     @ClosedSessionAtStart
117     fun nullDelegate_requireProgressOnOpen() {
118         assertThat("Progress delegate should be null",
119                    sessionRule.session.progressDelegate, nullValue())
120 
121         sessionRule.session.open()
122     }
123 
124     @Test fun waitForPageStop() {
125         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
126         sessionRule.waitForPageStop()
127 
128         var counter = 0
129 
130         sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
131             override fun onPageStop(session: GeckoSession, success: Boolean) {
132                 counter++
133             }
134         })
135 
136         assertThat("Callback count should be correct", counter, equalTo(1))
137     }
138 
139     @Test(expected = AssertionError::class)
140     fun waitForPageStop_throwOnChangedCallback() {
141         sessionRule.session.progressDelegate = Callbacks.Default
142         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
143         sessionRule.waitForPageStop()
144     }
145 
146     @Test fun waitForPageStops() {
147         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
148         sessionRule.session.reload()
149         sessionRule.waitForPageStops(2)
150 
151         var counter = 0
152 
153         sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
154             override fun onPageStop(session: GeckoSession, success: Boolean) {
155                 counter++
156             }
157         })
158 
159         assertThat("Callback count should be correct", counter, equalTo(2))
160     }
161 
162     @Test(expected = AssertionError::class)
163     @NullDelegate(GeckoSession.ProgressDelegate::class)
164     @ClosedSessionAtStart
165     fun waitForPageStops_throwOnNullDelegate() {
166         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
167         sessionRule.waitForPageStop()
168 
169         sessionRule.session.open(sessionRule.runtime) // Avoid waiting for initial load
170         sessionRule.session.reload()
171         sessionRule.session.waitForPageStops(2)
172     }
173 
174     @Test fun waitUntilCalled_anyInterfaceMethod() {
175         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
176         sessionRule.waitUntilCalled(GeckoSession.ProgressDelegate::class)
177 
178         var counter = 0
179 
180         sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
181             override fun onPageStart(session: GeckoSession, url: String) {
182                 counter++
183             }
184 
185             override fun onPageStop(session: GeckoSession, success: Boolean) {
186                 counter++
187             }
188 
189             override fun onSecurityChange(session: GeckoSession,
190                                           securityInfo: GeckoSession.ProgressDelegate.SecurityInformation) {
191                 counter++
192             }
193 
194             override fun onProgressChange(session: GeckoSession, progress: Int) {
195                 counter++
196             }
197 
198             override fun onSessionStateChange(session: GeckoSession, state: GeckoSession.SessionState) {
199                 counter++
200             }
201         })
202 
203         assertThat("Callback count should be correct", counter, equalTo(1))
204     }
205 
206     @Test fun waitUntilCalled_specificInterfaceMethod() {
207         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
208         sessionRule.waitUntilCalled(GeckoSession.ProgressDelegate::class,
209                                      "onPageStart", "onPageStop")
210 
211         var counter = 0
212 
213         sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
214             override fun onPageStart(session: GeckoSession, url: String) {
215                 counter++
216             }
217 
218             override fun onPageStop(session: GeckoSession, success: Boolean) {
219                 counter++
220             }
221         })
222 
223         assertThat("Callback count should be correct", counter, equalTo(2))
224     }
225 
226     @Test(expected = AssertionError::class)
227     fun waitUntilCalled_throwOnNotGeckoSessionInterface() {
228         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
229         sessionRule.waitUntilCalled(CharSequence::class)
230     }
231 
232     fun waitUntilCalled_notThrowOnCallbackInterface() {
233         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
234         sessionRule.waitUntilCalled(Callbacks.ProgressDelegate::class)
235     }
236 
237     @Test(expected = AssertionError::class)
238     @NullDelegate(GeckoSession.ScrollDelegate::class)
239     fun waitUntilCalled_throwOnNullDelegateInterface() {
240         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
241         sessionRule.waitForPageStop()
242 
243         sessionRule.session.reload()
244         sessionRule.session.waitUntilCalled(Callbacks.All::class)
245     }
246 
247     @NullDelegate(GeckoSession.ScrollDelegate::class)
248     @Test fun waitUntilCalled_notThrowOnNonNullDelegateMethod() {
249         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
250         sessionRule.waitForPageStop()
251 
252         sessionRule.session.reload()
253         sessionRule.session.waitUntilCalled(Callbacks.All::class, "onPageStop")
254     }
255 
256     @Test fun waitUntilCalled_anyObjectMethod() {
257         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
258 
259         var counter = 0
260 
261         sessionRule.waitUntilCalled(object : Callbacks.ProgressDelegate {
262             override fun onPageStart(session: GeckoSession, url: String) {
263                 counter++
264             }
265 
266             override fun onPageStop(session: GeckoSession, success: Boolean) {
267                 counter++
268             }
269 
270             override fun onSecurityChange(session: GeckoSession,
271                                           securityInfo: GeckoSession.ProgressDelegate.SecurityInformation) {
272                 counter++
273             }
274 
275             override fun onProgressChange(session: GeckoSession, progress: Int) {
276                 counter++
277             }
278 
279             override fun onSessionStateChange(session: GeckoSession, state: GeckoSession.SessionState) {
280                 counter++
281             }
282         })
283 
284         assertThat("Callback count should be correct", counter, equalTo(1))
285     }
286 
287     @Test fun waitUntilCalled_specificObjectMethod() {
288         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
289 
290         var counter = 0
291 
292         sessionRule.waitUntilCalled(object : Callbacks.ProgressDelegate {
293             @AssertCalled
294             override fun onPageStart(session: GeckoSession, url: String) {
295                 counter++
296             }
297 
298             @AssertCalled
299             override fun onPageStop(session: GeckoSession, success: Boolean) {
300                 counter++
301             }
302         })
303 
304         assertThat("Callback count should be correct", counter, equalTo(2))
305     }
306 
307     @Test(expected = AssertionError::class)
308     @NullDelegate(GeckoSession.ScrollDelegate::class)
309     fun waitUntilCalled_throwOnNullDelegateObject() {
310         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
311         sessionRule.waitForPageStop()
312 
313         sessionRule.session.reload()
314         sessionRule.session.waitUntilCalled(object : Callbacks.All {
315             @AssertCalled
316             override fun onScrollChanged(session: GeckoSession, scrollX: Int, scrollY: Int) {
317             }
318         })
319     }
320 
321     @NullDelegate(GeckoSession.ScrollDelegate::class)
322     @Test fun waitUntilCalled_notThrowOnNonNullDelegateObject() {
323         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
324         sessionRule.waitForPageStop()
325 
326         sessionRule.session.reload()
327         sessionRule.session.waitUntilCalled(object : Callbacks.All {
328             @AssertCalled
329             override fun onPageStop(session: GeckoSession, success: Boolean) {
330             }
331         })
332     }
333 
334     @Test fun waitUntilCalled_multipleCount() {
335         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
336         sessionRule.session.reload()
337 
338         var counter = 0
339 
340         sessionRule.waitUntilCalled(object : Callbacks.ProgressDelegate {
341             @AssertCalled(count = 2)
342             override fun onPageStart(session: GeckoSession, url: String) {
343                 counter++
344             }
345 
346             @AssertCalled(count = 2)
347             override fun onPageStop(session: GeckoSession, success: Boolean) {
348                 counter++
349             }
350         })
351 
352         assertThat("Callback count should be correct", counter, equalTo(4))
353     }
354 
355     @Test fun waitUntilCalled_currentCall() {
356         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
357         sessionRule.session.reload()
358 
359         var counter = 0
360 
361         sessionRule.waitUntilCalled(object : Callbacks.ProgressDelegate {
362             @AssertCalled(count = 2, order = [1, 2])
363             override fun onPageStop(session: GeckoSession, success: Boolean) {
364                 val info = sessionRule.currentCall
365                 assertThat("Method info should be valid", info, notNullValue())
366                 assertThat("Counter should be correct",
367                            info.counter, equalTo(forEachCall(1, 2)))
368                 assertThat("Order should equal counter",
369                            info.order, equalTo(info.counter))
370                 counter++
371             }
372         })
373 
374         assertThat("Callback count should be correct", counter, equalTo(2))
375     }
376 
377     @Test(expected = IllegalStateException::class)
378     fun waitUntilCalled_passThroughExceptions() {
379         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
380         sessionRule.waitUntilCalled(object : Callbacks.ProgressDelegate {
381             @AssertCalled
382             override fun onPageStop(session: GeckoSession, success: Boolean) {
383                 throw IllegalStateException()
384             }
385         })
386     }
387 
388     @Test fun waitUntilCalled_zeroCount() {
389         // Support having @AssertCalled(count = 0) annotations for waitUntilCalled calls.
390         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
391         sessionRule.waitUntilCalled(object : Callbacks.ProgressDelegate, Callbacks.ScrollDelegate {
392             @AssertCalled(count = 1)
393             override fun onPageStop(session: GeckoSession, success: Boolean) {
394             }
395 
396             @AssertCalled(count = 0)
397             override fun onScrollChanged(session: GeckoSession, scrollX: Int, scrollY: Int) {
398             }
399         })
400     }
401 
402     @Test fun forCallbacksDuringWait_anyMethod() {
403         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
404         sessionRule.waitForPageStop()
405 
406         var counter = 0
407 
408         sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
409             override fun onPageStop(session: GeckoSession, success: Boolean) {
410                 counter++
411             }
412         })
413 
414         assertThat("Callback count should be correct", counter, equalTo(1))
415     }
416 
417     @Test(expected = AssertionError::class)
418     fun forCallbacksDuringWait_throwOnAnyMethodNotCalled() {
419         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
420         sessionRule.waitForPageStop()
421 
422         sessionRule.forCallbacksDuringWait(object : GeckoSession.ScrollDelegate {})
423     }
424 
425     @Test fun forCallbacksDuringWait_specificMethod() {
426         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
427         sessionRule.waitForPageStop()
428 
429         var counter = 0
430 
431         sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
432             @AssertCalled
433             override fun onPageStart(session: GeckoSession, url: String) {
434                 counter++
435             }
436 
437             @AssertCalled
438             override fun onPageStop(session: GeckoSession, success: Boolean) {
439                 counter++
440             }
441         })
442 
443         assertThat("Callback count should be correct", counter, equalTo(2))
444     }
445 
446     @Test fun forCallbacksDuringWait_specificMethodMultipleTimes() {
447         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
448         sessionRule.session.reload()
449         sessionRule.waitForPageStops(2)
450 
451         var counter = 0
452 
453         sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
454             @AssertCalled
455             override fun onPageStart(session: GeckoSession, url: String) {
456                 counter++
457             }
458 
459             @AssertCalled
460             override fun onPageStop(session: GeckoSession, success: Boolean) {
461                 counter++
462             }
463         })
464 
465         assertThat("Callback count should be correct", counter, equalTo(4))
466     }
467 
468     @Test(expected = AssertionError::class)
469     fun forCallbacksDuringWait_throwOnSpecificMethodNotCalled() {
470         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
471         sessionRule.waitForPageStop()
472 
473         sessionRule.forCallbacksDuringWait(object : GeckoSession.ScrollDelegate {
474             @AssertCalled
475             override fun onScrollChanged(session: GeckoSession, scrollX: Int, scrollY: Int) {
476             }
477         })
478     }
479 
480     @Test fun forCallbacksDuringWait_specificCount() {
481         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
482         sessionRule.session.reload()
483         sessionRule.waitForPageStops(2)
484 
485         var counter = 0
486 
487         sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
488             @AssertCalled(count = 2)
489             override fun onPageStart(session: GeckoSession, url: String) {
490                 counter++
491             }
492 
493             @AssertCalled(count = 2)
494             override fun onPageStop(session: GeckoSession, success: Boolean) {
495                 counter++
496             }
497         })
498 
499         assertThat("Callback count should be correct", counter, equalTo(4))
500     }
501 
502     @Test(expected = AssertionError::class)
503     fun forCallbacksDuringWait_throwOnWrongCount() {
504         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
505         sessionRule.session.reload()
506         sessionRule.waitForPageStops(2)
507 
508         sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
509             @AssertCalled(count = 1)
510             override fun onPageStart(session: GeckoSession, url: String) {
511             }
512 
513             @AssertCalled(count = 1)
514             override fun onPageStop(session: GeckoSession, success: Boolean) {
515             }
516         })
517     }
518 
519     @Test fun forCallbacksDuringWait_specificOrder() {
520         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
521         sessionRule.waitForPageStop()
522 
523         sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
524             @AssertCalled(order = [1])
525             override fun onPageStart(session: GeckoSession, url: String) {
526             }
527 
528             @AssertCalled(order = [2])
529             override fun onPageStop(session: GeckoSession, success: Boolean) {
530             }
531         })
532     }
533 
534     @Test(expected = AssertionError::class)
535     fun forCallbacksDuringWait_throwOnWrongOrder() {
536         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
537         sessionRule.waitForPageStop()
538 
539         sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
540             @AssertCalled(order = [2])
541             override fun onPageStart(session: GeckoSession, url: String) {
542             }
543 
544             @AssertCalled(order = [1])
545             override fun onPageStop(session: GeckoSession, success: Boolean) {
546             }
547         })
548     }
549 
550     @Test fun forCallbacksDuringWait_multipleOrder() {
551         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
552         sessionRule.session.reload()
553         sessionRule.waitForPageStops(2)
554 
555         sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
556             @AssertCalled(order = [1, 3, 1])
557             override fun onPageStart(session: GeckoSession, url: String) {
558             }
559 
560             @AssertCalled(order = [2, 4, 1])
561             override fun onPageStop(session: GeckoSession, success: Boolean) {
562             }
563         })
564     }
565 
566     @Test(expected = AssertionError::class)
567     fun forCallbacksDuringWait_throwOnWrongMultipleOrder() {
568         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
569         sessionRule.session.reload()
570         sessionRule.waitForPageStops(2)
571 
572         sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
573             @AssertCalled(order = [1, 2, 1])
574             override fun onPageStart(session: GeckoSession, url: String) {
575             }
576 
577             @AssertCalled(order = [3, 4, 1])
578             override fun onPageStop(session: GeckoSession, success: Boolean) {
579             }
580         })
581     }
582 
583     @Test fun forCallbacksDuringWait_notCalled() {
584         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
585         sessionRule.waitForPageStop()
586 
587         sessionRule.forCallbacksDuringWait(object : GeckoSession.ScrollDelegate {
588             @AssertCalled(false)
589             override fun onScrollChanged(session: GeckoSession, scrollX: Int, scrollY: Int) {
590             }
591         })
592     }
593 
594     @Test(expected = AssertionError::class)
595     fun forCallbacksDuringWait_throwOnCallingNoCall() {
596         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
597         sessionRule.waitForPageStop()
598 
599         sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
600             @AssertCalled(false)
601             override fun onPageStop(session: GeckoSession, success: Boolean) {
602             }
603         })
604     }
605 
606     @Test fun forCallbacksDuringWait_zeroCountEqualsNotCalled() {
607         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
608         sessionRule.waitForPageStop()
609 
610         sessionRule.forCallbacksDuringWait(object : GeckoSession.ScrollDelegate {
611             @AssertCalled(count = 0)
612             override fun onScrollChanged(session: GeckoSession, scrollX: Int, scrollY: Int) {
613             }
614         })
615     }
616 
617     @Test(expected = AssertionError::class)
618     fun forCallbacksDuringWait_throwOnCallingZeroCount() {
619         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
620         sessionRule.waitForPageStop()
621 
622         sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
623             @AssertCalled(count = 0)
624             override fun onPageStop(session: GeckoSession, success: Boolean) {
625             }
626         })
627     }
628 
629     @Test fun forCallbacksDuringWait_limitedToLastWait() {
630         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
631         sessionRule.session.reload()
632         sessionRule.session.reload()
633         sessionRule.session.reload()
634 
635         // Wait for Gecko to finish all loads.
636         Thread.sleep(100)
637 
638         sessionRule.waitForPageStop() // Wait for loadUri.
639         sessionRule.waitForPageStop() // Wait for first reload.
640 
641         var counter = 0
642 
643         // assert should only apply to callbacks within range (loadUri, first reload].
644         sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
645             @AssertCalled(count = 1)
646             override fun onPageStart(session: GeckoSession, url: String) {
647                 counter++
648             }
649 
650             @AssertCalled(count = 1)
651             override fun onPageStop(session: GeckoSession, success: Boolean) {
652                 counter++
653             }
654         })
655 
656         assertThat("Callback count should be correct", counter, equalTo(2))
657     }
658 
659     @Test fun forCallbacksDuringWait_currentCall() {
660         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
661         sessionRule.waitForPageStop()
662 
663         sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
664             @AssertCalled(count = 1)
665             override fun onPageStop(session: GeckoSession, success: Boolean) {
666                 val info = sessionRule.currentCall
667                 assertThat("Method info should be valid", info, notNullValue())
668                 assertThat("Counter should be correct",
669                            info.counter, equalTo(1))
670                 assertThat("Order should equal counter",
671                            info.order, equalTo(0))
672             }
673         })
674     }
675 
676     @Test(expected = IllegalStateException::class)
677     fun forCallbacksDuringWait_passThroughExceptions() {
678         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
679         sessionRule.waitForPageStop()
680 
681         sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
682             @AssertCalled
683             override fun onPageStop(session: GeckoSession, success: Boolean) {
684                 throw IllegalStateException()
685             }
686         })
687     }
688 
689     @Test(expected = AssertionError::class)
690     @NullDelegate(GeckoSession.ScrollDelegate::class)
691     fun forCallbacksDuringWait_throwOnAnyNullDelegate() {
692         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
693         sessionRule.waitForPageStop()
694         sessionRule.session.reload()
695         sessionRule.session.waitForPageStop()
696 
697         sessionRule.session.forCallbacksDuringWait(object : Callbacks.All {})
698     }
699 
700     @Test(expected = AssertionError::class)
701     @NullDelegate(GeckoSession.ScrollDelegate::class)
702     fun forCallbacksDuringWait_throwOnSpecificNullDelegate() {
703         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
704         sessionRule.waitForPageStop()
705         sessionRule.session.reload()
706         sessionRule.session.waitForPageStop()
707 
708         sessionRule.session.forCallbacksDuringWait(object : Callbacks.All {
709             @AssertCalled
710             override fun onScrollChanged(session: GeckoSession, scrollX: Int, scrollY: Int) {
711             }
712         })
713     }
714 
715     @NullDelegate(GeckoSession.ScrollDelegate::class)
716     @Test fun forCallbacksDuringWait_notThrowOnNonNullDelegate() {
717         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
718         sessionRule.waitForPageStop()
719         sessionRule.session.reload()
720         sessionRule.session.waitForPageStop()
721 
722         sessionRule.session.forCallbacksDuringWait(object : Callbacks.All {
723             @AssertCalled
724             override fun onPageStop(session: GeckoSession, success: Boolean) {
725             }
726         })
727     }
728 
729     @Test(expected = AssertionError::class)
730     fun getCurrentCall_throwOnNoCurrentCall() {
731         sessionRule.currentCall
732     }
733 
734     @Test fun delegateUntilTestEnd() {
735         var counter = 0
736 
737         sessionRule.delegateUntilTestEnd(object : Callbacks.ProgressDelegate {
738             @AssertCalled(count = 1, order = [1])
739             override fun onPageStart(session: GeckoSession, url: String) {
740                 counter++
741             }
742 
743             @AssertCalled(count = 1, order = [2])
744             override fun onPageStop(session: GeckoSession, success: Boolean) {
745                 counter++
746             }
747         })
748 
749         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
750         sessionRule.waitForPageStop()
751 
752         assertThat("Callback count should be correct", counter, equalTo(2))
753     }
754 
755     @Test fun delegateUntilTestEnd_notCalled() {
756         sessionRule.delegateUntilTestEnd(object : GeckoSession.ScrollDelegate {
757             @AssertCalled(false)
758             override fun onScrollChanged(session: GeckoSession, scrollX: Int, scrollY: Int) {
759             }
760         })
761     }
762 
763     @Test(expected = AssertionError::class)
764     fun delegateUntilTestEnd_throwOnNotCalled() {
765         sessionRule.delegateUntilTestEnd(object : GeckoSession.ScrollDelegate {
766             @AssertCalled(count = 1)
767             override fun onScrollChanged(session: GeckoSession, scrollX: Int, scrollY: Int) {
768             }
769         })
770         sessionRule.performTestEndCheck()
771     }
772 
773     @Test(expected = AssertionError::class)
774     fun delegateUntilTestEnd_throwOnCallingNoCall() {
775         sessionRule.delegateUntilTestEnd(object : Callbacks.ProgressDelegate {
776             @AssertCalled(false)
777             override fun onPageStop(session: GeckoSession, success: Boolean) {
778             }
779         })
780 
781         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
782         sessionRule.waitForPageStop()
783     }
784 
785     @Test(expected = AssertionError::class)
786     fun delegateUntilTestEnd_throwOnWrongOrder() {
787         sessionRule.delegateUntilTestEnd(object : Callbacks.ProgressDelegate {
788             @AssertCalled(count = 1, order = [2])
789             override fun onPageStart(session: GeckoSession, url: String) {
790             }
791 
792             @AssertCalled(count = 1, order = [1])
793             override fun onPageStop(session: GeckoSession, success: Boolean) {
794             }
795         })
796 
797         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
798         sessionRule.waitForPageStop()
799     }
800 
801     @Test fun delegateUntilTestEnd_currentCall() {
802         sessionRule.delegateUntilTestEnd(object : Callbacks.ProgressDelegate {
803             @AssertCalled(count = 1)
804             override fun onPageStop(session: GeckoSession, success: Boolean) {
805                 val info = sessionRule.currentCall
806                 assertThat("Method info should be valid", info, notNullValue())
807                 assertThat("Counter should be correct",
808                            info.counter, equalTo(1))
809                 assertThat("Order should equal counter",
810                            info.order, equalTo(0))
811             }
812         })
813 
814         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
815         sessionRule.waitForPageStop()
816     }
817 
818     @Test fun delegateDuringNextWait() {
819         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
820         sessionRule.waitForPageStop()
821         var counter = 0
822 
823         sessionRule.delegateDuringNextWait(object : Callbacks.ProgressDelegate {
824             @AssertCalled(count = 1, order = [1])
825             override fun onPageStart(session: GeckoSession, url: String) {
826                 counter++
827             }
828 
829             @AssertCalled(count = 1, order = [2])
830             override fun onPageStop(session: GeckoSession, success: Boolean) {
831                 counter++
832             }
833         })
834 
835         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
836         sessionRule.waitForPageStop()
837 
838         assertThat("Should have delegated", counter, equalTo(2))
839 
840         sessionRule.session.reload()
841         sessionRule.waitForPageStop()
842 
843         assertThat("Delegate should be cleared", counter, equalTo(2))
844     }
845 
846     @Test(expected = AssertionError::class)
847     fun delegateDuringNextWait_throwOnNotCalled() {
848         sessionRule.delegateDuringNextWait(object : GeckoSession.ScrollDelegate {
849             @AssertCalled(count = 1)
850             override fun onScrollChanged(session: GeckoSession, scrollX: Int, scrollY: Int) {
851             }
852         })
853         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
854         sessionRule.waitForPageStop()
855     }
856 
857     @Test(expected = AssertionError::class)
858     fun delegateDuringNextWait_throwOnNotCalledAtTestEnd() {
859         sessionRule.delegateDuringNextWait(object : GeckoSession.ScrollDelegate {
860             @AssertCalled(count = 1)
861             override fun onScrollChanged(session: GeckoSession, scrollX: Int, scrollY: Int) {
862             }
863         })
864         sessionRule.performTestEndCheck()
865     }
866 
867     @Test fun delegateDuringNextWait_hasPrecedence() {
868         var testCounter = 0
869         var waitCounter = 0
870 
871         sessionRule.delegateUntilTestEnd(object : Callbacks.ProgressDelegate,
872                                                   Callbacks.NavigationDelegate {
873             @AssertCalled(count = 1, order = [2])
874             override fun onPageStart(session: GeckoSession, url: String) {
875                 testCounter++
876             }
877 
878             @AssertCalled(count = 1, order = [4])
879             override fun onPageStop(session: GeckoSession, success: Boolean) {
880                 testCounter++
881             }
882 
883             @AssertCalled(count = 2, order = [1, 3])
884             override fun onCanGoBack(session: GeckoSession, canGoBack: Boolean) {
885                 testCounter++
886             }
887 
888             @AssertCalled(count = 2, order = [1, 3])
889             override fun onCanGoForward(session: GeckoSession, canGoForward: Boolean) {
890                 testCounter++
891             }
892         })
893 
894         sessionRule.delegateDuringNextWait(object : Callbacks.ProgressDelegate {
895             @AssertCalled(count = 1, order = [1])
896             override fun onPageStart(session: GeckoSession, url: String) {
897                 waitCounter++
898             }
899 
900             @AssertCalled(count = 1, order = [2])
901             override fun onPageStop(session: GeckoSession, success: Boolean) {
902                 waitCounter++
903             }
904         })
905 
906         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
907         sessionRule.waitForPageStop()
908 
909         assertThat("Text delegate should be overridden",
910                    testCounter, equalTo(2))
911         assertThat("Wait delegate should be used", waitCounter, equalTo(2))
912 
913         sessionRule.session.reload()
914         sessionRule.waitForPageStop()
915 
916         assertThat("Test delegate should be used", testCounter, equalTo(6))
917         assertThat("Wait delegate should be cleared", waitCounter, equalTo(2))
918     }
919 
920     @Test(expected = IllegalStateException::class)
921     fun delegateDuringNextWait_passThroughExceptions() {
922         sessionRule.delegateDuringNextWait(object : Callbacks.ProgressDelegate {
923             @AssertCalled
924             override fun onPageStop(session: GeckoSession, success: Boolean) {
925                 throw IllegalStateException()
926             }
927         })
928 
929         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
930         sessionRule.waitForPageStop()
931     }
932 
933     @Test(expected = AssertionError::class)
934     @NullDelegate(GeckoSession.NavigationDelegate::class)
935     fun delegateDuringNextWait_throwOnNullDelegate() {
936         sessionRule.session.delegateDuringNextWait(object : Callbacks.NavigationDelegate {
937             override fun onLocationChange(session: GeckoSession, url: String?) {
938             }
939         })
940     }
941 
942     @Test fun wrapSession() {
943         val session = sessionRule.wrapSession(
944           GeckoSession(sessionRule.session.settings))
945         sessionRule.openSession(session)
946         session.reload()
947         session.waitForPageStop()
948     }
949 
950     @Test fun createOpenSession() {
951         val newSession = sessionRule.createOpenSession()
952         assertThat("Can create session", newSession, notNullValue())
953         assertThat("New session is open", newSession.isOpen, equalTo(true))
954         assertThat("New session has same settings",
955                    newSession.settings, equalTo(sessionRule.session.settings))
956     }
957 
958     @Test fun createOpenSession_withSettings() {
959         val settings = GeckoSessionSettings.Builder(sessionRule.session.settings)
960                 .usePrivateMode(true)
961                 .build()
962 
963         val newSession = sessionRule.createOpenSession(settings)
964         assertThat("New session has same settings", newSession.settings, equalTo(settings))
965     }
966 
967     @Test fun createOpenSession_canInterleaveOtherCalls() {
968         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
969 
970         val newSession = sessionRule.createOpenSession()
971         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
972         sessionRule.waitForPageStops(2)
973 
974         newSession.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
975             @AssertCalled(false)
976             override fun onPageStop(session: GeckoSession, success: Boolean) {
977             }
978         })
979 
980         sessionRule.session.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
981             @AssertCalled(count = 2)
982             override fun onPageStop(session: GeckoSession, success: Boolean) {
983             }
984         })
985     }
986 
987     @Test fun createClosedSession() {
988         val newSession = sessionRule.createClosedSession()
989         assertThat("Can create session", newSession, notNullValue())
990         assertThat("New session is open", newSession.isOpen, equalTo(false))
991         assertThat("New session has same settings",
992                    newSession.settings, equalTo(sessionRule.session.settings))
993     }
994 
995     @Test fun createClosedSession_withSettings() {
996         val settings = GeckoSessionSettings.Builder(sessionRule.session.settings).usePrivateMode(true).build()
997 
998         val newSession = sessionRule.createClosedSession(settings)
999         assertThat("New session has same settings", newSession.settings, equalTo(settings))
1000     }
1001 
1002     @Test(expected = UiThreadUtils.TimeoutException::class)
1003     @TimeoutMillis(2000)
1004     @ClosedSessionAtStart
1005     fun noPendingCallbacks_withSpecificSession() {
1006         sessionRule.createOpenSession()
1007         // Make sure we don't have unexpected pending callbacks after opening a session.
1008         sessionRule.waitUntilCalled(object : Callbacks.All {
1009             // There may be extraneous onSessionStateChange and onHistoryStateChange calls
1010             // after a test, so ignore the first received.
1011             @AssertCalled(count = 2)
1012             override fun onSessionStateChange(session: GeckoSession, state: GeckoSession.SessionState) {
1013             }
1014 
1015             @AssertCalled(count = 2)
1016             override fun onHistoryStateChange(session: GeckoSession, historyList: GeckoSession.HistoryDelegate.HistoryList) {
1017             }
1018         })
1019     }
1020 
1021     @Test fun waitForPageStop_withSpecificSession() {
1022         val newSession = sessionRule.createOpenSession()
1023         newSession.loadTestPath(HELLO_HTML_PATH)
1024         newSession.waitForPageStop()
1025     }
1026 
1027     @Test fun waitForPageStop_withAllSessions() {
1028         val newSession = sessionRule.createOpenSession()
1029         newSession.loadTestPath(HELLO_HTML_PATH)
1030         sessionRule.waitForPageStop()
1031     }
1032 
1033     @Test(expected = AssertionError::class)
1034     fun waitForPageStop_throwOnNotWrapped() {
1035         GeckoSession(sessionRule.session.settings).waitForPageStop()
1036     }
1037 
1038     @Test fun waitForPageStops_withSpecificSessions() {
1039         val newSession = sessionRule.createOpenSession()
1040         newSession.loadTestPath(HELLO_HTML_PATH)
1041         newSession.reload()
1042         newSession.waitForPageStops(2)
1043     }
1044 
1045     @Test fun waitForPageStops_withAllSessions() {
1046         val newSession = sessionRule.createOpenSession()
1047         newSession.loadTestPath(HELLO_HTML_PATH)
1048         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1049         sessionRule.waitForPageStops(2)
1050     }
1051 
1052     @Test fun waitForPageStops_acrossSessionCreation() {
1053         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1054         val session = sessionRule.createOpenSession()
1055         sessionRule.session.reload()
1056         session.loadTestPath(HELLO_HTML_PATH)
1057         sessionRule.waitForPageStops(3)
1058     }
1059 
1060     @Test fun waitUntilCalled_interfaceWithSpecificSession() {
1061         val newSession = sessionRule.createOpenSession()
1062         newSession.loadTestPath(HELLO_HTML_PATH)
1063         newSession.waitUntilCalled(Callbacks.ProgressDelegate::class, "onPageStop")
1064     }
1065 
1066     @Test fun waitUntilCalled_interfaceWithAllSessions() {
1067         val newSession = sessionRule.createOpenSession()
1068         newSession.loadTestPath(HELLO_HTML_PATH)
1069         sessionRule.waitUntilCalled(Callbacks.ProgressDelegate::class, "onPageStop")
1070     }
1071 
1072     @Test fun waitUntilCalled_callbackWithSpecificSession() {
1073         val newSession = sessionRule.createOpenSession()
1074         newSession.loadTestPath(HELLO_HTML_PATH)
1075         newSession.waitUntilCalled(object : Callbacks.ProgressDelegate {
1076             @AssertCalled(count = 1)
1077             override fun onPageStop(session: GeckoSession, success: Boolean) {
1078             }
1079         })
1080     }
1081 
1082     @Test fun waitUntilCalled_callbackWithAllSessions() {
1083         val newSession = sessionRule.createOpenSession()
1084         newSession.loadTestPath(HELLO_HTML_PATH)
1085         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1086         sessionRule.waitUntilCalled(object : Callbacks.ProgressDelegate {
1087             @AssertCalled(count = 2)
1088             override fun onPageStop(session: GeckoSession, success: Boolean) {
1089             }
1090         })
1091     }
1092 
1093     @Test fun forCallbacksDuringWait_withSpecificSession() {
1094         val newSession = sessionRule.createOpenSession()
1095         newSession.loadTestPath(HELLO_HTML_PATH)
1096         newSession.waitForPageStop()
1097 
1098         var counter = 0
1099 
1100         newSession.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
1101             @AssertCalled(count = 1)
1102             override fun onPageStop(session: GeckoSession, success: Boolean) {
1103                 counter++
1104             }
1105         })
1106 
1107         sessionRule.session.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
1108             @AssertCalled(false)
1109             override fun onPageStop(session: GeckoSession, success: Boolean) {
1110                 counter++
1111             }
1112         })
1113 
1114         assertThat("Callback count should be correct", counter, equalTo(1))
1115     }
1116 
1117     @Test fun forCallbacksDuringWait_withAllSessions() {
1118         val newSession = sessionRule.createOpenSession()
1119         newSession.loadTestPath(HELLO_HTML_PATH)
1120         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1121         sessionRule.waitForPageStops(2)
1122 
1123         var counter = 0
1124 
1125         sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
1126             @AssertCalled(count = 2)
1127             override fun onPageStop(session: GeckoSession, success: Boolean) {
1128                 counter++
1129             }
1130         })
1131 
1132         assertThat("Callback count should be correct", counter, equalTo(2))
1133     }
1134 
1135     @Test fun forCallbacksDuringWait_limitedToLastSessionWait() {
1136         val newSession = sessionRule.createOpenSession()
1137 
1138         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1139         sessionRule.session.waitForPageStop()
1140 
1141         newSession.loadTestPath(HELLO_HTML_PATH)
1142         newSession.waitForPageStop()
1143 
1144         // forCallbacksDuringWait calls strictly apply to the last wait, session-specific or not.
1145         var counter = 0
1146 
1147         sessionRule.session.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
1148             @AssertCalled(false)
1149             override fun onPageStop(session: GeckoSession, success: Boolean) {
1150                 counter++
1151             }
1152         })
1153 
1154         newSession.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
1155             @AssertCalled(count = 1)
1156             override fun onPageStop(session: GeckoSession, success: Boolean) {
1157                 counter++
1158             }
1159         })
1160 
1161         sessionRule.forCallbacksDuringWait(object : Callbacks.ProgressDelegate {
1162             @AssertCalled(count = 1)
1163             override fun onPageStop(session: GeckoSession, success: Boolean) {
1164                 counter++
1165             }
1166         })
1167 
1168         assertThat("Callback count should be correct", counter, equalTo(2))
1169     }
1170 
1171     @Test fun delegateUntilTestEnd_withSpecificSession() {
1172         val newSession = sessionRule.createOpenSession()
1173 
1174         var counter = 0
1175 
1176         newSession.delegateUntilTestEnd(object : Callbacks.ProgressDelegate {
1177             @AssertCalled(count = 1)
1178             override fun onPageStop(session: GeckoSession, success: Boolean) {
1179                 counter++
1180             }
1181         })
1182 
1183         sessionRule.session.delegateUntilTestEnd(object : Callbacks.ProgressDelegate {
1184             @AssertCalled(false)
1185             override fun onPageStop(session: GeckoSession, success: Boolean) {
1186                 counter++
1187             }
1188         })
1189 
1190         newSession.loadTestPath(HELLO_HTML_PATH)
1191         newSession.waitForPageStop()
1192 
1193         assertThat("Callback count should be correct", counter, equalTo(1))
1194     }
1195 
1196     @Test fun delegateUntilTestEnd_withAllSessions() {
1197         var counter = 0
1198 
1199         sessionRule.delegateUntilTestEnd(object : Callbacks.ProgressDelegate {
1200             @AssertCalled(count = 1)
1201             override fun onPageStop(session: GeckoSession, success: Boolean) {
1202                 counter++
1203             }
1204         })
1205 
1206         val newSession = sessionRule.createOpenSession()
1207         newSession.loadTestPath(HELLO_HTML_PATH)
1208         newSession.waitForPageStop()
1209 
1210         assertThat("Callback count should be correct", counter, equalTo(1))
1211     }
1212 
1213     @Test fun delegateDuringNextWait_hasPrecedenceWithSpecificSession() {
1214         val newSession = sessionRule.createOpenSession()
1215         var counter = 0
1216 
1217         newSession.delegateDuringNextWait(object : Callbacks.ProgressDelegate {
1218             @AssertCalled(count = 1)
1219             override fun onPageStop(session: GeckoSession, success: Boolean) {
1220                 counter++
1221             }
1222         })
1223 
1224         newSession.delegateUntilTestEnd(object : Callbacks.ProgressDelegate {
1225             @AssertCalled(false)
1226             override fun onPageStop(session: GeckoSession, success: Boolean) {
1227                 counter++
1228             }
1229         })
1230 
1231         newSession.loadTestPath(HELLO_HTML_PATH)
1232         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1233         sessionRule.waitForPageStops(2)
1234 
1235         assertThat("Callback count should be correct", counter, equalTo(1))
1236     }
1237 
1238     @Test fun delegateDuringNextWait_specificSessionOverridesAll() {
1239         val newSession = sessionRule.createOpenSession()
1240         var counter = 0
1241 
1242         newSession.delegateDuringNextWait(object : Callbacks.ProgressDelegate {
1243             @AssertCalled(count = 1)
1244             override fun onPageStop(session: GeckoSession, success: Boolean) {
1245                 counter++
1246             }
1247         })
1248 
1249         sessionRule.delegateDuringNextWait(object : Callbacks.ProgressDelegate {
1250             @AssertCalled(count = 1)
1251             override fun onPageStop(session: GeckoSession, success: Boolean) {
1252                 counter++
1253             }
1254         })
1255 
1256         newSession.loadTestPath(HELLO_HTML_PATH)
1257         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1258         sessionRule.waitForPageStops(2)
1259 
1260         assertThat("Callback count should be correct", counter, equalTo(2))
1261     }
1262 
1263     @WithDisplay(width = 100, height = 100)
1264     @Test fun synthesizeTap() {
1265         sessionRule.session.loadTestPath(CLICK_TO_RELOAD_HTML_PATH)
1266         sessionRule.session.waitForPageStop()
1267 
1268         sessionRule.session.synthesizeTap(50, 50)
1269         sessionRule.session.waitForPageStop()
1270     }
1271 
1272     @Test fun evaluateJS() {
1273         sessionRule.session.loadTestPath(HELLO_HTML_PATH);
1274         sessionRule.session.waitForPageStop();
1275 
1276         assertThat("JS string result should be correct",
1277                    sessionRule.session.evaluateJS("'foo'") as String, equalTo("foo"))
1278 
1279         assertThat("JS number result should be correct",
1280                    sessionRule.session.evaluateJS("1+1") as Double, equalTo(2.0))
1281 
1282         assertThat("JS boolean result should be correct",
1283                    sessionRule.session.evaluateJS("!0") as Boolean, equalTo(true))
1284 
1285         val expected = JSONObject("{bar:42,baz:true,foo:'bar'}")
1286         val actual = sessionRule.session.evaluateJS("({foo:'bar',bar:42,baz:true})") as JSONObject
1287         for (key in expected.keys()) {
1288             assertThat("JS object result should be correct",
1289                     actual.get(key), equalTo(expected.get(key)))
1290         }
1291 
1292         assertThat("JS array result should be correct",
1293                    sessionRule.session.evaluateJS("[1,2,3]") as JSONArray,
1294                    equalTo(JSONArray("[1,2,3]")))
1295 
1296         assertThat("JS DOM object result should be correct",
1297                    sessionRule.session.evaluateJS("document.body.tagName") as String,
1298                    equalTo("BODY"))
1299     }
1300 
1301     @Test fun evaluateJS_windowObject() {
1302         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1303         sessionRule.session.waitForPageStop()
1304 
1305         assertThat("JS DOM window result should be correct",
1306                    (sessionRule.session.evaluateJS("window.location.pathname")) as String,
1307                    equalTo(HELLO_HTML_PATH))
1308     }
1309 
1310     @Test fun evaluateJS_multipleSessions() {
1311         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1312         sessionRule.session.waitForPageStop()
1313 
1314         sessionRule.session.evaluateJS("this.foo = 42")
1315         assertThat("Variable should be set",
1316                    sessionRule.session.evaluateJS("this.foo") as Double, equalTo(42.0))
1317 
1318         val newSession = sessionRule.createOpenSession()
1319         newSession.loadTestPath(HELLO_HTML_PATH)
1320         newSession.waitForPageStop()
1321 
1322         val result = newSession.evaluateJS("this.foo")
1323         assertThat("New session should have separate JS context",
1324                    result, nullValue())
1325     }
1326 
1327     @Test fun evaluateJS_supportPromises() {
1328         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1329         sessionRule.session.waitForPageStop()
1330 
1331         assertThat("Can get resolved promise",
1332             sessionRule.session.evaluatePromiseJS(
1333                     "new Promise(resolve => resolve('foo'))").value as String,
1334             equalTo("foo"));
1335 
1336         val promise = sessionRule.session.evaluatePromiseJS(
1337                 "new Promise(r => window.resolve = r)")
1338 
1339         sessionRule.session.evaluateJS("window.resolve('bar')")
1340 
1341         assertThat("Can wait for promise to resolve",
1342                 promise.value as String, equalTo("bar"))
1343     }
1344 
1345     @Test(expected = RejectedPromiseException::class)
1346     fun evaluateJS_throwOnRejectedPromise() {
1347         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1348         sessionRule.session.waitForPageStop()
1349         sessionRule.session.evaluatePromiseJS("Promise.reject('foo')").value
1350     }
1351 
1352     @Test fun evaluateJS_notBlockMainThread() {
1353         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1354         sessionRule.session.waitForPageStop()
1355         // Test that we can still receive delegate callbacks during evaluateJS,
1356         // by calling alert(), which blocks until prompt delegate is called.
1357         assertThat("JS blocking result should be correct",
1358                    sessionRule.session.evaluateJS("alert(); 'foo'") as String,
1359                    equalTo("foo"))
1360     }
1361 
1362     @TimeoutMillis(1000)
1363     @Test(expected = UiThreadUtils.TimeoutException::class)
1364     fun evaluateJS_canTimeout() {
1365         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1366         sessionRule.session.waitForPageStop()
1367         sessionRule.session.delegateUntilTestEnd(object : Callbacks.PromptDelegate {
1368             override fun onAlertPrompt(session: GeckoSession, prompt: GeckoSession.PromptDelegate.AlertPrompt): GeckoResult<GeckoSession.PromptDelegate.PromptResponse> {
1369                 // Return a GeckoResult that we will never complete, so it hangs.
1370                 val res = GeckoResult<GeckoSession.PromptDelegate.PromptResponse>()
1371                 return res
1372             }
1373         })
1374         sessionRule.session.evaluateJS("alert()")
1375     }
1376 
1377     @Test(expected = RuntimeException::class)
1378     fun evaluateJS_throwOnJSException() {
1379         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1380         sessionRule.session.waitForPageStop()
1381         sessionRule.session.evaluateJS("throw Error()")
1382     }
1383 
1384     @Test(expected = RuntimeException::class)
1385     fun evaluateJS_throwOnSyntaxError() {
1386         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1387         sessionRule.session.waitForPageStop()
1388         sessionRule.session.evaluateJS("<{[")
1389     }
1390 
1391     @Test(expected = RuntimeException::class)
1392     fun evaluateJS_throwOnChromeAccess() {
1393         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1394         sessionRule.session.waitForPageStop()
1395         sessionRule.session.evaluateJS("ChromeUtils")
1396     }
1397 
1398     @Test fun getPrefs_undefinedPrefReturnsNull() {
1399         assertThat("Undefined pref should have null value",
1400                    sessionRule.getPrefs("invalid.pref")[0], equalTo(JSONObject.NULL))
1401     }
1402 
1403     @Test fun setPrefsUntilTestEnd() {
1404         sessionRule.setPrefsUntilTestEnd(mapOf(
1405                 "test.pref.bool" to true,
1406                 "test.pref.int" to 1,
1407                 "test.pref.foo" to "foo"))
1408 
1409         var prefs = sessionRule.getPrefs(
1410                 "test.pref.bool",
1411                 "test.pref.int",
1412                 "test.pref.foo",
1413                 "test.pref.bar")
1414 
1415         assertThat("Prefs should be set", prefs[0] as Boolean, equalTo(true))
1416         assertThat("Prefs should be set", prefs[1] as Int, equalTo(1))
1417         assertThat("Prefs should be set", prefs[2] as String, equalTo("foo"))
1418         assertThat("Prefs should be set", prefs[3], equalTo(JSONObject.NULL))
1419 
1420         sessionRule.setPrefsUntilTestEnd(mapOf(
1421                 "test.pref.foo" to "bar",
1422                 "test.pref.bar" to "baz"))
1423 
1424         prefs = sessionRule.getPrefs(
1425                         "test.pref.bool",
1426                         "test.pref.int",
1427                         "test.pref.foo",
1428                         "test.pref.bar")
1429 
1430         assertThat("New prefs should be set", prefs[0] as Boolean, equalTo(true))
1431         assertThat("New prefs should be set", prefs[1] as Int, equalTo(1))
1432         assertThat("New prefs should be set", prefs[2] as String, equalTo("bar"))
1433         assertThat("New prefs should be set", prefs[3] as String, equalTo("baz"))
1434     }
1435 
1436     @Test fun setPrefsDuringNextWait() {
1437         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1438         sessionRule.waitForPageStop()
1439 
1440         sessionRule.setPrefsDuringNextWait(mapOf(
1441                 "test.pref.bool" to true,
1442                 "test.pref.int" to 1,
1443                 "test.pref.foo" to "foo"))
1444 
1445         var prefs = sessionRule.getPrefs(
1446                         "test.pref.bool",
1447                         "test.pref.int",
1448                         "test.pref.foo")
1449 
1450         assertThat("Prefs should be set before wait", prefs[0] as Boolean, equalTo(true))
1451         assertThat("Prefs should be set before wait", prefs[1] as Int, equalTo(1))
1452         assertThat("Prefs should be set before wait", prefs[2] as String, equalTo("foo"))
1453 
1454         sessionRule.session.reload()
1455         sessionRule.session.waitForPageStop()
1456 
1457         prefs = sessionRule.getPrefs(
1458                 "test.pref.bool",
1459                 "test.pref.int",
1460                 "test.pref.foo")
1461 
1462         assertThat("Prefs should be cleared after wait", prefs[0], equalTo(JSONObject.NULL))
1463         assertThat("Prefs should be cleared after wait", prefs[1], equalTo(JSONObject.NULL))
1464         assertThat("Prefs should be cleared after wait", prefs[2], equalTo(JSONObject.NULL))
1465     }
1466 
1467     @Test fun setPrefsDuringNextWait_hasPrecedence() {
1468         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1469         sessionRule.waitForPageStop()
1470 
1471         sessionRule.setPrefsUntilTestEnd(mapOf(
1472                 "test.pref.int" to 1,
1473                 "test.pref.foo" to "foo"))
1474 
1475         sessionRule.setPrefsDuringNextWait(mapOf(
1476                 "test.pref.foo" to "bar",
1477                 "test.pref.bar" to "baz"))
1478 
1479         var prefs = sessionRule.getPrefs(
1480                 "test.pref.int",
1481                 "test.pref.foo",
1482                 "test.pref.bar")
1483 
1484         assertThat("Prefs should be overridden", prefs[0] as Int, equalTo(1))
1485         assertThat("Prefs should be overridden", prefs[1] as String, equalTo("bar"))
1486         assertThat("Prefs should be overridden", prefs[2] as String, equalTo("baz"))
1487 
1488         sessionRule.session.reload()
1489         sessionRule.session.waitForPageStop()
1490 
1491         prefs = sessionRule.getPrefs(
1492                         "test.pref.int",
1493                         "test.pref.foo",
1494                         "test.pref.bar")
1495 
1496         assertThat("Overriden prefs should be restored", prefs[0] as Int, equalTo(1))
1497         assertThat("Overriden prefs should be restored", prefs[1] as String, equalTo("foo"))
1498         assertThat("Overriden prefs should be restored", prefs[2], equalTo(JSONObject.NULL))
1499     }
1500 
1501     @Test fun waitForJS() {
1502         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1503         sessionRule.waitForPageStop()
1504 
1505         assertThat("waitForJS should return correct result",
1506                    sessionRule.session.waitForJS("alert(), 'foo'") as String,
1507                    equalTo("foo"))
1508 
1509         sessionRule.session.forCallbacksDuringWait(object : Callbacks.PromptDelegate {
1510             @AssertCalled(count = 1)
1511             override fun onAlertPrompt(session: GeckoSession, prompt: GeckoSession.PromptDelegate.AlertPrompt): GeckoResult<GeckoSession.PromptDelegate.PromptResponse>? {
1512                 return null;
1513             }
1514         })
1515     }
1516 
1517     @Test fun waitForJS_resolvePromise() {
1518         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1519         sessionRule.waitForPageStop()
1520         assertThat("waitForJS should wait for promises",
1521                    sessionRule.session.waitForJS("Promise.resolve('foo')") as String,
1522                    equalTo("foo"))
1523     }
1524 
1525     @Test fun waitForJS_delegateDuringWait() {
1526         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1527         sessionRule.waitForPageStop()
1528 
1529         var count = 0
1530         sessionRule.session.delegateDuringNextWait(object : Callbacks.PromptDelegate {
1531             override fun onAlertPrompt(session: GeckoSession, prompt: GeckoSession.PromptDelegate.AlertPrompt): GeckoResult<GeckoSession.PromptDelegate.PromptResponse> {
1532                 count++
1533                 return GeckoResult.fromValue(prompt.dismiss())
1534             }
1535         })
1536 
1537         sessionRule.session.waitForJS("alert()")
1538         sessionRule.session.waitForJS("alert()")
1539 
1540         // The delegate set through delegateDuringNextWait
1541         // should have been cleared after the first wait.
1542         assertThat("Delegate should only run once", count, equalTo(1))
1543     }
1544 
1545     private interface TestDelegate {
1546         fun onDelegate(foo: String, bar: String): Int
1547     }
1548 
1549     @Test fun addExternalDelegateUntilTestEnd() {
1550         lateinit var delegate: TestDelegate
1551 
1552         sessionRule.addExternalDelegateUntilTestEnd(
1553                 TestDelegate::class, { newDelegate -> delegate = newDelegate }, { },
1554                 object : TestDelegate {
1555             @AssertCalled(count = 1)
1556             override fun onDelegate(foo: String, bar: String): Int {
1557                 assertThat("First argument should be correct", foo, equalTo("foo"))
1558                 assertThat("Second argument should be correct", bar, equalTo("bar"))
1559                 return 42
1560             }
1561         })
1562 
1563         assertThat("Delegate should be registered", delegate, notNullValue())
1564         assertThat("Delegate return value should be correct",
1565                    delegate.onDelegate("foo", "bar"), equalTo(42))
1566         sessionRule.performTestEndCheck()
1567     }
1568 
1569     @Test(expected = AssertionError::class)
1570     fun addExternalDelegateUntilTestEnd_throwOnNotCalled() {
1571         sessionRule.addExternalDelegateUntilTestEnd(TestDelegate::class, { }, { },
1572                                                     object : TestDelegate {
1573             @AssertCalled(count = 1)
1574             override fun onDelegate(foo: String, bar: String): Int {
1575                 return 42
1576             }
1577         })
1578         sessionRule.performTestEndCheck()
1579     }
1580 
1581     @Test fun addExternalDelegateDuringNextWait() {
1582         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1583         sessionRule.waitForPageStop()
1584 
1585         var delegate: Runnable? = null
1586 
1587         sessionRule.addExternalDelegateDuringNextWait(Runnable::class,
1588                                                       { newDelegate -> delegate = newDelegate },
1589                                                       { delegate = null }, Runnable { })
1590 
1591         assertThat("Delegate should be registered", delegate, notNullValue())
1592         delegate?.run()
1593 
1594         mainSession.reload()
1595         mainSession.waitForPageStop()
1596         mainSession.forCallbacksDuringWait(Runnable @AssertCalled(count = 1) {})
1597 
1598         assertThat("Delegate should be unregistered after wait", delegate, nullValue())
1599     }
1600 
1601     @Test fun addExternalDelegateDuringNextWait_hasPrecedence() {
1602         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
1603         sessionRule.waitForPageStop()
1604 
1605         var delegate: TestDelegate? = null
1606         val register = { newDelegate: TestDelegate -> delegate = newDelegate }
1607         val unregister = { _: TestDelegate -> delegate = null }
1608 
1609         sessionRule.addExternalDelegateDuringNextWait(TestDelegate::class, register, unregister,
1610                 object : TestDelegate {
1611                     @AssertCalled(count = 1)
1612                     override fun onDelegate(foo: String, bar: String): Int {
1613                         return 24
1614                     }
1615                 })
1616 
1617         sessionRule.addExternalDelegateUntilTestEnd(TestDelegate::class, register, unregister,
1618                 object : TestDelegate {
1619                     @AssertCalled(count = 1)
1620                     override fun onDelegate(foo: String, bar: String): Int {
1621                         return 42
1622                     }
1623                 })
1624 
1625         assertThat("Wait delegate should be registered", delegate, notNullValue())
1626         assertThat("Wait delegate return value should be correct",
1627                 delegate?.onDelegate("", ""), equalTo(24))
1628 
1629         mainSession.reload()
1630         mainSession.waitForPageStop()
1631 
1632         assertThat("Test delegate should still be registered", delegate, notNullValue())
1633         assertThat("Test delegate return value should be correct",
1634                 delegate?.onDelegate("", ""), equalTo(42))
1635         sessionRule.performTestEndCheck()
1636     }
1637 
1638     @IgnoreCrash
1639     @Test fun contentCrashIgnored() {
1640         assumeThat(sessionRule.env.isMultiprocess, equalTo(true))
1641 
1642         mainSession.loadUri(CONTENT_CRASH_URL)
1643         mainSession.waitUntilCalled(object : Callbacks.ContentDelegate {
1644             @AssertCalled(count = 1)
1645             override fun onCrash(session: GeckoSession) = Unit
1646         })
1647     }
1648 
1649     @Test(expected = ChildCrashedException::class)
1650     fun contentCrashFails() {
1651         assumeThat(sessionRule.env.isMultiprocess, equalTo(true))
1652         assumeThat(sessionRule.env.shouldShutdownOnCrash(), equalTo(false))
1653 
1654         sessionRule.session.loadUri(CONTENT_CRASH_URL)
1655         sessionRule.waitForPageStop()
1656     }
1657 
1658     @Test fun waitForResult() {
1659         val handler = Handler(Looper.getMainLooper())
1660         val result = object : GeckoResult<Int>() {
1661             init {
1662                 handler.postDelayed({
1663                     complete(42)
1664                 }, 100)
1665             }
1666         }
1667 
1668         val value = sessionRule.waitForResult(result)
1669         assertThat("Value should match", value, equalTo(42))
1670     }
1671 
1672     @Test(expected = IllegalStateException::class)
1673     fun waitForResultExceptionally() {
1674         val handler = Handler(Looper.getMainLooper())
1675         val result = object : GeckoResult<Int>() {
1676             init {
1677                 handler.postDelayed({
1678                     completeExceptionally(IllegalStateException("boom"))
1679                 }, 100)
1680             }
1681         }
1682 
1683         sessionRule.waitForResult(result)
1684     }
1685 }
1686