Skip to content

Commit 53e3230

Browse files
authored
[skip changelog] Optimize a bit integration tests (#943)
1 parent 47aafe3 commit 53e3230

13 files changed

+309
-177
lines changed

poetry.lock

+129-14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ authors = []
66

77
[tool.poetry.dependencies]
88
python = "^3.8"
9-
pytest = "5.3.4"
9+
pytest = "6.0.2"
1010
simplejson = "3.17.0"
1111
semver = "2.9.0"
1212
pyserial = "3.4"
@@ -17,6 +17,9 @@ pytest-timeout = "1.3.4"
1717
invoke = "1.4.1"
1818
flake8 = "^3.8.3"
1919
black = { version = "^19.10b0", allow-prereleases = true }
20+
filelock = "^3.0.12"
21+
pytest-xdist = "^2.1.0"
22+
pytest_httpserver = "^0.3.5"
2023

2124
[tool.black]
2225
line-length = 120

test/common.py

+10
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
# a commercial license, send an email to [email protected].
1515
import os
1616
import collections
17+
import json
1718

1819

1920
Board = collections.namedtuple("Board", "address fqbn package architecture id core")
@@ -25,3 +26,12 @@ def running_on_ci():
2526
"""
2627
val = os.getenv("APPVEYOR") or os.getenv("DRONE") or os.getenv("GITHUB_WORKFLOW")
2728
return val is not None
29+
30+
31+
def parse_json_traces(log_json_lines):
32+
trace_entries = []
33+
for entry in log_json_lines:
34+
entry = json.loads(entry)
35+
if entry.get("level") == "trace":
36+
trace_entries.append(entry.get("msg"))
37+
return trace_entries

test/conftest.py

+44-6
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@
1616
import platform
1717
import signal
1818
import shutil
19+
import time
1920
from pathlib import Path
2021

2122
import pytest
2223
import simplejson as json
2324
from invoke import Local
2425
from invoke.context import Context
2526
import tempfile
27+
from filelock import FileLock
2628

2729
from .common import Board
2830

@@ -54,17 +56,32 @@ def data_dir(tmpdir_factory):
5456
if platform.system() == "Windows":
5557
with tempfile.TemporaryDirectory() as tmp:
5658
yield tmp
59+
# shutil.rmtree(tmp, ignore_errors=True)
5760
else:
58-
yield str(tmpdir_factory.mktemp("ArduinoTest"))
61+
data = tmpdir_factory.mktemp("ArduinoTest")
62+
yield str(data)
63+
# shutil.rmtree(data, ignore_errors=True)
5964

6065

6166
@pytest.fixture(scope="session")
62-
def downloads_dir(tmpdir_factory):
67+
def downloads_dir(tmpdir_factory, worker_id):
6368
"""
6469
To save time and bandwidth, all the tests will access
6570
the same download cache folder.
6671
"""
67-
return str(tmpdir_factory.mktemp("ArduinoTest"))
72+
download_dir = tmpdir_factory.mktemp("ArduinoTest", numbered=False)
73+
74+
# This folders should be created only once per session, if we're running
75+
# tests in parallel using multiple processes we need to make sure this
76+
# this fixture is executed only once, thus the use of the lockfile
77+
if not worker_id == "master":
78+
lock = Path(download_dir / "lock")
79+
with FileLock(lock):
80+
if not lock.is_file():
81+
lock.touch()
82+
83+
yield str(download_dir)
84+
# shutil.rmtree(download_dir, ignore_errors=True)
6885

6986

7087
@pytest.fixture(scope="function")
@@ -74,7 +91,9 @@ def working_dir(tmpdir_factory):
7491
will be created before running each test and deleted
7592
at the end, this way all the tests work in isolation.
7693
"""
77-
return str(tmpdir_factory.mktemp("ArduinoTestWork"))
94+
work_dir = tmpdir_factory.mktemp("ArduinoTestWork")
95+
yield str(work_dir)
96+
# shutil.rmtree(work_dir, ignore_errors=True)
7897

7998

8099
@pytest.fixture(scope="function")
@@ -95,9 +114,12 @@ def run_command(pytestconfig, data_dir, downloads_dir, working_dir):
95114
}
96115
(Path(data_dir) / "packages").mkdir()
97116

98-
def _run(cmd_string, custom_working_dir=None):
117+
def _run(cmd_string, custom_working_dir=None, custom_env=None):
118+
99119
if not custom_working_dir:
100120
custom_working_dir = working_dir
121+
if not custom_env:
122+
custom_env = env
101123
cli_full_line = '"{}" {}'.format(cli_path, cmd_string)
102124
run_context = Context()
103125
# It might happen that we need to change directories between drives on Windows,
@@ -109,7 +131,7 @@ def _run(cmd_string, custom_working_dir=None):
109131
# It escapes spaces in the path using "\ " but it doesn't always work,
110132
# wrapping the path in quotation marks is the safest approach
111133
with run_context.prefix(f'{cd_command} "{custom_working_dir}"'):
112-
return run_context.run(cli_full_line, echo=False, hide=True, warn=True, env=env)
134+
return run_context.run(cli_full_line, echo=False, hide=True, warn=True, env=custom_env)
113135

114136
return _run
115137

@@ -195,3 +217,19 @@ def copy_sketch(working_dir):
195217
test_sketch_path = Path(working_dir) / "sketch_simple"
196218
shutil.copytree(sketch_path, test_sketch_path)
197219
yield str(test_sketch_path)
220+
221+
222+
@pytest.fixture(scope="function")
223+
def wait_for_board(run_command):
224+
def _waiter(seconds=10):
225+
# Waits for the specified amount of second for a board to be visible.
226+
# This is necessary since it might happen that a board is not immediately
227+
# available after a test upload and subsequent tests might consequently fail.
228+
time_end = time.time() + seconds
229+
while time.time() < time_end:
230+
result = run_command("board list --format json")
231+
ports = json.loads(result.stdout)
232+
if len([p.get("boards", []) for p in ports]) > 0:
233+
break
234+
235+
return _waiter

test/pytest.ini

+8-2
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,11 @@ filterwarnings =
77
markers =
88
slow: marks tests as slow (deselect with '-m "not slow"')
99

10-
# atm some tests depend on each other, better to exit at first failure (-x)
11-
addopts = -x -s --verbose --tb=short
10+
# -x to exit at first failure
11+
# -s to disable per-test capture
12+
# --verbose is what is says it is
13+
# --tb=long sets the length of the traceback in case of failures
14+
# -n=auto sets the numbers of parallel processes to use
15+
# --dist=loadfile distributes the tests in the parallel processes dividing them per file
16+
# See https://pypi.org/project/pytest-xdist/#parallelization for more info on parallelization
17+
addopts = -x -s --verbose --tb=long -n=auto --dist=loadfile

0 commit comments

Comments
 (0)