Skip to content

Commit abac980

Browse files
committed
add optional sample timeout to tox sample runner
1 parent 8e8a378 commit abac980

File tree

1 file changed

+107
-21
lines changed

1 file changed

+107
-21
lines changed

scripts/devops_tasks/test_run_samples.py

Lines changed: 107 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import os
1212
import logging
1313
from fnmatch import fnmatch
14+
from subprocess import check_call, CalledProcessError, TimeoutExpired
1415
from common_tasks import (
1516
run_check_call,
1617
process_glob_string,
@@ -20,6 +21,22 @@
2021

2122
root_dir = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", ".."))
2223

24+
"""
25+
Some samples may "run forever" or need to be timed out after a period of time. Add them here in the following format:
26+
TIMEOUT_SAMPLES = {
27+
"<package-name>": {
28+
"<sample_file_name.py>": (<timeout (seconds)>, <pass if timeout? (bool)>)
29+
}
30+
}
31+
"""
32+
TIMEOUT_SAMPLES = {
33+
"azure-eventhub": {
34+
"receive_batch_with_checkpoint.py": (3, True)
35+
}
36+
}
37+
38+
39+
# Add your library + sample file if you do not want a particular sample to be run
2340
IGNORED_SAMPLES = {
2441
"azure-eventgrid": [
2542
"__init__.py",
@@ -29,10 +46,20 @@
2946
"sample_publish_events_to_a_topic_using_sas_credential.py",
3047
"sample_publish_events_to_a_topic_using_sas_credential_async.py"],
3148
"azure-eventhub": [
49+
"send.py",
50+
"send_async.py",
51+
"send_stream.py",
52+
"send_stream_async.py",
53+
"recv_for_period_async.py",
54+
"client_creation_async.py",
55+
"connection_string_authentication.py",
56+
"connection_string_authentication_async.py",
57+
"client_identity_authentication_async.py",
58+
"client_creation.py",
59+
"client_identity_authentication.py",
3260
"authenticate_with_sas_token.py",
3361
"connection_to_custom_endpoint_address.py",
3462
"proxy.py",
35-
"receive_batch_with_checkpoint.py",
3663
"recv.py",
3764
"recv_track_last_enqueued_event_prop.py",
3865
"recv_with_checkpoint_by_event_count.py",
@@ -87,45 +114,104 @@
87114
}
88115

89116

117+
def run_check_call_with_timeout(
118+
command_array,
119+
working_directory,
120+
acceptable_return_codes=[],
121+
always_exit=False,
122+
timeout=None,
123+
pass_if_timeout=False
124+
):
125+
"""This is copied from common_tasks.py with some additions.
126+
Don't want to break anyone that's using the original code.
127+
"""
128+
try:
129+
logging.info(
130+
"Command Array: {0}, Target Working Directory: {1}".format(
131+
command_array, working_directory
132+
)
133+
)
134+
check_call(command_array, cwd=working_directory, timeout=timeout)
135+
except CalledProcessError as err:
136+
if err.returncode not in acceptable_return_codes:
137+
logging.error(err) # , file = sys.stderr
138+
if always_exit:
139+
exit(1)
140+
else:
141+
return err
142+
except TimeoutExpired as err:
143+
if pass_if_timeout:
144+
logging.info(
145+
"Sample timed out successfully"
146+
)
147+
else:
148+
logging.info(
149+
"Fail: Sample timed out"
150+
)
151+
return err
152+
153+
154+
def execute_sample(sample, samples_errors, timed):
155+
if isinstance(sample, tuple):
156+
sample, timeout, pass_if_timeout = sample
157+
158+
if sys.version_info < (3, 5) and sample.endswith("_async.py"):
159+
return
160+
161+
logging.info(
162+
"Testing {}".format(sample)
163+
)
164+
command_array = [sys.executable, sample]
165+
166+
if not timed:
167+
errors = run_check_call(command_array, root_dir)
168+
else:
169+
errors = run_check_call_with_timeout(
170+
command_array, root_dir, timeout=timeout, pass_if_timeout=pass_if_timeout
171+
)
172+
173+
sample_name = os.path.basename(sample)
174+
if errors:
175+
samples_errors.append(sample_name)
176+
logging.info(
177+
"ERROR: {}".format(sample_name)
178+
)
179+
else:
180+
logging.info(
181+
"SUCCESS: {}.".format(sample_name)
182+
)
183+
184+
90185
def run_samples(targeted_package):
91186
logging.info("running samples for {}".format(targeted_package))
92187

93188
samples_errors = []
94189
sample_paths = []
190+
timed_sample_paths = []
191+
95192
samples_dir_path = os.path.abspath(os.path.join(targeted_package, "samples"))
96193
package_name = os.path.basename(targeted_package)
194+
samples_need_timeout = TIMEOUT_SAMPLES.get(package_name, {})
97195

98196
for path, subdirs, files in os.walk(samples_dir_path):
99197
for name in files:
100-
if fnmatch(name, "*.py") and name not in IGNORED_SAMPLES.get(package_name, []):
198+
if fnmatch(name, "*.py") and name in samples_need_timeout:
199+
timeout, pass_if_timeout = samples_need_timeout[name]
200+
timed_sample_paths.append((os.path.abspath(os.path.join(path, name)), timeout, pass_if_timeout))
201+
elif fnmatch(name, "*.py") and name not in IGNORED_SAMPLES.get(package_name, []):
101202
sample_paths.append(os.path.abspath(os.path.join(path, name)))
102203

103-
if not sample_paths:
204+
if not sample_paths and not timed_sample_paths:
104205
logging.info(
105206
"No samples found in {}".format(targeted_package)
106207
)
107208
exit(0)
108209

109210
for sample in sample_paths:
110-
if sys.version_info < (3, 5) and sample.endswith("_async.py"):
111-
continue
211+
execute_sample(sample, samples_errors, timed=False)
112212

113-
logging.info(
114-
"Testing {}".format(sample)
115-
)
116-
command_array = [sys.executable, sample]
117-
errors = run_check_call(command_array, root_dir, always_exit=False)
118-
119-
sample_name = os.path.basename(sample)
120-
if errors:
121-
samples_errors.append(sample_name)
122-
logging.info(
123-
"ERROR: {}".format(sample_name)
124-
)
125-
else:
126-
logging.info(
127-
"SUCCESS: {}.".format(sample_name)
128-
)
213+
for sample in timed_sample_paths:
214+
execute_sample(sample, samples_errors, timed=True)
129215

130216
if samples_errors:
131217
logging.error("Sample(s) that ran with errors: {}".format(samples_errors))

0 commit comments

Comments
 (0)