mirror of
https://github.com/SeedSigner/seedsigner.git
synced 2026-10-05 15:08:25 +00:00
Merge branch 'dev' into ci
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
## Description
|
||||
|
||||
_Describe the change simply. Provide a reason for the change._
|
||||
|
||||
_Include screenshots of any new or modified screens (or at least explain why they were omitted)_
|
||||
|
||||
This pull request is categorized as a:
|
||||
|
||||
- [ ] New feature
|
||||
- [ ] Bug fix
|
||||
- [ ] Code refactor
|
||||
- [ ] Documentation
|
||||
- [ ] Other
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] I’ve run `pytest` and made sure all unit tests pass before sumbitting the PR
|
||||
|
||||
If you modified or added functionality/workflow, did you add new unit tests?
|
||||
|
||||
- [ ] No, I’m a fool
|
||||
- [ ] Yes
|
||||
- [ ] N/A
|
||||
|
||||
I have tested this PR on the following platforms/os:
|
||||
|
||||
- [ ] Raspberry Pi OS [Manual Build](https://github.com/SeedSigner/seedsigner/blob/dev/docs/manual_installation.md)
|
||||
- [ ] [SeedSigner OS](https://github.com/SeedSigner/seedsigner-os) on a Pi0/Pi0W board
|
||||
- [ ] Other
|
||||
|
||||
|
||||
Note: Keep your changes limited in scope; if you uncover other issues or improvements along the way, ideally submit those as a separate PR. The more complicated the PR the harder to review, test, and merge.
|
||||
@@ -1,6 +1,6 @@
|
||||
# Build an offline, airgapped Bitcoin signing device for less than $50!
|
||||
|
||||

|
||||

|
||||
|
||||
---------------
|
||||
|
||||
@@ -82,27 +82,27 @@ Notes:
|
||||
## A Special Note On Minimizing Trust
|
||||
As is the nature of pre-packaged software downloads, downloading and using the prepared SeedSigner release images means implicitly placing trust in the individual preparing those images; in our project the release images are prepared and signed by the eponymous creator of the project, SeedSigner "the person". That individual is additionally the only person in possession of the PGP keys that are used to sign the release images.
|
||||
|
||||
However, one of the many advantages of the open source software model is that the need for this kind of trust can be negated by our users' ability to (1) review the project's source code and (2) assemble the operating image necessary to use the software themselves. From our project's inception, instructions to build a SeedSigner operating image (using precisely the same process that is used to create the prepared release images) have been made availabile. We have put a lot of thought and work into making these instructions easy to understand and follow, even for less technical users. These instructions can be found [here](docs/manual_installation.md).
|
||||
However, one of the many advantages of the open source software model is that the need for this kind of trust can be negated by our users' ability to (1) review the project's source code and (2) assemble the operating image necessary to use the software themselves. From our project's inception, instructions to build a SeedSigner operating image (using precisely the same process that is used to create the prepared release images) have been made available. We have put a lot of thought and work into making these instructions easy to understand and follow, even for less technical users. These instructions can be found [here](docs/manual_installation.md).
|
||||
|
||||
## Downloading the Software
|
||||
|
||||
|
||||
Download the current Version (0.6.0) software image that is compatible with your Raspberry Pi Hardware. The Pi Zero 1.3 is the most common and recommended board.
|
||||
Download the current Version (0.7.0) software image that is compatible with your Raspberry Pi Hardware. The Pi Zero 1.3 is the most common and recommended board.
|
||||
| Board | Download Image Link/Name |
|
||||
| --------------------- | --------------------------------- |
|
||||
|**[Raspberry Pi Zero 1.3](https://www.raspberrypi.com/products/raspberry-pi-zero/)** |[`seedsigner_os.0.6.0.pi0.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.6.0/seedsigner_os.0.6.0.pi0.img) |
|
||||
|[Raspberry Pi Zero W](https://www.raspberrypi.com/products/raspberry-pi-zero-w/) |[`seedsigner_os.0.6.0.pi0.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.6.0/seedsigner_os.0.6.0.pi0.img) |
|
||||
|[Raspberry Pi Zero 2 W](https://www.raspberrypi.com/products/raspberry-pi-zero-2-w/) |[`seedsigner_os.0.6.0.pi02w.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.6.0/seedsigner_os.0.6.0.pi02w.img) |
|
||||
|[Raspberry Pi 2 Model B](https://www.raspberrypi.com/products/raspberry-pi-2-model-b/) |[`seedsigner_os.0.6.0.pi2.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.6.0/seedsigner_os.0.6.0.pi2.img) |
|
||||
|[Raspberry Pi 3 Model B](https://www.raspberrypi.com/products/raspberry-pi-3-model-b/) |[`seedsigner_os.0.6.0.pi02w.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.6.0/seedsigner_os.0.6.0.pi02w.img) |
|
||||
|[Raspberry Pi 4 Model B](https://www.raspberrypi.com/products/raspberry-pi-4-model-b/) |[`seedsigner_os.0.6.0.pi4.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.6.0/seedsigner_os.0.6.0.pi4.img) |
|
||||
|[Raspberry Pi 400](https://www.raspberrypi.com/products/raspberry-pi-400-unit/) |[`seedsigner_os.0.6.0.pi4.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.6.0/seedsigner_os.0.6.0.pi4.img) |
|
||||
|**[Raspberry Pi Zero 1.3](https://www.raspberrypi.com/products/raspberry-pi-zero/)** |[`seedsigner_os.0.7.0.pi0.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.7.0/seedsigner_os.0.7.0.pi0.img) |
|
||||
|[Raspberry Pi Zero W](https://www.raspberrypi.com/products/raspberry-pi-zero-w/) |[`seedsigner_os.0.7.0.pi0.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.7.0/seedsigner_os.0.7.0.pi0.img) |
|
||||
|[Raspberry Pi Zero 2 W](https://www.raspberrypi.com/products/raspberry-pi-zero-2-w/) |[`seedsigner_os.0.7.0.pi02w.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.7.0/seedsigner_os.0.7.0.pi02w.img) |
|
||||
|[Raspberry Pi 2 Model B](https://www.raspberrypi.com/products/raspberry-pi-2-model-b/) |[`seedsigner_os.0.7.0.pi2.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.7.0/seedsigner_os.0.7.0.pi2.img) |
|
||||
|[Raspberry Pi 3 Model B](https://www.raspberrypi.com/products/raspberry-pi-3-model-b/) |[`seedsigner_os.0.7.0.pi02w.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.7.0/seedsigner_os.0.7.0.pi02w.img) |
|
||||
|[Raspberry Pi 4 Model B](https://www.raspberrypi.com/products/raspberry-pi-4-model-b/) |[`seedsigner_os.0.7.0.pi4.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.7.0/seedsigner_os.0.7.0.pi4.img) |
|
||||
|[Raspberry Pi 400](https://www.raspberrypi.com/products/raspberry-pi-400-unit/) |[`seedsigner_os.0.7.0.pi4.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.7.0/seedsigner_os.0.7.0.pi4.img) |
|
||||
|
||||
Note: If you have physically removed the WiFi component from your board, you will still use the image file of the original(un-modified) hardware. (Our files are compiled/based on the *processor* architecture). Although it is better to spend a few minutes upfront to determine which specific Pi hardware/model you have, if you are still unsure which hardware you have, you can try using the pi0.img file. Making an incorrect choice here will not ruin your board, because this is software, not firmware.
|
||||
|
||||
**also download** these 2 signature verification files to the same folder
|
||||
[The Plaintext manifest file](https://github.com/SeedSigner/seedsigner/releases/download/0.6.0/seedsigner.0.6.0.sha256)
|
||||
[The Signature of the manifest file](https://github.com/SeedSigner/seedsigner/releases/download/0.6.0/seedsigner.0.6.0.sha256.sig)
|
||||
[The Plaintext manifest file](https://github.com/SeedSigner/seedsigner/releases/download/0.7.0/seedsigner.0.7.0.sha256.txt)
|
||||
[The Signature of the manifest file](https://github.com/SeedSigner/seedsigner/releases/download/0.7.0/seedsigner.0.7.0.sha256.txt.sig)
|
||||
|
||||
Users familiar with older versions of the SeedSigner software might be surprised with how fast their software downloads now are, because since version 0.6.0 the software image files are now 100x smaller! Each image file is now under 42 Megabytes so your downloads and verifications will be very quick now (and might even seem *too* quick)!
|
||||
|
||||
@@ -131,7 +131,7 @@ The result should confirm that 1 key was *either* imported or updated. *Ignore*
|
||||
|
||||
Next, you will run the *verify* command on the signature (.sig) file. (*Verify* must be run from inside the same folder that you downloaded the files into earlier. The `*`'s in this command will auto-fill the version from your current folder, so it should be copied and pasted as-is.)
|
||||
```
|
||||
gpg --verify seedsigner.0.6.*.sha256.sig
|
||||
gpg --verify seedsigner.0.7.*.sha256.txt.sig
|
||||
```
|
||||
|
||||
When the verify command completes successfully, it should display output like this:
|
||||
@@ -201,21 +201,21 @@ Now that you have confirmed that you do have the real SeedSigner Project's Publi
|
||||
|
||||
**On Linux or OSX:** Run this command
|
||||
```
|
||||
shasum -a 256 --ignore-missing --check seedsigner.0.6.*.sha256
|
||||
shasum -a 256 --ignore-missing --check seedsigner.0.7.*.sha256.txt
|
||||
```
|
||||
|
||||
**On Windows (inside Powershell):** Run this command
|
||||
```
|
||||
CertUtil -hashfile seedsigner_os.0.6.0.Insert_Your_Pi_Models_binary_here_For_Example_pi02w.img SHA256
|
||||
CertUtil -hashfile seedsigner_os.0.7.0.Insert_Your_Pi_Models_binary_here_For_Example_pi02w.img SHA256
|
||||
```
|
||||
On Windows, you must then manually compare the resulting file hash value to the corresponding hash value shown inside the .SHA256 cleartext file.
|
||||
<BR>
|
||||
|
||||
Wait up to 30 seconds for the command to complete, and it should display:
|
||||
```
|
||||
seedsigner_os.0.6.x.[Your_Pi_Model_For_Example:pi02w].img: OK
|
||||
seedsigner_os.0.7.x.[Your_Pi_Model_For_Example:pi02w].img: OK
|
||||
```
|
||||
**If you receive the "OK" message** for your **seedsigner_os.0.6.x.[Your_Pi_Model_For_Example:pi02w].img file**, as shown above, then your verification is fully complete!
|
||||
**If you receive the "OK" message** for your **seedsigner_os.0.7.x.[Your_Pi_Model_For_Example:pi02w].img file**, as shown above, then your verification is fully complete!
|
||||
**All of your downloaded files have now been confirmed as both authentic and unaltered!** You can proceed to create/write your MicroSD card😄😄 !!
|
||||
|
||||
If your file result shows "FAILED", then you must stop here immediately. Do not continue. Contact us for assistance at the Telegram group address above.
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
version: '3.7'
|
||||
|
||||
services:
|
||||
seedsigner-dev:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile
|
||||
command: sh -c 'bash -c "docker/setup.sh"'
|
||||
volumes:
|
||||
- ../seedsigner:/seedsigner
|
||||
@@ -0,0 +1,20 @@
|
||||
FROM python:3.10-bullseye
|
||||
|
||||
# install zbar dependencyy
|
||||
RUN apt-get -qq update
|
||||
RUN apt-get -y -qq install zbar-tools
|
||||
|
||||
# temp copy requirements files to local repo to do pip3 install
|
||||
COPY ../requirements.txt /requirements.txt
|
||||
COPY ../tests/requirements.txt /tests-requirements.txt
|
||||
|
||||
WORKDIR /
|
||||
RUN pip3 install -r requirements.txt
|
||||
RUN pip3 install -r tests-requirements.txt
|
||||
|
||||
# clean up copied files
|
||||
RUN rm /requirements.txt
|
||||
RUN rm /tests-requirements.txt
|
||||
|
||||
# set working dir
|
||||
WORKDIR /seedsigner
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
pip3 install -e .
|
||||
tail -f /dev/null
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 98 KiB |
@@ -1,10 +1,12 @@
|
||||
# Manual Installation Instructions
|
||||
|
||||
Begin by acquiring a specific copy of the Raspberry Pi Lite operating system, dated 2021-05-28; this version can be found here:
|
||||
Begin by acquiring the latest 32-bit, Buster-based Raspberry Pi Lite operating system. This guide was tested using the version dated 2023-05-03; which can be found here:
|
||||
|
||||
https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2021-05-28/
|
||||
https://downloads.raspberrypi.org/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2023-05-03/
|
||||
|
||||
Best practice is to verify the downloaded .zip file containing the Raspberry Pi Lite OS matches the published SHA256 hash of the file; for additional reference that hash is: c5dad159a2775c687e9281b1a0e586f7471690ae28f2f2282c90e7d59f64273c. After verifying the file's data integrity, you can decompress the .zip file to obtain the operating system image that it contains. You can then use Balena's Etcher tool (https://www.balena.io/etcher/) to write the Raspberry Pi Lite software image to a memory card (4 GB or larger). It's important to note that an image authoring tool must be used (the operating system image cannot be simply copied into a file storage partition on the memory card).
|
||||
SeedSigner does not work any of the more recent versions of Debian. This is a known limitation and there are open tickets to track the progress of this ([Debian 11 ticket](https://github.com/SeedSigner/seedsigner/issues/431), [Debian 12 ticket](https://github.com/SeedSigner/seedsigner/issues/430)). This guide does not work on the 64-bit versions of Buster, however pull requests to update it to be compatible are welcome.
|
||||
|
||||
Best practice is to verify the downloaded file containing the Raspberry Pi Lite OS matches the published SHA256 hash of the file; for additional reference that hash is: 3d210e61b057de4de90eadb46e28837585a9b24247c221998f5bead04f88624c. After verifying the file's data integrity, you can decompress the .tar.xz file to obtain the operating system image that it contains. You can then use Balena's Etcher tool (https://www.balena.io/etcher/) to write the Raspberry Pi Lite software image to a memory card (4 GB or larger). It's important to note that an image authoring tool must be used (the operating system image cannot be simply copied into a file storage partition on the memory card).
|
||||
|
||||
The manual SeedSigner installation and configuration process requires an internet connection on the Pi to download the necessary libraries and code.
|
||||
If your Pi does not have onboard wifi, you have two options:
|
||||
@@ -177,6 +179,20 @@ git clone https://github.com/SeedSigner/seedsigner
|
||||
cd seedsigner
|
||||
```
|
||||
|
||||
### Adding swap space
|
||||
Compiling the dependencies requires more RAM than is available on a Raspberry
|
||||
Pi 3B, let alone a Zero. Temporarily adding 1GB of additional swap space will
|
||||
work around this limitation. The `/swapfile` can be deleted after you reboot.
|
||||
|
||||
If building on a Raspberry Pi board with more than 1GB of RAM, this step can
|
||||
be safely skipped.
|
||||
|
||||
```bash
|
||||
sudo dd if=/dev/zero of=/swapfile bs=4096 count=$((1024*256))
|
||||
sudo chmod 0600 /swapfile
|
||||
sudo mkswap /swapfile
|
||||
sudo swapon /swapfile
|
||||
```
|
||||
|
||||
### Install Python `pip` dependencies:
|
||||
```bash
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
embit==0.7.0
|
||||
numpy==1.21.1
|
||||
Pillow==8.2.0
|
||||
-e git+https://github.com/seedsigner/pyzbar.git@c3c237821c6a20b17953efe59b90df0b514a1c03#egg=pyzbar
|
||||
numpy==1.25.2
|
||||
Pillow==9.4.0
|
||||
pyzbar @ git+https://github.com/seedsigner/pyzbar.git@c3c237821c6a20b17953efe59b90df0b514a1c03
|
||||
qrcode==7.3.1
|
||||
six==1.16.0
|
||||
urtypes @ git+https://github.com/selfcustody/urtypes.git@7fb280eab3b3563dfc57d2733b0bf5cbc0a96a6a
|
||||
|
||||
@@ -23,5 +23,5 @@ setuptools.setup(
|
||||
package_dir={"": "src"},
|
||||
packages=setuptools.find_packages(where="src"),
|
||||
include_package_data=True,
|
||||
python_requires=">=3.6",
|
||||
python_requires=">=3.10",
|
||||
)
|
||||
|
||||
@@ -93,7 +93,7 @@ class Controller(Singleton):
|
||||
rather than at the top in order avoid circular imports.
|
||||
"""
|
||||
|
||||
VERSION = "0.7.0-rc1"
|
||||
VERSION = "0.7.0"
|
||||
|
||||
# Declare class member vars with type hints to enable richer IDE support throughout
|
||||
# the code.
|
||||
|
||||
@@ -215,7 +215,7 @@ class Fonts(Singleton):
|
||||
cls.fonts[font_name][size] = ImageFont.truetype(os.path.join(cls.font_path, f"{font_name}.{file_extension}"), size)
|
||||
except OSError as e:
|
||||
if "cannot open resource" in str(e):
|
||||
raise Exception(f"Font {font_name}.ttf not found: {repr(e)}")
|
||||
raise Exception(f"Font {font_name}.{file_extension} not found: {repr(e)}")
|
||||
else:
|
||||
raise e
|
||||
|
||||
|
||||
@@ -174,36 +174,3 @@ class ScanScreen(BaseScreen):
|
||||
self.camera.stop_video_stream_mode()
|
||||
break
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class SettingsUpdatedScreen(ButtonListScreen):
|
||||
config_name: str = None
|
||||
title: str = "Settings QR"
|
||||
is_bottom_list: bool = True
|
||||
|
||||
def __post_init__(self):
|
||||
# Customize defaults
|
||||
self.button_data = ["Home"]
|
||||
self.show_back_button = False
|
||||
|
||||
super().__post_init__()
|
||||
|
||||
start_y = self.top_nav.height + 20
|
||||
if self.config_name:
|
||||
self.config_name_textarea = TextArea(
|
||||
text=f'"{self.config_name}"',
|
||||
is_text_centered=True,
|
||||
auto_line_break=True,
|
||||
screen_y=start_y
|
||||
)
|
||||
self.components.append(self.config_name_textarea)
|
||||
start_y = self.config_name_textarea.screen_y + 50
|
||||
|
||||
self.components.append(TextArea(
|
||||
text="Settings imported successfully!",
|
||||
is_text_centered=True,
|
||||
auto_line_break=True,
|
||||
screen_y=start_y
|
||||
))
|
||||
|
||||
|
||||
@@ -296,3 +296,36 @@ class DonateScreen(BaseTopNavScreen):
|
||||
supersampling_factor=1,
|
||||
screen_y=self.components[-1].screen_y + self.components[-1].height + GUIConstants.COMPONENT_PADDING
|
||||
))
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class SettingsQRConfirmationScreen(ButtonListScreen):
|
||||
config_name: str = None
|
||||
title: str = "Settings QR"
|
||||
status_message: str = "Settings updated..."
|
||||
is_bottom_list: bool = True
|
||||
|
||||
def __post_init__(self):
|
||||
# Customize defaults
|
||||
self.button_data = ["Home"]
|
||||
self.show_back_button = False
|
||||
super().__post_init__()
|
||||
|
||||
start_y = self.top_nav.height + 20
|
||||
if self.config_name:
|
||||
self.config_name_textarea = TextArea(
|
||||
text=f'"{self.config_name}"',
|
||||
is_text_centered=True,
|
||||
auto_line_break=True,
|
||||
screen_y=start_y
|
||||
)
|
||||
self.components.append(self.config_name_textarea)
|
||||
start_y = self.config_name_textarea.screen_y + 50
|
||||
|
||||
self.components.append(TextArea(
|
||||
text=self.status_message,
|
||||
is_text_centered=True,
|
||||
auto_line_break=True,
|
||||
screen_y=start_y
|
||||
))
|
||||
|
||||
@@ -87,6 +87,12 @@ class Settings(Singleton):
|
||||
values = value
|
||||
for v in values:
|
||||
if v not in [opt[0] for opt in settings_entry.selection_options]:
|
||||
if settings_entry.attr_name == SettingsConstants.SETTING__PERSISTENT_SETTINGS and v == SettingsConstants.OPTION__ENABLED:
|
||||
# Special case: trying to enable Persistent Settings when
|
||||
# DISABLED is the only option allowed (because the SD card is not
|
||||
# inserted. Explicitly set to DISABLED.
|
||||
value = SettingsConstants.OPTION__DISABLED
|
||||
break
|
||||
raise InvalidSettingsQRData(f"""{abbreviated_name} = '{v}' is not valid""")
|
||||
|
||||
updated_settings[settings_entry.attr_name] = value
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import logging
|
||||
from seedsigner.gui.components import SeedSignerIconConstants
|
||||
from seedsigner.hardware.microsd import MicroSD
|
||||
|
||||
from .view import View, Destination, MainMenuView
|
||||
|
||||
@@ -195,14 +196,21 @@ class SettingsIngestSettingsQRView(View):
|
||||
# May raise an Exception which will bubble up to the Controller to display to the
|
||||
# user.
|
||||
self.config_name, settings_update_dict = Settings.parse_settingsqr(data)
|
||||
|
||||
self.settings.update(settings_update_dict)
|
||||
|
||||
|
||||
if MicroSD.get_instance().is_inserted and self.settings.get_value(SettingsConstants.SETTING__PERSISTENT_SETTINGS) == SettingsConstants.OPTION__ENABLED:
|
||||
self.status_message = "Persistent Settings enabled. Settings saved to SD card."
|
||||
else:
|
||||
self.status_message = "Settings updated in temporary memory"
|
||||
|
||||
|
||||
def run(self):
|
||||
from seedsigner.gui.screens.scan_screens import SettingsUpdatedScreen
|
||||
from seedsigner.gui.screens.settings_screens import SettingsQRConfirmationScreen
|
||||
self.run_screen(
|
||||
SettingsUpdatedScreen,
|
||||
config_name=self.config_name
|
||||
SettingsQRConfirmationScreen,
|
||||
config_name=self.config_name,
|
||||
status_message=self.status_message,
|
||||
)
|
||||
|
||||
# Only one exit point
|
||||
|
||||
+22
-3
@@ -1,6 +1,6 @@
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from mock import MagicMock, patch
|
||||
from mock import MagicMock, Mock, patch
|
||||
from typing import Callable
|
||||
|
||||
# Prevent importing modules w/Raspi hardware dependencies.
|
||||
@@ -11,17 +11,28 @@ sys.modules['seedsigner.gui.toast'] = MagicMock()
|
||||
sys.modules['seedsigner.views.screensaver'] = MagicMock()
|
||||
sys.modules['seedsigner.hardware.buttons'] = MagicMock()
|
||||
sys.modules['seedsigner.hardware.camera'] = MagicMock()
|
||||
sys.modules['seedsigner.hardware.microsd'] = MagicMock()
|
||||
|
||||
from seedsigner.controller import Controller, FlowBasedTestException, StopFlowBasedTest
|
||||
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.views.view import Destination, MainMenuView, View
|
||||
|
||||
|
||||
|
||||
|
||||
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 (it's reset to
|
||||
# True before each test in `BaseTest.setup_method()`).
|
||||
is_inserted: bool = True
|
||||
|
||||
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
# Ensure there are no on-disk artifacts after running tests.
|
||||
@@ -30,6 +41,13 @@ class BaseTest:
|
||||
# Mock out the loading screen so it can't spawn. View classes must import locally!
|
||||
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
|
||||
def teardown_class(cls):
|
||||
@@ -62,11 +80,12 @@ class BaseTest:
|
||||
|
||||
|
||||
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_settings()
|
||||
self.controller = Controller.get_instance()
|
||||
self.settings = Settings.get_instance()
|
||||
self.mock_microsd.is_inserted = True
|
||||
|
||||
|
||||
def teardown_method(self):
|
||||
|
||||
@@ -110,10 +110,11 @@ def test_generate_screenshots(target_locale):
|
||||
continue
|
||||
|
||||
settings_views_list.append((settings_views.SettingsEntryUpdateSelectionView, dict(attr_name=settings_entry.attr_name), f"SettingsEntryUpdateSelectionView_{settings_entry.attr_name}"))
|
||||
settings_views_list.append(settings_views.IOTestView)
|
||||
settings_views_list.append(settings_views.DonateView)
|
||||
|
||||
|
||||
settingsqr_data_persistent = "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"
|
||||
settingsqr_data_not_persistent = "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"
|
||||
|
||||
screenshot_sections = {
|
||||
"Main Menu Views": [
|
||||
MainMenuView,
|
||||
@@ -123,7 +124,6 @@ def test_generate_screenshots(target_locale):
|
||||
PowerOptionsView,
|
||||
RestartView,
|
||||
PowerOffView,
|
||||
(settings_views.SettingsIngestSettingsQRView, dict(data="settings::v1 name=Uncle_Jim's_noob_mode")),
|
||||
],
|
||||
"Seed Views": [
|
||||
seed_views.SeedsMenuView,
|
||||
@@ -215,7 +215,12 @@ def test_generate_screenshots(target_locale):
|
||||
tools_views.ToolsAddressExplorerAddressListView,
|
||||
#tools_views.ToolsAddressExplorerAddressView,
|
||||
],
|
||||
"Settings Views": settings_views_list,
|
||||
"Settings Views": settings_views_list + [
|
||||
settings_views.IOTestView,
|
||||
settings_views.DonateView,
|
||||
(settings_views.SettingsIngestSettingsQRView, dict(data=settingsqr_data_persistent), "SettingsIngestSettingsQRView_persistent"),
|
||||
(settings_views.SettingsIngestSettingsQRView, dict(data=settingsqr_data_not_persistent), "SettingsIngestSettingsQRView_not_persistent"),
|
||||
],
|
||||
"Misc Error Views": [
|
||||
NotYetImplementedView,
|
||||
(UnhandledExceptionView, dict(error=UnhandledExceptionViewFood)),
|
||||
@@ -227,7 +232,7 @@ def test_generate_screenshots(target_locale):
|
||||
text="QRCode is invalid or is a data format not yet supported.",
|
||||
button_text="Back",
|
||||
)),
|
||||
],
|
||||
]
|
||||
}
|
||||
|
||||
readme = f"""# SeedSigner Screenshots\n"""
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import os
|
||||
from typing import Callable
|
||||
|
||||
from mock import PropertyMock, patch
|
||||
|
||||
# Must import test base before the Controller
|
||||
from base import FlowTest, FlowStep
|
||||
@@ -6,13 +9,13 @@ from base import FlowTest, FlowStep
|
||||
from seedsigner.models.settings import Settings
|
||||
from seedsigner.models.settings_definition import SettingsDefinition, SettingsConstants
|
||||
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 import settings_views
|
||||
from seedsigner.views import scan_views, settings_views
|
||||
|
||||
|
||||
|
||||
class TestSettingsFlows(FlowTest):
|
||||
|
||||
def test_persistent_settings(self):
|
||||
""" Basic flow from MainMenuView to enable/disable persistent settings """
|
||||
# Which option are we testing?
|
||||
@@ -67,3 +70,69 @@ class TestSettingsFlows(FlowTest):
|
||||
FlowStep(settings_views.DonateView),
|
||||
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
|
||||
|
||||
# Have to jump through some hoops to completely simulate the SD card being
|
||||
# removed; we need Settings to restrict Persistent Settings to only allow
|
||||
# DISABLED.
|
||||
with patch('seedsigner.models.settings.Settings.HOSTNAME', new_callable=PropertyMock) as mock_hostname:
|
||||
# Must identify itself as SeedSigner OS to trigger the SD card removal logic
|
||||
mock_hostname.return_value = Settings.SEEDSIGNER_OS
|
||||
Settings.handle_microsd_state_change(MicroSD.ACTION__REMOVED)
|
||||
|
||||
selection_options = SettingsDefinition.get_settings_entry(SettingsConstants.SETTING__PERSISTENT_SETTINGS).selection_options
|
||||
assert len(selection_options) == 1
|
||||
assert selection_options[0][0] == SettingsConstants.OPTION__DISABLED
|
||||
assert self.settings.get_value(SettingsConstants.SETTING__PERSISTENT_SETTINGS) == SettingsConstants.OPTION__DISABLED
|
||||
|
||||
_run_test(
|
||||
initial_setting_state=SettingsConstants.OPTION__DISABLED,
|
||||
load_settingsqr_into_decoder=load_persistent_settingsqr_into_decoder,
|
||||
expected_setting_state=SettingsConstants.OPTION__DISABLED
|
||||
)
|
||||
|
||||
@@ -8,6 +8,7 @@ from seedsigner.models.settings_definition import SettingsConstants
|
||||
class TestSettings(BaseTest):
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
super().setup_class()
|
||||
cls.settings = Settings.get_instance()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user