locust 2.25.1.dev8__py3-none-any.whl → 2.26.0__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 +2 -3
- locust/clients.py +1 -1
- locust/contrib/fasthttp.py +3 -3
- locust/dispatch.py +3 -18
- locust/event.py +2 -1
- locust/html.py +1 -2
- locust/input_events.py +1 -2
- locust/main.py +8 -25
- locust/runners.py +1 -3
- locust/stats.py +5 -6
- locust/test/mock_logging.py +2 -3
- locust/test/test_debugging.py +1 -2
- locust/test/test_dispatch.py +7 -33
- locust/test/test_env.py +3 -3
- locust/test/test_fasthttp.py +9 -9
- locust/test/test_http.py +7 -7
- locust/test/test_interruptable_task.py +1 -2
- locust/test/test_locust_class.py +4 -4
- locust/test/test_main.py +7 -8
- locust/test/test_parser.py +9 -8
- locust/test/test_runners.py +34 -26
- locust/test/test_web.py +2 -6
- locust/test/testcases.py +2 -4
- locust/user/task.py +1 -2
- locust/web.py +1 -2
- {locust-2.25.1.dev8.dist-info → locust-2.26.0.dist-info}/METADATA +3 -3
- {locust-2.25.1.dev8.dist-info → locust-2.26.0.dist-info}/RECORD +32 -32
- {locust-2.25.1.dev8.dist-info → locust-2.26.0.dist-info}/LICENSE +0 -0
- {locust-2.25.1.dev8.dist-info → locust-2.26.0.dist-info}/WHEEL +0 -0
- {locust-2.25.1.dev8.dist-info → locust-2.26.0.dist-info}/entry_points.txt +0 -0
- {locust-2.25.1.dev8.dist-info → locust-2.26.0.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.
|
16
|
-
__version_tuple__ = version_tuple = (2,
|
15
|
+
__version__ = version = '2.26.0'
|
16
|
+
__version_tuple__ = version_tuple = (2, 26, 0)
|
locust/argument_parser.py
CHANGED
@@ -80,8 +80,7 @@ class LocustTomlConfigParser(configargparse.TomlConfigParser):
|
|
80
80
|
result = OrderedDict()
|
81
81
|
|
82
82
|
for section in self.sections:
|
83
|
-
data
|
84
|
-
if data:
|
83
|
+
if data := configargparse.get_toml_section(config, section):
|
85
84
|
for key, value in data.items():
|
86
85
|
if isinstance(value, list):
|
87
86
|
result[key] = value
|
@@ -667,7 +666,7 @@ Typically ONLY these options (and --locustfile) need to be specified on workers,
|
|
667
666
|
nargs="*",
|
668
667
|
metavar="<tag>",
|
669
668
|
env_var="LOCUST_TAGS",
|
670
|
-
help="List of tags to include in the test, so only tasks with
|
669
|
+
help="List of tags to include in the test, so only tasks with at least one matching tag will be executed",
|
671
670
|
)
|
672
671
|
tag_group.add_argument(
|
673
672
|
"-E",
|
locust/clients.py
CHANGED
locust/contrib/fasthttp.py
CHANGED
@@ -12,11 +12,12 @@ import socket
|
|
12
12
|
import time
|
13
13
|
import traceback
|
14
14
|
from base64 import b64encode
|
15
|
+
from collections.abc import Generator
|
15
16
|
from contextlib import contextmanager
|
16
17
|
from http.cookiejar import CookieJar
|
17
18
|
from json.decoder import JSONDecodeError
|
18
19
|
from ssl import SSLError
|
19
|
-
from typing import Any, Callable,
|
20
|
+
from typing import Any, Callable, cast
|
20
21
|
from urllib.parse import urlparse, urlunparse
|
21
22
|
|
22
23
|
import gevent
|
@@ -404,8 +405,7 @@ class FastHttpUser(User):
|
|
404
405
|
except Exception as e:
|
405
406
|
error_lines = []
|
406
407
|
for l in traceback.format_exc().split("\n"):
|
407
|
-
m
|
408
|
-
if m:
|
408
|
+
if m := self._callstack_regex.match(l):
|
409
409
|
filename = re.sub(r"/(home|Users/\w*)/", "~/", m.group(1))
|
410
410
|
error_lines.append(filename + ":" + m.group(2) + m.group(3))
|
411
411
|
short_resp = resp.text[:200] if resp.text else resp.text
|
locust/dispatch.py
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import contextlib
|
4
|
-
import functools
|
5
4
|
import itertools
|
6
5
|
import math
|
7
|
-
import sys
|
8
6
|
import time
|
9
7
|
from collections import defaultdict
|
8
|
+
from collections.abc import Generator, Iterator
|
10
9
|
from operator import attrgetter
|
11
|
-
from typing import TYPE_CHECKING
|
10
|
+
from typing import TYPE_CHECKING
|
12
11
|
|
13
12
|
import gevent
|
14
13
|
from roundrobin import smooth
|
@@ -18,20 +17,6 @@ if TYPE_CHECKING:
|
|
18
17
|
from locust.runners import WorkerNode
|
19
18
|
|
20
19
|
|
21
|
-
def compatible_math_gcd(*args: int) -> int:
|
22
|
-
"""
|
23
|
-
This function is a workaround for the fact that `math.gcd` in:
|
24
|
-
- 3.5 <= Python < 3.9 doesn't accept more than two arguments.
|
25
|
-
- 3.9 <= Python can accept more than two arguments.
|
26
|
-
See more at https://docs.python.org/3.9/library/math.html#math.gcd
|
27
|
-
"""
|
28
|
-
if (3, 5) <= sys.version_info < (3, 9):
|
29
|
-
return functools.reduce(math.gcd, args)
|
30
|
-
elif sys.version_info >= (3, 9):
|
31
|
-
return math.gcd(*args)
|
32
|
-
raise NotImplementedError("This function is only implemented for Python from 3.5")
|
33
|
-
|
34
|
-
|
35
20
|
# To profile line-by-line, uncomment the code below (i.e. `import line_profiler ...`) and
|
36
21
|
# place `@profile` on the functions/methods you wish to profile. Then, in the unit test you are
|
37
22
|
# running, use `from locust.dispatch import profile; profile.print_stats()` at the end of the unit test.
|
@@ -406,7 +391,7 @@ class UsersDispatcher(Iterator):
|
|
406
391
|
max_order_of_magnitude = _get_order_of_magnitude(min(abs(u[1]) for u in users))
|
407
392
|
weights = tuple(int(u[1] * max_order_of_magnitude) for u in users)
|
408
393
|
|
409
|
-
greatest_common_divisor =
|
394
|
+
greatest_common_divisor = math.gcd(*weights)
|
410
395
|
normalized_values = [
|
411
396
|
(
|
412
397
|
user[0].__name__,
|
locust/event.py
CHANGED
@@ -3,8 +3,9 @@ from __future__ import annotations
|
|
3
3
|
import logging
|
4
4
|
import time
|
5
5
|
import traceback
|
6
|
+
from collections.abc import Generator
|
6
7
|
from contextlib import contextmanager
|
7
|
-
from typing import Any
|
8
|
+
from typing import Any
|
8
9
|
|
9
10
|
from . import log
|
10
11
|
from .exception import InterruptTaskSet, RescheduleTask, RescheduleTaskImmediately, StopUser
|
locust/html.py
CHANGED
@@ -41,8 +41,7 @@ def get_html_report(
|
|
41
41
|
start_ts = stats.start_time
|
42
42
|
start_time = datetime.datetime.utcfromtimestamp(start_ts).strftime("%Y-%m-%d %H:%M:%S")
|
43
43
|
|
44
|
-
end_ts
|
45
|
-
if end_ts:
|
44
|
+
if end_ts := stats.last_request_timestamp:
|
46
45
|
end_time = datetime.datetime.utcfromtimestamp(end_ts).strftime("%Y-%m-%d %H:%M:%S")
|
47
46
|
else:
|
48
47
|
end_time = start_time
|
locust/input_events.py
CHANGED
locust/main.py
CHANGED
@@ -216,8 +216,7 @@ def main():
|
|
216
216
|
gc.collect() # avoid freezing garbage
|
217
217
|
gc.freeze() # move all objects to perm gen so ref counts dont get updated
|
218
218
|
for _ in range(options.processes):
|
219
|
-
child_pid
|
220
|
-
if child_pid:
|
219
|
+
if child_pid := gevent.fork():
|
221
220
|
children.append(child_pid)
|
222
221
|
logging.debug(f"Started child worker with pid #{child_pid}")
|
223
222
|
else:
|
@@ -250,12 +249,8 @@ def main():
|
|
250
249
|
# nothing more to do, just wait for the children to exit
|
251
250
|
for child_pid in children:
|
252
251
|
_, child_status = os.waitpid(child_pid, 0)
|
253
|
-
|
254
|
-
|
255
|
-
child_exit_code = os.waitstatus_to_exitcode(child_status)
|
256
|
-
exit_code = max(exit_code, child_exit_code)
|
257
|
-
except AttributeError:
|
258
|
-
pass # dammit python 3.8...
|
252
|
+
child_exit_code = os.waitstatus_to_exitcode(child_status)
|
253
|
+
exit_code = max(exit_code, child_exit_code)
|
259
254
|
sys.exit(exit_code)
|
260
255
|
else:
|
261
256
|
options.master = True
|
@@ -270,12 +265,8 @@ def main():
|
|
270
265
|
try:
|
271
266
|
_, child_status = os.waitpid(child_pid, os.WNOHANG)
|
272
267
|
children.remove(child_pid)
|
273
|
-
|
274
|
-
|
275
|
-
child_exit_code = os.waitstatus_to_exitcode(child_status)
|
276
|
-
exit_code = max(exit_code, child_exit_code)
|
277
|
-
except AttributeError:
|
278
|
-
pass # dammit python 3.8...
|
268
|
+
child_exit_code = os.waitstatus_to_exitcode(child_status)
|
269
|
+
exit_code = max(exit_code, child_exit_code)
|
279
270
|
except OSError as e:
|
280
271
|
if e.errno == errno.EINTR:
|
281
272
|
time.sleep(0.1)
|
@@ -291,12 +282,8 @@ def main():
|
|
291
282
|
pass # never mind, process was already dead
|
292
283
|
for child_pid in children:
|
293
284
|
_, child_status = os.waitpid(child_pid, 0)
|
294
|
-
|
295
|
-
|
296
|
-
child_exit_code = os.waitstatus_to_exitcode(child_status)
|
297
|
-
exit_code = max(exit_code, child_exit_code)
|
298
|
-
except AttributeError:
|
299
|
-
pass # dammit python 3.8...
|
285
|
+
child_exit_code = os.waitstatus_to_exitcode(child_status)
|
286
|
+
exit_code = max(exit_code, child_exit_code)
|
300
287
|
if exit_code > 1:
|
301
288
|
logging.error(f"Bad response code from worker children: {exit_code}")
|
302
289
|
# ensure master doesnt finish until output from workers has arrived
|
@@ -327,8 +314,7 @@ def main():
|
|
327
314
|
|
328
315
|
# make sure specified User exists
|
329
316
|
if options.user_classes:
|
330
|
-
missing
|
331
|
-
if missing:
|
317
|
+
if missing := set(options.user_classes) - set(user_classes.keys()):
|
332
318
|
logger.error(f"Unknown User(s): {', '.join(missing)}\n")
|
333
319
|
sys.exit(1)
|
334
320
|
else:
|
@@ -356,9 +342,6 @@ It's not high enough for load testing, and the OS didn't allow locust to increas
|
|
356
342
|
See https://github.com/locustio/locust/wiki/Installation#increasing-maximum-number-of-open-files-limit for more info."""
|
357
343
|
)
|
358
344
|
|
359
|
-
if sys.version_info < (3, 9):
|
360
|
-
logger.warning("Python 3.8 support is deprecated and will be removed soon")
|
361
|
-
|
362
345
|
# create locust Environment
|
363
346
|
locustfile_path = None if not locustfile else os.path.basename(locustfile)
|
364
347
|
|
locust/runners.py
CHANGED
@@ -14,7 +14,7 @@ import time
|
|
14
14
|
import traceback
|
15
15
|
from abc import abstractmethod
|
16
16
|
from collections import defaultdict
|
17
|
-
from collections.abc import MutableMapping
|
17
|
+
from collections.abc import Iterator, MutableMapping, ValuesView
|
18
18
|
from operator import (
|
19
19
|
itemgetter,
|
20
20
|
methodcaller,
|
@@ -24,10 +24,8 @@ from typing import (
|
|
24
24
|
TYPE_CHECKING,
|
25
25
|
Any,
|
26
26
|
Callable,
|
27
|
-
Iterator,
|
28
27
|
NoReturn,
|
29
28
|
TypedDict,
|
30
|
-
ValuesView,
|
31
29
|
cast,
|
32
30
|
)
|
33
31
|
from uuid import uuid4
|
locust/stats.py
CHANGED
@@ -10,6 +10,10 @@ import signal
|
|
10
10
|
import time
|
11
11
|
from abc import abstractmethod
|
12
12
|
from collections import OrderedDict, defaultdict, namedtuple
|
13
|
+
from collections import (
|
14
|
+
OrderedDict as OrderedDictType,
|
15
|
+
)
|
16
|
+
from collections.abc import Iterable
|
13
17
|
from copy import copy
|
14
18
|
from html import escape
|
15
19
|
from itertools import chain
|
@@ -19,16 +23,12 @@ from typing import (
|
|
19
23
|
TYPE_CHECKING,
|
20
24
|
Any,
|
21
25
|
Callable,
|
22
|
-
Iterable,
|
23
26
|
NoReturn,
|
24
27
|
Protocol,
|
25
28
|
TypedDict,
|
26
29
|
TypeVar,
|
27
30
|
cast,
|
28
31
|
)
|
29
|
-
from typing import (
|
30
|
-
OrderedDict as OrderedDictType,
|
31
|
-
)
|
32
32
|
|
33
33
|
import gevent
|
34
34
|
|
@@ -177,8 +177,7 @@ def diff_response_time_dicts(latest: dict[int, int], old: dict[int, int]) -> dic
|
|
177
177
|
"""
|
178
178
|
new = {}
|
179
179
|
for t in latest:
|
180
|
-
diff
|
181
|
-
if diff:
|
180
|
+
if diff := latest[t] - old.get(t, 0):
|
182
181
|
new[t] = diff
|
183
182
|
return new
|
184
183
|
|
locust/test/mock_logging.py
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import logging
|
4
|
-
|
5
|
-
from typing import List, Union, Dict
|
6
4
|
from types import TracebackType
|
5
|
+
from typing import Union
|
7
6
|
|
8
|
-
LogMessage =
|
7
|
+
LogMessage = list[Union[str, dict[str, TracebackType]]]
|
9
8
|
|
10
9
|
|
11
10
|
class MockedLoggingHandler(logging.Handler):
|
locust/test/test_debugging.py
CHANGED
@@ -27,8 +27,7 @@ class TestDebugging(DebugTestCase):
|
|
27
27
|
pass
|
28
28
|
|
29
29
|
def _stop_user():
|
30
|
-
user
|
31
|
-
if user:
|
30
|
+
if user := getattr(debug._env, "single_user_instance", None):
|
32
31
|
user._state = LOCUST_STATE_STOPPING
|
33
32
|
|
34
33
|
t = Timer(1, _stop_user)
|
locust/test/test_dispatch.py
CHANGED
@@ -2167,9 +2167,7 @@ class TestLargeScale(unittest.TestCase):
|
|
2167
2167
|
dispatch_iteration_duration <= tol
|
2168
2168
|
for dispatch_iteration_duration in users_dispatcher.dispatch_iteration_durations
|
2169
2169
|
),
|
2170
|
-
"One or more dispatch took more than {:.0f}s to compute (max = {}ms)"
|
2171
|
-
tol * 1000, 1000 * max(users_dispatcher.dispatch_iteration_durations)
|
2172
|
-
),
|
2170
|
+
f"One or more dispatch took more than {tol * 1000:.0f}s to compute (max = {1000 * max(users_dispatcher.dispatch_iteration_durations)}ms)",
|
2173
2171
|
)
|
2174
2172
|
|
2175
2173
|
self.assertEqual(_user_count(all_dispatched_users[-1]), target_user_count)
|
@@ -2181,9 +2179,7 @@ class TestLargeScale(unittest.TestCase):
|
|
2181
2179
|
self.assertLessEqual(
|
2182
2180
|
max(user_count_on_workers) - min(user_count_on_workers),
|
2183
2181
|
1,
|
2184
|
-
"One or more workers have too much users compared to the other workers when user count is {}"
|
2185
|
-
_user_count(dispatch_users)
|
2186
|
-
),
|
2182
|
+
f"One or more workers have too much users compared to the other workers when user count is {_user_count(dispatch_users)}",
|
2187
2183
|
)
|
2188
2184
|
|
2189
2185
|
for i, dispatch_users in enumerate(all_dispatched_users):
|
@@ -2202,9 +2198,7 @@ class TestLargeScale(unittest.TestCase):
|
|
2202
2198
|
self.assertLessEqual(
|
2203
2199
|
error_percent,
|
2204
2200
|
tol,
|
2205
|
-
"Distribution for user class {} is off by more than {}% when user count is {}"
|
2206
|
-
user_class, tol, _user_count(dispatch_users)
|
2207
|
-
),
|
2201
|
+
f"Distribution for user class {user_class} is off by more than {tol}% when user count is {_user_count(dispatch_users)}",
|
2208
2202
|
)
|
2209
2203
|
|
2210
2204
|
def test_ramp_down_from_100_000_to_0_users_with_50_user_classes_and_1000_workers_and_5000_spawn_rate(self):
|
@@ -2236,9 +2230,7 @@ class TestLargeScale(unittest.TestCase):
|
|
2236
2230
|
dispatch_iteration_duration <= tol
|
2237
2231
|
for dispatch_iteration_duration in users_dispatcher.dispatch_iteration_durations
|
2238
2232
|
),
|
2239
|
-
"One or more dispatch took more than {:.0f}ms to compute (max = {}ms)"
|
2240
|
-
tol * 1000, 1000 * max(users_dispatcher.dispatch_iteration_durations)
|
2241
|
-
),
|
2233
|
+
f"One or more dispatch took more than {tol * 1000:.0f}ms to compute (max = {1000 * max(users_dispatcher.dispatch_iteration_durations)}ms)",
|
2242
2234
|
)
|
2243
2235
|
|
2244
2236
|
self.assertEqual(_user_count(all_dispatched_users[-1]), 0)
|
@@ -2250,9 +2242,7 @@ class TestLargeScale(unittest.TestCase):
|
|
2250
2242
|
self.assertLessEqual(
|
2251
2243
|
max(user_count_on_workers) - min(user_count_on_workers),
|
2252
2244
|
1,
|
2253
|
-
"One or more workers have too much users compared to the other workers when user count is {}"
|
2254
|
-
_user_count(dispatch_users)
|
2255
|
-
),
|
2245
|
+
f"One or more workers have too much users compared to the other workers when user count is {_user_count(dispatch_users)}",
|
2256
2246
|
)
|
2257
2247
|
|
2258
2248
|
for dispatch_users in all_dispatched_users[:-1]:
|
@@ -2267,9 +2257,7 @@ class TestLargeScale(unittest.TestCase):
|
|
2267
2257
|
self.assertLessEqual(
|
2268
2258
|
error_percent,
|
2269
2259
|
tol,
|
2270
|
-
"Distribution for user class {} is off by more than {}% when user count is {}"
|
2271
|
-
user_class, tol, _user_count(dispatch_users)
|
2272
|
-
),
|
2260
|
+
f"Distribution for user class {user_class} is off by more than {tol}% when user count is {_user_count(dispatch_users)}",
|
2273
2261
|
)
|
2274
2262
|
|
2275
2263
|
|
@@ -3448,9 +3436,7 @@ class TestRampUpUsersFromZeroWithFixed(unittest.TestCase):
|
|
3448
3436
|
self.target_user_count = target_user_count
|
3449
3437
|
|
3450
3438
|
def __str__(self):
|
3451
|
-
return "<RampUpCase fixed_counts={} weights={} target_user_count={}>"
|
3452
|
-
self.fixed_counts, self.weights, self.target_user_count
|
3453
|
-
)
|
3439
|
+
return f"<RampUpCase fixed_counts={self.fixed_counts} weights={self.weights} target_user_count={self.target_user_count}>"
|
3454
3440
|
|
3455
3441
|
def case_handler(self, cases: list[RampUpCase], expected: list[dict[str, int]], user_classes: list[type[User]]):
|
3456
3442
|
self.assertEqual(len(cases), len(expected))
|
@@ -3734,8 +3720,6 @@ class TestRampUpDifferentUsers(unittest.TestCase):
|
|
3734
3720
|
|
3735
3721
|
worker_node1 = WorkerNode("1")
|
3736
3722
|
|
3737
|
-
sleep_time = 0.2
|
3738
|
-
|
3739
3723
|
user_dispatcher = UsersDispatcher(worker_nodes=[worker_node1], user_classes=[User1, User2, User3])
|
3740
3724
|
|
3741
3725
|
user_dispatcher.new_dispatch(target_user_count=3, spawn_rate=3)
|
@@ -3761,8 +3745,6 @@ class TestRampUpDifferentUsers(unittest.TestCase):
|
|
3761
3745
|
|
3762
3746
|
worker_node1 = WorkerNode("1")
|
3763
3747
|
|
3764
|
-
sleep_time = 0.2
|
3765
|
-
|
3766
3748
|
user_dispatcher = UsersDispatcher(worker_nodes=[worker_node1], user_classes=[User1, User2, User3])
|
3767
3749
|
|
3768
3750
|
user_dispatcher.new_dispatch(target_user_count=10, spawn_rate=10, user_classes=[User2])
|
@@ -3780,8 +3762,6 @@ class TestRampUpDifferentUsers(unittest.TestCase):
|
|
3780
3762
|
|
3781
3763
|
worker_node1 = WorkerNode("1")
|
3782
3764
|
|
3783
|
-
sleep_time = 0.2
|
3784
|
-
|
3785
3765
|
user_dispatcher = UsersDispatcher(worker_nodes=[worker_node1], user_classes=[User1, User2, User3])
|
3786
3766
|
|
3787
3767
|
user_dispatcher.new_dispatch(target_user_count=10, spawn_rate=10, user_classes=[User2])
|
@@ -3802,8 +3782,6 @@ class TestRampUpDifferentUsers(unittest.TestCase):
|
|
3802
3782
|
|
3803
3783
|
worker_node1 = WorkerNode("1")
|
3804
3784
|
|
3805
|
-
sleep_time = 0.2
|
3806
|
-
|
3807
3785
|
user_dispatcher = UsersDispatcher(worker_nodes=[worker_node1], user_classes=[User1, User2, User3])
|
3808
3786
|
|
3809
3787
|
user_dispatcher.new_dispatch(target_user_count=10, spawn_rate=10, user_classes=[User2])
|
@@ -3826,8 +3804,6 @@ class TestRampUpDifferentUsers(unittest.TestCase):
|
|
3826
3804
|
worker_node2 = WorkerNode("2")
|
3827
3805
|
worker_node3 = WorkerNode("3")
|
3828
3806
|
|
3829
|
-
sleep_time = 0.2
|
3830
|
-
|
3831
3807
|
user_dispatcher = UsersDispatcher(
|
3832
3808
|
worker_nodes=[worker_node1, worker_node2, worker_node3], user_classes=[User1, User2, User3]
|
3833
3809
|
)
|
@@ -3886,8 +3862,6 @@ class TestRampUpDifferentUsers(unittest.TestCase):
|
|
3886
3862
|
worker_node2 = WorkerNode("2")
|
3887
3863
|
worker_node3 = WorkerNode("3")
|
3888
3864
|
|
3889
|
-
sleep_time = 0.2
|
3890
|
-
|
3891
3865
|
user_dispatcher = UsersDispatcher(
|
3892
3866
|
worker_nodes=[worker_node1, worker_node2, worker_node3], user_classes=[User1, User2, User3]
|
3893
3867
|
)
|
locust/test/test_env.py
CHANGED
@@ -36,7 +36,7 @@ class TestEnvironment(LocustTestCase):
|
|
36
36
|
|
37
37
|
def test_user_classes_with_same_name_is_error(self):
|
38
38
|
with self.assertRaises(ValueError) as e:
|
39
|
-
|
39
|
+
Environment(user_classes=[MyUserWithSameName1, MyUserWithSameName2])
|
40
40
|
|
41
41
|
self.assertEqual(
|
42
42
|
e.exception.args[0],
|
@@ -186,7 +186,7 @@ class TestEnvironment(LocustTestCase):
|
|
186
186
|
pass
|
187
187
|
|
188
188
|
with self.assertRaises(ValueError) as e:
|
189
|
-
|
189
|
+
Environment(user_classes=[MyUser1, MyUser2])
|
190
190
|
|
191
191
|
self.assertEqual(
|
192
192
|
e.exception.args[0],
|
@@ -269,7 +269,7 @@ class TestEnvironment(LocustTestCase):
|
|
269
269
|
available_user_classes={"User1": MyUser1, "User2": MyUser2},
|
270
270
|
available_user_tasks={"User1": MyUser1.tasks, "User2": MyUser2.tasks},
|
271
271
|
)
|
272
|
-
|
272
|
+
worker_env.create_worker_runner("127.0.0.1", master.server.port)
|
273
273
|
|
274
274
|
master_env.update_user_class({"user_class_name": "User1", "host": "http://localhost", "tasks": ["my_task_2"]})
|
275
275
|
|
locust/test/test_fasthttp.py
CHANGED
@@ -96,7 +96,7 @@ class TestFastHttpSession(WebserverTestCase):
|
|
96
96
|
def test_slow_redirect(self):
|
97
97
|
s = self.get_client()
|
98
98
|
url = "/redirect?url=/redirect&delay=0.5"
|
99
|
-
|
99
|
+
s.get(url)
|
100
100
|
stats = self.runner.stats.get(url, method="GET")
|
101
101
|
self.assertEqual(1, stats.num_requests)
|
102
102
|
self.assertGreater(stats.avg_response_time, 500)
|
@@ -187,7 +187,7 @@ class TestFastHttpSession(WebserverTestCase):
|
|
187
187
|
with s.get("/fail", catch_response=True) as r:
|
188
188
|
r.success()
|
189
189
|
raise OtherException("wtf")
|
190
|
-
except OtherException
|
190
|
+
except OtherException:
|
191
191
|
pass
|
192
192
|
else:
|
193
193
|
self.fail("OtherException should have been raised")
|
@@ -197,14 +197,14 @@ class TestFastHttpSession(WebserverTestCase):
|
|
197
197
|
|
198
198
|
def test_catch_response_default_success(self):
|
199
199
|
s = self.get_client()
|
200
|
-
with s.get("/ultra_fast", catch_response=True)
|
200
|
+
with s.get("/ultra_fast", catch_response=True):
|
201
201
|
pass
|
202
202
|
self.assertEqual(1, self.environment.stats.get("/ultra_fast", "GET").num_requests)
|
203
203
|
self.assertEqual(0, self.environment.stats.get("/ultra_fast", "GET").num_failures)
|
204
204
|
|
205
205
|
def test_catch_response_default_fail(self):
|
206
206
|
s = self.get_client()
|
207
|
-
with s.get("/fail", catch_response=True)
|
207
|
+
with s.get("/fail", catch_response=True):
|
208
208
|
pass
|
209
209
|
self.assertEqual(1, self.environment.stats.total.num_requests)
|
210
210
|
self.assertEqual(1, self.environment.stats.total.num_failures)
|
@@ -301,7 +301,7 @@ class TestRequestStatsWithWebserver(WebserverTestCase):
|
|
301
301
|
|
302
302
|
l = MyUser(self.environment)
|
303
303
|
path = "/no_content_length"
|
304
|
-
|
304
|
+
l.client.get(path)
|
305
305
|
self.assertEqual(
|
306
306
|
self.runner.stats.get(path, "GET").avg_content_length,
|
307
307
|
len("This response does not have content-length in the header"),
|
@@ -313,7 +313,7 @@ class TestRequestStatsWithWebserver(WebserverTestCase):
|
|
313
313
|
|
314
314
|
l = MyUser(self.environment)
|
315
315
|
path = "/no_content_length"
|
316
|
-
|
316
|
+
l.client.get(path, stream=True)
|
317
317
|
self.assertEqual(0, self.runner.stats.get(path, "GET").avg_content_length)
|
318
318
|
|
319
319
|
def test_request_stats_named_endpoint(self):
|
@@ -526,7 +526,7 @@ class TestFastHttpUserClass(WebserverTestCase):
|
|
526
526
|
def test_slow_redirect(self):
|
527
527
|
s = FastHttpSession(self.environment, "http://127.0.0.1:%i" % self.port, user=None)
|
528
528
|
url = "/redirect?url=/redirect&delay=0.5"
|
529
|
-
|
529
|
+
s.get(url)
|
530
530
|
stats = self.runner.stats.get(url, method="GET")
|
531
531
|
self.assertEqual(1, stats.num_requests)
|
532
532
|
self.assertGreater(stats.avg_response_time, 500)
|
@@ -650,7 +650,7 @@ class TestFastHttpCatchResponse(WebserverTestCase):
|
|
650
650
|
self.assertEqual(1, self.num_success)
|
651
651
|
|
652
652
|
def test_catch_response_http_fail(self):
|
653
|
-
with self.user.client.get("/fail", catch_response=True)
|
653
|
+
with self.user.client.get("/fail", catch_response=True):
|
654
654
|
pass
|
655
655
|
self.assertEqual(1, self.num_failures)
|
656
656
|
self.assertEqual(0, self.num_success)
|
@@ -683,7 +683,7 @@ class TestFastHttpCatchResponse(WebserverTestCase):
|
|
683
683
|
class MyTaskSet(TaskSet):
|
684
684
|
@task
|
685
685
|
def interrupted_task(self):
|
686
|
-
with self.client.get("/ultra_fast", catch_response=True)
|
686
|
+
with self.client.get("/ultra_fast", catch_response=True):
|
687
687
|
raise InterruptTaskSet()
|
688
688
|
|
689
689
|
class MyUser(FastHttpUser):
|
locust/test/test_http.py
CHANGED
@@ -65,7 +65,7 @@ class TestHttpSession(WebserverTestCase):
|
|
65
65
|
def test_slow_redirect(self):
|
66
66
|
s = self.get_client()
|
67
67
|
url = "/redirect?url=/redirect&delay=0.5"
|
68
|
-
|
68
|
+
s.get(url)
|
69
69
|
stats = self.runner.stats.get(url, method="GET")
|
70
70
|
self.assertEqual(1, stats.num_requests)
|
71
71
|
self.assertGreater(stats.avg_response_time, 500)
|
@@ -217,7 +217,7 @@ class TestHttpSession(WebserverTestCase):
|
|
217
217
|
with s.get("/fail", catch_response=True) as r:
|
218
218
|
r.success()
|
219
219
|
raise OtherException("wtf")
|
220
|
-
except OtherException
|
220
|
+
except OtherException:
|
221
221
|
pass
|
222
222
|
else:
|
223
223
|
self.fail("OtherException should have been raised")
|
@@ -228,9 +228,9 @@ class TestHttpSession(WebserverTestCase):
|
|
228
228
|
def test_catch_response_response_error(self):
|
229
229
|
s = self.get_client()
|
230
230
|
try:
|
231
|
-
with s.get("/fail", catch_response=True)
|
231
|
+
with s.get("/fail", catch_response=True):
|
232
232
|
raise ResponseError("response error")
|
233
|
-
except ResponseError
|
233
|
+
except ResponseError:
|
234
234
|
self.fail("ResponseError should not have been raised")
|
235
235
|
|
236
236
|
self.assertEqual(1, self.environment.stats.total.num_requests)
|
@@ -238,14 +238,14 @@ class TestHttpSession(WebserverTestCase):
|
|
238
238
|
|
239
239
|
def test_catch_response_default_success(self):
|
240
240
|
s = self.get_client()
|
241
|
-
with s.get("/ultra_fast", catch_response=True)
|
241
|
+
with s.get("/ultra_fast", catch_response=True):
|
242
242
|
pass
|
243
243
|
self.assertEqual(1, self.environment.stats.get("/ultra_fast", "GET").num_requests)
|
244
244
|
self.assertEqual(0, self.environment.stats.get("/ultra_fast", "GET").num_failures)
|
245
245
|
|
246
246
|
def test_catch_response_default_fail(self):
|
247
247
|
s = self.get_client()
|
248
|
-
with s.get("/fail", catch_response=True)
|
248
|
+
with s.get("/fail", catch_response=True):
|
249
249
|
pass
|
250
250
|
self.assertEqual(1, self.environment.stats.total.num_requests)
|
251
251
|
self.assertEqual(1, self.environment.stats.total.num_failures)
|
@@ -260,7 +260,7 @@ class TestHttpSession(WebserverTestCase):
|
|
260
260
|
|
261
261
|
self.environment.events.request.add_listener(on_request)
|
262
262
|
|
263
|
-
with s.get("/wrong_url/01", name="replaced_url_name")
|
263
|
+
with s.get("/wrong_url/01", name="replaced_url_name"):
|
264
264
|
pass
|
265
265
|
|
266
266
|
self.assertIn("for url: replaced_url_name", str(kwargs["exception"]))
|
@@ -3,12 +3,11 @@ from locust.env import Environment
|
|
3
3
|
from locust.exception import StopUser
|
4
4
|
|
5
5
|
from collections import defaultdict
|
6
|
-
from typing import DefaultDict
|
7
6
|
from unittest import TestCase
|
8
7
|
|
9
8
|
|
10
9
|
class InterruptableTaskSet(SequentialTaskSet):
|
11
|
-
counter:
|
10
|
+
counter: defaultdict[str, int] = defaultdict(int)
|
12
11
|
|
13
12
|
def on_start(self):
|
14
13
|
super().on_start()
|
locust/test/test_locust_class.py
CHANGED
@@ -748,19 +748,19 @@ class TestCatchResponse(WebserverTestCase):
|
|
748
748
|
self.assertEqual(1, self.num_failures)
|
749
749
|
self.assertEqual(0, self.num_success)
|
750
750
|
|
751
|
-
with self.locust.client.get("/ultra_fast", catch_response=True)
|
751
|
+
with self.locust.client.get("/ultra_fast", catch_response=True):
|
752
752
|
pass
|
753
753
|
self.assertEqual(1, self.num_failures)
|
754
754
|
self.assertEqual(1, self.num_success)
|
755
755
|
|
756
|
-
with self.locust.client.get("/ultra_fast", catch_response=True)
|
756
|
+
with self.locust.client.get("/ultra_fast", catch_response=True):
|
757
757
|
raise ResponseError("Not working")
|
758
758
|
|
759
759
|
self.assertEqual(2, self.num_failures)
|
760
760
|
self.assertEqual(1, self.num_success)
|
761
761
|
|
762
762
|
def test_catch_response_http_fail(self):
|
763
|
-
with self.locust.client.get("/fail", catch_response=True)
|
763
|
+
with self.locust.client.get("/fail", catch_response=True):
|
764
764
|
pass
|
765
765
|
self.assertEqual(1, self.num_failures)
|
766
766
|
self.assertEqual(0, self.num_success)
|
@@ -793,7 +793,7 @@ class TestCatchResponse(WebserverTestCase):
|
|
793
793
|
class MyTaskSet(TaskSet):
|
794
794
|
@task
|
795
795
|
def interrupted_task(self):
|
796
|
-
with self.client.get("/ultra_fast", catch_response=True)
|
796
|
+
with self.client.get("/ultra_fast", catch_response=True):
|
797
797
|
raise InterruptTaskSet()
|
798
798
|
|
799
799
|
class MyUser(HttpUser):
|
locust/test/test_main.py
CHANGED
@@ -483,7 +483,6 @@ class StandaloneIntegrationTests(ProcessIntegrationTest):
|
|
483
483
|
self.assertIn("Shutting down (exit code 0)", stderr)
|
484
484
|
self.assertEqual(0, proc.returncode)
|
485
485
|
|
486
|
-
@unittest.skipIf(sys.version_info < (3, 9), reason="dies in 3.8 on GH and I cant be bothered to investigate it")
|
487
486
|
def test_default_headless_spawn_options_with_shape(self):
|
488
487
|
content = MOCK_LOCUSTFILE_CONTENT + textwrap.dedent(
|
489
488
|
"""
|
@@ -812,7 +811,7 @@ class StandaloneIntegrationTests(ProcessIntegrationTest):
|
|
812
811
|
try:
|
813
812
|
response = requests.get(f"http://localhost:{port}/")
|
814
813
|
except ConnectionError:
|
815
|
-
|
814
|
+
success = False
|
816
815
|
try:
|
817
816
|
_, stderr = proc.communicate(timeout=5)
|
818
817
|
except subprocess.TimeoutExpired:
|
@@ -1085,7 +1084,7 @@ class StandaloneIntegrationTests(ProcessIntegrationTest):
|
|
1085
1084
|
with mock_locustfile() as mocked:
|
1086
1085
|
with temporary_file("", suffix=".html") as html_report_file_path:
|
1087
1086
|
try:
|
1088
|
-
|
1087
|
+
subprocess.check_output(
|
1089
1088
|
[
|
1090
1089
|
"locust",
|
1091
1090
|
"-f",
|
@@ -1618,9 +1617,10 @@ class SecondUser(HttpUser):
|
|
1618
1617
|
pass
|
1619
1618
|
"""
|
1620
1619
|
)
|
1621
|
-
with
|
1622
|
-
|
1623
|
-
|
1620
|
+
with (
|
1621
|
+
mock_locustfile(content=LOCUSTFILE_CONTENT) as mocked,
|
1622
|
+
patch_env("LOCUST_WAIT_FOR_WORKERS_REPORT_AFTER_RAMP_UP", "0.01") as _,
|
1623
|
+
):
|
1624
1624
|
proc = subprocess.Popen(
|
1625
1625
|
[
|
1626
1626
|
"locust",
|
@@ -2225,7 +2225,6 @@ class AnyUser(User):
|
|
2225
2225
|
|
2226
2226
|
self.assertNotIn("Traceback", stderr)
|
2227
2227
|
self.assertIn("INFO/locust.runners: sys.exit(42) called", stderr)
|
2228
|
-
|
2229
|
-
self.assertEqual(status_code, 42)
|
2228
|
+
self.assertEqual(status_code, 42)
|
2230
2229
|
self.assertNotIn("Traceback", master_stderr)
|
2231
2230
|
self.assertIn("failed to send heartbeat, setting state to missing", master_stderr)
|
locust/test/test_parser.py
CHANGED
@@ -69,6 +69,8 @@ class TestParser(unittest.TestCase):
|
|
69
69
|
web-port = 45787
|
70
70
|
headless = true
|
71
71
|
tags = ["Critical", "Normal"]
|
72
|
+
[tool.something_else]
|
73
|
+
this = "should be ignored by locust"
|
72
74
|
"""
|
73
75
|
|
74
76
|
file.write(config_data)
|
@@ -248,7 +250,7 @@ class TestArgumentParser(LocustTestCase):
|
|
248
250
|
def test_parse_locustfile_empty_directory_error(self):
|
249
251
|
with mock.patch("sys.stderr", new=StringIO()):
|
250
252
|
with self.assertRaises(SystemExit):
|
251
|
-
|
253
|
+
parse_locustfile_option(
|
252
254
|
args=[
|
253
255
|
"-f",
|
254
256
|
self.parent_dir.name,
|
@@ -258,7 +260,7 @@ class TestArgumentParser(LocustTestCase):
|
|
258
260
|
def test_parse_locustfile_invalid_directory_error(self):
|
259
261
|
with mock.patch("sys.stderr", new=StringIO()):
|
260
262
|
with self.assertRaises(SystemExit):
|
261
|
-
|
263
|
+
parse_locustfile_option(
|
262
264
|
args=[
|
263
265
|
"-f",
|
264
266
|
"non_existent_dir",
|
@@ -401,13 +403,12 @@ class TestFindLocustfiles(LocustTestCase):
|
|
401
403
|
def test_find_locustfiles_with_multiple_locustfiles(self):
|
402
404
|
with mock_locustfile() as mocked1:
|
403
405
|
with mock_locustfile() as mocked2:
|
404
|
-
|
405
|
-
locustfiles = find_locustfiles([mocked1.file_path, mocked2.file_path], False)
|
406
|
+
locustfiles = find_locustfiles([mocked1.file_path, mocked2.file_path], False)
|
406
407
|
|
407
|
-
|
408
|
-
|
408
|
+
self.assertIn(mocked1.file_path, locustfiles)
|
409
|
+
self.assertIn(mocked2.file_path, locustfiles)
|
409
410
|
|
410
|
-
|
411
|
+
assert 2 == len(locustfiles)
|
411
412
|
|
412
413
|
def test_find_locustfiles_error_for_invalid_file_extension(self):
|
413
414
|
with mock.patch("sys.stderr", new=StringIO()):
|
@@ -450,7 +451,7 @@ class TestLocustfileIsDirectory(LocustTestCase):
|
|
450
451
|
|
451
452
|
def test_locustfile_is_directory_single_locustfile_without_file_extension(self):
|
452
453
|
prefix_name = "foobar"
|
453
|
-
with NamedTemporaryFile(prefix=prefix_name, suffix=".py")
|
454
|
+
with NamedTemporaryFile(prefix=prefix_name, suffix=".py"):
|
454
455
|
is_dir = locustfile_is_directory([prefix_name])
|
455
456
|
assert not is_dir
|
456
457
|
|
locust/test/test_runners.py
CHANGED
@@ -992,8 +992,9 @@ class TestMasterWorkerRunners(LocustTestCase):
|
|
992
992
|
context={},
|
993
993
|
)
|
994
994
|
|
995
|
-
with
|
996
|
-
"
|
995
|
+
with (
|
996
|
+
mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=0.3),
|
997
|
+
patch_env("LOCUST_WAIT_FOR_WORKERS_REPORT_AFTER_RAMP_UP", "0.1"),
|
997
998
|
):
|
998
999
|
# start a Master runner
|
999
1000
|
options = parse_options(["--enable-rebalancing"])
|
@@ -1419,9 +1420,12 @@ class TestMasterWorkerRunners(LocustTestCase):
|
|
1419
1420
|
return None
|
1420
1421
|
|
1421
1422
|
locust_worker_additional_wait_before_ready_after_stop = 5
|
1422
|
-
with
|
1423
|
-
"
|
1424
|
-
|
1423
|
+
with (
|
1424
|
+
mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=0.3),
|
1425
|
+
patch_env(
|
1426
|
+
"LOCUST_WORKER_ADDITIONAL_WAIT_BEFORE_READY_AFTER_STOP",
|
1427
|
+
str(locust_worker_additional_wait_before_ready_after_stop),
|
1428
|
+
),
|
1425
1429
|
):
|
1426
1430
|
stop_timeout = 5
|
1427
1431
|
master_env = Environment(
|
@@ -1676,9 +1680,12 @@ class TestMasterWorkerRunners(LocustTestCase):
|
|
1676
1680
|
user_class.weight = random.uniform(1, 20)
|
1677
1681
|
|
1678
1682
|
locust_worker_additional_wait_before_ready_after_stop = 5
|
1679
|
-
with
|
1680
|
-
"
|
1681
|
-
|
1683
|
+
with (
|
1684
|
+
mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=0.3),
|
1685
|
+
patch_env(
|
1686
|
+
"LOCUST_WORKER_ADDITIONAL_WAIT_BEFORE_READY_AFTER_STOP",
|
1687
|
+
str(locust_worker_additional_wait_before_ready_after_stop),
|
1688
|
+
),
|
1682
1689
|
):
|
1683
1690
|
stop_timeout = 5
|
1684
1691
|
master_env = Environment(
|
@@ -1706,12 +1713,7 @@ class TestMasterWorkerRunners(LocustTestCase):
|
|
1706
1713
|
while master.state != STATE_STOPPED:
|
1707
1714
|
self.assertTrue(time.time() - ts <= master_env.shape_class.stages[-1][0] + 60, master.state)
|
1708
1715
|
print(
|
1709
|
-
"{:.2f}/{:.2f} | {} | {:.0f} | "
|
1710
|
-
time.time() - ts,
|
1711
|
-
master_env.shape_class.stages[-1][0],
|
1712
|
-
master.state,
|
1713
|
-
sum(master.reported_user_classes_count.values()),
|
1714
|
-
)
|
1716
|
+
f"{time.time() - ts:.2f}/{master_env.shape_class.stages[-1][0]:.2f} | {master.state} | {sum(master.reported_user_classes_count.values()):.0f} | "
|
1715
1717
|
+ json.dumps(dict(sorted(master.reported_user_classes_count.items(), key=itemgetter(0))))
|
1716
1718
|
)
|
1717
1719
|
sleep(1)
|
@@ -1822,9 +1824,12 @@ class TestMasterWorkerRunners(LocustTestCase):
|
|
1822
1824
|
return None
|
1823
1825
|
|
1824
1826
|
locust_worker_additional_wait_before_ready_after_stop = 2
|
1825
|
-
with
|
1826
|
-
"
|
1827
|
-
|
1827
|
+
with (
|
1828
|
+
mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=0.3),
|
1829
|
+
patch_env(
|
1830
|
+
"LOCUST_WORKER_ADDITIONAL_WAIT_BEFORE_READY_AFTER_STOP",
|
1831
|
+
str(locust_worker_additional_wait_before_ready_after_stop),
|
1832
|
+
),
|
1828
1833
|
):
|
1829
1834
|
master_env = Environment(user_classes=[TestUser1], shape_class=TestShape())
|
1830
1835
|
|
@@ -2192,8 +2197,9 @@ class TestMasterRunner(LocustRunnerTestCase):
|
|
2192
2197
|
def my_task(self):
|
2193
2198
|
gevent.sleep(600)
|
2194
2199
|
|
2195
|
-
with
|
2196
|
-
"
|
2200
|
+
with (
|
2201
|
+
mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server,
|
2202
|
+
patch_env("LOCUST_WAIT_FOR_WORKERS_REPORT_AFTER_RAMP_UP", "0.1"),
|
2197
2203
|
):
|
2198
2204
|
master = self.get_runner(user_classes=[TestUser])
|
2199
2205
|
server.mocked_send(Message("client_ready", __version__, "fake_client1"))
|
@@ -2220,8 +2226,9 @@ class TestMasterRunner(LocustRunnerTestCase):
|
|
2220
2226
|
def my_task(self):
|
2221
2227
|
gevent.sleep(600)
|
2222
2228
|
|
2223
|
-
with
|
2224
|
-
"
|
2229
|
+
with (
|
2230
|
+
mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server,
|
2231
|
+
patch_env("LOCUST_WAIT_FOR_WORKERS_REPORT_AFTER_RAMP_UP", "0.1"),
|
2225
2232
|
):
|
2226
2233
|
master = self.get_runner(user_classes=[TestUser])
|
2227
2234
|
server.mocked_send(Message("client_ready", __version__, "fake_client1"))
|
@@ -2291,8 +2298,9 @@ class TestMasterRunner(LocustRunnerTestCase):
|
|
2291
2298
|
def my_task(self):
|
2292
2299
|
gevent.sleep(600)
|
2293
2300
|
|
2294
|
-
with
|
2295
|
-
"
|
2301
|
+
with (
|
2302
|
+
mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server,
|
2303
|
+
patch_env("LOCUST_WAIT_FOR_WORKERS_REPORT_AFTER_RAMP_UP", "0.1"),
|
2296
2304
|
):
|
2297
2305
|
master = self.get_runner(user_classes=[TestUser])
|
2298
2306
|
server.mocked_send(Message("client_ready", __version__, "fake_client1"))
|
@@ -3154,8 +3162,9 @@ class TestMasterRunner(LocustRunnerTestCase):
|
|
3154
3162
|
assert_cache_hits()
|
3155
3163
|
|
3156
3164
|
master._wait_for_workers_report_after_ramp_up.cache_clear()
|
3157
|
-
with
|
3158
|
-
"
|
3165
|
+
with (
|
3166
|
+
mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=1.5),
|
3167
|
+
patch_env("LOCUST_WAIT_FOR_WORKERS_REPORT_AFTER_RAMP_UP", "5.7 * WORKER_REPORT_INTERVAL"),
|
3159
3168
|
):
|
3160
3169
|
self.assertEqual(master._wait_for_workers_report_after_ramp_up(), 5.7 * 1.5)
|
3161
3170
|
assert_cache_hits()
|
@@ -3538,7 +3547,6 @@ class TestWorkerRunner(LocustTestCase):
|
|
3538
3547
|
pass
|
3539
3548
|
|
3540
3549
|
with mock.patch("locust.rpc.rpc.Client", mocked_rpc(raise_on_close=False)) as client:
|
3541
|
-
client_id = id(client)
|
3542
3550
|
worker = self.get_runner(environment=Environment(), user_classes=[MyUser], client=client)
|
3543
3551
|
client.mocked_send(
|
3544
3552
|
Message(
|
locust/test/test_web.py
CHANGED
@@ -1110,11 +1110,7 @@ class TestWebUI(LocustTestCase, _HeaderCheckMixin):
|
|
1110
1110
|
self.environment.available_user_classes = {"User1": MyUser, "User2": MyUser2}
|
1111
1111
|
self.environment.available_user_tasks = {"User1": MyUser.tasks, "User2": MyUser2.tasks}
|
1112
1112
|
|
1113
|
-
|
1114
|
-
available_user_tasks = {"User1": ["my_task", "my_task_2"], "User2": []}
|
1115
|
-
|
1116
|
-
# environment.update_user_class({"user_class_name": "User1", "host": "http://localhost", "tasks": ["my_task_2"]})
|
1117
|
-
response = requests.post(
|
1113
|
+
requests.post(
|
1118
1114
|
"http://127.0.0.1:%i/user" % self.web_port,
|
1119
1115
|
json={"user_class_name": "User1", "host": "http://localhost", "tasks": ["my_task_2"]},
|
1120
1116
|
)
|
@@ -1290,7 +1286,7 @@ class TestModernWebUI(LocustTestCase, _HeaderCheckMixin):
|
|
1290
1286
|
def setUp(self):
|
1291
1287
|
super().setUp()
|
1292
1288
|
|
1293
|
-
|
1289
|
+
get_parser(default_config_files=[])
|
1294
1290
|
self.stats = self.environment.stats
|
1295
1291
|
|
1296
1292
|
self.web_ui = self.environment.create_web_ui("127.0.0.1", 0, modern_ui=True)
|
locust/test/testcases.py
CHANGED
@@ -34,8 +34,7 @@ def fast():
|
|
34
34
|
|
35
35
|
@app.route("/slow")
|
36
36
|
def slow():
|
37
|
-
delay
|
38
|
-
if delay:
|
37
|
+
if delay := request.args.get("delay"):
|
39
38
|
gevent.sleep(float(delay))
|
40
39
|
else:
|
41
40
|
gevent.sleep(random.choice([0.5, 1, 1.5]))
|
@@ -85,8 +84,7 @@ def status_204():
|
|
85
84
|
|
86
85
|
@app.route("/redirect", methods=["GET", "POST"])
|
87
86
|
def do_redirect():
|
88
|
-
delay
|
89
|
-
if delay:
|
87
|
+
if delay := request.args.get("delay"):
|
90
88
|
gevent.sleep(float(delay))
|
91
89
|
url = request.args.get("url", "/ultra_fast")
|
92
90
|
return redirect(url)
|
locust/user/task.py
CHANGED
@@ -10,7 +10,6 @@ from typing import (
|
|
10
10
|
TYPE_CHECKING,
|
11
11
|
Callable,
|
12
12
|
Protocol,
|
13
|
-
Type,
|
14
13
|
TypeVar,
|
15
14
|
final,
|
16
15
|
overload,
|
@@ -25,7 +24,7 @@ if TYPE_CHECKING:
|
|
25
24
|
|
26
25
|
|
27
26
|
logger = logging.getLogger(__name__)
|
28
|
-
TaskT = TypeVar("TaskT", Callable[..., None],
|
27
|
+
TaskT = TypeVar("TaskT", Callable[..., None], type["TaskSet"])
|
29
28
|
|
30
29
|
LOCUST_STATE_RUNNING, LOCUST_STATE_WAITING, LOCUST_STATE_STOPPING = ["running", "waiting", "stopping"]
|
31
30
|
|
locust/web.py
CHANGED
@@ -604,8 +604,7 @@ class WebUI:
|
|
604
604
|
|
605
605
|
options = self.environment.parsed_options
|
606
606
|
|
607
|
-
is_distributed
|
608
|
-
if is_distributed:
|
607
|
+
if is_distributed := isinstance(self.environment.runner, MasterRunner):
|
609
608
|
worker_count = self.environment.runner.worker_count
|
610
609
|
else:
|
611
610
|
worker_count = 0
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: locust
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.26.0
|
4
4
|
Summary: Developer friendly load testing framework
|
5
5
|
License: MIT
|
6
6
|
Project-URL: Homepage, https://github.com/locustio/locust
|
@@ -32,7 +32,7 @@ Requires-Dist: Werkzeug >=2.0.0
|
|
32
32
|
Requires-Dist: requests >=2.26.0
|
33
33
|
Requires-Dist: msgpack >=1.0.0
|
34
34
|
Requires-Dist: pyzmq >=25.0.0
|
35
|
-
Requires-Dist: geventhttpclient
|
35
|
+
Requires-Dist: geventhttpclient ==2.2.1
|
36
36
|
Requires-Dist: ConfigArgParse >=1.5.5
|
37
37
|
Requires-Dist: psutil >=5.9.1
|
38
38
|
Requires-Dist: Flask-Login >=0.6.3
|
@@ -44,7 +44,7 @@ Requires-Dist: tomli >=1.1.0 ; python_version < "3.11"
|
|
44
44
|
# Locust
|
45
45
|
|
46
46
|
[](https://pypi.org/project/locust/)
|
47
|
-

|
48
48
|
[](https://pepy.tech/project/locust)
|
49
49
|
[](https://github.com/locustio/locust/actions?query=workflow%3ATests)
|
50
50
|
[](https://github.com/locustio/locust/graphs/contributors)
|
@@ -1,24 +1,24 @@
|
|
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=fMCGN_nkzccFJR0ERimrQlI738fFJWzDZ_f2UP5Pbqs,413
|
4
|
+
locust/argument_parser.py,sha256=gOyB1rqEEFNVkhGa-oAuCxf573aB_lATSY9w6FlCbHk,32008
|
5
|
+
locust/clients.py,sha256=-vKHkTkUQwYUXUpuROvHdiAbSbOPY8s4V7xFDF5KU1A,14819
|
6
6
|
locust/debug.py,sha256=We6Z9W0btkKSc7PxWmrZx-xMynvOOsKhG6jmDgQin0g,5134
|
7
|
-
locust/dispatch.py,sha256=
|
7
|
+
locust/dispatch.py,sha256=t1gMvCdE7SFq7OIyYEhmKi5nW_8_d__iNUJgf4P_c6c,19502
|
8
8
|
locust/env.py,sha256=nd6ui1bv6n-kkLkP3r61ZkskDY627dsKOAkYHhtOW7o,12472
|
9
|
-
locust/event.py,sha256=
|
9
|
+
locust/event.py,sha256=xgNKbcejxy1TNUfIdgV75KgD2_BOwQmvjrJ4hWuydRw,7740
|
10
10
|
locust/exception.py,sha256=jGgJ32ubuf4pWdlaVOkbh2Y0LlG0_DHi-lv3ib8ppOE,1791
|
11
|
-
locust/html.py,sha256=
|
12
|
-
locust/input_events.py,sha256=
|
11
|
+
locust/html.py,sha256=IrOYxmmowzcO96c9fytzR4U0lifTJyMTA7Rd96WOXkk,5708
|
12
|
+
locust/input_events.py,sha256=WZtjFMJQpPszxgqwZkxzRBOhygHIersHsvt-9S34M9k,3271
|
13
13
|
locust/log.py,sha256=2IVp9YL4ZPfWdj3sBFuOHfgneg3g7m7tUGR-sy2s3E8,3155
|
14
|
-
locust/main.py,sha256=
|
14
|
+
locust/main.py,sha256=emuULU-y1SmHTLcLqfOiZn_SnZzWDcf_IYRmB4Yc348,28194
|
15
15
|
locust/py.typed,sha256=gkWLl8yD4mIZnNYYAIRM8g9VarLvWmTAFeUfEbxJLBw,65
|
16
|
-
locust/runners.py,sha256=
|
16
|
+
locust/runners.py,sha256=OybWcJfhrr_UVWWPfmpiDM3fQF_mrR3D_GcAMArWt78,67970
|
17
17
|
locust/shape.py,sha256=t-lwBS8LOjWcKXNL7j2U3zroIXJ1b0fazUwpRYQOKXw,1973
|
18
|
-
locust/stats.py,sha256=
|
19
|
-
locust/web.py,sha256=
|
18
|
+
locust/stats.py,sha256=l2cxxVre8dvA4MIOD_ZKNj_fYySz5gTGC2f9Rc4-CL0,46134
|
19
|
+
locust/web.py,sha256=tgTRfzAxKBxudjhLkQr5JGppoEOD6hcmZ-i_rGvwOlQ,28245
|
20
20
|
locust/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
21
|
-
locust/contrib/fasthttp.py,sha256=
|
21
|
+
locust/contrib/fasthttp.py,sha256=B_VofSuvb9cehQxlUZnLVvYAr2AquedxeALua4mVOuM,26749
|
22
22
|
locust/rpc/__init__.py,sha256=nVGoHWFQxZjnhCDWjbgXIbmFbN9sizAjkhvSs9_642c,58
|
23
23
|
locust/rpc/protocol.py,sha256=oHR1yHdg_OysxZ2os3HS5Rf5hRi7I0d-3LIvWAn2QF0,1298
|
24
24
|
locust/rpc/zmqrpc.py,sha256=7DLIXzkQr7992zmZwAqNgcqzm7LOQAOQtz0tUGv5-Gg,2637
|
@@ -51,20 +51,20 @@ locust/test/__init__.py,sha256=CaVC4yA4DnCO8EY3LbedPHFg86a9Lqlpe92JuiX3THw,396
|
|
51
51
|
locust/test/fake_module1_for_env_test.py,sha256=dzGYWCr1SSkd8Yyo68paUNrCNW7YY_QgjRb7sM37gG0,164
|
52
52
|
locust/test/fake_module2_for_env_test.py,sha256=dzGYWCr1SSkd8Yyo68paUNrCNW7YY_QgjRb7sM37gG0,164
|
53
53
|
locust/test/mock_locustfile.py,sha256=N9sGjW-BmJ-J_x-5bEOR82VQ0DhR1hki313BHPOWq4g,1273
|
54
|
-
locust/test/mock_logging.py,sha256=
|
55
|
-
locust/test/test_debugging.py,sha256=
|
56
|
-
locust/test/test_dispatch.py,sha256=
|
57
|
-
locust/test/test_env.py,sha256=
|
58
|
-
locust/test/test_fasthttp.py,sha256=
|
59
|
-
locust/test/test_http.py,sha256=
|
60
|
-
locust/test/test_interruptable_task.py,sha256=
|
54
|
+
locust/test/mock_logging.py,sha256=qapKrKhTdlVc8foJB2Hxjn7SB6soaLeAj3VF4A6kZtw,806
|
55
|
+
locust/test/test_debugging.py,sha256=omQ0w5_Xh1xuTBzkd3VavEIircwtlmoOEHcMInY67vU,1053
|
56
|
+
locust/test/test_dispatch.py,sha256=i9fCVV-uYhB2ygIK5r_d3Zzqmxj4vy5X_p4VJiInsEM,167770
|
57
|
+
locust/test/test_env.py,sha256=l0fLl9nubdgzxwFNajmBkJvQc5cO5rOTE4p12lbCbs0,8919
|
58
|
+
locust/test/test_fasthttp.py,sha256=jVA5wWjZxXYW6emzy-lfPC0AOabzT6rDCX0N7DPP9mc,30727
|
59
|
+
locust/test/test_http.py,sha256=VQCVY0inLC0RS-V3E9WHL3vBLGokZjQt0zKSrTNlQmM,12536
|
60
|
+
locust/test/test_interruptable_task.py,sha256=LZKSV-aJNnwfvAxguz6SckBEuGEnfGimoIgVfJ2wQTA,1377
|
61
61
|
locust/test/test_load_locustfile.py,sha256=v-muHoM-CYu8t7DXm4AQtFP2q8RYfnTTUBqj7uVqhig,8494
|
62
|
-
locust/test/test_locust_class.py,sha256=
|
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=lchekHHgQBsY5wOuKbjjVm7U8cJ9qzJWmGSUu_Ebw7k,83553
|
65
65
|
locust/test/test_old_wait_api.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
66
|
-
locust/test/test_parser.py,sha256=
|
67
|
-
locust/test/test_runners.py,sha256=
|
66
|
+
locust/test/test_parser.py,sha256=R2RATAHVC1n4gRYZyRD3yO5P9QMFbruZ3A4dwaw8Up0,18287
|
67
|
+
locust/test/test_runners.py,sha256=rtmm9cV82lc7jqEYW3FTM8CNmLlswTIPr9UbeE5vfPc,159382
|
68
68
|
locust/test/test_sequential_taskset.py,sha256=QjVMWWfGHn9hU5AvPxRDU7Vo5DcVW1VkMVfDA0k9OPE,3398
|
69
69
|
locust/test/test_stats.py,sha256=g5WXKbKtjtPxR_Ixukz04wZUsEC7RkHx5hCNHzofob4,33928
|
70
70
|
locust/test/test_tags.py,sha256=mzhGLPMizSnSItTHLHizYvloxDfuIDAOgelwInyrf28,13138
|
@@ -72,14 +72,14 @@ locust/test/test_taskratio.py,sha256=SQ-sBqeFm2GhkfCD_57-fPzQrk1ilSw3DRb0_nwyxAI
|
|
72
72
|
locust/test/test_users.py,sha256=lp6yAKGK9_MIs9F7s1Vc3561P4oRavhpeVo2y9w3SUU,2135
|
73
73
|
locust/test/test_util.py,sha256=DmFTgNSWWx8zrsx9_ZGO6MsySmBV1H_GzNIVzzyapCM,1229
|
74
74
|
locust/test/test_wait_time.py,sha256=3evSEp6amMWFrzmSYs71MCeIsu7Rtarldb_HnwgSrU0,2353
|
75
|
-
locust/test/test_web.py,sha256=
|
75
|
+
locust/test/test_web.py,sha256=5EL7j8iAZSWlF2t4iGln8euWhC2HQmX6eKbjTSpkq1Q,52413
|
76
76
|
locust/test/test_zmqrpc.py,sha256=kONaZ11hwnneLwaVn7lIDVV7KHpEP2nkxuKhfb9ba3o,2173
|
77
|
-
locust/test/testcases.py,sha256=
|
77
|
+
locust/test/testcases.py,sha256=ZaPYNxSSChAs0nts_13mCGY7WFW8AjXQZdPOvwAK0TY,6961
|
78
78
|
locust/test/util.py,sha256=98HXLClkycNTxLiuy1d3W_tM6dBU9bA-p5ZXMfncaWE,2754
|
79
79
|
locust/user/__init__.py,sha256=S2yvmI_AU9kXirtTIVqiV_Hs7yXzqXvaSgkNo9ig-fk,71
|
80
80
|
locust/user/inspectuser.py,sha256=KgrWHyE5jhK6or58R7soLRf-_st42AaQrR72qbiXw9E,2641
|
81
81
|
locust/user/sequential_taskset.py,sha256=E8yykSZBO-QMcza1frr-7l8Cv_5bbSpjRO6sbkmGpZE,2544
|
82
|
-
locust/user/task.py,sha256=
|
82
|
+
locust/user/task.py,sha256=JvVVCQ1_UQSsahqaEZoFCD-cBXlOJLJ51ewXHNesSAI,16700
|
83
83
|
locust/user/users.py,sha256=qhOW5dDmGbsukWDVb1YDs92D_vbCKRIW60jB5I2bRxs,9950
|
84
84
|
locust/user/wait_time.py,sha256=bGRKMVx4lom75sX3POYJUa1CPeME2bEAXG6CEgxSO5U,2675
|
85
85
|
locust/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -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.
|
99
|
-
locust-2.
|
100
|
-
locust-2.
|
101
|
-
locust-2.
|
102
|
-
locust-2.
|
103
|
-
locust-2.
|
98
|
+
locust-2.26.0.dist-info/LICENSE,sha256=78XGpIn3fHVBfaxlPNUfjVufSN7QsdhpJMRJHv2AFpo,1095
|
99
|
+
locust-2.26.0.dist-info/METADATA,sha256=nEAL6OBW_otmbuSOAv8kOQu8D17K-yvNUt_gwVkDtc4,7248
|
100
|
+
locust-2.26.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
101
|
+
locust-2.26.0.dist-info/entry_points.txt,sha256=RAdt8Ku-56m7bFjmdj-MBhbF6h4NX7tVODR9QNnOg0E,44
|
102
|
+
locust-2.26.0.dist-info/top_level.txt,sha256=XSsjgPA8Ggf9TqKVbkwSqZFuPlZ085X13M9orDycE20,7
|
103
|
+
locust-2.26.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|