16
16
import platform
17
17
import signal
18
18
import shutil
19
+ import time
19
20
from pathlib import Path
20
21
21
22
import pytest
22
23
import simplejson as json
23
24
from invoke import Local
24
25
from invoke .context import Context
25
26
import tempfile
27
+ from filelock import FileLock
26
28
27
29
from .common import Board
28
30
@@ -54,17 +56,32 @@ def data_dir(tmpdir_factory):
54
56
if platform .system () == "Windows" :
55
57
with tempfile .TemporaryDirectory () as tmp :
56
58
yield tmp
59
+ # shutil.rmtree(tmp, ignore_errors=True)
57
60
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)
59
64
60
65
61
66
@pytest .fixture (scope = "session" )
62
- def downloads_dir (tmpdir_factory ):
67
+ def downloads_dir (tmpdir_factory , worker_id ):
63
68
"""
64
69
To save time and bandwidth, all the tests will access
65
70
the same download cache folder.
66
71
"""
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)
68
85
69
86
70
87
@pytest .fixture (scope = "function" )
@@ -74,7 +91,9 @@ def working_dir(tmpdir_factory):
74
91
will be created before running each test and deleted
75
92
at the end, this way all the tests work in isolation.
76
93
"""
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)
78
97
79
98
80
99
@pytest .fixture (scope = "function" )
@@ -95,9 +114,12 @@ def run_command(pytestconfig, data_dir, downloads_dir, working_dir):
95
114
}
96
115
(Path (data_dir ) / "packages" ).mkdir ()
97
116
98
- def _run (cmd_string , custom_working_dir = None ):
117
+ def _run (cmd_string , custom_working_dir = None , custom_env = None ):
118
+
99
119
if not custom_working_dir :
100
120
custom_working_dir = working_dir
121
+ if not custom_env :
122
+ custom_env = env
101
123
cli_full_line = '"{}" {}' .format (cli_path , cmd_string )
102
124
run_context = Context ()
103
125
# 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):
109
131
# It escapes spaces in the path using "\ " but it doesn't always work,
110
132
# wrapping the path in quotation marks is the safest approach
111
133
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 )
113
135
114
136
return _run
115
137
@@ -195,3 +217,19 @@ def copy_sketch(working_dir):
195
217
test_sketch_path = Path (working_dir ) / "sketch_simple"
196
218
shutil .copytree (sketch_path , test_sketch_path )
197
219
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
0 commit comments