Skip to content

Commit

Permalink
Update new flash functionality in prep for merging to master.
Browse files Browse the repository at this point in the history
  • Loading branch information
ntoll committed Jul 9, 2018
1 parent 246994d commit f4f8ce1
Show file tree
Hide file tree
Showing 7 changed files with 14,416 additions and 14,275 deletions.
28,554 changes: 14,339 additions & 14,215 deletions mu/contrib/uflash.py

Large diffs are not rendered by default.

47 changes: 26 additions & 21 deletions mu/modes/microbit.py
Expand Up @@ -21,6 +21,7 @@
import sys
import os.path
import logging
import semver
from tokenize import TokenError
from mu.logic import HOME_DIRECTORY
from mu.contrib import uflash, microfs
Expand Down Expand Up @@ -176,9 +177,6 @@ class MicrobitMode(MicroPythonMode):
(0x0D28, 0x0204), # micro:bit USB VID, PID
]

user_defined_microbit_path = None

micropython_version = "0.1.0"
python_script = ''

def actions(self):
Expand Down Expand Up @@ -234,6 +232,7 @@ def flash(self):
WARNING: This method is getting more complex due to several edge
cases. Ergo, it's a target for refactoring.
"""
user_defined_microbit_path = None
self.python_script = ''
logger.info('Preparing to flash script.')
# The first thing to do is check the script is valid and of the
Expand Down Expand Up @@ -300,16 +299,12 @@ def flash(self):
# Determine the location of the BBC micro:bit. If it can't be found
# fall back to asking the user to locate it.
if path_to_microbit is None:
# Has the path to the device already been specified?
if self.user_defined_microbit_path:
path_to_microbit = self.user_defined_microbit_path
else:
# Ask the user to locate the device.
path_to_microbit = self.view.get_microbit_path(HOME_DIRECTORY)
# Store the user's specification of the path for future use.
self.user_defined_microbit_path = path_to_microbit
logger.debug('User defined path to micro:bit: {}'.format(
self.user_defined_microbit_path))
# Ask the user to locate the device.
path_to_microbit = self.view.get_microbit_path(HOME_DIRECTORY)
# Store the user's specification of the path for future use.
user_defined_microbit_path = path_to_microbit
logger.debug('User defined path to micro:bit: {}'.format(
user_defined_microbit_path))
# Check the path and that it exists simply because the path maybe based
# on stale data.
if path_to_microbit and os.path.exists(path_to_microbit):
Expand All @@ -319,12 +314,24 @@ def flash(self):
try:
version_info = microfs.version()
logger.info(version_info)
board_version = version_info['version'].split('-', 1)[0]
board_version = board_version.replace('micro:bit v', '')
board_info = version_info['version'].split()
if (board_info[0] == 'micro:bit' and
board_info[1].startswith('v')):
# New style versions, so the correct information will be
# in the "release" field.
board_version = version_info['release']
else:
# 0.0.1 indicates an old unknown version. This is just a
# valid arbitrary flag for semver comparison a couple of
# lines below.
board_version = '0.0.1'
logger.info('Board MicroPython: {}'.format(board_version))
logger.info(
'Mu MicroPython: {}'.format(uflash.MICROPYTHON_VERSION))
# If there's an older version of MicroPython on the device,
# update it with the one packaged with Mu.
if board_version < self.micropython_version:
if semver.compare(board_version,
uflash.MICROPYTHON_VERSION) < 0:
force_flash = True
except Exception:
# Could not get version of MicroPython. This means either the
Expand All @@ -344,14 +351,14 @@ def flash(self):
self.editor.microbit_runtime = ''
# Check for use of user defined path (to save hex onto local
# file system.
if self.user_defined_microbit_path:
if user_defined_microbit_path:
force_flash = True
# If we need to flash the device with a clean hex, do so now.
if force_flash:
logger.info('Flashing new MicroPython runtime onto device')
self.editor.show_status_message(message, 10)
self.set_buttons(flash=False)
if self.user_defined_microbit_path:
if user_defined_microbit_path:
# The user has provided a path to a location on the
# filesystem. In this case save the combined hex/script
# in the specified path_to_microbit.
Expand All @@ -372,7 +379,7 @@ def flash(self):
# Windows blocks on write.
self.flash_thread.finished.connect(self.flash_finished)
else:
if self.user_defined_microbit_path:
if user_defined_microbit_path:
# Call the flash_finished immediately the thread
# finishes if Mu is writing the hex file to a user
# defined location on the local filesystem.
Expand All @@ -393,8 +400,6 @@ def flash(self):
except Exception as ex:
self.flash_failed(ex)
else:
# Reset user defined path since it's incorrect.
self.user_defined_microbit_path = None
# Try to be helpful... essentially there is nothing Mu can do but
# prompt for patience while the device is mounted and/or do the
# classic "have you tried switching it off and on again?" trick.
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Expand Up @@ -20,6 +20,7 @@ pytest
pytest-cov
pytest-random-order
requests
semver
scrapy
setuptools
sphinx
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -17,7 +17,7 @@
'pgzero==1.2', 'PyQtChart==5.11.2', 'appdirs>=1.4.3',
'gpiozero>=1.4.1', 'guizero>=0.5.2',
'pigpio>=1.40.post1', 'Pillow>=5.2.0',
'requests>=2.19.1']
'requests>=2.19.1', 'semver>=2.8.0', ]

# Exclude packages not available for ARM in PyPI/piwheels (Raspberry Pi)
try:
Expand Down
85 changes: 47 additions & 38 deletions tests/modes/test_microbit.py
Expand Up @@ -8,6 +8,7 @@
from mu.logic import HOME_DIRECTORY
from mu.modes.microbit import MicrobitMode, FileManager, DeviceFlasher
from mu.modes.api import MICROBIT_APIS, SHARED_APIS
from mu.contrib import uflash
from unittest import mock
from tokenize import TokenError

Expand Down Expand Up @@ -227,7 +228,7 @@ def test_flash_with_attached_device_has_latest_firmware():
version_info = {
'sysname': 'microbit',
'nodename': 'microbit',
'release': '1.0',
'release': uflash.MICROPYTHON_VERSION,
'version': ("micro:bit v0.1.0-b'e10a5ff' on 2018-6-8; MicroPython "
"v1.9.2-34-gd64154c73 on 2017-09-01"),
'machine': 'micro:bit with nRF51822',
Expand Down Expand Up @@ -265,7 +266,7 @@ def test_flash_with_attached_device_has_latest_firmware_encounters_problem():
version_info = {
'sysname': 'microbit',
'nodename': 'microbit',
'release': '1.0',
'release': uflash.MICROPYTHON_VERSION,
'version': ("micro:bit v0.1.0-b'e10a5ff' on 2018-6-8; MicroPython "
"v1.9.2-34-gd64154c73 on 2017-09-01"),
'machine': 'micro:bit with nRF51822',
Expand Down Expand Up @@ -299,6 +300,49 @@ def test_flash_with_attached_device_has_latest_firmware_encounters_problem():
mm.flash_failed.assert_called_once_with(error)


def test_flash_with_attached_device_has_old_firmware():
"""
"""
version_info = {
'sysname': 'microbit',
'nodename': 'microbit',
'release': '1.0',
'version': ("v1.9.2-34-gd64154c73 on 2017-09-01"),
'machine': 'micro:bit with nRF51822',
}
mock_flasher = mock.MagicMock()
mock_flasher_class = mock.MagicMock(return_value=mock_flasher)
with mock.patch('mu.modes.microbit.uflash.find_microbit',
return_value='bar'),\
mock.patch('mu.modes.microbit.microfs.find_microbit',
return_value=('bar', '12345')),\
mock.patch('mu.modes.microbit.microfs.version',
return_value=version_info),\
mock.patch('mu.modes.microbit.os.path.exists', return_value=True),\
mock.patch('mu.modes.microbit.DeviceFlasher',
mock_flasher_class), \
mock.patch('mu.modes.microbit.sys.platform', 'win32'):
view = mock.MagicMock()
view.current_tab.text = mock.MagicMock(return_value='foo')
view.show_message = mock.MagicMock()
editor = mock.MagicMock()
editor.minify = False
editor.microbit_runtime = ''
mm = MicrobitMode(editor, view)
mm.copy_main = mock.MagicMock()
mm.set_buttons = mock.MagicMock()
mm.flash()
assert mm.flash_thread == mock_flasher
assert editor.show_status_message.call_count == 1
mm.set_buttons.assert_called_once_with(flash=False)
mock_flasher_class.assert_called_once_with(['bar', ], b'', None)
mock_flasher.finished.connect.\
assert_called_once_with(mm.flash_finished)
mock_flasher.on_flash_fail.connect.\
assert_called_once_with(mm.flash_failed)
mock_flasher.start.assert_called_once_with()


def test_flash_force_with_no_micropython():
"""
Ensure the expected calls are made to DeviceFlasher and a helpful status
Expand Down Expand Up @@ -469,7 +513,7 @@ def test_force_flash_user_specified_device_path():
version_info = {
'sysname': 'microbit',
'nodename': 'microbit',
'release': '1.0',
'release': uflash.MICROPYTHON_VERSION,
'version': ("micro:bit v0.1.0-b'e10a5ff' on 2018-6-8; MicroPython "
"v1.9.2-34-gd64154c73 on 2017-09-01"),
'machine': 'micro:bit with nRF51822',
Expand Down Expand Up @@ -498,45 +542,11 @@ def test_force_flash_user_specified_device_path():
mm.flash()
home = HOME_DIRECTORY
view.get_microbit_path.assert_called_once_with(home)
assert mm.user_defined_microbit_path == 'bar'
mock_flasher_class.assert_called_once_with(['bar', ], b'foo', None)
mock_flasher.finished.connect.\
assert_called_once_with(mm.flash_finished)


def test_flash_existing_user_specified_device_path():
"""
Ensure that if a micro:bit is not automatically found by uflash and the
user has previously specified a path to the device, then the hex is saved
in the specified location.
"""
mock_flasher = mock.MagicMock()
mock_flasher_class = mock.MagicMock(return_value=mock_flasher)
with mock.patch('mu.contrib.uflash.find_microbit', return_value=None),\
mock.patch('mu.contrib.microfs.find_microbit',
return_value=(None, None)),\
mock.patch('mu.logic.os.path.exists', return_value=True),\
mock.patch('mu.modes.microbit.DeviceFlasher',
mock_flasher_class), \
mock.patch('mu.modes.microbit.sys.platform', 'win32'):
view = mock.MagicMock()
view.current_tab.text = mock.MagicMock(return_value='foo')
view.get_microbit_path = mock.MagicMock(return_value='bar')
view.show_message = mock.MagicMock()
editor = mock.MagicMock()
editor.minify = False
editor.microbit_runtime = '/foo/bar'
mm = MicrobitMode(editor, view)
mm.user_defined_microbit_path = 'baz'
mm.flash()
assert view.get_microbit_path.call_count == 0
assert editor.show_status_message.call_count == 1
# The content of the Python script is to be embedded into the resulting
# hex file.
mock_flasher_class.assert_called_once_with(['baz', ], b'foo',
'/foo/bar')


def test_flash_path_specified_does_not_exist():
"""
Ensure that if a micro:bit is not automatically found by uflash and the
Expand Down Expand Up @@ -565,7 +575,6 @@ def test_flash_path_specified_does_not_exist():
" the device remains unfound.")
view.show_message.assert_called_once_with(message, information)
assert s.call_count == 0
assert mm.user_defined_microbit_path is None


def test_flash_without_device():
Expand Down
1 change: 1 addition & 0 deletions win_installer32.cfg
Expand Up @@ -62,6 +62,7 @@ pypi_wheels=
chardet==3.0.4
idna==2.6
urllib3==1.22
semver==2.8.0

packages=
jedi
Expand Down
1 change: 1 addition & 0 deletions win_installer64.cfg
Expand Up @@ -62,6 +62,7 @@ pypi_wheels=
chardet==3.0.4
idna==2.6
urllib3==1.22
semver==2.8.0

packages=
jedi
Expand Down

0 comments on commit f4f8ce1

Please sign in to comment.