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