blob: 7694cfddcbb319a30550ff181e075d71b536905e [file] [log] [blame]
#!/usr/bin/env python2
# Copyright 2017 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Unit tests for UI module."""
from __future__ import print_function
import json
import Queue
import random
import re
import sys
import unittest
import mock
from six import assertRegex
from six import iteritems
from six.moves import xrange
import factory_common # pylint: disable=unused-import
from cros.factory.test import event as test_event
from cros.factory.test import state
from cros.factory.test import test_ui
from cros.factory.unittest_utils import mock_time_utils
from cros.factory.utils import type_utils
_MOCK_TEST = 'mock.test'
_MOCK_INVOCATION = 'mock-invocation'
_EventType = test_event.Event.Type
class EventLoopTestBase(unittest.TestCase):
def setUp(self):
self._patchers = []
self._timeline = mock_time_utils.TimeLine()
self._patchers.extend(mock_time_utils.MockAll(self._timeline))
mock_session = self._CreatePatcher(test_ui, 'session')
mock_session.GetCurrentTestPath.return_value = _MOCK_TEST
mock_session.GetCurrentTestInvocation.return_value = _MOCK_INVOCATION
self.mock_logging = self._CreatePatcher(test_ui, 'logging')
self._handler_exceptions = []
self._event_callback = None
self.event_client = mock.Mock(spec=test_event.BlockingEventClient)
self.event_client.is_closed.return_value = False
self.event_loop = test_ui.EventLoop(
self._RecordException, event_client_class=self._GetMockEventClient)
def _CreatePatcher(self, *args, **kwargs):
patcher = mock.patch.object(*args, **kwargs)
self._patchers.append(patcher)
return patcher.start()
def _GetMockEventClient(self, callback):
self._event_callback = callback
return self.event_client
def _RecordException(self):
self._handler_exceptions.append(sys.exc_info()[1])
def tearDown(self):
for patcher in self._patchers:
patcher.stop()
def AssertTestUIEvent(self, event):
self.assertEqual(_EventType.TEST_UI_EVENT, event.type)
self.assertEqual(_MOCK_TEST, event.test)
self.assertEqual(_MOCK_INVOCATION, event.invocation)
class EventLoopTest(EventLoopTestBase):
def testPostEvent(self):
self.event_loop.PostEvent(
test_event.Event(_EventType.TEST_UI_EVENT, data='data'))
self.event_client.post_event.assert_called_once()
posted_event = self.event_client.post_event.call_args[0][0]
self.AssertTestUIEvent(posted_event)
self.assertEqual('data', posted_event.data)
def testPostNewEvent(self):
self.event_loop.PostNewEvent(_EventType.TEST_UI_EVENT, data='data')
self.event_client.post_event.assert_called_once()
posted_event = self.event_client.post_event.call_args[0][0]
self.AssertTestUIEvent(posted_event)
self.assertEqual('data', posted_event.data)
def _MockNewEvent(self, event_type=_EventType.TEST_UI_EVENT, **kwargs):
kwargs.setdefault('test', _MOCK_TEST)
kwargs.setdefault('invocation', _MOCK_INVOCATION)
self._event_callback(test_event.Event(event_type, **kwargs))
def testHandleEvent(self):
def _Handler(name, event):
self.AssertTestUIEvent(event)
received_data.append((name, event.data))
self.event_loop.AddEventHandler(
'type1', lambda event: _Handler('handler1', event))
self.event_loop.AddEventHandler(
'type1', lambda event: _Handler('handler2', event))
self.event_loop.AddEventHandler(
'type2', lambda event: _Handler('handler3', event))
received_data = []
self._MockNewEvent(subtype='type1', data='data')
self.assertEqual([('handler1', 'data'), ('handler2', 'data')],
received_data)
received_data = []
self._MockNewEvent(subtype='type2', data='data')
self.assertEqual([('handler3', 'data')], received_data)
received_data = []
self._MockNewEvent(subtype='type3', data='data')
self.assertEqual([], received_data)
# Wrong event type
received_data = []
self._MockNewEvent(
event_type=_EventType.END_EVENT_LOOP, subtype='type1')
self.assertEqual([], received_data)
# Wrong test or invocation.
received_data = []
self._MockNewEvent(test='footest', subtype='type1')
self.assertEqual([], received_data)
received_data = []
self._MockNewEvent(invocation='fooinvocation', subtype='type1')
self.assertEqual([], received_data)
self.event_loop.RemoveEventHandler('type1')
self.event_loop.RemoveEventHandler('type3')
received_data = []
self._MockNewEvent(subtype='type1', data='data')
self._MockNewEvent(subtype='type2', data='data')
self.assertEqual([('handler3', 'data')], received_data)
def testHandleEventException(self):
def _Handler(event):
del event # Unused.
raise RuntimeError('Some unexpected error.')
self.event_loop.AddEventHandler('type1', _Handler)
self._MockNewEvent(_EventType.TEST_UI_EVENT, subtype='type1', data='data')
self.assertTrue(self._handler_exceptions)
self.assertIsInstance(self._handler_exceptions[0], RuntimeError)
def testHandleEventTimeLimit(self):
def _Handler(event):
del event # Unused.
self._timeline.AdvanceTime(10)
self.event_loop.AddEventHandler('type1', _Handler)
self._MockNewEvent(_EventType.TEST_UI_EVENT, subtype='type1', data='data')
self.mock_logging.warn.assert_called_once()
assertRegex(self, self.mock_logging.warn.call_args[0][0],
r'The handler .* takes too long to finish')
def testCatchException(self):
self.assertEqual('foo', self.event_loop.CatchException(lambda: 'foo')())
def _Func():
raise RuntimeError('Some unexpected error.')
wrapped_func = self.event_loop.CatchException(_Func)
self.assertEqual('_Func', wrapped_func.__name__)
wrapped_func()
self.assertTrue(self._handler_exceptions)
self.assertIsInstance(self._handler_exceptions[0], RuntimeError)
_PROBE_INTERVAL = 100
class EventLoopRunTest(EventLoopTestBase):
def setUp(self):
super(EventLoopRunTest, self).setUp()
self._fake_event_client_queue = mock_time_utils.FakeQueue(
timeline=self._timeline)
self.event_client.wait.side_effect = self._MockWaitEndEventLoopEvent
# pylint: disable=protected-access
# Change the probing interval to 100 to make all time values integer.
self._original_probe_interval = test_ui._EVENT_LOOP_PROBE_INTERVAL
test_ui._EVENT_LOOP_PROBE_INTERVAL = _PROBE_INTERVAL
def tearDown(self):
super(EventLoopRunTest, self).tearDown()
# pylint: disable=protected-access
test_ui._EVENT_LOOP_PROBE_INTERVAL = self._original_probe_interval
def _MockWaitEndEventLoopEvent(self, condition, timeout):
try:
end_time = self._timeline.GetTime() + timeout
while True:
event = self._fake_event_client_queue.get(
timeout=end_time - self._timeline.GetTime())
self._event_callback(event)
if condition(event):
return event
except Queue.Empty:
return None
def _MockEndEventLoopEvent(self,
event_time,
status=state.TestState.PASSED,
**kwargs):
event = test_event.Event(
_EventType.END_EVENT_LOOP,
test=_MOCK_TEST,
invocation=_MOCK_INVOCATION,
status=status,
**kwargs)
self._timeline.AddEvent(
event_time, lambda: self._fake_event_client_queue.put(event))
def testRunPassed(self):
self._MockEndEventLoopEvent(10)
self.event_loop.Run()
def testRunFailed(self):
self._MockEndEventLoopEvent(
10, status=state.TestState.FAILED, error_msg='test failed.')
end_event = self.event_loop.Run()
self.assertEqual(end_event.type, _EventType.END_EVENT_LOOP)
self.assertEqual(end_event.status, state.TestState.FAILED)
self.assertEqual(end_event.error_msg, 'test failed.')
def testAddTimedHandler(self):
"""Test add a timed non-repeating handler.
Time line:
95: AddTimedHandler is called with time_sec = 10.
105: The handler is called.
120: Event loop ends.
"""
def _Handler():
self._timeline.AssertTimeAt(105)
self._timeline.AddEvent(
95, lambda: self.event_loop.AddTimedHandler(_Handler, 10))
self._MockEndEventLoopEvent(120)
self.event_loop.Run()
def testAddTimedHandlerRepeating(self):
"""Test add a timed repeating handler.
Time line:
10: AddTimedHandler is called with time_sec = 30, repeat = True.
100: The timed handler is checked and called.
100, 130, 160, 190, 220: The handler is called.
240: Event loop ends.
"""
called_times = []
def _Handler():
called_times.append(self._timeline.GetTime())
self._timeline.AddEvent(
10, lambda: self.event_loop.AddTimedHandler(_Handler, 30, repeat=True))
self._MockEndEventLoopEvent(240)
self.event_loop.Run()
expected_times = [100, 130, 160, 190, 220]
self.assertEqual(expected_times, called_times)
def testAddTimedHandlerRepeatingLongTimeout(self):
"""Test add a timed repeating handler.
Time line:
10: AddTimedHandler is called with time_sec = 330, repeat = True.
100: The timed handler is checked and called.
100, 430, 760, 1090: The handler is called.
1100: Event loop ends.
"""
called_times = []
def _Handler():
called_times.append(self._timeline.GetTime())
self._timeline.AddEvent(
10, lambda: self.event_loop.AddTimedHandler(_Handler, 330, repeat=True))
self._MockEndEventLoopEvent(1100)
self.event_loop.Run()
expected_times = [100, 430, 760, 1090]
self.assertEqual(expected_times, called_times)
def testAddTimedHandlerStopIteration(self):
"""Test add a timed repeating handler that raises StopIteration.
Time line:
10: AddTimedHandler is called with time_sec = 30, repeat = True.
100: The timed handler is checked and executed.
100, 130: The handler is called.
160: The handler is called and StopIteration is raised.
240: Event loop ends.
"""
called_times = []
def _Handler():
t = self._timeline.GetTime()
called_times.append(t)
if t > 150:
raise StopIteration
self._timeline.AddEvent(
10, lambda: self.event_loop.AddTimedHandler(_Handler, 30, repeat=True))
self._MockEndEventLoopEvent(240)
self.event_loop.Run()
expected_times = [100, 130, 160]
self.assertEqual(expected_times, called_times)
def testAddTimedIterator(self):
"""Test add a timed iterator.
Time line:
10: AddTimedIterable is called with time_sec = 30.
100: The timed iterator is checked.
100, 130, 160, 190, 220: The iterator is called.
300: Event loop ends.
"""
called_times = []
def _Iterator():
for unused_i in range(5):
called_times.append(self._timeline.GetTime())
yield
self._timeline.AddEvent(
10, lambda: self.event_loop.AddTimedIterable(_Iterator(), 30))
self._MockEndEventLoopEvent(300)
self.event_loop.Run()
expected_times = [100, 130, 160, 190, 220]
self.assertEqual(expected_times, called_times)
def testAddMultiple(self):
"""Test add multiple random timed events and event handler."""
TOTAL_TIME = 1000
calls = {}
called_times = []
def _Log(name):
calls.setdefault(name, []).append(self._timeline.GetTime())
called_times.append(self._timeline.GetTime())
def _MockEvent(subtype):
self._fake_event_client_queue.put(
test_event.Event(
_EventType.TEST_UI_EVENT,
test=_MOCK_TEST,
invocation=_MOCK_INVOCATION,
subtype=subtype))
self.event_loop.AddEventHandler(
'type1', lambda event: _Log('handler1'))
self.event_loop.AddEventHandler(
'type2', lambda event: _Log('handler2'))
expected_calls = {}
def _AddRandomEvent():
event_type = random.randint(1, 3)
event_time = random.randrange(TOTAL_TIME)
self._timeline.AddEvent(
event_time, lambda: _MockEvent('type%d' % event_type))
if event_type != 3:
expected_calls.setdefault('handler%d' % event_type,
[]).append(event_time)
for unused_i in range(300):
_AddRandomEvent()
# Non-repeating timed handler.
def _AddRandomHandler():
event_time = random.randrange(TOTAL_TIME)
event_delay = random.randrange(TOTAL_TIME)
name = 'timed-%d-%d' % (event_time, event_delay)
self._timeline.AddEvent(event_time,
(lambda: self.event_loop.AddTimedHandler(
lambda: _Log(name), event_delay)))
expected_time = event_time + event_delay
expected_calls.setdefault(name, []).append(expected_time)
for unused_i in range(300):
_AddRandomHandler()
# Repeating timed handler.
def _AddRandomRepeatingHandler():
event_time = random.randrange(TOTAL_TIME)
event_delay = random.randrange(TOTAL_TIME)
name = 'timed-repeat-%d-%d' % (event_time, event_delay)
self._timeline.AddEvent(
event_time, (lambda: self.event_loop.AddTimedHandler(
lambda: _Log(name), event_delay, repeat=True)))
expected_times = expected_calls.setdefault(name, [])
while event_time <= TOTAL_TIME:
expected_times.append(event_time)
event_time += event_delay
for unused_i in range(100):
_AddRandomRepeatingHandler()
# Repeating timed iterable.
def _AddRandomRepeatingIterable():
event_time = random.randrange(TOTAL_TIME)
event_delay = random.randrange(TOTAL_TIME)
iter_count = random.randrange(1, 30)
name = 'iter-repeat-%d-%d-%d' % (event_time, event_delay, iter_count)
def _Iterator():
for unused_i in xrange(iter_count):
_Log(name)
yield
self._timeline.AddEvent(
event_time, (lambda: self.event_loop.AddTimedIterable(
_Iterator(), event_delay)))
expected_calls.setdefault(name, []).extend(
[event_time + event_delay * i for i in xrange(iter_count)])
for unused_i in range(100):
_AddRandomRepeatingIterable()
self._MockEndEventLoopEvent(TOTAL_TIME)
self.event_loop.Run()
self.assertEqual(sorted(called_times), called_times)
for name, expected_times in iteritems(expected_calls):
expected_times = sorted(expected_times)
actual_times = calls.get(name, [])
if name.startswith('handler'):
# Event handler should be at correct time.
self.assertEqual(expected_times, actual_times)
else:
self.assertLessEqual(len(actual_times), len(expected_times))
for i, expected_time in enumerate(expected_times):
if i < len(actual_times):
# Timed handler may be delayed by at most _PROBE_INTERVAL.
self.assertGreaterEqual(actual_times[i], expected_time)
self.assertLess(actual_times[i], expected_time + _PROBE_INTERVAL)
else:
# The call happens after TOTAL_TIME.
self.assertGreater(expected_time + _PROBE_INTERVAL, TOTAL_TIME)
class EnsureI18nTest(unittest.TestCase):
def setUp(self):
self.patcher = mock.patch.object(test_ui.i18n_test_ui, 'MakeI18nLabel')
fake_make_i18n_label = self.patcher.start()
def FakeMakeI18nLabel(text):
items = ('%s=%s' % (key, value) for key, value in sorted(text.items()))
return '[%s]' % ','.join(items)
fake_make_i18n_label.side_effect = FakeMakeI18nLabel
def tearDown(self):
self.patcher.stop()
def testBasic(self):
self.assertEqual('meow', test_ui.EnsureI18n('meow'))
self.assertEqual('123456', test_ui.EnsureI18n(123456))
self.assertEqual(u'\u260e', test_ui.EnsureI18n(u'\u260e'))
def testList(self):
self.assertEqual('', test_ui.EnsureI18n([]))
self.assertEqual('foo', test_ui.EnsureI18n(['foo']))
self.assertEqual('foobar', test_ui.EnsureI18n(['foo', 'bar']))
self.assertEqual('csie217', test_ui.EnsureI18n(['csie', 217]))
self.assertEqual('<a>b=0,<b>d=1;alert(1)</b></a>',
test_ui.EnsureI18n([
'<a>', ['b', '=', 0], ',<b>',
['d', '=', '1;alert(1)', []], '</b>', '</a>'
]))
def testI18n(self):
self.assertEqual('[en=a,zh=b]', test_ui.EnsureI18n({'en': 'a', 'zh': 'b'}))
self.assertEqual('<a>[en=a,zh=b]</a>',
test_ui.EnsureI18n(
['<a>', {'en': 'a', 'zh': 'b'}, '</a>']))
self.assertEqual('x=[en=a,zh=b],y=[en=c,zh=d],z=1',
test_ui.EnsureI18n([
['x=', {'en': 'a', 'zh': 'b'}], ',',
['y=', {'zh': 'd', 'en': 'c'}], ',', ['z=', 1]]))
_MOCK_HTML = 'mock-html'
_MOCK_ID = 'mock-id'
class UITestBase(unittest.TestCase):
def setUp(self):
self._event_loop = mock.Mock()
def AssertRunJSEqual(self, js, event_js, event_args):
"""An ad-hoc function to assert two JavaScript snippets are equal.
This is done by use simple substitute to inline all arguments, and remove
all spaces in both JavaScript, then compare the results.
Args:
js: The target JavaScript to be executed.
event_js: The JavaScript from the event.
event_args: Arguments for the JavaScript.
"""
strip_js = re.sub(r'\s', '', js)
strip_event_js = event_js
for name, arg in iteritems(event_args):
strip_event_js = re.sub(r'\b%s\b' % re.escape('args.%s' % name),
json.dumps(arg), strip_event_js)
strip_event_js = re.sub(r'\s', '', strip_event_js)
self.assertEqual(
strip_js, strip_event_js,
'RunJS event not equal, js = %r, event_js = %r, event_args = %r' %
(js, event_js, event_args))
def AssertEventsPosted(self, *events):
called_events = self._event_loop.PostNewEvent.call_args_list
self.assertEqual(len(called_events), len(events))
for (args, kwargs), (target_type, target_kwargs) in zip(
called_events, events):
event_type = args[0]
self.assertEqual(event_type, target_type)
if event_type == _EventType.RUN_JS:
# Use ad-hoc method of comparing RUN_JS events.
self.AssertRunJSEqual(target_kwargs['js'], kwargs['js'], kwargs['args'])
else:
self.assertEqual(kwargs, target_kwargs)
def ResetEventsPosted(self):
"""Reset the list of posted events."""
self._event_loop.PostNewEvent.reset_mock()
def _SetHTMLEvent(self, **kwargs):
event = {
'html': _MOCK_HTML,
'id': _MOCK_ID,
'append': False,
'autoscroll': False
}
event.update(kwargs)
return (_EventType.SET_HTML, event)
def _RunJSEvent(self, js):
return (_EventType.RUN_JS, {'js': js})
class UITest(UITestBase):
def setUp(self):
super(UITest, self).setUp()
self._ui = test_ui.UI(event_loop=self._event_loop)
def testSetHTML(self):
self._ui.SetHTML(_MOCK_HTML, id=_MOCK_ID)
self._ui.SetHTML(_MOCK_HTML, id=_MOCK_ID, append=True)
self._ui.SetHTML(_MOCK_HTML, id=_MOCK_ID, autoscroll=True)
self.AssertEventsPosted(
self._SetHTMLEvent(),
self._SetHTMLEvent(append=True),
self._SetHTMLEvent(autoscroll=True))
def testAppendHTML(self):
self._ui.AppendHTML(_MOCK_HTML, id=_MOCK_ID)
self._ui.AppendHTML(_MOCK_HTML, id=_MOCK_ID, autoscroll=True)
self.AssertEventsPosted(
self._SetHTMLEvent(append=True),
self._SetHTMLEvent(append=True, autoscroll=True))
def testAppendCSS(self):
_MOCK_CSS = 'mock-css'
self._ui.AppendCSS(_MOCK_CSS)
self.AssertEventsPosted(
self._SetHTMLEvent(
html='<style type="text/css">%s</style>' % _MOCK_CSS,
id='head',
append=True))
def testAppendCSSLink(self):
_MOCK_CSS_LINK = 'mock-css-link'
self._ui.AppendCSSLink(_MOCK_CSS_LINK)
self.AssertEventsPosted(
self._SetHTMLEvent(
html='<link rel="stylesheet" type="text/css" href="%s">' %
_MOCK_CSS_LINK,
id='head',
append=True))
def testRunJS(self):
self._ui.RunJS('alert(1);')
self._ui.RunJS('alert(args.msg);', msg='foobar')
self.AssertEventsPosted(
self._RunJSEvent('alert(1);'),
self._RunJSEvent('alert("foobar");'))
def testCallJSFunction(self):
self._ui.CallJSFunction('test.alert', '123')
self.AssertEventsPosted(self._RunJSEvent('test.alert("123")'))
def testInitJSTestObject(self):
js_object = self._ui.InitJSTestObject('someTest', 2, 1, 7)
js_object.Hopping()
js_object.Jump()
self.AssertEventsPosted(
self._RunJSEvent('window.testObject = new someTest(...[2, 1, 7])'),
self._RunJSEvent('window.testObject.hopping()'),
self._RunJSEvent('window.testObject.jump()'))
def testBindStandardKeys(self):
self._ui.BindStandardKeys()
self._ui.BindStandardPassKeys()
self._ui.BindStandardFailKeys()
self.AssertEventsPosted(
self._RunJSEvent('test.bindStandardKeys()'),
self._RunJSEvent('test.bindStandardPassKeys()'),
self._RunJSEvent('test.bindStandardFailKeys()'))
def testBindKeyJS(self):
self._ui.BindKeyJS('A', 'a()')
self._ui.BindKeyJS('B', 'b()', once=True)
self._ui.BindKeyJS(
test_ui.ENTER_KEY, 'onEnter()', once=True, virtual_key=False)
self.AssertEventsPosted(
self._RunJSEvent('test.bindKey("A", (event) => { a() }, false, true)'),
self._RunJSEvent('test.bindKey("B", (event) => { b() }, true, true)'),
self._RunJSEvent(
'test.bindKey("ENTER", (event) => { onEnter() }, true, false)'))
def testBindKey(self):
def _Handler(event):
del event # Unused.
self._ui.BindKey('U', _Handler)
self._event_loop.AddEventHandler.assert_called_once()
uuid, handler = self._event_loop.AddEventHandler.call_args[0]
self.assertEqual(_Handler, handler)
self.AssertEventsPosted(
self._RunJSEvent(
'test.bindKey("U", (event) => { test.sendTestEvent("%s", '
'{}); }, false, true)' % uuid))
def testUnbindKey(self):
self._ui.UnbindKey('A')
self._ui.UnbindAllKeys()
self.AssertEventsPosted(
self._RunJSEvent('test.unbindKey("A")'),
self._RunJSEvent('test.unbindAllKeys()'))
def testPlayAudio(self):
self._ui.PlayAudioFile('a.mp4')
self.AssertEventsPosted(
self._RunJSEvent("""
const audioElement = new Audio("/sounds/a.mp4");
audioElement.addEventListener(
"canplaythrough", () => { audioElement.play(); });
"""))
def testSetFocus(self):
self._ui.SetFocus('main')
self.AssertEventsPosted(
self._RunJSEvent('document.getElementById("main").focus()'))
def testSetSelected(self):
self._ui.SetSelected('main')
self.AssertEventsPosted(
self._RunJSEvent('document.getElementById("main").select()'))
def testAlert(self):
self._ui.Alert('1')
self.AssertEventsPosted(self._RunJSEvent('test.alert("1")'))
def testShowHideElement(self):
self._ui.HideElement('main')
self._ui.ShowElement('main')
self.AssertEventsPosted(
self._RunJSEvent(
'document.getElementById("main").style.display = "none"'),
self._RunJSEvent(
'document.getElementById("main").style.display = "initial"'))
def testImportHTML(self):
self._ui.ImportHTML('fragment.html')
self.AssertEventsPosted((_EventType.IMPORT_HTML, {'url': 'fragment.html'}))
def testToggleClass(self):
self._ui.ToggleClass('id', 'class')
self._ui.ToggleClass('id', 'class', True)
self._ui.ToggleClass('id', 'class', False)
self.AssertEventsPosted(
self._RunJSEvent(
'document.getElementById("id").classList.toggle("class")'),
self._RunJSEvent(
'document.getElementById("id").classList.toggle("class", true)'),
self._RunJSEvent(
'document.getElementById("id").classList.toggle("class", false)'))
class UIKeyTest(unittest.TestCase):
def setUp(self):
self._event_loop = mock.Mock()
self._ui = test_ui.UI(event_loop=self._event_loop)
self._patchers = []
self._timeline = mock_time_utils.TimeLine()
self._patchers.extend(mock_time_utils.MockAll(self._timeline))
self.key_callbacks = {}
self._CreatePatcher(self._ui, 'BindKey').side_effect = self._StubBindKey
self._CreatePatcher(self._ui, 'UnbindKey').side_effect = self._StubUnbindKey
self._CreatePatcher(test_ui.threading,
'current_thread')().name = 'other_thread'
def _StubBindKey(self, key, callback):
self.key_callbacks[key] = callback
def _StubUnbindKey(self, key):
del self.key_callbacks[key]
def _CreatePatcher(self, *args, **kwargs):
patcher = mock.patch.object(*args, **kwargs)
self._patchers.append(patcher)
return patcher.start()
def tearDown(self):
for patcher in self._patchers:
patcher.stop()
def _SimulateKeyPress(self, key):
if key in self.key_callbacks:
self.key_callbacks[key](None)
def testWaitKeysOnce(self):
self._timeline.AddEvent(
3, lambda: self._SimulateKeyPress(test_ui.ENTER_KEY))
self.assertEqual(test_ui.ENTER_KEY,
self._ui.WaitKeysOnce(test_ui.ENTER_KEY))
self.assertFalse(self.key_callbacks)
self._timeline.AssertTimeAt(3)
def testWaitKeysOnceTimeout(self):
self._timeline.AddEvent(
3, lambda: self._SimulateKeyPress(test_ui.ENTER_KEY))
self.assertEqual(test_ui.ENTER_KEY,
self._ui.WaitKeysOnce(test_ui.ENTER_KEY, timeout=5))
self.assertFalse(self.key_callbacks)
self._timeline.AssertTimeAt(3)
def testWaitKeysOnceTimeoutNoKey(self):
self._timeline.AddEvent(
10, lambda: self._SimulateKeyPress(test_ui.ENTER_KEY))
self.assertIsNone(self._ui.WaitKeysOnce(test_ui.ENTER_KEY, timeout=5))
self.assertFalse(self.key_callbacks)
self._timeline.AssertTimeAt(5)
def testWaitKeysOnceMultipleKey(self):
self._timeline.AddEvent(
2, lambda: self._SimulateKeyPress('A'))
self._timeline.AddEvent(
1, lambda: self._SimulateKeyPress('B'))
self._timeline.AddEvent(
7, lambda: self._SimulateKeyPress('C'))
self.assertEqual('B', self._ui.WaitKeysOnce(['A', 'B', 'C']))
self.assertFalse(self.key_callbacks)
self._timeline.AssertTimeAt(1)
def testWaitKeysOnceMultipleKeyTimeout(self):
self._timeline.AddEvent(
2, lambda: self._SimulateKeyPress('A'))
self._timeline.AddEvent(
1, lambda: self._SimulateKeyPress('B'))
self._timeline.AddEvent(
7, lambda: self._SimulateKeyPress('C'))
self.assertEqual('B', self._ui.WaitKeysOnce(['A', 'B', 'C'], timeout=10))
self.assertFalse(self.key_callbacks)
self._timeline.AssertTimeAt(1)
def testWaitKeysOnceMultipleKeyTimeoutNoKey(self):
self._timeline.AddEvent(
2, lambda: self._SimulateKeyPress('A'))
self._timeline.AddEvent(
7, lambda: self._SimulateKeyPress('B'))
self._timeline.AddEvent(
4, lambda: self._SimulateKeyPress('C'))
self.assertIsNone(self._ui.WaitKeysOnce(['A', 'B', 'C'], timeout=1))
self.assertFalse(self.key_callbacks)
self._timeline.AssertTimeAt(1)
class StandardUITest(UITestBase):
def setUp(self):
super(StandardUITest, self).setUp()
self._ui = test_ui.StandardUI(event_loop=self._event_loop)
self._patchers = []
self._timeline = mock_time_utils.TimeLine()
self._patchers.extend(mock_time_utils.MockAll(self._timeline))
self._import_template_event = (_EventType.IMPORT_HTML, {
'url': '/templates.html'
})
def tearDown(self):
super(StandardUITest, self).tearDown()
for patcher in self._patchers:
patcher.stop()
def testSetTitle(self):
self._ui.SetTitle('some title')
self.AssertEventsPosted(
self._import_template_event,
self._RunJSEvent('window.template.setTitle("some title")'))
def testSetState(self):
self._ui.SetState('some state')
self._ui.SetState('some state', append=True)
self.AssertEventsPosted(
self._import_template_event,
self._RunJSEvent('window.template.setState("some state", false)'),
self._RunJSEvent('window.template.setState("some state", true)'))
def testSetInstruction(self):
self._ui.SetInstruction('something')
self.AssertEventsPosted(
self._import_template_event,
self._RunJSEvent('window.template.setInstruction("something")'))
def testDrawProgressBar(self):
self._ui.DrawProgressBar()
self._ui.DrawProgressBar(217)
self.AssertEventsPosted(
self._import_template_event,
self._RunJSEvent('window.template.drawProgressBar(1)'),
self._RunJSEvent('window.template.drawProgressBar(217)'))
def testAdvanceProgress(self):
self._ui.AdvanceProgress()
self.AssertEventsPosted(
self._import_template_event,
self._RunJSEvent('window.template.advanceProgress()'))
def testSetProgress(self):
self._ui.SetProgress(100)
self._ui.SetProgress(0.5)
self.AssertEventsPosted(
self._import_template_event,
self._RunJSEvent('window.template.setProgress(100)'),
self._RunJSEvent('window.template.setProgress(0.5)'))
def testSetTimerValue(self):
self._ui.SetTimerValue(10)
self.AssertEventsPosted(
self._import_template_event,
self._RunJSEvent('window.template.setTimerValue(10)'))
def testHideTimer(self):
self._ui.HideTimer()
self.AssertEventsPosted(
self._import_template_event,
self._RunJSEvent('window.template.hideTimer("timer")'))
def testSetView(self):
self._ui.SetView('view')
self.AssertEventsPosted(
self._import_template_event,
self._RunJSEvent('window.template.setView("view")'))
def testToggleTemplateClass(self):
self._ui.ToggleTemplateClass('class')
self._ui.ToggleTemplateClass('class', True)
self._ui.ToggleTemplateClass('class', False)
self.AssertEventsPosted(
self._import_template_event,
self._RunJSEvent('window.template.classList.toggle("class")'),
self._RunJSEvent('window.template.classList.toggle("class", true)'),
self._RunJSEvent('window.template.classList.toggle("class", false)'))
def testStartCountdownTimer(self):
_TIMEOUT = 5
_handler = mock.Mock()
_handler.side_effect = lambda: self.assertEqual(self._timeline.GetTime(),
_TIMEOUT)
self._ui.StartCountdownTimer(_TIMEOUT, _handler)
self._event_loop.AddTimedIterable.assert_called_once()
iterable = self._event_loop.AddTimedIterable.call_args[0][0]
while True:
self.ResetEventsPosted()
try:
next(iterable)
except StopIteration:
break
self.AssertEventsPosted(
self._RunJSEvent('window.template.setTimerValue(%d)' % (
_TIMEOUT - self._timeline.GetTime())))
self._timeline.AdvanceTime(1)
self.AssertEventsPosted(self._RunJSEvent(
'window.template.hideTimer("timer")'))
_handler.assert_called_once()
def testStartCountdownTimerStopEvent(self):
_TIMEOUT = 5
_STOP_TIME = 3
_handler = mock.Mock()
stop_event = self._ui.StartCountdownTimer(_TIMEOUT, _handler)
self._event_loop.AddTimedIterable.assert_called_once()
iterable = self._event_loop.AddTimedIterable.call_args[0][0]
while True:
self.ResetEventsPosted()
try:
next(iterable)
except StopIteration:
break
self.AssertEventsPosted(
self._RunJSEvent('window.template.setTimerValue(%d)' % (
_TIMEOUT - self._timeline.GetTime())))
self._timeline.AdvanceTime(1)
if self._timeline.GetTime() >= _STOP_TIME:
stop_event.set()
self.AssertEventsPosted(self._RunJSEvent(
'window.template.hideTimer("timer")'))
_handler.assert_not_called()
def testStartFailingCountdownTimer(self):
_TIMEOUT = 5
self._ui.StartFailingCountdownTimer(_TIMEOUT)
self._event_loop.AddTimedIterable.assert_called_once()
iterable = self._event_loop.AddTimedIterable.call_args[0][0]
while True:
self.ResetEventsPosted()
try:
next(iterable)
except type_utils.TestFailure as e:
assertRegex(self, str(e), r'^Timed out')
break
self.AssertEventsPosted(
self._RunJSEvent('window.template.setTimerValue(%d)' % (
_TIMEOUT - self._timeline.GetTime())))
self._timeline.AdvanceTime(1)
self.AssertEventsPosted()
if __name__ == '__main__':
random.seed(0)
unittest.main()