locust 2.26.1.dev16__py3-none-any.whl → 2.26.1.dev48__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- locust/_version.py +2 -2
- locust/argument_parser.py +61 -150
- locust/clients.py +1 -2
- locust/main.py +1 -1
- locust/runners.py +3 -7
- locust/test/test_main.py +70 -29
- locust/test/test_parser.py +35 -57
- {locust-2.26.1.dev16.dist-info → locust-2.26.1.dev48.dist-info}/METADATA +2 -2
- {locust-2.26.1.dev16.dist-info → locust-2.26.1.dev48.dist-info}/RECORD +13 -13
- {locust-2.26.1.dev16.dist-info → locust-2.26.1.dev48.dist-info}/LICENSE +0 -0
- {locust-2.26.1.dev16.dist-info → locust-2.26.1.dev48.dist-info}/WHEEL +0 -0
- {locust-2.26.1.dev16.dist-info → locust-2.26.1.dev48.dist-info}/entry_points.txt +0 -0
- {locust-2.26.1.dev16.dist-info → locust-2.26.1.dev48.dist-info}/top_level.txt +0 -0
locust/_version.py
CHANGED
@@ -12,5 +12,5 @@ __version__: str
|
|
12
12
|
__version_tuple__: VERSION_TUPLE
|
13
13
|
version_tuple: VERSION_TUPLE
|
14
14
|
|
15
|
-
__version__ = version = '2.26.1.
|
16
|
-
__version_tuple__ = version_tuple = (2, 26, 1, '
|
15
|
+
__version__ = version = '2.26.1.dev48'
|
16
|
+
__version_tuple__ = version_tuple = (2, 26, 1, 'dev48')
|
locust/argument_parser.py
CHANGED
@@ -93,90 +93,54 @@ class LocustTomlConfigParser(configargparse.TomlConfigParser):
|
|
93
93
|
return result
|
94
94
|
|
95
95
|
|
96
|
-
def
|
96
|
+
def parse_locustfile_paths(paths: list[str]) -> list[str]:
|
97
97
|
"""
|
98
|
-
|
99
|
-
"""
|
100
|
-
return os.path.isdir(path) and os.path.exists(os.path.join(path, "__init__.py"))
|
101
|
-
|
102
|
-
|
103
|
-
def find_locustfile(locustfile: str) -> str | None:
|
104
|
-
"""
|
105
|
-
Attempt to locate a locustfile, either explicitly or by searching parent dirs.
|
106
|
-
"""
|
107
|
-
# Obtain env value
|
108
|
-
names = [locustfile]
|
109
|
-
# Create .py version if necessary
|
110
|
-
if not names[0].endswith(".py"):
|
111
|
-
names.append(names[0] + ".py")
|
112
|
-
# Does the name contain path elements?
|
113
|
-
if os.path.dirname(names[0]):
|
114
|
-
# If so, expand home-directory markers and test for existence
|
115
|
-
for name in names:
|
116
|
-
expanded = os.path.expanduser(name)
|
117
|
-
if os.path.exists(expanded):
|
118
|
-
if name.endswith(".py") or _is_package(expanded):
|
119
|
-
return os.path.abspath(expanded)
|
120
|
-
else:
|
121
|
-
# Otherwise, start in cwd and work downwards towards filesystem root
|
122
|
-
path = os.path.abspath(".")
|
123
|
-
while True:
|
124
|
-
for name in names:
|
125
|
-
joined = os.path.join(path, name)
|
126
|
-
if os.path.exists(joined):
|
127
|
-
if name.endswith(".py") or _is_package(joined):
|
128
|
-
return os.path.abspath(joined)
|
129
|
-
parent_path = os.path.dirname(path)
|
130
|
-
if parent_path == path:
|
131
|
-
# we've reached the root path which has been checked this iteration
|
132
|
-
break
|
133
|
-
path = parent_path
|
134
|
-
|
135
|
-
return None
|
136
|
-
|
98
|
+
Returns a list of relative file paths.
|
137
99
|
|
138
|
-
|
139
|
-
|
140
|
-
Returns a list of relative file paths for the Locustfile Picker. If is_directory is True,
|
141
|
-
locustfiles is expected to have a single index which is a directory that will be searched for
|
142
|
-
locustfiles.
|
100
|
+
Args:
|
101
|
+
paths (list[str]): paths taken from the -f command
|
143
102
|
|
144
|
-
|
103
|
+
Returns:
|
104
|
+
list[str]: Parsed locust file paths
|
145
105
|
"""
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
106
|
+
# Parse each path and unpack the returned lists as a single list
|
107
|
+
return [parsed for path in paths for parsed in _parse_locustfile_path(path)]
|
108
|
+
|
109
|
+
|
110
|
+
def _parse_locustfile_path(path: str) -> list[str]:
|
111
|
+
parsed_paths = []
|
112
|
+
if is_url(path):
|
113
|
+
# Download the file and use the new path as locustfile
|
114
|
+
parsed_paths.append(download_locustfile_from_url(path))
|
115
|
+
elif os.path.isdir(path):
|
116
|
+
# Find all .py files in directory tree
|
117
|
+
for root, _dirs, fs in os.walk(path):
|
118
|
+
parsed_paths.extend(
|
119
|
+
[
|
120
|
+
os.path.abspath(os.path.join(root, f))
|
121
|
+
for f in fs
|
122
|
+
if os.path.isfile(os.path.join(root, f)) and f.endswith(".py") and not f.startswith("_")
|
123
|
+
]
|
124
|
+
)
|
125
|
+
if not parsed_paths:
|
126
|
+
sys.stderr.write(f"Could not find any locustfiles in directory '{path}'")
|
161
127
|
sys.exit(1)
|
162
|
-
|
163
|
-
for root, dirs, files in os.walk(locustdir):
|
164
|
-
for file in files:
|
165
|
-
if not file.startswith("_") and file.endswith(".py"):
|
166
|
-
file_path = os.path.join(root, file)
|
167
|
-
file_paths.append(file_path)
|
168
128
|
else:
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
129
|
+
# If file exists add the abspath
|
130
|
+
if os.path.exists(path) and path.endswith(".py"):
|
131
|
+
parsed_paths.append(os.path.abspath(path))
|
132
|
+
else:
|
133
|
+
note_about_file_endings = "Ensure your locustfile ends with '.py' or is a directory with locustfiles. "
|
134
|
+
sys.stderr.write(f"Could not find '{path}'. {note_about_file_endings}See --help for available options.\n")
|
135
|
+
sys.exit(1)
|
175
136
|
|
176
|
-
return
|
137
|
+
return parsed_paths
|
177
138
|
|
178
139
|
|
179
140
|
def is_url(url: str) -> bool:
|
141
|
+
"""
|
142
|
+
Check if path is an url
|
143
|
+
"""
|
180
144
|
try:
|
181
145
|
result = urlparse(url)
|
182
146
|
if result.scheme == "https" or result.scheme == "http":
|
@@ -188,6 +152,10 @@ def is_url(url: str) -> bool:
|
|
188
152
|
|
189
153
|
|
190
154
|
def download_locustfile_from_url(url: str) -> str:
|
155
|
+
"""
|
156
|
+
Attempt to download and save locustfile from url.
|
157
|
+
Returns path to downloaded file.
|
158
|
+
"""
|
191
159
|
try:
|
192
160
|
response = requests.get(url)
|
193
161
|
# Check if response is valid python code
|
@@ -244,7 +212,7 @@ See documentation for more details, including how to set options using a file or
|
|
244
212
|
"-f",
|
245
213
|
"--locustfile",
|
246
214
|
metavar="<filename>",
|
247
|
-
default="locustfile",
|
215
|
+
default="locustfile.py",
|
248
216
|
help="The Python file or module that contains your test, e.g. 'my_test.py'. Accepts multiple comma-separated .py files, a package name/directory or a url to a remote locustfile. Defaults to 'locustfile'.",
|
249
217
|
env_var="LOCUST_LOCUSTFILE",
|
250
218
|
)
|
@@ -314,7 +282,7 @@ def parse_locustfile_option(args=None) -> list[str]:
|
|
314
282
|
parser
|
315
283
|
|
316
284
|
Returns:
|
317
|
-
|
285
|
+
parsed_paths (List): List of locustfile paths
|
318
286
|
"""
|
319
287
|
parser = get_empty_argument_parser(add_help=False)
|
320
288
|
parser.add_argument(
|
@@ -354,6 +322,10 @@ def parse_locustfile_option(args=None) -> list[str]:
|
|
354
322
|
|
355
323
|
options, _ = parser.parse_known_args(args=args)
|
356
324
|
|
325
|
+
if options.help or options.version:
|
326
|
+
# if --help or --version is specified we'll call parse_options which will print the help/version message
|
327
|
+
parse_options(args=args)
|
328
|
+
|
357
329
|
if options.locustfile == "-":
|
358
330
|
if not options.worker:
|
359
331
|
sys.stderr.write(
|
@@ -364,51 +336,21 @@ def parse_locustfile_option(args=None) -> list[str]:
|
|
364
336
|
filename = download_locustfile_from_master(options.master_host, options.master_port)
|
365
337
|
return [filename]
|
366
338
|
|
367
|
-
|
368
|
-
|
369
|
-
download_locustfile_from_url(f) if is_url(f.strip()) else f.strip() for f in options.locustfile.split(",")
|
370
|
-
]
|
339
|
+
locustfile_list = [f.strip() for f in options.locustfile.split(",")]
|
340
|
+
parsed_paths = parse_locustfile_paths(locustfile_list)
|
371
341
|
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
locustfile = None
|
342
|
+
if not parsed_paths:
|
343
|
+
note_about_file_endings = ""
|
344
|
+
user_friendly_locustfile_name = options.locustfile
|
376
345
|
|
377
|
-
if not
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
locustfiles = find_locustfiles(locustfile_as_list, is_directory=False)
|
386
|
-
locustfile = None
|
387
|
-
else:
|
388
|
-
# Is a single file
|
389
|
-
locustfile = find_locustfile(locustfile_as_list[0])
|
390
|
-
locustfiles = []
|
391
|
-
|
392
|
-
if not locustfile:
|
393
|
-
if options.help or options.version:
|
394
|
-
# if --help or --version is specified we'll call parse_options which will print the help/version message
|
395
|
-
parse_options(args=args)
|
396
|
-
note_about_file_endings = ""
|
397
|
-
user_friendly_locustfile_name = options.locustfile
|
398
|
-
if options.locustfile == "locustfile":
|
399
|
-
user_friendly_locustfile_name = "locustfile.py"
|
400
|
-
elif not options.locustfile.endswith(".py"):
|
401
|
-
note_about_file_endings = (
|
402
|
-
"Ensure your locustfile ends with '.py' or is a directory with locustfiles. "
|
403
|
-
)
|
404
|
-
sys.stderr.write(
|
405
|
-
f"Could not find '{user_friendly_locustfile_name}'. {note_about_file_endings}See --help for available options.\n"
|
406
|
-
)
|
407
|
-
sys.exit(1)
|
408
|
-
else:
|
409
|
-
locustfiles.append(locustfile)
|
410
|
-
|
411
|
-
return locustfiles
|
346
|
+
if not options.locustfile.endswith(".py"):
|
347
|
+
note_about_file_endings = "Ensure your locustfile ends with '.py' or is a directory with parsed_paths. "
|
348
|
+
sys.stderr.write(
|
349
|
+
f"Could not find '{user_friendly_locustfile_name}'. {note_about_file_endings}See --help for available options.\n"
|
350
|
+
)
|
351
|
+
sys.exit(1)
|
352
|
+
|
353
|
+
return parsed_paths
|
412
354
|
|
413
355
|
|
414
356
|
def setup_parser_arguments(parser):
|
@@ -857,34 +799,3 @@ def ui_extra_args_dict(args=None) -> dict[str, dict[str, Any]]:
|
|
857
799
|
}
|
858
800
|
|
859
801
|
return extra_args
|
860
|
-
|
861
|
-
|
862
|
-
def locustfile_is_directory(locustfiles: list[str]) -> bool:
|
863
|
-
"""
|
864
|
-
If a user passes in a locustfile without a file extension and there is a directory with the same name,
|
865
|
-
this function defaults to using the file and will raise a warning.
|
866
|
-
In this example, foobar.py will be used:
|
867
|
-
├── src/
|
868
|
-
│ ├── foobar.py
|
869
|
-
├── foobar/
|
870
|
-
│ ├── locustfile.py
|
871
|
-
|
872
|
-
locust -f foobar
|
873
|
-
"""
|
874
|
-
if len(locustfiles) > 1:
|
875
|
-
return False
|
876
|
-
|
877
|
-
locustfile = locustfiles[0]
|
878
|
-
|
879
|
-
# Checking if the locustfile could be both a file and a directory
|
880
|
-
if not locustfile.endswith(".py"):
|
881
|
-
if os.path.isfile(locustfile) and os.path.isdir(locustfile):
|
882
|
-
msg = f"WARNING: Using {locustfile}.py instead of directory {os.path.abspath(locustfile)}\n"
|
883
|
-
sys.stderr.write(msg)
|
884
|
-
|
885
|
-
return False
|
886
|
-
|
887
|
-
if os.path.isdir(locustfile):
|
888
|
-
return True
|
889
|
-
|
890
|
-
return False
|
locust/clients.py
CHANGED
@@ -113,8 +113,7 @@ class HttpSession(requests.Session):
|
|
113
113
|
:param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
|
114
114
|
:param files: (optional) Dictionary of ``'filename': file-like-objects`` for multipart encoding upload.
|
115
115
|
:param auth: (optional) Auth tuple or callable to enable Basic/Digest/Custom HTTP Auth.
|
116
|
-
:param timeout: (optional) How long
|
117
|
-
or a (`connect timeout, read timeout <user/advanced.html#timeouts>`_) tuple.
|
116
|
+
:param timeout: (optional) How long to wait for the server to send data before giving up, as a float, or a :ref:`(connect timeout, read timeout) <timeouts>` tuple.
|
118
117
|
:type timeout: float or tuple
|
119
118
|
:param allow_redirects: (optional) Set to True by default.
|
120
119
|
:type allow_redirects: bool
|
locust/main.py
CHANGED
@@ -126,7 +126,7 @@ def main():
|
|
126
126
|
|
127
127
|
user_classes[key] = value
|
128
128
|
available_user_classes[key] = value
|
129
|
-
available_user_tasks[key] = value.tasks or
|
129
|
+
available_user_tasks[key] = value.tasks or {}
|
130
130
|
|
131
131
|
if len(stats.PERCENTILES_TO_CHART) != 2:
|
132
132
|
logging.error("stats.PERCENTILES_TO_CHART parameter should be 2 parameters \n")
|
locust/runners.py
CHANGED
@@ -1032,23 +1032,19 @@ class MasterRunner(DistributedRunner):
|
|
1032
1032
|
elif msg.type == "locustfile":
|
1033
1033
|
logging.debug("Worker requested locust file")
|
1034
1034
|
assert self.environment.parsed_options
|
1035
|
-
filename =
|
1036
|
-
"locustfile.py"
|
1037
|
-
if self.environment.parsed_options.locustfile == "locustfile"
|
1038
|
-
else self.environment.parsed_options.locustfile
|
1039
|
-
)
|
1035
|
+
filename = self.environment.parsed_options.locustfile
|
1040
1036
|
try:
|
1041
1037
|
with open(filename) as f:
|
1042
1038
|
file_contents = f.read()
|
1043
1039
|
except Exception as e:
|
1044
1040
|
logger.error(
|
1045
|
-
f"--locustfile must be a
|
1041
|
+
f"--locustfile must be a full path to a single locustfile for file distribution to work {e}"
|
1046
1042
|
)
|
1047
1043
|
self.send_message(
|
1048
1044
|
"locustfile",
|
1049
1045
|
client_id=client_id,
|
1050
1046
|
data={
|
1051
|
-
"error": f"locustfile
|
1047
|
+
"error": f"locustfile must be a full path to a single locustfile for file distribution to work (was '{filename}')"
|
1052
1048
|
},
|
1053
1049
|
)
|
1054
1050
|
else:
|
locust/test/test_main.py
CHANGED
@@ -1026,6 +1026,32 @@ class StandaloneIntegrationTests(ProcessIntegrationTest):
|
|
1026
1026
|
self.assertIn("Shutting down (exit code 0)", output)
|
1027
1027
|
self.assertEqual(0, proc.returncode)
|
1028
1028
|
|
1029
|
+
def test_with_package_as_locustfile(self):
|
1030
|
+
with TemporaryDirectory() as temp_dir:
|
1031
|
+
with open(f"{temp_dir}/__init__.py", mode="w"):
|
1032
|
+
with mock_locustfile(dir=temp_dir):
|
1033
|
+
proc = subprocess.Popen(
|
1034
|
+
[
|
1035
|
+
"locust",
|
1036
|
+
"-f",
|
1037
|
+
temp_dir,
|
1038
|
+
"--headless",
|
1039
|
+
"--exit-code-on-error",
|
1040
|
+
"0",
|
1041
|
+
"--run-time",
|
1042
|
+
"2",
|
1043
|
+
],
|
1044
|
+
stdout=PIPE,
|
1045
|
+
stderr=PIPE,
|
1046
|
+
text=True,
|
1047
|
+
)
|
1048
|
+
stdout, stderr = proc.communicate()
|
1049
|
+
self.assertIn("Starting Locust", stderr)
|
1050
|
+
self.assertIn("All users spawned:", stderr)
|
1051
|
+
self.assertIn('"UserSubclass": 1', stderr)
|
1052
|
+
self.assertIn("Shutting down (exit code 0)", stderr)
|
1053
|
+
self.assertEqual(0, proc.returncode)
|
1054
|
+
|
1029
1055
|
def test_command_line_user_selection(self):
|
1030
1056
|
LOCUSTFILE_CONTENT = textwrap.dedent(
|
1031
1057
|
"""
|
@@ -1132,7 +1158,7 @@ class StandaloneIntegrationTests(ProcessIntegrationTest):
|
|
1132
1158
|
stderr=PIPE,
|
1133
1159
|
text=True,
|
1134
1160
|
)
|
1135
|
-
gevent.sleep(
|
1161
|
+
gevent.sleep(2)
|
1136
1162
|
proc.send_signal(signal.SIGTERM)
|
1137
1163
|
stdout, stderr = proc.communicate()
|
1138
1164
|
|
@@ -1801,34 +1827,49 @@ class SecondUser(HttpUser):
|
|
1801
1827
|
"""
|
1802
1828
|
)
|
1803
1829
|
with mock_locustfile(content=LOCUSTFILE_CONTENT) as mocked:
|
1804
|
-
|
1805
|
-
|
1806
|
-
|
1807
|
-
|
1808
|
-
|
1809
|
-
|
1810
|
-
|
1811
|
-
|
1812
|
-
|
1813
|
-
|
1814
|
-
|
1815
|
-
|
1816
|
-
|
1817
|
-
|
1818
|
-
|
1819
|
-
|
1820
|
-
|
1821
|
-
|
1822
|
-
|
1823
|
-
|
1824
|
-
|
1825
|
-
|
1826
|
-
|
1827
|
-
|
1828
|
-
|
1829
|
-
|
1830
|
-
|
1831
|
-
|
1830
|
+
with mock_locustfile() as mocked2:
|
1831
|
+
proc = subprocess.Popen(
|
1832
|
+
[
|
1833
|
+
"locust",
|
1834
|
+
"-f",
|
1835
|
+
f"{mocked.file_path}, {mocked2.file_path}",
|
1836
|
+
"--headless",
|
1837
|
+
"--master",
|
1838
|
+
"-L",
|
1839
|
+
"debug",
|
1840
|
+
],
|
1841
|
+
stderr=STDOUT,
|
1842
|
+
stdout=PIPE,
|
1843
|
+
text=True,
|
1844
|
+
)
|
1845
|
+
proc_worker = subprocess.Popen(
|
1846
|
+
[
|
1847
|
+
"locust",
|
1848
|
+
"-f",
|
1849
|
+
"-",
|
1850
|
+
"--worker",
|
1851
|
+
],
|
1852
|
+
stderr=STDOUT,
|
1853
|
+
stdout=PIPE,
|
1854
|
+
text=True,
|
1855
|
+
)
|
1856
|
+
|
1857
|
+
try:
|
1858
|
+
stdout = proc_worker.communicate(timeout=5)[0]
|
1859
|
+
self.assertIn(
|
1860
|
+
"Got error from master: locustfile must be a full path to a single locustfile for file distribution to work",
|
1861
|
+
stdout,
|
1862
|
+
)
|
1863
|
+
proc.kill()
|
1864
|
+
master_stdout = proc.communicate()[0]
|
1865
|
+
self.assertIn(
|
1866
|
+
"--locustfile must be a full path to a single locustfile for file distribution", master_stdout
|
1867
|
+
)
|
1868
|
+
except Exception:
|
1869
|
+
proc.kill()
|
1870
|
+
proc_worker.kill()
|
1871
|
+
stdout, worker_stderr = proc_worker.communicate()
|
1872
|
+
assert False, f"worker never finished: {stdout}"
|
1832
1873
|
|
1833
1874
|
def test_json_schema(self):
|
1834
1875
|
LOCUSTFILE_CONTENT = textwrap.dedent(
|
locust/test/test_parser.py
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
import locust
|
2
2
|
from locust.argument_parser import (
|
3
|
-
find_locustfiles,
|
4
3
|
get_parser,
|
5
|
-
locustfile_is_directory,
|
6
4
|
parse_locustfile_option,
|
5
|
+
parse_locustfile_paths,
|
7
6
|
parse_options,
|
8
7
|
ui_extra_args_dict,
|
9
8
|
)
|
@@ -91,6 +90,7 @@ class TestArgumentParser(LocustTestCase):
|
|
91
90
|
super().setUp()
|
92
91
|
self.parent_dir = TemporaryDirectory()
|
93
92
|
self.child_dir = TemporaryDirectory(dir=self.parent_dir.name)
|
93
|
+
self.child_dir2 = TemporaryDirectory(dir=self.parent_dir.name)
|
94
94
|
|
95
95
|
def tearDown(self):
|
96
96
|
super().tearDown()
|
@@ -257,6 +257,33 @@ class TestArgumentParser(LocustTestCase):
|
|
257
257
|
]
|
258
258
|
)
|
259
259
|
|
260
|
+
def test_parse_locustfile_and_directory(self):
|
261
|
+
with mock_locustfile(filename_prefix="mock_locustfile1", dir=self.parent_dir.name) as mock_locustfile1:
|
262
|
+
with mock_locustfile(filename_prefix="mock_locustfile2", dir=self.parent_dir.name) as mock_locustfile2:
|
263
|
+
with mock_locustfile(filename_prefix="mock_locustfile3", dir=self.child_dir.name) as mock_locustfile3:
|
264
|
+
locustfiles = parse_locustfile_option(
|
265
|
+
args=[
|
266
|
+
"-f",
|
267
|
+
f"{mock_locustfile1.file_path},{self.child_dir.name}",
|
268
|
+
]
|
269
|
+
)
|
270
|
+
self.assertIn(mock_locustfile1.file_path, locustfiles)
|
271
|
+
self.assertNotIn(mock_locustfile2.file_path, locustfiles)
|
272
|
+
self.assertIn(mock_locustfile3.file_path, locustfiles)
|
273
|
+
|
274
|
+
def test_parse_multiple_directories(self):
|
275
|
+
with mock_locustfile(filename_prefix="mock_locustfile1", dir=self.child_dir.name) as mock_locustfile1:
|
276
|
+
with mock_locustfile(filename_prefix="mock_locustfile2", dir=self.child_dir2.name) as mock_locustfile2:
|
277
|
+
locustfiles = parse_locustfile_option(
|
278
|
+
args=[
|
279
|
+
"-f",
|
280
|
+
f"{self.child_dir.name},{self.child_dir2.name}",
|
281
|
+
]
|
282
|
+
)
|
283
|
+
|
284
|
+
self.assertIn(mock_locustfile1.file_path, locustfiles)
|
285
|
+
self.assertIn(mock_locustfile2.file_path, locustfiles)
|
286
|
+
|
260
287
|
def test_parse_locustfile_invalid_directory_error(self):
|
261
288
|
with mock.patch("sys.stderr", new=StringIO()):
|
262
289
|
with self.assertRaises(SystemExit):
|
@@ -377,7 +404,7 @@ class TestFindLocustfiles(LocustTestCase):
|
|
377
404
|
with mock_locustfile(dir=self.parent_dir1.name) as mocked1:
|
378
405
|
with mock_locustfile(dir=self.child_dir.name) as mocked2:
|
379
406
|
with mock_locustfile(dir=self.child_dir.name) as mocked3:
|
380
|
-
locustfiles =
|
407
|
+
locustfiles = parse_locustfile_paths([self.parent_dir1.name])
|
381
408
|
|
382
409
|
self.assertIn(mocked1.file_path, locustfiles)
|
383
410
|
self.assertIn(mocked2.file_path, locustfiles)
|
@@ -387,13 +414,13 @@ class TestFindLocustfiles(LocustTestCase):
|
|
387
414
|
def test_find_locustfiles_error_if_directory_doesnt_exist(self):
|
388
415
|
with mock.patch("sys.stderr", new=StringIO()):
|
389
416
|
with self.assertRaises(SystemExit):
|
390
|
-
|
417
|
+
parse_locustfile_paths(["some_directory"])
|
391
418
|
|
392
419
|
def test_find_locustfiles_ignores_invalid_files_in_directory(self):
|
393
420
|
with NamedTemporaryFile(suffix=".py", prefix="_", dir=self.parent_dir1.name) as invalid_file1:
|
394
421
|
with NamedTemporaryFile(suffix=".txt", prefix="", dir=self.parent_dir1.name) as invalid_file2:
|
395
422
|
with mock_locustfile(filename_prefix="mock_locustfile1", dir=self.parent_dir1.name) as mock_locustfile1:
|
396
|
-
locustfiles =
|
423
|
+
locustfiles = parse_locustfile_paths([self.parent_dir1.name])
|
397
424
|
|
398
425
|
self.assertIn(mock_locustfile1.file_path, locustfiles)
|
399
426
|
self.assertNotIn(invalid_file1.name, locustfiles)
|
@@ -403,7 +430,7 @@ class TestFindLocustfiles(LocustTestCase):
|
|
403
430
|
def test_find_locustfiles_with_multiple_locustfiles(self):
|
404
431
|
with mock_locustfile() as mocked1:
|
405
432
|
with mock_locustfile() as mocked2:
|
406
|
-
locustfiles =
|
433
|
+
locustfiles = parse_locustfile_paths([mocked1.file_path, mocked2.file_path])
|
407
434
|
|
408
435
|
self.assertIn(mocked1.file_path, locustfiles)
|
409
436
|
self.assertIn(mocked2.file_path, locustfiles)
|
@@ -415,58 +442,9 @@ class TestFindLocustfiles(LocustTestCase):
|
|
415
442
|
with mock_locustfile() as valid_file:
|
416
443
|
with self.assertRaises(SystemExit):
|
417
444
|
invalid_file = NamedTemporaryFile(suffix=".txt")
|
418
|
-
|
419
|
-
|
420
|
-
def test_find_locustfiles_error_if_invalid_directory(self):
|
421
|
-
with mock.patch("sys.stderr", new=StringIO()):
|
422
|
-
with mock_locustfile() as valid_file:
|
423
|
-
with self.assertRaises(SystemExit):
|
424
|
-
find_locustfiles([valid_file.file_path], True)
|
445
|
+
parse_locustfile_paths([valid_file.file_path, invalid_file.name])
|
425
446
|
|
426
447
|
def test_find_locustfiles_error_if_multiple_values_for_directory(self):
|
427
448
|
with mock.patch("sys.stderr", new=StringIO()):
|
428
449
|
with self.assertRaises(SystemExit):
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
class TestLocustfileIsDirectory(LocustTestCase):
|
433
|
-
def setUp(self):
|
434
|
-
super().setUp()
|
435
|
-
self.random_prefix = "locust/test/foobar_" + str(randint(1000, 9999))
|
436
|
-
self.mock_filename = self.random_prefix + ".py"
|
437
|
-
|
438
|
-
self.mock_locustfile = open(self.mock_filename, "w")
|
439
|
-
self.mock_locustfile.close()
|
440
|
-
self.mock_dir = os.mkdir(self.random_prefix)
|
441
|
-
|
442
|
-
def tearDown(self):
|
443
|
-
super().tearDown()
|
444
|
-
os.remove(self.mock_filename)
|
445
|
-
os.rmdir(self.random_prefix)
|
446
|
-
|
447
|
-
def test_locustfile_is_directory_single_locustfile(self):
|
448
|
-
with mock_locustfile() as mocked:
|
449
|
-
is_dir = locustfile_is_directory([mocked.file_path])
|
450
|
-
assert not is_dir
|
451
|
-
|
452
|
-
def test_locustfile_is_directory_single_locustfile_without_file_extension(self):
|
453
|
-
prefix_name = "foobar"
|
454
|
-
with NamedTemporaryFile(prefix=prefix_name, suffix=".py"):
|
455
|
-
is_dir = locustfile_is_directory([prefix_name])
|
456
|
-
assert not is_dir
|
457
|
-
|
458
|
-
def test_locustfile_is_directory_multiple_locustfiles(self):
|
459
|
-
with mock_locustfile() as mocked1:
|
460
|
-
with mock_locustfile() as mocked2:
|
461
|
-
is_dir = locustfile_is_directory([mocked1.file_path, mocked2.file_path])
|
462
|
-
assert not is_dir
|
463
|
-
|
464
|
-
def test_locustfile_is_directory_true_if_directory(self):
|
465
|
-
with TemporaryDirectory() as mocked_dir:
|
466
|
-
is_dir = locustfile_is_directory([mocked_dir])
|
467
|
-
assert is_dir
|
468
|
-
|
469
|
-
def test_locustfile_is_directory_false_if_file_and_directory_share_the_same_name(self):
|
470
|
-
"""See locustfile_is_directory docstring of an example of this usecase"""
|
471
|
-
is_dir = locustfile_is_directory([self.random_prefix, self.mock_filename])
|
472
|
-
assert not is_dir
|
450
|
+
parse_locustfile_paths([self.parent_dir1.name, self.parent_dir2.name])
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: locust
|
3
|
-
Version: 2.26.1.
|
3
|
+
Version: 2.26.1.dev48
|
4
4
|
Summary: Developer friendly load testing framework
|
5
5
|
License: MIT
|
6
6
|
Project-URL: Homepage, https://github.com/locustio/locust
|
@@ -105,7 +105,7 @@ Locust's code base is intentionally kept small and doesn't solve everything out
|
|
105
105
|
* [Send real time reporting data to TimescaleDB and visualize it in Grafana](https://github.com/SvenskaSpel/locust-plugins/blob/master/locust_plugins/dashboards/README.md)
|
106
106
|
* [Wrap calls to handle the peculiarities of your REST API](https://github.com/SvenskaSpel/locust-plugins/blob/8af21862d8129a5c3b17559677fe92192e312d8f/examples/rest_ex.py#L87)
|
107
107
|
* [Use a totally custom load shape/profile](https://docs.locust.io/en/latest/custom-load-shape.html#custom-load-shape)
|
108
|
-
* ...
|
108
|
+
* [...](https://github.com/locustio/locust/wiki/Extensions)
|
109
109
|
|
110
110
|
## Links
|
111
111
|
|
@@ -1,8 +1,8 @@
|
|
1
1
|
locust/__init__.py,sha256=g6oA-Ba_hs3gLWVf5MKJ1mvfltI8MFnDWG8qslqm8yg,1402
|
2
2
|
locust/__main__.py,sha256=vBQ82334kX06ImDbFlPFgiBRiLIinwNk3z8Khs6hd74,31
|
3
|
-
locust/_version.py,sha256=
|
4
|
-
locust/argument_parser.py,sha256=
|
5
|
-
locust/clients.py,sha256
|
3
|
+
locust/_version.py,sha256=gadSK3eqNa51B7ixflfmjSwqkQoR97QFFuc15b40Crw,428
|
4
|
+
locust/argument_parser.py,sha256=izMXLuMZWUpS6m8SrGRmOjLfPPuYWXCvQFicRmn-a90,28774
|
5
|
+
locust/clients.py,sha256=YKuAyMAbxs8_-w7XJw0hc67KFBNNLxibsw6FwiS01Q8,14781
|
6
6
|
locust/debug.py,sha256=We6Z9W0btkKSc7PxWmrZx-xMynvOOsKhG6jmDgQin0g,5134
|
7
7
|
locust/dispatch.py,sha256=S2pAMOlbadOrtMTLTDkq1Pvqes3HVUdZl-K5SDss6ig,19313
|
8
8
|
locust/env.py,sha256=nd6ui1bv6n-kkLkP3r61ZkskDY627dsKOAkYHhtOW7o,12472
|
@@ -11,9 +11,9 @@ locust/exception.py,sha256=jGgJ32ubuf4pWdlaVOkbh2Y0LlG0_DHi-lv3ib8ppOE,1791
|
|
11
11
|
locust/html.py,sha256=IrOYxmmowzcO96c9fytzR4U0lifTJyMTA7Rd96WOXkk,5708
|
12
12
|
locust/input_events.py,sha256=VQIrgXaoph3JgLo6REKtPBThEPUXYXG5Kcedly5aRYc,3272
|
13
13
|
locust/log.py,sha256=2IVp9YL4ZPfWdj3sBFuOHfgneg3g7m7tUGR-sy2s3E8,3155
|
14
|
-
locust/main.py,sha256=
|
14
|
+
locust/main.py,sha256=Un9THvATWEOewIeeoLkecxBluxQcoC7BkFhmk_UgTxM,28150
|
15
15
|
locust/py.typed,sha256=gkWLl8yD4mIZnNYYAIRM8g9VarLvWmTAFeUfEbxJLBw,65
|
16
|
-
locust/runners.py,sha256=
|
16
|
+
locust/runners.py,sha256=MGgzDU6_hhZ58Myn6bAWY92NQJKuwV9NyP4B9SdN9ek,67797
|
17
17
|
locust/shape.py,sha256=t-lwBS8LOjWcKXNL7j2U3zroIXJ1b0fazUwpRYQOKXw,1973
|
18
18
|
locust/stats.py,sha256=l2cxxVre8dvA4MIOD_ZKNj_fYySz5gTGC2f9Rc4-CL0,46134
|
19
19
|
locust/web.py,sha256=zj0Lm3tQq0MhbeJ1oKROnvauNibwUSBXsTehw2q2nAA,28251
|
@@ -61,9 +61,9 @@ locust/test/test_interruptable_task.py,sha256=LZKSV-aJNnwfvAxguz6SckBEuGEnfGimoI
|
|
61
61
|
locust/test/test_load_locustfile.py,sha256=v-muHoM-CYu8t7DXm4AQtFP2q8RYfnTTUBqj7uVqhig,8494
|
62
62
|
locust/test/test_locust_class.py,sha256=oGhhOX848jHRQnIfFlhLlW-kHGYLyYsfDX8hM07Ro7g,25506
|
63
63
|
locust/test/test_log.py,sha256=YPY6vgTAy1KaNU2qoVvQrTH5x_mzRrljEHrkSBy3yxs,7553
|
64
|
-
locust/test/test_main.py,sha256=
|
64
|
+
locust/test/test_main.py,sha256=Ae3F8KTj65YakX_S8eckhJNbqKrRsivoPkFoZaHeyWI,85344
|
65
65
|
locust/test/test_old_wait_api.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
66
|
-
locust/test/test_parser.py,sha256
|
66
|
+
locust/test/test_parser.py,sha256=-2VO5Dopg-VoWvIgXrmr7GN40cqrnjUoctBHmVlyewg,17826
|
67
67
|
locust/test/test_runners.py,sha256=6FPd-3Glp5_xtVIE8yCHDonM0aYJ0A6He5KSxuWOk34,159381
|
68
68
|
locust/test/test_sequential_taskset.py,sha256=QjVMWWfGHn9hU5AvPxRDU7Vo5DcVW1VkMVfDA0k9OPE,3398
|
69
69
|
locust/test/test_stats.py,sha256=F51VkL3k3y4OhYBlRyV6vWzisenSAOmSWKy2IPVrnWM,33929
|
@@ -95,9 +95,9 @@ locust/webui/dist/report.html,sha256=sOdZZVgZbqgu86BBCSQf3uQUYXgmgSnXF32JpnyAII8
|
|
95
95
|
locust/webui/dist/assets/favicon.ico,sha256=IUl-rYqfpHdV38e-s0bkmFIeLS-n3Ug0DQxk-h202hI,8348
|
96
96
|
locust/webui/dist/assets/index-941b6e82.js,sha256=G3n5R81Svt0HzbWaV3AV20jLWGLr4X50UZ-Adu2KcxU,1645614
|
97
97
|
locust/webui/dist/assets/logo.png,sha256=EIVPqr6wE_yqguHaqFHIsH0ZACLSrvNWyYO7PbyIj4w,19299
|
98
|
-
locust-2.26.1.
|
99
|
-
locust-2.26.1.
|
100
|
-
locust-2.26.1.
|
101
|
-
locust-2.26.1.
|
102
|
-
locust-2.26.1.
|
103
|
-
locust-2.26.1.
|
98
|
+
locust-2.26.1.dev48.dist-info/LICENSE,sha256=78XGpIn3fHVBfaxlPNUfjVufSN7QsdhpJMRJHv2AFpo,1095
|
99
|
+
locust-2.26.1.dev48.dist-info/METADATA,sha256=9VQCOppNg6gNaf0Np6zUnh-1qPSGR-2gm52m8ewpeJM,7301
|
100
|
+
locust-2.26.1.dev48.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
101
|
+
locust-2.26.1.dev48.dist-info/entry_points.txt,sha256=RAdt8Ku-56m7bFjmdj-MBhbF6h4NX7tVODR9QNnOg0E,44
|
102
|
+
locust-2.26.1.dev48.dist-info/top_level.txt,sha256=XSsjgPA8Ggf9TqKVbkwSqZFuPlZ085X13M9orDycE20,7
|
103
|
+
locust-2.26.1.dev48.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|