diff --git a/test/common.py b/test/common.py index 67dd2cbb5c7..3743f43daa7 100644 --- a/test/common.py +++ b/test/common.py @@ -12,8 +12,13 @@ # otherwise use the software for commercial activities involving the Arduino # software without disclosing the source code of your own applications. To purchase # a commercial license, send an email to license@arduino.cc. +import collections import os +from invoke.context import Context + +Board = collections.namedtuple("Board", "address fqbn package architecture id core") + def running_on_ci(): """ @@ -21,3 +26,28 @@ def running_on_ci(): """ val = os.getenv("APPVEYOR") or os.getenv("DRONE") or os.getenv("GITHUB_WORKFLOW") return val is not None + + +def build_runner(cli_path, env, working_dir): + """ + Provide a wrapper around invoke's `run` API so that every test + will work in its own temporary folder. + + Useful reference: + http://docs.pyinvoke.org/en/1.2/api/runners.html#invoke.runners.Result + + :param cli_path: the path to the ``arduino-cli`` executable file. + :param env: a ``dict`` with the environment variables to use. + :param working_dir: the CWD where the command will be executed. + + :returns a runner function with the mechanic to run an ``arduino-cli`` instance + with a given environment ``env`` in the directory ```working_dir`. + """ + + def _run(cmd_string): + cli_full_line = "{} {}".format(cli_path, cmd_string) + run_context = Context() + with run_context.cd(working_dir): + return run_context.run(cli_full_line, echo=False, hide=True, warn=True, env=env) + + return _run diff --git a/test/conftest.py b/test/conftest.py index a11e46e2fd0..4e3145ee57b 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -12,10 +12,12 @@ # otherwise use the software for commercial activities involving the Arduino # software without disclosing the source code of your own applications. To purchase # a commercial license, send an email to license@arduino.cc. +import json import os import pytest -from invoke.context import Context + +from .common import build_runner, Board @pytest.fixture(scope="function") @@ -50,23 +52,76 @@ def working_dir(tmpdir_factory): @pytest.fixture(scope="function") def run_command(pytestconfig, data_dir, downloads_dir, working_dir): """ - Provide a wrapper around invoke's `run` API so that every test - will work in the same temporary folder. + Run the ``arduino-cli`` command to perform a the real test on the CLI. + """ + cli_path = os.path.join(pytestconfig.rootdir, "..", "arduino-cli") + env = { + "ARDUINO_DATA_DIR": data_dir, + "ARDUINO_DOWNLOADS_DIR": downloads_dir, + "ARDUINO_SKETCHBOOK_DIR": data_dir, + } + + return build_runner(cli_path, env, working_dir) - Useful reference: - http://docs.pyinvoke.org/en/1.2/api/runners.html#invoke.runners.Result + +@pytest.fixture(scope="session") +def _run_session_command(pytestconfig, tmpdir_factory, downloads_dir): + """ + Run the ``arduino-cli`` command to collect general metadata and store it in + a `session` scope for the tests. """ cli_path = os.path.join(pytestconfig.rootdir, "..", "arduino-cli") + data_dir = tmpdir_factory.mktemp("SessionDataDir") env = { "ARDUINO_DATA_DIR": data_dir, "ARDUINO_DOWNLOADS_DIR": downloads_dir, "ARDUINO_SKETCHBOOK_DIR": data_dir, } + # it looks like the pyinvoke library has a few problems in dealing with the path + # object, so to avoid this issue we can convert them to str. + # Take a look at https://github.com/pyinvoke/invoke/issues/454 for more details. + working_dir = str(tmpdir_factory.mktemp("SessionTestWork")) + + return build_runner(cli_path, env, working_dir) + + +@pytest.fixture(scope="session") +def detected_boards(_run_session_command): + """This fixture provides a list of all the boards attached to the host. + + This fixture will parse the JSON output of the ``arduino-cli board list --format json`` + command to extract all the connected boards data. + + :returns a list ``Board`` objects. + """ + + result = _run_session_command("core update-index") + assert result.ok + + result = _run_session_command("board list --format json") + assert result.ok + + detected_boards = [] + + ports = json.loads(result.stdout) + assert isinstance(ports, list) + for port in ports: + boards = port.get('boards', []) + assert isinstance(boards, list) + for board in boards: + fqbn = board.get('FQBN') + package, architecture, _id = fqbn.split(":") + detected_boards.append( + Board( + address=port.get('address'), + fqbn=fqbn, + package=package, + architecture=architecture, + id=_id, + core="{}:{}".format(package, architecture) + ) + ) - def _run(cmd_string): - cli_full_line = "{} {}".format(cli_path, cmd_string) - run_context = Context() - with run_context.cd(working_dir): - return run_context.run(cli_full_line, echo=False, hide=True, warn=True, env=env) + assert len(detected_boards) >= 1, "There are no boards available for testing" - return _run + return detected_boards diff --git a/test/test_compile.py b/test/test_compile.py index 08756867e10..42348037d9a 100644 --- a/test/test_compile.py +++ b/test/test_compile.py @@ -71,15 +71,7 @@ def test_compile_with_simple_sketch(run_command, data_dir): @pytest.mark.skipif(running_on_ci(), reason="VMs have no serial ports") -def test_compile_and_compile_combo(run_command, data_dir): - # Init the environment explicitly - result = run_command("core update-index") - assert result.ok - - # Install required core(s) - result = run_command("core install arduino:avr") - result = run_command("core install arduino:samd") - assert result.ok +def test_compile_and_compile_combo(run_command, detected_boards, data_dir): # Create a test sketch sketch_name = "CompileAndUploadIntegrationTest" @@ -88,49 +80,23 @@ def test_compile_and_compile_combo(run_command, data_dir): assert result.ok assert "Sketch created in: {}".format(sketch_path) in result.stdout - # - # Build a list of detected boards to test, if any. - # - result = run_command("board list --format json") - assert result.ok - - # - # The `board list --format json` returns a JSON that looks like to the following: - # - # [ - # { - # "address": "/dev/cu.usbmodem14201", - # "protocol": "serial", - # "protocol_label": "Serial Port (USB)", - # "boards": [ - # { - # "name": "Arduino NANO 33 IoT", - # "FQBN": "arduino:samd:nano_33_iot" - # } - # ] - # } - # ] - - detected_boards = [] - - ports = json.loads(result.stdout) - assert isinstance(ports, list) - for port in ports: - boards = port.get('boards') - assert isinstance(boards, list) - for board in boards: - detected_boards.append(dict(address=port.get('address'), fqbn=board.get('FQBN'))) - - assert len(detected_boards) >= 1, "There are no boards available for testing" - # Build sketch for each detected board for board in detected_boards: - log_file_name = "{fqbn}-compile.log".format(fqbn=board.get('fqbn').replace(":", "-")) + + # Init the environment explicitly + result = run_command("core update-index") + assert result.ok + + # Install required core(s) + result = run_command("core install {}".format(board.core)) + assert result.ok + + log_file_name = "{fqbn}-compile.log".format(fqbn=board.fqbn.replace(":", "-")) log_file_path = os.path.join(data_dir, log_file_name) command_log_flags = "--log-format json --log-file {} --log-level trace".format(log_file_path) result = run_command("compile -b {fqbn} --upload -p {address} {sketch_path} {log_flags}".format( - fqbn=board.get('fqbn'), - address=board.get('address'), + fqbn=board.fqbn, + address=board.address, sketch_path=sketch_path, log_flags=command_log_flags )) @@ -139,10 +105,10 @@ def test_compile_and_compile_combo(run_command, data_dir): log_json = open(log_file_path, 'r') json_log_lines = log_json.readlines() expected_trace_sequence = [ - "Compile {sketch} for {fqbn} started".format(sketch=sketch_path, fqbn=board.get('fqbn')), - "Compile {sketch} for {fqbn} successful".format(sketch=sketch_name, fqbn=board.get('fqbn')), - "Upload {sketch} on {fqbn} started".format(sketch=sketch_path, fqbn=board.get('fqbn')), - "Upload {sketch} on {fqbn} successful".format(sketch=sketch_name, fqbn=board.get('fqbn')) + "Compile {sketch} for {fqbn} started".format(sketch=sketch_path, fqbn=board.fqbn), + "Compile {sketch} for {fqbn} successful".format(sketch=sketch_name, fqbn=board.fqbn), + "Upload {sketch} on {fqbn} started".format(sketch=sketch_path, fqbn=board.fqbn), + "Upload {sketch} on {fqbn} successful".format(sketch=sketch_name, fqbn=board.fqbn) ] assert is_message_sequence_in_json_log_traces(expected_trace_sequence, json_log_lines) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy