circfirm 1.0.0__tar.gz → 2.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {circfirm-1.0.0 → circfirm-2.0.0}/PKG-INFO +1 -1
- circfirm-2.0.0/VERSION +1 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/circfirm/backend.py +30 -15
- {circfirm-1.0.0 → circfirm-2.0.0}/circfirm/cli.py +20 -10
- {circfirm-1.0.0 → circfirm-2.0.0}/circfirm.egg-info/PKG-INFO +1 -1
- circfirm-2.0.0/tests/assets/boot_out.txt +3 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/helpers.py +6 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/test_backend.py +44 -25
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/test_cli.py +36 -20
- circfirm-1.0.0/VERSION +0 -1
- circfirm-1.0.0/tests/assets/boot_out.txt +0 -3
- {circfirm-1.0.0 → circfirm-2.0.0}/.github/workflows/codeql.yml +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/.github/workflows/publish.yml +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/.github/workflows/push.yml +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/.gitignore +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/.pre-commit-config.yaml +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/.reuse/dep5 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/LICENSE +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/LICENSES/MIT.txt +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/Makefile +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/README.rst +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/README.rst.license +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/VERSION.license +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/circfirm/__init__.py +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/circfirm/startup.py +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/circfirm.egg-info/SOURCES.txt +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/circfirm.egg-info/dependency_links.txt +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/circfirm.egg-info/entry_points.txt +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/circfirm.egg-info/requires.txt +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/circfirm.egg-info/top_level.txt +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/pyproject.toml +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/requirements-dev.txt +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/requirements.txt +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/scripts/rmdir.py +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/setup.cfg +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-en_US-7.0.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-en_US-7.1.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-en_US-7.2.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-fr-7.0.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-fr-7.1.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-fr-7.2.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-zh_Latn_pinyin-7.0.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-zh_Latn_pinyin-7.1.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-zh_Latn_pinyin-7.2.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-en_US-7.0.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-en_US-7.1.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-en_US-7.2.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-fr-7.0.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-fr-7.1.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-fr-7.2.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-zh_Latn_pinyin-7.0.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-zh_Latn_pinyin-7.1.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-zh_Latn_pinyin-7.2.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-en_US-7.0.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-en_US-7.1.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-en_US-7.2.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-fr-7.0.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-fr-7.1.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-fr-7.2.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-zh_Latn_pinyin-7.0.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-zh_Latn_pinyin-7.1.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-zh_Latn_pinyin-7.2.0.uf2 +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/info_uf2.txt +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/responses/full_list.txt +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/responses/full_list.txt.license +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/responses/specific_board.txt +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/assets/responses/specific_board.txt.license +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/conftest.py +0 -0
- {circfirm-1.0.0 → circfirm-2.0.0}/tests/test_startup.py +0 -0
circfirm-2.0.0/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.0.0
|
|
@@ -10,6 +10,7 @@ Author(s): Alec Delaney
|
|
|
10
10
|
import enum
|
|
11
11
|
import os
|
|
12
12
|
import pathlib
|
|
13
|
+
import re
|
|
13
14
|
from typing import Dict, List, Optional, Set, Tuple
|
|
14
15
|
|
|
15
16
|
import packaging.version
|
|
@@ -45,6 +46,18 @@ class Language(enum.Enum):
|
|
|
45
46
|
MANDARIN_LATIN_PINYIN = "zh_Latn_pinyin"
|
|
46
47
|
|
|
47
48
|
|
|
49
|
+
_ALL_LANGAGES = [language.value for language in Language]
|
|
50
|
+
_ALL_LANGUAGES_REGEX = "|".join(_ALL_LANGAGES)
|
|
51
|
+
FIRMWARE_REGEX = "-".join(
|
|
52
|
+
[
|
|
53
|
+
r"adafruit-circuitpython-(.*)",
|
|
54
|
+
f"({_ALL_LANGUAGES_REGEX})",
|
|
55
|
+
r"(\d+\.\d+\.\d+(?:-(?:\balpha\b|\bbeta\b)\.\d+)*)\.uf2",
|
|
56
|
+
]
|
|
57
|
+
)
|
|
58
|
+
BOARD_ID_REGEX = r"Board ID:\s*(.*)"
|
|
59
|
+
|
|
60
|
+
|
|
48
61
|
def _find_device(filename: str) -> Optional[str]:
|
|
49
62
|
"""Find a specific connected device."""
|
|
50
63
|
for partition in psutil.disk_partitions():
|
|
@@ -69,19 +82,20 @@ def find_bootloader() -> Optional[str]:
|
|
|
69
82
|
|
|
70
83
|
def get_board_name(device_path: str) -> str:
|
|
71
84
|
"""Get the attached CircuitPython board's name."""
|
|
72
|
-
|
|
73
|
-
with open(
|
|
85
|
+
bootout_file = pathlib.Path(device_path) / circfirm.BOOTOUT_FILE
|
|
86
|
+
with open(bootout_file, encoding="utf-8") as infofile:
|
|
74
87
|
contents = infofile.read()
|
|
75
|
-
|
|
76
|
-
|
|
88
|
+
board_match = re.search(BOARD_ID_REGEX, contents)
|
|
89
|
+
if not board_match:
|
|
90
|
+
raise ValueError("Could not parse the board name from the boot out file")
|
|
91
|
+
return board_match[1]
|
|
77
92
|
|
|
78
93
|
|
|
79
94
|
def download_uf2(board: str, version: str, language: str = "en_US") -> None:
|
|
80
95
|
"""Download a version of CircuitPython for a specific board."""
|
|
81
96
|
file = get_uf2_filename(board, version, language=language)
|
|
82
|
-
board_name = board.replace(" ", "_").lower()
|
|
83
97
|
uf2_file = get_uf2_filepath(board, version, language=language, ensure=True)
|
|
84
|
-
url = f"https://downloads.circuitpython.org/bin/{
|
|
98
|
+
url = f"https://downloads.circuitpython.org/bin/{board}/{language}/{file}"
|
|
85
99
|
response = requests.get(url)
|
|
86
100
|
|
|
87
101
|
SUCCESS = 200
|
|
@@ -105,8 +119,7 @@ def get_uf2_filepath(
|
|
|
105
119
|
) -> pathlib.Path:
|
|
106
120
|
"""Get the path to a downloaded UF2 file."""
|
|
107
121
|
file = get_uf2_filename(board, version, language)
|
|
108
|
-
|
|
109
|
-
uf2_folder = pathlib.Path(circfirm.UF2_ARCHIVE) / board_name
|
|
122
|
+
uf2_folder = pathlib.Path(circfirm.UF2_ARCHIVE) / board
|
|
110
123
|
if ensure:
|
|
111
124
|
circfirm.startup.ensure_dir(uf2_folder)
|
|
112
125
|
return pathlib.Path(uf2_folder) / file
|
|
@@ -114,21 +127,23 @@ def get_uf2_filepath(
|
|
|
114
127
|
|
|
115
128
|
def get_uf2_filename(board: str, version: str, language: str = "en_US") -> str:
|
|
116
129
|
"""Get the structured name for a specific board/version CircuitPython."""
|
|
117
|
-
|
|
118
|
-
return f"adafruit-circuitpython-{board_name}-{language}-{version}.uf2"
|
|
130
|
+
return f"adafruit-circuitpython-{board}-{language}-{version}.uf2"
|
|
119
131
|
|
|
120
132
|
|
|
121
133
|
def get_board_folder(board: str) -> pathlib.Path:
|
|
122
134
|
"""Get the board folder path."""
|
|
123
|
-
|
|
124
|
-
return pathlib.Path(circfirm.UF2_ARCHIVE) / board_name
|
|
135
|
+
return pathlib.Path(circfirm.UF2_ARCHIVE) / board
|
|
125
136
|
|
|
126
137
|
|
|
127
138
|
def get_firmware_info(uf2_filename: str) -> Tuple[str, str]:
|
|
128
139
|
"""Get firmware info."""
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
140
|
+
regex_match = re.match(FIRMWARE_REGEX, uf2_filename)
|
|
141
|
+
if regex_match is None:
|
|
142
|
+
raise ValueError(
|
|
143
|
+
"Firmware information could not be determined from the filename"
|
|
144
|
+
)
|
|
145
|
+
version = regex_match[3]
|
|
146
|
+
language = regex_match[2]
|
|
132
147
|
return version, language
|
|
133
148
|
|
|
134
149
|
|
|
@@ -11,6 +11,7 @@ import os
|
|
|
11
11
|
import pathlib
|
|
12
12
|
import shutil
|
|
13
13
|
import sys
|
|
14
|
+
import time
|
|
14
15
|
from typing import Optional
|
|
15
16
|
|
|
16
17
|
import click
|
|
@@ -30,8 +31,23 @@ def cli() -> None:
|
|
|
30
31
|
@cli.command()
|
|
31
32
|
@click.argument("version")
|
|
32
33
|
@click.option("-l", "--language", default="en_US", help="CircuitPython language/locale")
|
|
33
|
-
|
|
34
|
+
@click.option("-b", "--board", default=None, help="Assume the given board name")
|
|
35
|
+
def install(version: str, language: str, board: Optional[str]) -> None:
|
|
34
36
|
"""Install the specified version of CircuitPython."""
|
|
37
|
+
if not board:
|
|
38
|
+
circuitpy = circfirm.backend.find_circuitpy()
|
|
39
|
+
if not circuitpy and circfirm.backend.find_bootloader():
|
|
40
|
+
click.echo("CircuitPython device found, but it is in bootloader mode!")
|
|
41
|
+
click.echo(
|
|
42
|
+
"Please put the device out of bootloader mode, or use the --board option."
|
|
43
|
+
)
|
|
44
|
+
sys.exit(3)
|
|
45
|
+
board = circfirm.backend.get_board_name(circuitpy)
|
|
46
|
+
|
|
47
|
+
click.echo("Board name detected, please switch the device to bootloader mode.")
|
|
48
|
+
while not circfirm.backend.find_bootloader():
|
|
49
|
+
time.sleep(1)
|
|
50
|
+
|
|
35
51
|
mount_path = circfirm.backend.find_bootloader()
|
|
36
52
|
if not mount_path:
|
|
37
53
|
circuitpy = circfirm.backend.find_circuitpy()
|
|
@@ -44,8 +60,6 @@ def install(version: str, language: str) -> None:
|
|
|
44
60
|
click.echo("Check that the device is connected and mounted.")
|
|
45
61
|
sys.exit(1)
|
|
46
62
|
|
|
47
|
-
board = circfirm.backend.get_board_name(mount_path)
|
|
48
|
-
|
|
49
63
|
if not circfirm.backend.is_downloaded(board, version, language):
|
|
50
64
|
click.echo("Downloading UF2...")
|
|
51
65
|
circfirm.backend.download_uf2(board, version, language)
|
|
@@ -76,8 +90,6 @@ def clear(
|
|
|
76
90
|
click.echo("Cache cleared!")
|
|
77
91
|
return
|
|
78
92
|
|
|
79
|
-
board = board.replace(" ", "_").lower()
|
|
80
|
-
|
|
81
93
|
glob_pattern = "*-*" if board is None else f"*-{board}"
|
|
82
94
|
language_pattern = "-*" if language is None else f"-{language}"
|
|
83
95
|
glob_pattern += language_pattern
|
|
@@ -99,19 +111,17 @@ def clear(
|
|
|
99
111
|
@click.option("-b", "--board", default=None, help="CircuitPython board name")
|
|
100
112
|
def cache_list(board: Optional[str]) -> None:
|
|
101
113
|
"""List all the boards/versions cached."""
|
|
102
|
-
if board is not None:
|
|
103
|
-
board_name = board.replace(" ", "_").lower()
|
|
104
114
|
board_list = os.listdir(circfirm.UF2_ARCHIVE)
|
|
105
115
|
|
|
106
116
|
if not board_list:
|
|
107
117
|
click.echo("Versions have not been cached yet for any boards.")
|
|
108
118
|
sys.exit(0)
|
|
109
119
|
|
|
110
|
-
if board is not None and
|
|
111
|
-
click.echo(f"No versions for board '{
|
|
120
|
+
if board is not None and board not in board_list:
|
|
121
|
+
click.echo(f"No versions for board '{board}' are not cached.")
|
|
112
122
|
sys.exit(0)
|
|
113
123
|
|
|
114
|
-
specified_board =
|
|
124
|
+
specified_board = board if board is not None else None
|
|
115
125
|
boards = circfirm.backend.get_sorted_boards(specified_board)
|
|
116
126
|
|
|
117
127
|
for rec_boardname, rec_boardvers in boards.items():
|
|
@@ -42,6 +42,12 @@ def get_mount_node(path: str, must_exist: bool = False) -> str:
|
|
|
42
42
|
return node_location
|
|
43
43
|
|
|
44
44
|
|
|
45
|
+
def delete_mount_node(path: str, missing_okay: bool = False) -> None:
|
|
46
|
+
"""Delete a file on the mounted druve."""
|
|
47
|
+
node_file = get_mount_node(path)
|
|
48
|
+
pathlib.Path(node_file).unlink(missing_ok=missing_okay)
|
|
49
|
+
|
|
50
|
+
|
|
45
51
|
def touch_mount_node(path: str, exist_ok: bool = False) -> str:
|
|
46
52
|
"""Touch a file on the mounted drive."""
|
|
47
53
|
node_location = get_mount_node(path)
|
|
@@ -50,17 +50,33 @@ def test_find_bootloader() -> None:
|
|
|
50
50
|
|
|
51
51
|
def test_get_board_name() -> None:
|
|
52
52
|
"""Tests getting the board name from the UF2 info file."""
|
|
53
|
+
# Setup
|
|
54
|
+
tests.helpers.delete_mount_node(circfirm.UF2INFO_FILE)
|
|
55
|
+
tests.helpers.copy_boot_out()
|
|
56
|
+
|
|
57
|
+
# Test successful parsing
|
|
53
58
|
mount_location = tests.helpers.get_mount()
|
|
54
59
|
board_name = circfirm.backend.get_board_name(mount_location)
|
|
55
|
-
assert board_name == "
|
|
60
|
+
assert board_name == "feather_m4_express"
|
|
61
|
+
|
|
62
|
+
# Test unsuccessful parsing
|
|
63
|
+
with open(
|
|
64
|
+
tests.helpers.get_mount_node(circfirm.BOOTOUT_FILE), mode="w", encoding="utf-8"
|
|
65
|
+
) as bootfile:
|
|
66
|
+
bootfile.write("junktext")
|
|
67
|
+
with pytest.raises(ValueError):
|
|
68
|
+
circfirm.backend.get_board_name(mount_location)
|
|
69
|
+
|
|
70
|
+
# Clean up
|
|
71
|
+
tests.helpers.delete_mount_node(circfirm.BOOTOUT_FILE)
|
|
72
|
+
tests.helpers.copy_uf2_info()
|
|
56
73
|
|
|
57
74
|
|
|
58
75
|
def test_get_board_folder() -> None:
|
|
59
76
|
"""Tests getting UF2 information."""
|
|
60
|
-
board_name = "
|
|
61
|
-
formatted_board_name = board_name.replace(" ", "_").lower()
|
|
77
|
+
board_name = "feather_m4_express"
|
|
62
78
|
board_path = circfirm.backend.get_board_folder(board_name)
|
|
63
|
-
expected_path = pathlib.Path(circfirm.UF2_ARCHIVE) /
|
|
79
|
+
expected_path = pathlib.Path(circfirm.UF2_ARCHIVE) / board_name
|
|
64
80
|
assert board_path.resolve() == expected_path.resolve()
|
|
65
81
|
|
|
66
82
|
|
|
@@ -71,7 +87,7 @@ def test_get_uf2_filepath() -> None:
|
|
|
71
87
|
version = "7.0.0"
|
|
72
88
|
|
|
73
89
|
created_path = circfirm.backend.get_uf2_filepath(
|
|
74
|
-
"
|
|
90
|
+
"feather_m4_express", "7.0.0", "en_US", ensure=True
|
|
75
91
|
)
|
|
76
92
|
expected_path = (
|
|
77
93
|
pathlib.Path(circfirm.UF2_ARCHIVE)
|
|
@@ -83,16 +99,14 @@ def test_get_uf2_filepath() -> None:
|
|
|
83
99
|
|
|
84
100
|
def test_download_uf2() -> None:
|
|
85
101
|
"""Tests the UF2 download functionality."""
|
|
86
|
-
board_name = "
|
|
102
|
+
board_name = "feather_m4_express"
|
|
87
103
|
language = "en_US"
|
|
88
104
|
version = "junktext"
|
|
89
105
|
|
|
90
|
-
formatted_board_name = board_name.replace(" ", "_").lower()
|
|
91
|
-
|
|
92
106
|
# Test bad download candidate
|
|
93
107
|
expected_path = (
|
|
94
108
|
circfirm.backend.get_board_folder(board_name)
|
|
95
|
-
/ f"adafruit-circuitpython-{
|
|
109
|
+
/ f"adafruit-circuitpython-{board_name}-{language}-{version}.uf2"
|
|
96
110
|
)
|
|
97
111
|
with pytest.raises(ConnectionError):
|
|
98
112
|
circfirm.backend.download_uf2(board_name, version, language)
|
|
@@ -105,7 +119,7 @@ def test_download_uf2() -> None:
|
|
|
105
119
|
circfirm.backend.download_uf2(board_name, version, language)
|
|
106
120
|
expected_path = (
|
|
107
121
|
circfirm.backend.get_board_folder(board_name)
|
|
108
|
-
/ f"adafruit-circuitpython-{
|
|
122
|
+
/ f"adafruit-circuitpython-{board_name}-{language}-{version}.uf2"
|
|
109
123
|
)
|
|
110
124
|
assert expected_path.exists()
|
|
111
125
|
assert circfirm.backend.is_downloaded(board_name, version)
|
|
@@ -116,20 +130,25 @@ def test_download_uf2() -> None:
|
|
|
116
130
|
|
|
117
131
|
def test_get_firmware_info() -> None:
|
|
118
132
|
"""Tests the ability to get firmware information."""
|
|
119
|
-
board_name = "
|
|
133
|
+
board_name = "feather_m4_express"
|
|
120
134
|
language = "en_US"
|
|
121
|
-
version = "7.0.0"
|
|
122
135
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
+
# Test successful parsing
|
|
137
|
+
for version in ("8.0.0", "9.0.0-beta.2"):
|
|
138
|
+
try:
|
|
139
|
+
board_folder = circfirm.backend.get_board_folder(board_name)
|
|
140
|
+
circfirm.backend.download_uf2(board_name, version, language)
|
|
141
|
+
downloaded_filename = [file.name for file in board_folder.glob("*")][0]
|
|
142
|
+
|
|
143
|
+
parsed_version, parsed_language = circfirm.backend.get_firmware_info(
|
|
144
|
+
downloaded_filename
|
|
145
|
+
)
|
|
146
|
+
assert parsed_version == version
|
|
147
|
+
assert parsed_language == language
|
|
148
|
+
finally:
|
|
149
|
+
# Clean up post tests
|
|
150
|
+
shutil.rmtree(board_folder)
|
|
151
|
+
|
|
152
|
+
# Test failed parsing
|
|
153
|
+
with pytest.raises(ValueError):
|
|
154
|
+
circfirm.backend.get_firmware_info("cannotparse")
|
|
@@ -10,6 +10,8 @@ Author(s): Alec Delaney
|
|
|
10
10
|
import os
|
|
11
11
|
import pathlib
|
|
12
12
|
import shutil
|
|
13
|
+
import threading
|
|
14
|
+
import time
|
|
13
15
|
|
|
14
16
|
from click.testing import CliRunner
|
|
15
17
|
|
|
@@ -20,36 +22,55 @@ from circfirm.cli import cli
|
|
|
20
22
|
|
|
21
23
|
def test_install() -> None:
|
|
22
24
|
"""Tests the install command."""
|
|
23
|
-
|
|
25
|
+
|
|
26
|
+
def wait_and_add() -> None:
|
|
27
|
+
"""Wait then add the boot_out.txt file."""
|
|
28
|
+
time.sleep(2)
|
|
29
|
+
tests.helpers.delete_mount_node(circfirm.BOOTOUT_FILE)
|
|
30
|
+
tests.helpers.copy_uf2_info()
|
|
31
|
+
|
|
32
|
+
version = "8.0.0-beta.6"
|
|
24
33
|
runner = CliRunner()
|
|
25
34
|
|
|
26
35
|
# Test successfully installing the firmware
|
|
36
|
+
tests.helpers.delete_mount_node(circfirm.UF2INFO_FILE)
|
|
37
|
+
tests.helpers.copy_boot_out()
|
|
38
|
+
threading.Thread(target=wait_and_add).start()
|
|
27
39
|
result = runner.invoke(cli, ["install", version])
|
|
28
40
|
assert result.exit_code == 0
|
|
29
|
-
expected_uf2_filename = circfirm.backend.get_uf2_filename(
|
|
41
|
+
expected_uf2_filename = circfirm.backend.get_uf2_filename(
|
|
42
|
+
"feather_m4_express", version
|
|
43
|
+
)
|
|
30
44
|
expected_uf2_filepath = tests.helpers.get_mount_node(expected_uf2_filename)
|
|
31
45
|
assert os.path.exists(expected_uf2_filepath)
|
|
32
46
|
os.remove(expected_uf2_filepath)
|
|
33
47
|
|
|
34
48
|
ERR_NOT_FOUND = 1
|
|
35
49
|
ERR_FOUND_CIRCUITPY = 2
|
|
50
|
+
ERR_IN_BOOTLOADER = 3
|
|
36
51
|
try:
|
|
37
52
|
# Test not finding the mounted drive
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
53
|
+
tests.helpers.delete_mount_node(circfirm.UF2INFO_FILE)
|
|
54
|
+
result = runner.invoke(
|
|
55
|
+
cli, ["install", version, "--board", "feather_m4_express"]
|
|
56
|
+
)
|
|
41
57
|
assert result.exit_code == ERR_NOT_FOUND
|
|
42
58
|
|
|
43
59
|
# Test finding the mounted drive as CIRCUITPY
|
|
44
60
|
tests.helpers.copy_boot_out()
|
|
45
|
-
result = runner.invoke(
|
|
61
|
+
result = runner.invoke(
|
|
62
|
+
cli, ["install", version, "--board", "feather_m4_express"]
|
|
63
|
+
)
|
|
46
64
|
assert result.exit_code == ERR_FOUND_CIRCUITPY
|
|
47
|
-
|
|
48
|
-
os.remove(bootout)
|
|
65
|
+
tests.helpers.delete_mount_node(circfirm.BOOTOUT_FILE)
|
|
49
66
|
finally:
|
|
50
67
|
tests.helpers.copy_uf2_info()
|
|
51
68
|
|
|
52
|
-
|
|
69
|
+
# Test using install when in bootloader mode
|
|
70
|
+
result = runner.invoke(cli, ["install", version])
|
|
71
|
+
assert result.exit_code == ERR_IN_BOOTLOADER
|
|
72
|
+
|
|
73
|
+
board_folder = circfirm.backend.get_board_folder("feather_m4_express")
|
|
53
74
|
shutil.rmtree(board_folder)
|
|
54
75
|
|
|
55
76
|
|
|
@@ -77,24 +98,19 @@ def test_cache_list() -> None:
|
|
|
77
98
|
"tests/assets/responses/specific_board.txt", encoding="utf-8"
|
|
78
99
|
) as respfile:
|
|
79
100
|
expected_response = respfile.read()
|
|
80
|
-
result = runner.invoke(cli, ["cache", "list", "--board", "
|
|
81
|
-
print(result.output)
|
|
101
|
+
result = runner.invoke(cli, ["cache", "list", "--board", "feather_m4_express"])
|
|
82
102
|
assert result.exit_code == 0
|
|
83
103
|
assert result.output == expected_response
|
|
84
104
|
|
|
85
105
|
# Test specific board, version, and language response
|
|
86
|
-
fake_board = "
|
|
87
|
-
fake_board_name = fake_board.replace(" ", "_").lower()
|
|
106
|
+
fake_board = "does_not_exist"
|
|
88
107
|
with open(
|
|
89
108
|
"tests/assets/responses/specific_board.txt", encoding="utf-8"
|
|
90
109
|
) as respfile:
|
|
91
110
|
expected_response = respfile.read()
|
|
92
111
|
result = runner.invoke(cli, ["cache", "list", "--board", fake_board])
|
|
93
|
-
print(result.output)
|
|
94
112
|
assert result.exit_code == 0
|
|
95
|
-
assert
|
|
96
|
-
result.output == f"No versions for board '{fake_board_name}' are not cached.\n"
|
|
97
|
-
)
|
|
113
|
+
assert result.output == f"No versions for board '{fake_board}' are not cached.\n"
|
|
98
114
|
|
|
99
115
|
# Clean Up after test
|
|
100
116
|
shutil.rmtree(circfirm.UF2_ARCHIVE)
|
|
@@ -103,7 +119,7 @@ def test_cache_list() -> None:
|
|
|
103
119
|
|
|
104
120
|
def test_cache_save() -> None:
|
|
105
121
|
"""Tests the cache save command."""
|
|
106
|
-
board = "
|
|
122
|
+
board = "feather_m4_express"
|
|
107
123
|
version = "7.3.0"
|
|
108
124
|
langauge = "fr"
|
|
109
125
|
runner = CliRunner()
|
|
@@ -132,7 +148,7 @@ def test_cache_save() -> None:
|
|
|
132
148
|
|
|
133
149
|
def test_cache_clear() -> None:
|
|
134
150
|
"""Tests the cache clear command."""
|
|
135
|
-
board = "
|
|
151
|
+
board = "feather_m4_express"
|
|
136
152
|
version = "7.1.0"
|
|
137
153
|
langauge = "zh_Latn_pinyin"
|
|
138
154
|
runner = CliRunner()
|
|
@@ -154,7 +170,7 @@ def test_cache_clear() -> None:
|
|
|
154
170
|
langauge,
|
|
155
171
|
],
|
|
156
172
|
)
|
|
157
|
-
board_folder = pathlib.Path(circfirm.UF2_ARCHIVE) / board
|
|
173
|
+
board_folder = pathlib.Path(circfirm.UF2_ARCHIVE) / board
|
|
158
174
|
uf2_file = (
|
|
159
175
|
board_folder
|
|
160
176
|
/ "adafruit-circuitpython-feather_m4_express-zh_Latn_pinyin-7.1.0.uf2"
|
circfirm-1.0.0/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
1.0.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|