Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 213ae36

Browse files
author
Maurizio Branca
committedSep 25, 2019
Add detected_board fixture
1 parent 54d2e2d commit 213ae36

File tree

3 files changed

+112
-62
lines changed

3 files changed

+112
-62
lines changed
 

‎test/common.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,14 @@
1212
# otherwise use the software for commercial activities involving the Arduino
1313
# software without disclosing the source code of your own applications. To purchase
1414
# a commercial license, send an email to license@arduino.cc.
15+
import collections
1516
import os
17+
import pytest
18+
import invoke
19+
20+
from invoke.context import Context
21+
22+
Board = collections.namedtuple("Board", "address fqbn package architecture id core")
1623

1724

1825
def running_on_ci():
@@ -21,3 +28,28 @@ def running_on_ci():
2128
"""
2229
val = os.getenv("APPVEYOR") or os.getenv("DRONE") or os.getenv("GITHUB_WORKFLOW")
2330
return val is not None
31+
32+
33+
def build_runner(cli_path, env, working_dir):
34+
"""
35+
Provide a wrapper around invoke's `run` API so that every test
36+
will work in its own temporary folder.
37+
38+
Useful reference:
39+
http://docs.pyinvoke.org/en/1.2/api/runners.html#invoke.runners.Result
40+
41+
:param cli_path: the path to the ``arduino-cli`` executable file.
42+
:param env: a ``dict`` with the environment variables to use.
43+
:param working_dir: the CWD where the command will be executed.
44+
45+
:returns a runner function with the mechanic to run an ``arduino-cli`` instance
46+
with a given environment ``env`` in the directory ```working_dir`.
47+
"""
48+
49+
def _run(cmd_string):
50+
cli_full_line = "{} {}".format(cli_path, cmd_string)
51+
run_context = Context()
52+
with run_context.cd(working_dir):
53+
return invoke.run(cli_full_line, echo=False, hide=True, warn=True, env=env)
54+
55+
return _run

‎test/conftest.py

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
# otherwise use the software for commercial activities involving the Arduino
1313
# software without disclosing the source code of your own applications. To purchase
1414
# a commercial license, send an email to license@arduino.cc.
15+
import json
1516
import os
1617

1718
import pytest
18-
from invoke.context import Context
19+
20+
from .common import build_runner, Board
1921

2022

2123
@pytest.fixture(scope="function")
@@ -50,23 +52,73 @@ def working_dir(tmpdir_factory):
5052
@pytest.fixture(scope="function")
5153
def run_command(pytestconfig, data_dir, downloads_dir, working_dir):
5254
"""
53-
Provide a wrapper around invoke's `run` API so that every test
54-
will work in the same temporary folder.
55+
Run the ``arduino-cli`` command to perform a the real test on the CLI.
56+
"""
57+
cli_path = os.path.join(pytestconfig.rootdir, "..", "arduino-cli")
58+
env = {
59+
"ARDUINO_DATA_DIR": data_dir,
60+
"ARDUINO_DOWNLOADS_DIR": downloads_dir,
61+
"ARDUINO_SKETCHBOOK_DIR": data_dir,
62+
}
63+
64+
return build_runner(cli_path, env, working_dir)
5565

56-
Useful reference:
57-
http://docs.pyinvoke.org/en/1.2/api/runners.html#invoke.runners.Result
66+
67+
@pytest.fixture(scope="session")
68+
def _run_session_command(pytestconfig, tmpdir_factory, downloads_dir):
69+
"""
70+
Run the ``arduino-cli`` command to collect general metadata and store it in
71+
a `session` scope for the tests.
5872
"""
5973
cli_path = os.path.join(pytestconfig.rootdir, "..", "arduino-cli")
74+
data_dir = tmpdir_factory.mktemp("SessionDataDir")
6075
env = {
6176
"ARDUINO_DATA_DIR": data_dir,
6277
"ARDUINO_DOWNLOADS_DIR": downloads_dir,
6378
"ARDUINO_SKETCHBOOK_DIR": data_dir,
6479
}
80+
working_dir = tmpdir_factory.mktemp("SessionTestWork")
81+
82+
return build_runner(cli_path, env, working_dir)
83+
84+
85+
@pytest.fixture(scope="session")
86+
def detected_boards(_run_session_command):
87+
"""This fixture provides a list of all the boards attached to the host.
88+
89+
This fixture will parse the JSON output of the ``arduino-cli board list --format json``
90+
command to extract all the connected boards data.
91+
92+
:returns a list ``Board`` objects.
93+
"""
94+
95+
result = _run_session_command("core update-index")
96+
assert result.ok
97+
98+
result = _run_session_command("board list --format json")
99+
assert result.ok
100+
101+
detected_boards = []
102+
103+
ports = json.loads(result.stdout)
104+
assert isinstance(ports, list)
105+
for port in ports:
106+
boards = port.get('boards', [])
107+
assert isinstance(boards, list)
108+
for board in boards:
109+
fqbn = board.get('FQBN')
110+
package, architecture, _id = fqbn.split(":")
111+
detected_boards.append(
112+
Board(
113+
address=port.get('address'),
114+
fqbn=fqbn,
115+
package=package,
116+
architecture=architecture,
117+
id=_id,
118+
core="{}:{}".format(package, architecture)
119+
)
120+
)
65121

66-
def _run(cmd_string):
67-
cli_full_line = "{} {}".format(cli_path, cmd_string)
68-
run_context = Context()
69-
with run_context.cd(working_dir):
70-
return run_context.run(cli_full_line, echo=False, hide=True, warn=True, env=env)
122+
assert len(detected_boards) >= 1, "There are no boards available for testing"
71123

72-
return _run
124+
return detected_boards

‎test/test_compile.py

Lines changed: 17 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,7 @@ def test_compile_with_simple_sketch(run_command, data_dir):
7171

7272

7373
@pytest.mark.skipif(running_on_ci(), reason="VMs have no serial ports")
74-
def test_compile_and_compile_combo(run_command, data_dir):
75-
# Init the environment explicitly
76-
result = run_command("core update-index")
77-
assert result.ok
78-
79-
# Install required core(s)
80-
result = run_command("core install arduino:avr")
81-
result = run_command("core install arduino:samd")
82-
assert result.ok
74+
def test_compile_and_compile_combo(run_command, detected_boards, data_dir):
8375

8476
# Create a test sketch
8577
sketch_name = "CompileAndUploadIntegrationTest"
@@ -88,49 +80,23 @@ def test_compile_and_compile_combo(run_command, data_dir):
8880
assert result.ok
8981
assert "Sketch created in: {}".format(sketch_path) in result.stdout
9082

91-
#
92-
# Build a list of detected boards to test, if any.
93-
#
94-
result = run_command("board list --format json")
95-
assert result.ok
96-
97-
#
98-
# The `board list --format json` returns a JSON that looks like to the following:
99-
#
100-
# [
101-
# {
102-
# "address": "/dev/cu.usbmodem14201",
103-
# "protocol": "serial",
104-
# "protocol_label": "Serial Port (USB)",
105-
# "boards": [
106-
# {
107-
# "name": "Arduino NANO 33 IoT",
108-
# "FQBN": "arduino:samd:nano_33_iot"
109-
# }
110-
# ]
111-
# }
112-
# ]
113-
114-
detected_boards = []
115-
116-
ports = json.loads(result.stdout)
117-
assert isinstance(ports, list)
118-
for port in ports:
119-
boards = port.get('boards')
120-
assert isinstance(boards, list)
121-
for board in boards:
122-
detected_boards.append(dict(address=port.get('address'), fqbn=board.get('FQBN')))
123-
124-
assert len(detected_boards) >= 1, "There are no boards available for testing"
125-
12683
# Build sketch for each detected board
12784
for board in detected_boards:
128-
log_file_name = "{fqbn}-compile.log".format(fqbn=board.get('fqbn').replace(":", "-"))
85+
86+
# Init the environment explicitly
87+
result = run_command("core update-index")
88+
assert result.ok
89+
90+
# Install required core(s)
91+
result = run_command("core install {}".format(board.core))
92+
assert result.ok
93+
94+
log_file_name = "{fqbn}-compile.log".format(fqbn=board.fqbn.replace(":", "-"))
12995
log_file_path = os.path.join(data_dir, log_file_name)
13096
command_log_flags = "--log-format json --log-file {} --log-level trace".format(log_file_path)
13197
result = run_command("compile -b {fqbn} --upload -p {address} {sketch_path} {log_flags}".format(
132-
fqbn=board.get('fqbn'),
133-
address=board.get('address'),
98+
fqbn=board.fqbn,
99+
address=board.address,
134100
sketch_path=sketch_path,
135101
log_flags=command_log_flags
136102
))
@@ -139,10 +105,10 @@ def test_compile_and_compile_combo(run_command, data_dir):
139105
log_json = open(log_file_path, 'r')
140106
json_log_lines = log_json.readlines()
141107
expected_trace_sequence = [
142-
"Compile {sketch} for {fqbn} started".format(sketch=sketch_path, fqbn=board.get('fqbn')),
143-
"Compile {sketch} for {fqbn} successful".format(sketch=sketch_name, fqbn=board.get('fqbn')),
144-
"Upload {sketch} on {fqbn} started".format(sketch=sketch_path, fqbn=board.get('fqbn')),
145-
"Upload {sketch} on {fqbn} successful".format(sketch=sketch_name, fqbn=board.get('fqbn'))
108+
"Compile {sketch} for {fqbn} started".format(sketch=sketch_path, fqbn=board.fqbn),
109+
"Compile {sketch} for {fqbn} successful".format(sketch=sketch_name, fqbn=board.fqbn),
110+
"Upload {sketch} on {fqbn} started".format(sketch=sketch_path, fqbn=board.fqbn),
111+
"Upload {sketch} on {fqbn} successful".format(sketch=sketch_name, fqbn=board.fqbn)
146112
]
147113
assert is_message_sequence_in_json_log_traces(expected_trace_sequence, json_log_lines)
148114

0 commit comments

Comments
 (0)
Please sign in to comment.