Merge pull request #272 from CanonicalLtd/mwhudson/testing-blue-sky
add some simple view unit tests and travis integration
This commit is contained in:
commit
ecdc01d0b2
|
@ -0,0 +1,21 @@
|
||||||
|
sudo: required
|
||||||
|
|
||||||
|
services:
|
||||||
|
- docker
|
||||||
|
|
||||||
|
language: bash
|
||||||
|
|
||||||
|
# Travis still doesn't support anything newer than trusty so we pull
|
||||||
|
# docker tricks to run things on Xenial. Maybe we should run things on
|
||||||
|
# bionic or whatever as well but this is a start...
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- docker pull ubuntu:xenial
|
||||||
|
- cid=`docker run --tty --detach --workdir /subiquity -v $(pwd):/subiquity ubuntu:xenial`
|
||||||
|
- docker exec $cid apt-get update
|
||||||
|
- docker exec $cid apt-get -y dist-upgrade
|
||||||
|
- docker exec $cid apt-get install -y --no-install-recommends libnl-3-dev libnl-genl-3-dev libnl-route-3-dev libsystemd-dev python3-distutils-extra pkg-config python3.5 python3-pip git lsb-release python3-setuptools gcc python3-dev python3-wheel
|
||||||
|
- docker exec $cid pip3 install -r requirements.txt
|
||||||
|
- docker exec $cid python3 setup.py build
|
||||||
|
script:
|
||||||
|
- docker exec $cid python3 -m unittest discover
|
|
@ -2,9 +2,11 @@ urwid==1.2.1
|
||||||
nose-cov
|
nose-cov
|
||||||
nose
|
nose
|
||||||
flake8
|
flake8
|
||||||
python3-parted
|
PyYAML
|
||||||
yaml
|
|
||||||
testtools
|
testtools
|
||||||
mock
|
mock
|
||||||
attr
|
attrs
|
||||||
systemd
|
systemd
|
||||||
|
jsonschema
|
||||||
|
pyudev
|
||||||
|
-e git+https://github.com/CanonicalLtd/probert#egg=probert
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from subiquitycore.controller import BaseController
|
from subiquitycore.controller import BaseController
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
"""
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
|
@ -459,3 +460,4 @@ class TestBlockdev(testtools.TestCase):
|
||||||
empty = self.bd.available_partitions
|
empty = self.bd.available_partitions
|
||||||
print(empty)
|
print(empty)
|
||||||
self.assertEqual(len(empty), 1)
|
self.assertEqual(len(empty), 1)
|
||||||
|
"""
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
#
|
|
@ -0,0 +1,49 @@
|
||||||
|
import re
|
||||||
|
|
||||||
|
import urwid
|
||||||
|
|
||||||
|
def find_with_pred(w, pred):
|
||||||
|
def _walk(w):
|
||||||
|
if pred(w):
|
||||||
|
return w
|
||||||
|
if hasattr(w, '_wrapped_widget'):
|
||||||
|
return _walk(w._wrapped_widget)
|
||||||
|
if hasattr(w, 'original_widget'):
|
||||||
|
return _walk(w.original_widget)
|
||||||
|
if isinstance(w, urwid.ListBox):
|
||||||
|
for w in w.body:
|
||||||
|
r = _walk(w)
|
||||||
|
if r:
|
||||||
|
return r
|
||||||
|
elif hasattr(w, 'contents'):
|
||||||
|
contents = w.contents
|
||||||
|
for w, _ in contents:
|
||||||
|
r = _walk(w)
|
||||||
|
if r:
|
||||||
|
return r
|
||||||
|
return _walk(w)
|
||||||
|
|
||||||
|
def find_button_matching(w, pat):
|
||||||
|
def pred(w):
|
||||||
|
return isinstance(w, urwid.Button) and re.match(pat, w.label)
|
||||||
|
return find_with_pred(w, pred)
|
||||||
|
|
||||||
|
def click(but):
|
||||||
|
but._emit('click')
|
||||||
|
|
||||||
|
def keypress(w, key, size=(30, 1)):
|
||||||
|
w.keypress(size, key)
|
||||||
|
|
||||||
|
def get_focus_path(w):
|
||||||
|
path = []
|
||||||
|
while True:
|
||||||
|
path.append(w)
|
||||||
|
if w.focus is not None:
|
||||||
|
w = w.focus
|
||||||
|
elif hasattr(w, '_wrapped_widget'):
|
||||||
|
w = w._wrapped_widget
|
||||||
|
elif hasattr(w, 'original_widget'):
|
||||||
|
w = w.original_widget
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
return path
|
|
@ -0,0 +1,32 @@
|
||||||
|
import unittest
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from subiquitycore.models.identity import IdentityModel
|
||||||
|
from subiquitycore.signals import Signal
|
||||||
|
|
||||||
|
from subiquity.controllers.identity import IdentityController
|
||||||
|
from subiquity.ui.views.identity import IdentityView
|
||||||
|
|
||||||
|
from subiquity.ui.views.tests import helpers
|
||||||
|
|
||||||
|
|
||||||
|
class IdentityViewTests(unittest.TestCase):
|
||||||
|
|
||||||
|
def make_view(self):
|
||||||
|
model = mock.create_autospec(spec=IdentityModel)
|
||||||
|
controller = mock.create_autospec(spec=IdentityController)
|
||||||
|
controller.signal = mock.create_autospec(spec=Signal)
|
||||||
|
return IdentityView(model, controller, {})
|
||||||
|
|
||||||
|
def test_done_initially_disabled(self):
|
||||||
|
view = self.make_view()
|
||||||
|
self.assertFalse(view.form.done_btn.enabled)
|
||||||
|
|
||||||
|
def test_initial_focus(self):
|
||||||
|
view = self.make_view()
|
||||||
|
focus_path = helpers.get_focus_path(view)
|
||||||
|
for w in reversed(focus_path):
|
||||||
|
if w is view.form.realname.widget:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.fail("Realname widget not focus")
|
|
@ -0,0 +1,33 @@
|
||||||
|
import unittest
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
|
||||||
|
from subiquity.controllers.installprogress import InstallProgressController
|
||||||
|
from subiquity.ui.views.installprogress import ProgressView
|
||||||
|
|
||||||
|
from subiquity.ui.views.tests import helpers
|
||||||
|
|
||||||
|
|
||||||
|
class IdentityViewTests(unittest.TestCase):
|
||||||
|
|
||||||
|
def make_view(self):
|
||||||
|
controller = mock.create_autospec(spec=InstallProgressController)
|
||||||
|
return ProgressView(controller)
|
||||||
|
|
||||||
|
def test_initial_focus(self):
|
||||||
|
view = self.make_view()
|
||||||
|
for w in reversed(helpers.get_focus_path(view)):
|
||||||
|
if w is view.listbox:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.fail("listbox widget not focus")
|
||||||
|
|
||||||
|
def test_show_complete(self):
|
||||||
|
view = self.make_view()
|
||||||
|
btn = helpers.find_button_matching(view, "^Reboot Now$")
|
||||||
|
self.assertIs(btn, None)
|
||||||
|
view.show_complete()
|
||||||
|
btn = helpers.find_button_matching(view, "^Reboot Now$")
|
||||||
|
self.assertIsNot(btn, None)
|
||||||
|
helpers.click(btn)
|
||||||
|
view.controller.reboot.assert_called_once_with()
|
|
@ -0,0 +1,42 @@
|
||||||
|
import unittest
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
import urwid
|
||||||
|
|
||||||
|
from subiquity.controllers.welcome import WelcomeController
|
||||||
|
from subiquity.models.locale import LocaleModel
|
||||||
|
from subiquity.ui.views.welcome import WelcomeView
|
||||||
|
|
||||||
|
from subiquity.ui.views.tests import helpers
|
||||||
|
|
||||||
|
|
||||||
|
class WelcomeViewTests(unittest.TestCase):
|
||||||
|
|
||||||
|
def make_view_with_languages(self, languages):
|
||||||
|
controller = mock.create_autospec(spec=WelcomeController)
|
||||||
|
model = mock.create_autospec(spec=LocaleModel)
|
||||||
|
model.get_languages.return_value = languages
|
||||||
|
return WelcomeView(model, controller)
|
||||||
|
|
||||||
|
def test_basic(self):
|
||||||
|
# Clicking the button for a language calls "switch_language"
|
||||||
|
# on the model and "done" on the controller.
|
||||||
|
view = self.make_view_with_languages([('code', 'lang', 'native')])
|
||||||
|
but = helpers.find_button_matching(view, "^native$")
|
||||||
|
helpers.click(but)
|
||||||
|
view.model.switch_language.assert_called_once_with("code")
|
||||||
|
view.controller.done.assert_called_once_with()
|
||||||
|
|
||||||
|
def test_initial_focus(self):
|
||||||
|
# The initial focus for the view is the button for the first
|
||||||
|
# language.
|
||||||
|
view = self.make_view_with_languages([
|
||||||
|
('code1', 'lang1', 'native1'),
|
||||||
|
('code2', 'lang2', 'native2'),
|
||||||
|
])
|
||||||
|
for w in reversed(helpers.get_focus_path(view)):
|
||||||
|
if isinstance(w, urwid.Button):
|
||||||
|
self.assertEqual(w.label, "native1")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self.fail("No button found in focus path")
|
|
@ -104,6 +104,7 @@ class BoundFormField(object):
|
||||||
self.form = form
|
self.form = form
|
||||||
self.in_error = False
|
self.in_error = False
|
||||||
self._help = None
|
self._help = None
|
||||||
|
self.help_text = Text("", align="center")
|
||||||
self._caption = None
|
self._caption = None
|
||||||
self.pile = None
|
self.pile = None
|
||||||
self._enabled = True
|
self._enabled = True
|
||||||
|
@ -223,7 +224,7 @@ class BoundFormField(object):
|
||||||
raise RuntimeError("do not call as_row more than once!")
|
raise RuntimeError("do not call as_row more than once!")
|
||||||
self.parent_view = view
|
self.parent_view = view
|
||||||
self._longest_caption = longest_caption
|
self._longest_caption = longest_caption
|
||||||
self.help_text = Text(self.help, align="center")
|
self.help_text.set_text(self.help)
|
||||||
cols = [
|
cols = [
|
||||||
(self._longest_caption, Text("")),
|
(self._longest_caption, Text("")),
|
||||||
self.help_text,
|
self.help_text,
|
||||||
|
@ -286,6 +287,9 @@ class Form(object, metaclass=MetaForm):
|
||||||
self._fields.append(bf)
|
self._fields.append(bf)
|
||||||
if field.name in initial:
|
if field.name in initial:
|
||||||
bf.value = initial[field.name]
|
bf.value = initial[field.name]
|
||||||
|
for bf in self._fields:
|
||||||
|
bf.validate()
|
||||||
|
self.validated()
|
||||||
|
|
||||||
def _click_done(self, sender):
|
def _click_done(self, sender):
|
||||||
emit_signal(self, 'submit', self)
|
emit_signal(self, 'submit', self)
|
||||||
|
|
Loading…
Reference in New Issue