summaryrefslogtreecommitdiff
path: root/crocoite/test_controller.py
diff options
context:
space:
mode:
Diffstat (limited to 'crocoite/test_controller.py')
-rw-r--r--crocoite/test_controller.py203
1 files changed, 203 insertions, 0 deletions
diff --git a/crocoite/test_controller.py b/crocoite/test_controller.py
new file mode 100644
index 0000000..7216a42
--- /dev/null
+++ b/crocoite/test_controller.py
@@ -0,0 +1,203 @@
+# Copyright (c) 2017–2018 crocoite contributors
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+import asyncio
+
+from yarl import URL
+from aiohttp import web
+
+import pytest
+
+from .logger import Logger
+from .controller import ControllerSettings, SinglePageController, SetEntry, \
+ IdleStateTracker
+from .browser import PageIdle
+from .devtools import Process
+from .test_browser import loader
+
+@pytest.mark.asyncio
+async def test_controller_timeout ():
+ """ Make sure the controller terminates, even if the site keeps reloading/fetching stuff """
+
+ async def f (req):
+ return web.Response (body="""<html>
+<body>
+<p>hello</p>
+<script>
+window.setTimeout (function () { window.location = '/' }, 250);
+window.setInterval (function () { fetch('/').then (function (e) { console.log (e) }) }, 150);
+</script>
+</body>
+</html>""", status=200, content_type='text/html', charset='utf-8')
+
+ url = URL.build (scheme='http', host='localhost', port=8080)
+ app = web.Application ()
+ app.router.add_route ('GET', '/', f)
+ runner = web.AppRunner(app)
+ await runner.setup()
+ site = web.TCPSite(runner, url.host, url.port)
+ await site.start()
+
+ loop = asyncio.get_event_loop ()
+ try:
+ logger = Logger ()
+ settings = ControllerSettings (idleTimeout=1, timeout=5)
+ controller = SinglePageController (url=url, logger=logger,
+ service=Process (), behavior=[], settings=settings)
+ # give the controller a little more time to finish, since there are
+ # hard-coded asyncio.sleep calls in there right now.
+ # XXX fix this
+ before = loop.time ()
+ await asyncio.wait_for (controller.run (), timeout=settings.timeout*2)
+ after = loop.time ()
+ assert after-before >= settings.timeout, (settings.timeout*2, after-before)
+ finally:
+ # give the browser some time to close before interrupting the
+ # connection by destroying the HTTP server
+ await asyncio.sleep (1)
+ await runner.cleanup ()
+
+@pytest.mark.asyncio
+async def test_controller_idle_timeout ():
+ """ Make sure the controller terminates, even if the site keeps reloading/fetching stuff """
+
+ async def f (req):
+ return web.Response (body="""<html>
+<body>
+<p>hello</p>
+<script>
+window.setInterval (function () { fetch('/').then (function (e) { console.log (e) }) }, 2000);
+</script>
+</body>
+</html>""", status=200, content_type='text/html', charset='utf-8')
+
+ url = URL.build (scheme='http', host='localhost', port=8080)
+ app = web.Application ()
+ app.router.add_route ('GET', '/', f)
+ runner = web.AppRunner(app)
+ await runner.setup()
+ site = web.TCPSite(runner, url.host, url.port)
+ await site.start()
+
+ loop = asyncio.get_event_loop ()
+ try:
+ logger = Logger ()
+ settings = ControllerSettings (idleTimeout=1, timeout=60)
+ controller = SinglePageController (url=url, logger=logger,
+ service=Process (), behavior=[], settings=settings)
+ before = loop.time ()
+ await asyncio.wait_for (controller.run (), settings.timeout*2)
+ after = loop.time ()
+ assert settings.idleTimeout <= after-before <= settings.idleTimeout*2+3
+ finally:
+ await runner.cleanup ()
+
+def test_set_entry ():
+ a = SetEntry (1, a=2, b=3)
+ assert a == a
+ assert hash (a) == hash (a)
+
+ b = SetEntry (1, a=2, b=4)
+ assert a == b
+ assert hash (a) == hash (b)
+
+ c = SetEntry (2, a=2, b=3)
+ assert a != c
+ assert hash (a) != hash (c)
+
+@pytest.mark.asyncio
+async def test_idle_state_tracker ():
+ # default is idle
+ loop = asyncio.get_event_loop ()
+ idle = IdleStateTracker (loop)
+ assert idle._idle
+
+ # idle change
+ await idle.push (PageIdle (False))
+ assert not idle._idle
+
+ # nothing happens for other objects
+ await idle.push ({})
+ assert not idle._idle
+
+ # no state change -> wait does not return
+ with pytest.raises (asyncio.TimeoutError):
+ await asyncio.wait_for (idle.wait (0.1), timeout=1)
+
+ # wait at least timeout
+ delta = 0.2
+ timeout = 1
+ await idle.push (PageIdle (True))
+ assert idle._idle
+ start = loop.time ()
+ await idle.wait (timeout)
+ end = loop.time ()
+ assert (timeout-delta) < (end-start) < (timeout+delta)
+
+@pytest.fixture
+async def recordingServer ():
+ """ Simple HTTP server that records raw requests """
+ url = URL ('http://localhost:8080')
+ reqs = []
+ async def record (request):
+ reqs.append (request)
+ return web.Response(text='ok', content_type='text/plain')
+ app = web.Application()
+ app.add_routes([web.get(url.path, record)])
+ runner = web.AppRunner(app)
+ await runner.setup()
+ site = web.TCPSite (runner, url.host, url.port)
+ await site.start()
+ yield url, reqs
+ await runner.cleanup ()
+
+from .test_devtools import tab, browser
+from http.cookies import Morsel, SimpleCookie
+
+@pytest.mark.asyncio
+async def test_set_cookies (tab, recordingServer):
+ """ Make sure cookies are set properly and only affect the domain they were
+ set for """
+
+ logger = Logger ()
+
+ url, reqs = recordingServer
+
+ cookies = []
+ c = Morsel ()
+ c.set ('foo', 'bar', '')
+ c['domain'] = 'localhost'
+ cookies.append (c)
+ c = Morsel ()
+ c.set ('buz', 'beef', '')
+ c['domain'] = 'nonexistent.example'
+
+ settings = ControllerSettings (idleTimeout=1, timeout=60, cookies=cookies)
+ controller = SinglePageController (url=url, logger=logger,
+ service=Process (), behavior=[], settings=settings)
+ await asyncio.wait_for (controller.run (), settings.timeout*2)
+
+ assert len (reqs) == 1
+ req = reqs[0]
+ reqCookies = SimpleCookie (req.headers['cookie'])
+ assert len (reqCookies) == 1
+ c = next (iter (reqCookies.values ()))
+ assert c.key == cookies[0].key
+ assert c.value == cookies[0].value