SettingsQR tests; mocked MicroSD.is_inserted

This commit is contained in:
kdmukai
2023-09-02 07:59:04 -05:00
parent 01461ccce4
commit afcc042052
3 changed files with 78 additions and 5 deletions
+21 -3
View File
@@ -1,6 +1,6 @@
import sys import sys
from dataclasses import dataclass from dataclasses import dataclass
from mock import MagicMock, patch from mock import MagicMock, Mock, patch
from typing import Callable from typing import Callable
# Prevent importing modules w/Raspi hardware dependencies. # Prevent importing modules w/Raspi hardware dependencies.
@@ -11,17 +11,27 @@ sys.modules['seedsigner.gui.toast'] = MagicMock()
sys.modules['seedsigner.views.screensaver'] = MagicMock() sys.modules['seedsigner.views.screensaver'] = MagicMock()
sys.modules['seedsigner.hardware.buttons'] = MagicMock() sys.modules['seedsigner.hardware.buttons'] = MagicMock()
sys.modules['seedsigner.hardware.camera'] = MagicMock() sys.modules['seedsigner.hardware.camera'] = MagicMock()
sys.modules['seedsigner.hardware.microsd'] = MagicMock()
from seedsigner.controller import Controller, FlowBasedTestException, StopFlowBasedTest from seedsigner.controller import Controller, FlowBasedTestException, StopFlowBasedTest
from seedsigner.gui.screens.screen import RET_CODE__BACK_BUTTON, RET_CODE__POWER_BUTTON from seedsigner.gui.screens.screen import RET_CODE__BACK_BUTTON, RET_CODE__POWER_BUTTON
from seedsigner.hardware.microsd import MicroSD
from seedsigner.models.settings import Settings from seedsigner.models.settings import Settings
from seedsigner.views.view import Destination, MainMenuView, View from seedsigner.views.view import Destination, MainMenuView, View
class BaseTest: class BaseTest:
class MockMicroSD(Mock):
"""
A test suite-friendly replacement for `MicroSD` that gives a test explicit
control over the reported state of the SD card.
"""
# Tests are free to directly manipulate this attribute as needed
is_inserted: bool = True
@classmethod @classmethod
def setup_class(cls): def setup_class(cls):
# Ensure there are no on-disk artifacts after running tests. # Ensure there are no on-disk artifacts after running tests.
@@ -30,6 +40,13 @@ class BaseTest:
# Mock out the loading screen so it can't spawn. View classes must import locally! # Mock out the loading screen so it can't spawn. View classes must import locally!
patch('seedsigner.gui.screens.screen.LoadingScreenThread').start() patch('seedsigner.gui.screens.screen.LoadingScreenThread').start()
# Instantiate the mocked MicroSD; hold on to the instance so tests can manipulate
# it later.
cls.mock_microsd = BaseTest.MockMicroSD()
# And mock it over `MicroSD`'s instance
MicroSD.get_instance = Mock(return_value=cls.mock_microsd)
@classmethod @classmethod
def teardown_class(cls): def teardown_class(cls):
@@ -62,11 +79,12 @@ class BaseTest:
def setup_method(self): def setup_method(self):
""" Guarantee a clean/default Controller and Settings state for each test case """ """ Guarantee a clean/default Controller, Settings, & MicroSD state for each test case """
BaseTest.reset_controller() BaseTest.reset_controller()
BaseTest.reset_settings() BaseTest.reset_settings()
self.controller = Controller.get_instance() self.controller = Controller.get_instance()
self.settings = Settings.get_instance() self.settings = Settings.get_instance()
self.mock_microsd.is_inserted = True
def teardown_method(self): def teardown_method(self):
+56 -2
View File
@@ -1,4 +1,5 @@
import os import os
from typing import Callable
# Must import test base before the Controller # Must import test base before the Controller
from base import FlowTest, FlowStep from base import FlowTest, FlowStep
@@ -6,13 +7,13 @@ from base import FlowTest, FlowStep
from seedsigner.models.settings import Settings from seedsigner.models.settings import Settings
from seedsigner.models.settings_definition import SettingsDefinition, SettingsConstants from seedsigner.models.settings_definition import SettingsDefinition, SettingsConstants
from seedsigner.gui.screens.screen import RET_CODE__BACK_BUTTON from seedsigner.gui.screens.screen import RET_CODE__BACK_BUTTON
from seedsigner.hardware.microsd import MicroSD
from seedsigner.views.view import MainMenuView from seedsigner.views.view import MainMenuView
from seedsigner.views import settings_views from seedsigner.views import scan_views, settings_views
class TestSettingsFlows(FlowTest): class TestSettingsFlows(FlowTest):
def test_persistent_settings(self): def test_persistent_settings(self):
""" Basic flow from MainMenuView to enable/disable persistent settings """ """ Basic flow from MainMenuView to enable/disable persistent settings """
# Which option are we testing? # Which option are we testing?
@@ -67,3 +68,56 @@ class TestSettingsFlows(FlowTest):
FlowStep(settings_views.DonateView), FlowStep(settings_views.DonateView),
FlowStep(settings_views.SettingsMenuView), FlowStep(settings_views.SettingsMenuView),
]) ])
def test_settingsqr(self):
"""
Scanning a SettingsQR should present the success screen and then return to
MainMenuView.
"""
def load_persistent_settingsqr_into_decoder(view: scan_views.ScanView):
settingsqr_data_persistent: str = "settings::v1 name=Total_noob_mode persistent=E coords=spa,spd denom=thr network=M qr_density=M xpub_export=E sigs=ss scripts=nat xpub_details=E passphrase=E camera=0 compact_seedqr=E bip85=D priv_warn=E dire_warn=E partners=E"
view.decoder.add_data(settingsqr_data_persistent)
def load_not_persistent_settingsqr_into_decoder(view: scan_views.ScanView):
settingsqr_data_not_persistent: str = "settings::v1 name=Ephemeral_noob_mode persistent=D coords=spa,spd denom=thr network=M qr_density=M xpub_export=E sigs=ss scripts=nat xpub_details=E passphrase=E camera=0 compact_seedqr=E bip85=D priv_warn=E dire_warn=E partners=E"
view.decoder.add_data(settingsqr_data_not_persistent)
def _run_test(initial_setting_state: str, load_settingsqr_into_decoder: Callable, expected_setting_state: str):
self.settings.set_value(SettingsConstants.SETTING__PERSISTENT_SETTINGS, initial_setting_state)
self.run_sequence([
FlowStep(MainMenuView, button_data_selection=MainMenuView.SCAN),
FlowStep(scan_views.ScanView, before_run=load_settingsqr_into_decoder), # simulate read message QR; ret val is ignored
FlowStep(settings_views.SettingsIngestSettingsQRView), # ret val is ignored
FlowStep(MainMenuView),
])
assert self.settings.get_value(SettingsConstants.SETTING__PERSISTENT_SETTINGS) == expected_setting_state
# First load a SettingsQR that enables persistent settings
self.mock_microsd.is_inserted = True
assert MicroSD.get_instance().is_inserted is True
_run_test(
initial_setting_state=SettingsConstants.OPTION__DISABLED,
load_settingsqr_into_decoder=load_persistent_settingsqr_into_decoder,
expected_setting_state=SettingsConstants.OPTION__ENABLED
)
# Then one that disables it
_run_test(
initial_setting_state=SettingsConstants.OPTION__ENABLED,
load_settingsqr_into_decoder=load_not_persistent_settingsqr_into_decoder,
expected_setting_state=SettingsConstants.OPTION__DISABLED
)
# Now try to enable persistent settings when the SD card is not inserted
self.mock_microsd.is_inserted = False
assert MicroSD.get_instance().is_inserted is False
_run_test(
initial_setting_state=SettingsConstants.OPTION__DISABLED,
load_settingsqr_into_decoder=load_persistent_settingsqr_into_decoder,
expected_setting_state=SettingsConstants.OPTION__DISABLED
)
+1
View File
@@ -8,6 +8,7 @@ from seedsigner.models.settings_definition import SettingsConstants
class TestSettings(BaseTest): class TestSettings(BaseTest):
@classmethod @classmethod
def setup_class(cls): def setup_class(cls):
super().setup_class()
cls.settings = Settings.get_instance() cls.settings = Settings.get_instance()