locust 2.29.2.dev34__py3-none-any.whl → 2.29.2.dev45__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 +6 -2
- locust/contrib/fasthttp.py +1 -1
- locust/dispatch.py +7 -6
- locust/main.py +0 -1
- {locust-2.29.2.dev34.dist-info → locust-2.29.2.dev45.dist-info}/METADATA +31 -26
- locust-2.29.2.dev45.dist-info/RECORD +49 -0
- locust-2.29.2.dev45.dist-info/WHEEL +4 -0
- locust-2.29.2.dev45.dist-info/entry_points.txt +3 -0
- locust/test/__init__.py +0 -15
- locust/test/fake_module1_for_env_test.py +0 -7
- locust/test/fake_module2_for_env_test.py +0 -7
- locust/test/mock_locustfile.py +0 -56
- locust/test/mock_logging.py +0 -28
- locust/test/test_debugging.py +0 -39
- locust/test/test_dispatch.py +0 -4170
- locust/test/test_env.py +0 -283
- locust/test/test_fasthttp.py +0 -785
- locust/test/test_http.py +0 -325
- locust/test/test_interruptable_task.py +0 -48
- locust/test/test_load_locustfile.py +0 -228
- locust/test/test_locust_class.py +0 -831
- locust/test/test_log.py +0 -237
- locust/test/test_main.py +0 -2264
- locust/test/test_old_wait_api.py +0 -0
- locust/test/test_parser.py +0 -450
- locust/test/test_runners.py +0 -4476
- locust/test/test_sequential_taskset.py +0 -157
- locust/test/test_stats.py +0 -866
- locust/test/test_tags.py +0 -440
- locust/test/test_taskratio.py +0 -94
- locust/test/test_users.py +0 -69
- locust/test/test_util.py +0 -33
- locust/test/test_wait_time.py +0 -79
- locust/test/test_web.py +0 -1257
- locust/test/test_zmqrpc.py +0 -58
- locust/test/testcases.py +0 -248
- locust/test/util.py +0 -88
- locust-2.29.2.dev34.dist-info/RECORD +0 -79
- locust-2.29.2.dev34.dist-info/WHEEL +0 -5
- locust-2.29.2.dev34.dist-info/entry_points.txt +0 -2
- locust-2.29.2.dev34.dist-info/top_level.txt +0 -1
- {locust-2.29.2.dev34.dist-info → locust-2.29.2.dev45.dist-info}/LICENSE +0 -0
locust/test/test_runners.py
DELETED
@@ -1,4476 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import locust
|
4
|
-
from locust import LoadTestShape, __version__, constant, runners
|
5
|
-
from locust.argument_parser import parse_options
|
6
|
-
from locust.dispatch import UsersDispatcher
|
7
|
-
from locust.env import Environment
|
8
|
-
from locust.exception import RPCError, RPCReceiveError, StopUser
|
9
|
-
from locust.log import LogReader
|
10
|
-
from locust.main import create_environment
|
11
|
-
from locust.rpc import Message
|
12
|
-
from locust.runners import (
|
13
|
-
STATE_INIT,
|
14
|
-
STATE_MISSING,
|
15
|
-
STATE_RUNNING,
|
16
|
-
STATE_SPAWNING,
|
17
|
-
STATE_STOPPED,
|
18
|
-
STATE_STOPPING,
|
19
|
-
LocalRunner,
|
20
|
-
WorkerNode,
|
21
|
-
WorkerRunner,
|
22
|
-
)
|
23
|
-
from locust.stats import RequestStats
|
24
|
-
from locust.user import TaskSet, User, task
|
25
|
-
|
26
|
-
import json
|
27
|
-
import logging
|
28
|
-
import random
|
29
|
-
import time
|
30
|
-
import unittest
|
31
|
-
from collections import defaultdict, deque
|
32
|
-
from operator import itemgetter
|
33
|
-
from unittest import mock
|
34
|
-
|
35
|
-
import gevent
|
36
|
-
import requests
|
37
|
-
from gevent import sleep
|
38
|
-
from gevent.pool import Group
|
39
|
-
from gevent.queue import Queue
|
40
|
-
from retry import retry # type: ignore
|
41
|
-
|
42
|
-
from .testcases import LocustTestCase
|
43
|
-
from .util import patch_env
|
44
|
-
|
45
|
-
NETWORK_BROKEN = "network broken"
|
46
|
-
BAD_MESSAGE = "bad message"
|
47
|
-
UNRECOGNIZED_HOST_MESSAGE = "unrecognized host message"
|
48
|
-
UNRECOGNIZED_MESSAGE = "unrecognized message"
|
49
|
-
|
50
|
-
|
51
|
-
def mocked_rpc(raise_on_close=True):
|
52
|
-
class MockedRpcServerClient:
|
53
|
-
queue = Queue()
|
54
|
-
outbox = []
|
55
|
-
raise_error_on_close = raise_on_close
|
56
|
-
|
57
|
-
def __init__(self, *args, **kwargs):
|
58
|
-
pass
|
59
|
-
|
60
|
-
@classmethod
|
61
|
-
def mocked_send(cls, message):
|
62
|
-
cls.queue.put(message.serialize())
|
63
|
-
sleep(0)
|
64
|
-
|
65
|
-
def recv(self):
|
66
|
-
results = self.queue.get()
|
67
|
-
msg = Message.unserialize(results)
|
68
|
-
if msg.data == NETWORK_BROKEN:
|
69
|
-
raise RPCError()
|
70
|
-
return msg
|
71
|
-
|
72
|
-
def send(self, message):
|
73
|
-
self.outbox.append(message)
|
74
|
-
|
75
|
-
def send_to_client(self, message):
|
76
|
-
print(message)
|
77
|
-
self.outbox.append(message)
|
78
|
-
|
79
|
-
@classmethod
|
80
|
-
def get_messages(cls, message_type=None) -> list:
|
81
|
-
return [message for message in cls.outbox if message_type is None or message.type == message_type]
|
82
|
-
|
83
|
-
def recv_from_client(self):
|
84
|
-
results = self.queue.get()
|
85
|
-
msg = Message.unserialize(results)
|
86
|
-
if msg.data == NETWORK_BROKEN:
|
87
|
-
raise RPCError()
|
88
|
-
if msg.data == BAD_MESSAGE:
|
89
|
-
raise RPCReceiveError(BAD_MESSAGE, addr=msg.node_id)
|
90
|
-
if msg.data == UNRECOGNIZED_HOST_MESSAGE:
|
91
|
-
raise RPCReceiveError(UNRECOGNIZED_HOST_MESSAGE, addr="FAKE")
|
92
|
-
if msg.data == UNRECOGNIZED_MESSAGE:
|
93
|
-
raise RPCReceiveError(UNRECOGNIZED_MESSAGE)
|
94
|
-
return msg.node_id, msg
|
95
|
-
|
96
|
-
def close(self, linger=None):
|
97
|
-
if self.raise_error_on_close:
|
98
|
-
raise RPCError()
|
99
|
-
else:
|
100
|
-
pass
|
101
|
-
|
102
|
-
return MockedRpcServerClient
|
103
|
-
|
104
|
-
|
105
|
-
class mocked_options:
|
106
|
-
def __init__(self):
|
107
|
-
self.spawn_rate = 5
|
108
|
-
self.num_users = 5
|
109
|
-
self.host = "/"
|
110
|
-
self.tags = None
|
111
|
-
self.exclude_tags = None
|
112
|
-
self.master_host = "localhost"
|
113
|
-
self.master_port = 5557
|
114
|
-
self.master_bind_host = "*"
|
115
|
-
self.master_bind_port = 5557
|
116
|
-
self.heartbeat_liveness = 3
|
117
|
-
self.heartbeat_interval = 1
|
118
|
-
self.stop_timeout = 0.0
|
119
|
-
self.connection_broken = False
|
120
|
-
|
121
|
-
def reset_stats(self):
|
122
|
-
pass
|
123
|
-
|
124
|
-
|
125
|
-
class HeyAnException(Exception):
|
126
|
-
pass
|
127
|
-
|
128
|
-
|
129
|
-
class LocustRunnerTestCase(LocustTestCase):
|
130
|
-
def __init__(self, *args, **kwargs):
|
131
|
-
super().__init__(*args, **kwargs)
|
132
|
-
self.runner_stopping = False
|
133
|
-
self.runner_stopped = False
|
134
|
-
|
135
|
-
def setUp(self):
|
136
|
-
super().setUp()
|
137
|
-
self.reset_state()
|
138
|
-
|
139
|
-
def reset_state(self):
|
140
|
-
self.runner_stopping = False
|
141
|
-
self.runner_stopped = False
|
142
|
-
|
143
|
-
|
144
|
-
class TestLocustRunner(LocustRunnerTestCase):
|
145
|
-
def test_missing_constructor_call_in_user(self):
|
146
|
-
class BadUser(User):
|
147
|
-
def __init__(self, *args, **kwargs):
|
148
|
-
pass # not calling base class constructor!!!
|
149
|
-
|
150
|
-
@task
|
151
|
-
def t(self):
|
152
|
-
pass
|
153
|
-
|
154
|
-
environment = Environment(user_classes=[BadUser])
|
155
|
-
runner = LocalRunner(environment)
|
156
|
-
with self.assertRaises(AssertionError) as assert_raises_context:
|
157
|
-
runner.spawn_users({BadUser.__name__: 1})
|
158
|
-
self.assertIn(
|
159
|
-
"Attribute 'environment' is missing on user BadUser. Perhaps you defined your own __init__ and forgot to call the base constructor",
|
160
|
-
str(assert_raises_context.exception),
|
161
|
-
)
|
162
|
-
|
163
|
-
def test_cpu_warning(self):
|
164
|
-
_monitor_interval = runners.CPU_MONITOR_INTERVAL
|
165
|
-
runners.CPU_MONITOR_INTERVAL = 2.0
|
166
|
-
try:
|
167
|
-
|
168
|
-
class CpuUser(User):
|
169
|
-
wait_time = constant(0.001)
|
170
|
-
|
171
|
-
@task
|
172
|
-
def cpu_task(self):
|
173
|
-
for i in range(1000000):
|
174
|
-
_ = 3 / 2
|
175
|
-
|
176
|
-
environment = Environment(user_classes=[CpuUser])
|
177
|
-
environment._cpu_warning_event_triggered = False
|
178
|
-
|
179
|
-
def cpu_warning(environment, cpu_usage, **kwargs):
|
180
|
-
environment._cpu_warning_event_triggered = True
|
181
|
-
environment._cpu_usage = cpu_usage
|
182
|
-
|
183
|
-
environment.events.cpu_warning.add_listener(cpu_warning)
|
184
|
-
runner = LocalRunner(environment)
|
185
|
-
self.assertFalse(runner.cpu_warning_emitted)
|
186
|
-
runner.spawn_users({CpuUser.__name__: 1}, wait=False)
|
187
|
-
sleep(2.5)
|
188
|
-
self.assertTrue(environment._cpu_warning_event_triggered)
|
189
|
-
self.assertGreater(environment._cpu_usage, 90)
|
190
|
-
runner.quit()
|
191
|
-
self.assertTrue(runner.cpu_warning_emitted)
|
192
|
-
finally:
|
193
|
-
runners.CPU_MONITOR_INTERVAL = _monitor_interval
|
194
|
-
|
195
|
-
def test_kill_locusts(self):
|
196
|
-
triggered = [False]
|
197
|
-
|
198
|
-
class BaseUser(User):
|
199
|
-
wait_time = constant(1)
|
200
|
-
|
201
|
-
@task
|
202
|
-
class task_set(TaskSet):
|
203
|
-
@task
|
204
|
-
def trigger(self):
|
205
|
-
triggered[0] = True
|
206
|
-
|
207
|
-
runner = Environment(user_classes=[BaseUser]).create_local_runner()
|
208
|
-
users = runner.spawn_users({BaseUser.__name__: 2}, wait=False)
|
209
|
-
self.assertEqual(2, len(users))
|
210
|
-
self.assertEqual(2, len(runner.user_greenlets))
|
211
|
-
g1 = list(runner.user_greenlets)[0]
|
212
|
-
g2 = list(runner.user_greenlets)[1]
|
213
|
-
runner.stop_users({BaseUser.__name__: 2})
|
214
|
-
self.assertEqual(0, len(runner.user_greenlets))
|
215
|
-
self.assertTrue(g1.dead)
|
216
|
-
self.assertTrue(g2.dead)
|
217
|
-
self.assertTrue(triggered[0])
|
218
|
-
|
219
|
-
def test_start_event(self):
|
220
|
-
class MyUser(User):
|
221
|
-
wait_time = constant(2)
|
222
|
-
task_run_count = 0
|
223
|
-
|
224
|
-
@task
|
225
|
-
def my_task(self):
|
226
|
-
MyUser.task_run_count += 1
|
227
|
-
|
228
|
-
test_start_run = [0]
|
229
|
-
|
230
|
-
environment = Environment(user_classes=[MyUser])
|
231
|
-
|
232
|
-
def on_test_start(*args, **kwargs):
|
233
|
-
test_start_run[0] += 1
|
234
|
-
|
235
|
-
environment.events.test_start.add_listener(on_test_start)
|
236
|
-
|
237
|
-
runner = LocalRunner(environment)
|
238
|
-
runner.start(user_count=3, spawn_rate=3, wait=False)
|
239
|
-
runner.spawning_greenlet.get(timeout=3)
|
240
|
-
|
241
|
-
self.assertEqual(1, test_start_run[0])
|
242
|
-
self.assertEqual(3, MyUser.task_run_count)
|
243
|
-
|
244
|
-
def test_stop_event(self):
|
245
|
-
class MyUser(User):
|
246
|
-
wait_time = constant(1)
|
247
|
-
|
248
|
-
@task
|
249
|
-
def my_task(self):
|
250
|
-
pass
|
251
|
-
|
252
|
-
environment = Environment(user_classes=[MyUser])
|
253
|
-
|
254
|
-
@environment.events.test_stopping.add_listener
|
255
|
-
def on_test_stopping(*_, **__):
|
256
|
-
self.runner_stopping = True
|
257
|
-
|
258
|
-
@environment.events.test_stop.add_listener
|
259
|
-
def on_test_stop(*_, **__):
|
260
|
-
self.runner_stopped = True
|
261
|
-
|
262
|
-
runner = LocalRunner(environment)
|
263
|
-
runner.start(user_count=3, spawn_rate=3, wait=False)
|
264
|
-
self.assertFalse(self.runner_stopping)
|
265
|
-
self.assertFalse(self.runner_stopped)
|
266
|
-
runner.stop()
|
267
|
-
self.assertTrue(self.runner_stopping)
|
268
|
-
self.assertTrue(self.runner_stopped)
|
269
|
-
|
270
|
-
def test_stop_event_quit(self):
|
271
|
-
class MyUser(User):
|
272
|
-
wait_time = constant(1)
|
273
|
-
|
274
|
-
@task
|
275
|
-
def my_task(self):
|
276
|
-
pass
|
277
|
-
|
278
|
-
environment = Environment(user_classes=[MyUser])
|
279
|
-
|
280
|
-
@environment.events.test_stopping.add_listener
|
281
|
-
def on_test_stopping(*_, **__):
|
282
|
-
self.runner_stopping = True
|
283
|
-
|
284
|
-
@environment.events.test_stop.add_listener
|
285
|
-
def on_test_stop(*_, **__):
|
286
|
-
self.runner_stopped = True
|
287
|
-
|
288
|
-
runner = LocalRunner(environment)
|
289
|
-
runner.start(user_count=3, spawn_rate=3, wait=False)
|
290
|
-
self.assertFalse(self.runner_stopping)
|
291
|
-
self.assertFalse(self.runner_stopped)
|
292
|
-
runner.quit()
|
293
|
-
self.assertTrue(self.runner_stopping)
|
294
|
-
self.assertTrue(self.runner_stopped)
|
295
|
-
|
296
|
-
def test_stop_event_stop_and_quit(self):
|
297
|
-
class MyUser(User):
|
298
|
-
wait_time = constant(1)
|
299
|
-
|
300
|
-
@task
|
301
|
-
def my_task(self):
|
302
|
-
pass
|
303
|
-
|
304
|
-
environment = Environment(user_classes=[MyUser])
|
305
|
-
|
306
|
-
@environment.events.test_stopping.add_listener
|
307
|
-
def on_test_stopping(*_, **__):
|
308
|
-
self.runner_stopping = True
|
309
|
-
|
310
|
-
@environment.events.test_stop.add_listener
|
311
|
-
def on_test_stop(*_, **__):
|
312
|
-
self.runner_stopped = True
|
313
|
-
|
314
|
-
runner = LocalRunner(environment)
|
315
|
-
runner.start(user_count=3, spawn_rate=3, wait=False)
|
316
|
-
self.assertFalse(self.runner_stopping)
|
317
|
-
self.assertFalse(self.runner_stopped)
|
318
|
-
runner.stop()
|
319
|
-
runner.quit()
|
320
|
-
self.assertTrue(self.runner_stopping)
|
321
|
-
self.assertTrue(self.runner_stopped)
|
322
|
-
|
323
|
-
def test_stopping_event(self):
|
324
|
-
on_stop_called = [False]
|
325
|
-
|
326
|
-
class MyUser(User):
|
327
|
-
on_stop_called = False
|
328
|
-
wait_time = constant(1)
|
329
|
-
|
330
|
-
@task
|
331
|
-
def my_task(self):
|
332
|
-
pass
|
333
|
-
|
334
|
-
def on_stop(self):
|
335
|
-
MyUser.on_stop_called = True
|
336
|
-
|
337
|
-
environment = Environment(user_classes=[MyUser])
|
338
|
-
|
339
|
-
@environment.events.test_stopping.add_listener
|
340
|
-
def on_test_stopping(*_, **__):
|
341
|
-
on_stop_called[0] = MyUser.on_stop_called
|
342
|
-
self.runner_stopping = True
|
343
|
-
|
344
|
-
runner = LocalRunner(environment)
|
345
|
-
runner.start(user_count=3, spawn_rate=3, wait=False)
|
346
|
-
runner.quit()
|
347
|
-
self.assertTrue(self.runner_stopping)
|
348
|
-
self.assertFalse(on_stop_called[0])
|
349
|
-
|
350
|
-
def test_change_user_count_during_spawning(self):
|
351
|
-
class MyUser(User):
|
352
|
-
wait_time = constant(1)
|
353
|
-
|
354
|
-
@task
|
355
|
-
def my_task(self):
|
356
|
-
pass
|
357
|
-
|
358
|
-
environment = Environment(user_classes=[MyUser])
|
359
|
-
runner = LocalRunner(environment)
|
360
|
-
runner.start(user_count=10, spawn_rate=5, wait=False)
|
361
|
-
sleep(0.6)
|
362
|
-
runner.start(user_count=5, spawn_rate=5, wait=False)
|
363
|
-
runner.spawning_greenlet.join()
|
364
|
-
self.assertEqual(5, len(runner.user_greenlets))
|
365
|
-
runner.quit()
|
366
|
-
|
367
|
-
def test_reset_stats(self):
|
368
|
-
class MyUser(User):
|
369
|
-
@task
|
370
|
-
class task_set(TaskSet):
|
371
|
-
@task
|
372
|
-
def my_task(self):
|
373
|
-
self.user.environment.events.request.fire(
|
374
|
-
request_type="GET",
|
375
|
-
name="/test",
|
376
|
-
response_time=666,
|
377
|
-
response_length=1337,
|
378
|
-
exception=None,
|
379
|
-
context={},
|
380
|
-
)
|
381
|
-
# Make sure each user only run this task once during the test
|
382
|
-
sleep(30)
|
383
|
-
|
384
|
-
environment = Environment(user_classes=[MyUser], reset_stats=True)
|
385
|
-
runner = LocalRunner(environment)
|
386
|
-
runner.start(user_count=6, spawn_rate=1, wait=False)
|
387
|
-
sleep(3)
|
388
|
-
self.assertGreaterEqual(runner.stats.get("/test", "GET").num_requests, 3)
|
389
|
-
sleep(3.25)
|
390
|
-
self.assertLessEqual(runner.stats.get("/test", "GET").num_requests, 1)
|
391
|
-
runner.quit()
|
392
|
-
|
393
|
-
def test_no_reset_stats(self):
|
394
|
-
class MyUser(User):
|
395
|
-
@task
|
396
|
-
class task_set(TaskSet):
|
397
|
-
@task
|
398
|
-
def my_task(self):
|
399
|
-
self.user.environment.events.request.fire(
|
400
|
-
request_type="GET",
|
401
|
-
name="/test",
|
402
|
-
response_time=666,
|
403
|
-
response_length=1337,
|
404
|
-
exception=None,
|
405
|
-
context={},
|
406
|
-
)
|
407
|
-
sleep(2)
|
408
|
-
|
409
|
-
environment = Environment(reset_stats=False, user_classes=[MyUser])
|
410
|
-
runner = LocalRunner(environment)
|
411
|
-
runner.start(user_count=6, spawn_rate=12, wait=False)
|
412
|
-
sleep(0.25)
|
413
|
-
self.assertGreaterEqual(runner.stats.get("/test", "GET").num_requests, 3)
|
414
|
-
sleep(0.3)
|
415
|
-
self.assertEqual(6, runner.stats.get("/test", "GET").num_requests)
|
416
|
-
runner.quit()
|
417
|
-
|
418
|
-
def test_runner_reference_on_environment(self):
|
419
|
-
env = Environment()
|
420
|
-
runner = env.create_local_runner()
|
421
|
-
self.assertEqual(env, runner.environment)
|
422
|
-
self.assertEqual(runner, env.runner)
|
423
|
-
|
424
|
-
def test_users_can_call_runner_quit_without_deadlocking(self):
|
425
|
-
class BaseUser(User):
|
426
|
-
stop_triggered = False
|
427
|
-
|
428
|
-
@task
|
429
|
-
def trigger(self):
|
430
|
-
self.environment.runner.quit()
|
431
|
-
|
432
|
-
def on_stop(self):
|
433
|
-
BaseUser.stop_triggered = True
|
434
|
-
|
435
|
-
runner = Environment(user_classes=[BaseUser]).create_local_runner()
|
436
|
-
users = runner.spawn_users({BaseUser.__name__: 1}, wait=False)
|
437
|
-
self.assertEqual(1, len(users))
|
438
|
-
timeout = gevent.Timeout(0.5)
|
439
|
-
timeout.start()
|
440
|
-
try:
|
441
|
-
runner.greenlet.join()
|
442
|
-
except gevent.Timeout:
|
443
|
-
self.fail("Got Timeout exception, runner must have hung somehow.")
|
444
|
-
finally:
|
445
|
-
timeout.cancel()
|
446
|
-
|
447
|
-
self.assertTrue(BaseUser.stop_triggered)
|
448
|
-
|
449
|
-
def test_runner_quit_can_run_on_stop_for_multiple_users_concurrently(self):
|
450
|
-
class BaseUser(User):
|
451
|
-
stop_count = 0
|
452
|
-
|
453
|
-
@task
|
454
|
-
def trigger(self):
|
455
|
-
pass
|
456
|
-
|
457
|
-
def on_stop(self):
|
458
|
-
gevent.sleep(0.1)
|
459
|
-
BaseUser.stop_count += 1
|
460
|
-
|
461
|
-
runner = Environment(user_classes=[BaseUser]).create_local_runner()
|
462
|
-
users = runner.spawn_users({BaseUser.__name__: 10}, wait=False)
|
463
|
-
self.assertEqual(10, len(users))
|
464
|
-
timeout = gevent.Timeout(0.3)
|
465
|
-
timeout.start()
|
466
|
-
try:
|
467
|
-
runner.quit()
|
468
|
-
except gevent.Timeout:
|
469
|
-
self.fail("Got Timeout exception, runner must have hung somehow.")
|
470
|
-
finally:
|
471
|
-
timeout.cancel()
|
472
|
-
|
473
|
-
self.assertEqual(10, BaseUser.stop_count) # verify that all users executed on_stop
|
474
|
-
|
475
|
-
def test_stop_users_with_spawn_rate(self):
|
476
|
-
"""
|
477
|
-
The spawn rate does not have an effect on the rate at which the users are stopped.
|
478
|
-
It is expected that the excess users will be stopped as soon as possible in parallel
|
479
|
-
(while respecting the stop_timeout).
|
480
|
-
"""
|
481
|
-
|
482
|
-
class MyUser(User):
|
483
|
-
wait_time = constant(1)
|
484
|
-
|
485
|
-
@task
|
486
|
-
def my_task(self):
|
487
|
-
pass
|
488
|
-
|
489
|
-
environment = Environment(user_classes=[MyUser])
|
490
|
-
runner = LocalRunner(environment)
|
491
|
-
|
492
|
-
# Start load test, wait for users to start, then trigger ramp down
|
493
|
-
ts = time.time()
|
494
|
-
runner.start(10, 10, wait=False)
|
495
|
-
runner.spawning_greenlet.join()
|
496
|
-
delta = time.time() - ts
|
497
|
-
self.assertTrue(
|
498
|
-
0 <= delta <= 0.05, f"Expected user count to increase to 10 instantaneously, instead it took {delta:f}"
|
499
|
-
)
|
500
|
-
self.assertTrue(
|
501
|
-
runner.user_count == 10, "User count has not decreased correctly to 2, it is : %i" % runner.user_count
|
502
|
-
)
|
503
|
-
|
504
|
-
ts = time.time()
|
505
|
-
runner.start(2, 4, wait=False)
|
506
|
-
runner.spawning_greenlet.join()
|
507
|
-
delta = time.time() - ts
|
508
|
-
self.assertTrue(0 <= delta <= 1.05, f"Expected user count to decrease to 2 in 1s, instead it took {delta:f}")
|
509
|
-
self.assertTrue(
|
510
|
-
runner.user_count == 2, "User count has not decreased correctly to 2, it is : %i" % runner.user_count
|
511
|
-
)
|
512
|
-
|
513
|
-
def test_attributes_populated_when_calling_start(self):
|
514
|
-
class MyUser1(User):
|
515
|
-
wait_time = constant(0)
|
516
|
-
|
517
|
-
@task
|
518
|
-
def my_task(self):
|
519
|
-
pass
|
520
|
-
|
521
|
-
class MyUser2(User):
|
522
|
-
wait_time = constant(0)
|
523
|
-
|
524
|
-
@task
|
525
|
-
def my_task(self):
|
526
|
-
pass
|
527
|
-
|
528
|
-
environment = Environment(user_classes=[MyUser1, MyUser2])
|
529
|
-
runner = LocalRunner(environment)
|
530
|
-
|
531
|
-
runner.start(user_count=10, spawn_rate=5, wait=False)
|
532
|
-
runner.spawning_greenlet.join()
|
533
|
-
self.assertDictEqual({"MyUser1": 5, "MyUser2": 5}, runner.user_classes_count)
|
534
|
-
|
535
|
-
runner.start(user_count=5, spawn_rate=5, wait=False)
|
536
|
-
runner.spawning_greenlet.join()
|
537
|
-
self.assertDictEqual({"MyUser1": 3, "MyUser2": 2}, runner.user_classes_count)
|
538
|
-
|
539
|
-
runner.quit()
|
540
|
-
|
541
|
-
def test_user_classes_count(self):
|
542
|
-
class MyUser1(User):
|
543
|
-
wait_time = constant(0)
|
544
|
-
|
545
|
-
@task
|
546
|
-
def my_task(self):
|
547
|
-
pass
|
548
|
-
|
549
|
-
class MyUser2(User):
|
550
|
-
wait_time = constant(0)
|
551
|
-
|
552
|
-
@task
|
553
|
-
def my_task(self):
|
554
|
-
pass
|
555
|
-
|
556
|
-
environment = Environment(user_classes=[MyUser1, MyUser2])
|
557
|
-
runner = LocalRunner(environment)
|
558
|
-
|
559
|
-
runner.start(user_count=10, spawn_rate=5, wait=False)
|
560
|
-
runner.spawning_greenlet.join()
|
561
|
-
self.assertDictEqual({"MyUser1": 5, "MyUser2": 5}, runner.user_classes_count)
|
562
|
-
|
563
|
-
runner.start(user_count=5, spawn_rate=5, wait=False)
|
564
|
-
runner.spawning_greenlet.join()
|
565
|
-
self.assertDictEqual({"MyUser1": 3, "MyUser2": 2}, runner.user_classes_count)
|
566
|
-
|
567
|
-
runner.quit()
|
568
|
-
|
569
|
-
def test_host_class_attribute_from_web(self):
|
570
|
-
"""If host is left empty from the webUI, we should not use it"""
|
571
|
-
|
572
|
-
class MyUser1(User):
|
573
|
-
host = "https://host1.com"
|
574
|
-
|
575
|
-
@task
|
576
|
-
def my_task(self):
|
577
|
-
pass
|
578
|
-
|
579
|
-
class MyUser2(User):
|
580
|
-
host = "https://host2.com"
|
581
|
-
|
582
|
-
@task
|
583
|
-
def my_task(self):
|
584
|
-
pass
|
585
|
-
|
586
|
-
opts = mocked_options()
|
587
|
-
# If left empty on the web, we get an empty string as host
|
588
|
-
opts.host = ""
|
589
|
-
environment = create_environment([MyUser1, MyUser2], opts)
|
590
|
-
runner = LocalRunner(environment)
|
591
|
-
# Start the runner to trigger problematic code
|
592
|
-
runner.start(user_count=2, spawn_rate=1, wait=False)
|
593
|
-
runner.spawning_greenlet.join()
|
594
|
-
|
595
|
-
# Make sure we did not overwrite the host variable
|
596
|
-
self.assertEqual(MyUser1.host, "https://host1.com")
|
597
|
-
self.assertEqual(MyUser2.host, "https://host2.com")
|
598
|
-
|
599
|
-
runner.quit()
|
600
|
-
|
601
|
-
def test_custom_message(self):
|
602
|
-
class MyUser(User):
|
603
|
-
wait_time = constant(1)
|
604
|
-
|
605
|
-
@task
|
606
|
-
def my_task(self):
|
607
|
-
pass
|
608
|
-
|
609
|
-
test_custom_msg = [False]
|
610
|
-
test_custom_msg_data = [{}]
|
611
|
-
|
612
|
-
def on_custom_msg(msg, **kw):
|
613
|
-
test_custom_msg[0] = True
|
614
|
-
test_custom_msg_data[0] = msg.data
|
615
|
-
|
616
|
-
environment = Environment(user_classes=[MyUser])
|
617
|
-
runner = LocalRunner(environment)
|
618
|
-
|
619
|
-
runner.register_message("test_custom_msg", on_custom_msg)
|
620
|
-
runner.send_message("test_custom_msg", {"test_data": 123})
|
621
|
-
|
622
|
-
self.assertTrue(test_custom_msg[0])
|
623
|
-
self.assertEqual(123, test_custom_msg_data[0]["test_data"])
|
624
|
-
|
625
|
-
def test_concurrent_custom_message(self):
|
626
|
-
class MyUser(User):
|
627
|
-
wait_time = constant(1)
|
628
|
-
|
629
|
-
@task
|
630
|
-
def my_task(self):
|
631
|
-
pass
|
632
|
-
|
633
|
-
test_custom_msg = [False]
|
634
|
-
test_custom_msg_data = [{}]
|
635
|
-
|
636
|
-
def on_custom_msg(msg, **kw):
|
637
|
-
test_custom_msg[0] = True
|
638
|
-
test_custom_msg_data[0] = msg.data
|
639
|
-
|
640
|
-
environment = Environment(user_classes=[MyUser])
|
641
|
-
runner = LocalRunner(environment)
|
642
|
-
|
643
|
-
runner.register_message("test_custom_msg", on_custom_msg, concurrent=True)
|
644
|
-
runner.send_message("test_custom_msg", {"test_data": 123})
|
645
|
-
|
646
|
-
gevent.sleep(0.5)
|
647
|
-
self.assertTrue(test_custom_msg[0])
|
648
|
-
self.assertEqual(123, test_custom_msg_data[0]["test_data"])
|
649
|
-
|
650
|
-
def test_undefined_custom_message(self):
|
651
|
-
class MyUser(User):
|
652
|
-
wait_time = constant(1)
|
653
|
-
|
654
|
-
@task
|
655
|
-
def my_task(self):
|
656
|
-
pass
|
657
|
-
|
658
|
-
test_custom_msg = [False]
|
659
|
-
|
660
|
-
def on_custom_msg(msg, **kw):
|
661
|
-
test_custom_msg[0] = True
|
662
|
-
|
663
|
-
environment = Environment(user_classes=[MyUser])
|
664
|
-
runner = LocalRunner(environment)
|
665
|
-
|
666
|
-
runner.register_message("test_custom_msg", on_custom_msg)
|
667
|
-
runner.send_message("test_different_custom_msg")
|
668
|
-
|
669
|
-
self.assertFalse(test_custom_msg[0])
|
670
|
-
self.assertEqual(1, len(self.mocked_log.warning))
|
671
|
-
msg = self.mocked_log.warning[0]
|
672
|
-
self.assertIn("Unknown message type received", msg)
|
673
|
-
|
674
|
-
def test_duplicate_message_handler_registration(self):
|
675
|
-
class MyUser(User):
|
676
|
-
@task
|
677
|
-
def my_task(self):
|
678
|
-
pass
|
679
|
-
|
680
|
-
def on_custom_msg(msg, **kw):
|
681
|
-
pass
|
682
|
-
|
683
|
-
environment = Environment(user_classes=[MyUser])
|
684
|
-
runner = LocalRunner(environment)
|
685
|
-
runner.register_message("test_custom_msg", on_custom_msg)
|
686
|
-
self.assertRaises(Exception, runner.register_message, "test_custom_msg", on_custom_msg)
|
687
|
-
|
688
|
-
def test_swarm_endpoint_is_non_blocking(self):
|
689
|
-
class TestUser1(User):
|
690
|
-
@task
|
691
|
-
def my_task(self):
|
692
|
-
gevent.sleep(600)
|
693
|
-
|
694
|
-
class TestUser2(User):
|
695
|
-
@task
|
696
|
-
def my_task(self):
|
697
|
-
gevent.sleep(600)
|
698
|
-
|
699
|
-
env = Environment(user_classes=[TestUser1, TestUser2])
|
700
|
-
local_runner = env.create_local_runner()
|
701
|
-
web_ui = env.create_web_ui("127.0.0.1", 0)
|
702
|
-
|
703
|
-
gevent.sleep(0.1)
|
704
|
-
|
705
|
-
ts = time.perf_counter()
|
706
|
-
response = requests.post(
|
707
|
-
f"http://127.0.0.1:{web_ui.server.server_port}/swarm",
|
708
|
-
data={"user_count": 20, "spawn_rate": 5, "host": "https://localhost"},
|
709
|
-
)
|
710
|
-
self.assertEqual(200, response.status_code)
|
711
|
-
self.assertTrue(0 <= time.perf_counter() - ts <= 1, "swarm endpoint is blocking")
|
712
|
-
|
713
|
-
ts = time.perf_counter()
|
714
|
-
while local_runner.state != STATE_RUNNING:
|
715
|
-
self.assertTrue(time.perf_counter() - ts <= 4, local_runner.state)
|
716
|
-
gevent.sleep(0.1)
|
717
|
-
|
718
|
-
self.assertTrue(3 <= time.perf_counter() - ts <= 5)
|
719
|
-
|
720
|
-
self.assertEqual(local_runner.user_count, 20)
|
721
|
-
|
722
|
-
local_runner.stop()
|
723
|
-
web_ui.stop()
|
724
|
-
|
725
|
-
def test_can_call_stop_endpoint_if_currently_swarming(self):
|
726
|
-
class TestUser1(User):
|
727
|
-
@task
|
728
|
-
def my_task(self):
|
729
|
-
gevent.sleep(600)
|
730
|
-
|
731
|
-
class TestUser2(User):
|
732
|
-
@task
|
733
|
-
def my_task(self):
|
734
|
-
gevent.sleep(600)
|
735
|
-
|
736
|
-
stop_timeout = 5
|
737
|
-
env = Environment(user_classes=[TestUser1, TestUser2], stop_timeout=stop_timeout)
|
738
|
-
local_runner = env.create_local_runner()
|
739
|
-
web_ui = env.create_web_ui("127.0.0.1", 0)
|
740
|
-
|
741
|
-
gevent.sleep(0.1)
|
742
|
-
|
743
|
-
ts = time.perf_counter()
|
744
|
-
response = requests.post(
|
745
|
-
f"http://127.0.0.1:{web_ui.server.server_port}/swarm",
|
746
|
-
data={"user_count": 20, "spawn_rate": 1, "host": "https://localhost"},
|
747
|
-
)
|
748
|
-
self.assertEqual(200, response.status_code)
|
749
|
-
self.assertTrue(0 <= time.perf_counter() - ts <= 1, "swarm endpoint is blocking")
|
750
|
-
|
751
|
-
gevent.sleep(5)
|
752
|
-
|
753
|
-
self.assertEqual(local_runner.state, STATE_SPAWNING)
|
754
|
-
self.assertLessEqual(local_runner.user_count, 10)
|
755
|
-
|
756
|
-
ts = time.perf_counter()
|
757
|
-
response = requests.get(
|
758
|
-
f"http://127.0.0.1:{web_ui.server.server_port}/stop",
|
759
|
-
)
|
760
|
-
self.assertEqual(200, response.status_code)
|
761
|
-
self.assertTrue(stop_timeout <= time.perf_counter() - ts <= stop_timeout + 5, "stop endpoint took too long")
|
762
|
-
|
763
|
-
ts = time.perf_counter()
|
764
|
-
while local_runner.state != STATE_STOPPED:
|
765
|
-
self.assertTrue(time.perf_counter() - ts <= 2)
|
766
|
-
gevent.sleep(0.1)
|
767
|
-
|
768
|
-
self.assertLessEqual(local_runner.user_count, 0)
|
769
|
-
|
770
|
-
local_runner.stop()
|
771
|
-
web_ui.stop()
|
772
|
-
|
773
|
-
def test_target_user_count_is_set_before_ramp_up(self):
|
774
|
-
"""Test for https://github.com/locustio/locust/issues/1883"""
|
775
|
-
|
776
|
-
class MyUser1(User):
|
777
|
-
wait_time = constant(0)
|
778
|
-
|
779
|
-
@task
|
780
|
-
def my_task(self):
|
781
|
-
pass
|
782
|
-
|
783
|
-
environment = Environment(user_classes=[MyUser1])
|
784
|
-
runner = LocalRunner(environment)
|
785
|
-
|
786
|
-
test_start_event_fired = [False]
|
787
|
-
|
788
|
-
@environment.events.test_start.add_listener
|
789
|
-
def on_test_start(*args, **kwargs):
|
790
|
-
test_start_event_fired[0] = True
|
791
|
-
self.assertEqual(runner.target_user_count, 3)
|
792
|
-
|
793
|
-
runner.start(user_count=3, spawn_rate=1, wait=False)
|
794
|
-
|
795
|
-
gevent.sleep(1)
|
796
|
-
|
797
|
-
self.assertEqual(runner.target_user_count, 3)
|
798
|
-
self.assertEqual(runner.user_count, 1)
|
799
|
-
# However, target_user_classes_count is only updated at the end of the ramp-up/ramp-down
|
800
|
-
# due to the way it is implemented.
|
801
|
-
self.assertDictEqual({}, runner.target_user_classes_count)
|
802
|
-
|
803
|
-
runner.spawning_greenlet.join()
|
804
|
-
|
805
|
-
self.assertEqual(runner.target_user_count, 3)
|
806
|
-
self.assertEqual(runner.user_count, 3)
|
807
|
-
self.assertDictEqual({"MyUser1": 3}, runner.target_user_classes_count)
|
808
|
-
|
809
|
-
runner.quit()
|
810
|
-
|
811
|
-
self.assertTrue(test_start_event_fired[0])
|
812
|
-
|
813
|
-
def test_stop_users_count(self):
|
814
|
-
user_count = 10
|
815
|
-
|
816
|
-
class BaseUser1(User):
|
817
|
-
wait_time = constant(1)
|
818
|
-
|
819
|
-
@task
|
820
|
-
def task_a(self):
|
821
|
-
pass
|
822
|
-
|
823
|
-
class BaseUser2(BaseUser1):
|
824
|
-
wait_time = constant(1)
|
825
|
-
|
826
|
-
runner = Environment(user_classes=[BaseUser1, BaseUser2]).create_local_runner()
|
827
|
-
runner.start(user_count=user_count, spawn_rate=10)
|
828
|
-
sleep(1)
|
829
|
-
self.assertEqual(user_count, runner.user_count)
|
830
|
-
|
831
|
-
runner.stop()
|
832
|
-
sleep(1)
|
833
|
-
self.assertEqual(0, runner.user_count)
|
834
|
-
|
835
|
-
def test_user_count_starts_from_specified_amount_when_creating_new_test_after_previous_step_has_been_stopped(self):
|
836
|
-
"""Test for https://github.com/locustio/locust/issues/2135"""
|
837
|
-
|
838
|
-
class MyUser1(User):
|
839
|
-
wait_time = constant(0)
|
840
|
-
|
841
|
-
@task
|
842
|
-
def my_task(self):
|
843
|
-
pass
|
844
|
-
|
845
|
-
env = Environment(user_classes=[MyUser1])
|
846
|
-
local_runner = env.create_local_runner()
|
847
|
-
web_ui = env.create_web_ui("127.0.0.1", 0)
|
848
|
-
|
849
|
-
gevent.sleep(0.1)
|
850
|
-
|
851
|
-
response = requests.post(
|
852
|
-
f"http://127.0.0.1:{web_ui.server.server_port}/swarm",
|
853
|
-
data={"user_count": 20, "spawn_rate": 20, "host": "https://localhost"},
|
854
|
-
)
|
855
|
-
self.assertEqual(200, response.status_code)
|
856
|
-
|
857
|
-
t0 = time.perf_counter()
|
858
|
-
while local_runner.user_count != 20:
|
859
|
-
self.assertTrue(time.perf_counter() - t0 <= 1, local_runner.user_count)
|
860
|
-
gevent.sleep(0.1)
|
861
|
-
|
862
|
-
response = requests.get(
|
863
|
-
f"http://127.0.0.1:{web_ui.server.server_port}/stop",
|
864
|
-
)
|
865
|
-
self.assertEqual(200, response.status_code)
|
866
|
-
|
867
|
-
t0 = time.perf_counter()
|
868
|
-
while local_runner.state != STATE_STOPPED:
|
869
|
-
self.assertTrue(time.perf_counter() - t0 <= 1, local_runner.state)
|
870
|
-
gevent.sleep(0.1)
|
871
|
-
|
872
|
-
t0 = time.perf_counter()
|
873
|
-
response = requests.post(
|
874
|
-
f"http://127.0.0.1:{web_ui.server.server_port}/swarm",
|
875
|
-
data={"user_count": 1, "spawn_rate": 1, "host": "https://localhost"},
|
876
|
-
)
|
877
|
-
self.assertEqual(200, response.status_code)
|
878
|
-
self.assertTrue(time.perf_counter() - t0 <= 1, "Stop endpoint should not be blocking")
|
879
|
-
|
880
|
-
# Make sure user count stays at 1 over 2s (2s is arbitrary, but we
|
881
|
-
# wait long enough to make sure it stays constant)
|
882
|
-
t0 = time.perf_counter()
|
883
|
-
while time.perf_counter() - t0 <= 2:
|
884
|
-
self.assertTrue(local_runner.user_count <= 1, local_runner.user_count)
|
885
|
-
gevent.sleep(0.1)
|
886
|
-
|
887
|
-
local_runner.stop()
|
888
|
-
web_ui.stop()
|
889
|
-
|
890
|
-
def test_custom_dispatcher_class(self):
|
891
|
-
@mock.create_autospec
|
892
|
-
class MyUsersDispatcher(UsersDispatcher):
|
893
|
-
def __init__(self, worker_nodes: list[WorkerNode], user_classes: list[type[User]]) -> None:
|
894
|
-
super().__init__(worker_nodes, user_classes)
|
895
|
-
|
896
|
-
def add_worker(self, worker_node: WorkerNode) -> None:
|
897
|
-
pass
|
898
|
-
|
899
|
-
def remove_worker(self, worker_node: WorkerNode) -> None:
|
900
|
-
pass
|
901
|
-
|
902
|
-
@property
|
903
|
-
def dispatch_in_progress(self) -> bool:
|
904
|
-
return True
|
905
|
-
|
906
|
-
def new_dispatch(self, target_user_count: int, spawn_rate: float, user_classes: list | None = None) -> None:
|
907
|
-
return super().new_dispatch(target_user_count, spawn_rate, user_classes)
|
908
|
-
|
909
|
-
class MyUser1(User):
|
910
|
-
wait_time = constant(0)
|
911
|
-
fixed_count = 1
|
912
|
-
|
913
|
-
@task
|
914
|
-
def my_task(self):
|
915
|
-
pass
|
916
|
-
|
917
|
-
environment = Environment(user_classes=[MyUser1], dispatcher_class=MyUsersDispatcher)
|
918
|
-
runner = LocalRunner(environment)
|
919
|
-
|
920
|
-
runner.start(user_count=1, spawn_rate=1)
|
921
|
-
sleep(1)
|
922
|
-
runner._users_dispatcher.new_dispatch.assert_called_with(1, 1, None)
|
923
|
-
runner._users_dispatcher.__iter__.assert_called_once()
|
924
|
-
runner.stop()
|
925
|
-
|
926
|
-
|
927
|
-
class TestMasterWorkerRunners(LocustTestCase):
|
928
|
-
def test_distributed_integration_run(self):
|
929
|
-
"""
|
930
|
-
Full integration test that starts both a MasterRunner and three WorkerRunner instances
|
931
|
-
and makes sure that their stats is sent to the Master. Also validates worker_connect event
|
932
|
-
"""
|
933
|
-
|
934
|
-
class TestUser(User):
|
935
|
-
wait_time = constant(0.1)
|
936
|
-
|
937
|
-
@task
|
938
|
-
def incr_stats(self):
|
939
|
-
self.environment.events.request.fire(
|
940
|
-
request_type="GET",
|
941
|
-
name="/",
|
942
|
-
response_time=1337,
|
943
|
-
response_length=666,
|
944
|
-
exception=None,
|
945
|
-
context={},
|
946
|
-
)
|
947
|
-
|
948
|
-
with mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=0.3):
|
949
|
-
# start a Master runner
|
950
|
-
master_env = Environment(user_classes=[TestUser])
|
951
|
-
worker_connect_events = []
|
952
|
-
|
953
|
-
def on_connect(client_id):
|
954
|
-
worker_connect_events.append(client_id)
|
955
|
-
|
956
|
-
master_env.events.worker_connect.add_listener(on_connect)
|
957
|
-
master = master_env.create_master_runner("*", 0)
|
958
|
-
sleep(0)
|
959
|
-
# start 3 Worker runners
|
960
|
-
workers = []
|
961
|
-
for i in range(3):
|
962
|
-
worker_env = Environment(user_classes=[TestUser])
|
963
|
-
worker: WorkerRunner = worker_env.create_worker_runner("127.0.0.1", master.server.port)
|
964
|
-
workers.append(worker)
|
965
|
-
|
966
|
-
# give workers time to connect
|
967
|
-
sleep(0.1)
|
968
|
-
# issue start command that should trigger TestUsers to be spawned in the Workers
|
969
|
-
master.start(6, spawn_rate=1000)
|
970
|
-
sleep(0.1)
|
971
|
-
# check that worker nodes have started locusts
|
972
|
-
for worker in workers:
|
973
|
-
self.assertEqual(2, worker.user_count)
|
974
|
-
# give time for users to generate stats, and stats to be sent to master
|
975
|
-
sleep(1)
|
976
|
-
master.quit()
|
977
|
-
|
978
|
-
for worker in workers:
|
979
|
-
# make sure users are killed
|
980
|
-
self.assertEqual(0, worker.user_count)
|
981
|
-
# make sure events happened correctly
|
982
|
-
self.assertIn(worker.client_id, worker_connect_events)
|
983
|
-
|
984
|
-
# check that stats are present in master
|
985
|
-
self.assertGreater(
|
986
|
-
master_env.runner.stats.total.num_requests,
|
987
|
-
20,
|
988
|
-
"For some reason the master node's stats has not come in",
|
989
|
-
)
|
990
|
-
|
991
|
-
def test_distributed_rebalanced_integration_run(self):
|
992
|
-
"""
|
993
|
-
Full integration test that starts both a MasterRunner and three WorkerRunner instances
|
994
|
-
and makes sure that their stats is sent to the Master.
|
995
|
-
"""
|
996
|
-
|
997
|
-
class TestUser(User):
|
998
|
-
wait_time = constant(0.1)
|
999
|
-
|
1000
|
-
@task
|
1001
|
-
def incr_stats(self):
|
1002
|
-
self.environment.events.request.fire(
|
1003
|
-
request_type="GET",
|
1004
|
-
name="/",
|
1005
|
-
response_time=1337,
|
1006
|
-
response_length=666,
|
1007
|
-
exception=None,
|
1008
|
-
context={},
|
1009
|
-
)
|
1010
|
-
|
1011
|
-
with (
|
1012
|
-
mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=0.3),
|
1013
|
-
patch_env("LOCUST_WAIT_FOR_WORKERS_REPORT_AFTER_RAMP_UP", "0.1"),
|
1014
|
-
):
|
1015
|
-
# start a Master runner
|
1016
|
-
options = parse_options(["--enable-rebalancing"])
|
1017
|
-
master_env = Environment(user_classes=[TestUser], parsed_options=options)
|
1018
|
-
master = master_env.create_master_runner("*", 0)
|
1019
|
-
sleep(0)
|
1020
|
-
# start 3 Worker runners
|
1021
|
-
workers = []
|
1022
|
-
|
1023
|
-
def add_worker():
|
1024
|
-
worker_env = Environment(user_classes=[TestUser])
|
1025
|
-
worker = worker_env.create_worker_runner("127.0.0.1", master.server.port)
|
1026
|
-
workers.append(worker)
|
1027
|
-
|
1028
|
-
for i in range(3):
|
1029
|
-
add_worker()
|
1030
|
-
|
1031
|
-
# give workers time to connect
|
1032
|
-
sleep(0.1)
|
1033
|
-
# issue start command that should trigger TestUsers to be spawned in the Workers
|
1034
|
-
master.start(6, spawn_rate=1000)
|
1035
|
-
sleep(0.1)
|
1036
|
-
# check that worker nodes have started locusts
|
1037
|
-
for worker in workers:
|
1038
|
-
self.assertEqual(2, worker.user_count)
|
1039
|
-
# give time for users to generate stats, and stats to be sent to master
|
1040
|
-
# Add 1 more workers (should be 4 now)
|
1041
|
-
add_worker()
|
1042
|
-
|
1043
|
-
@retry(AssertionError, tries=10, delay=0.5)
|
1044
|
-
def check_rebalanced_true():
|
1045
|
-
for worker in workers:
|
1046
|
-
self.assertTrue(worker.user_count > 0)
|
1047
|
-
|
1048
|
-
# Check that all workers have a user count > 0 at least
|
1049
|
-
check_rebalanced_true()
|
1050
|
-
# Add 2 more workers (should be 6 now)
|
1051
|
-
add_worker()
|
1052
|
-
add_worker()
|
1053
|
-
|
1054
|
-
@retry(AssertionError, tries=10, delay=0.5)
|
1055
|
-
def check_rebalanced_equals():
|
1056
|
-
for worker in workers:
|
1057
|
-
self.assertEqual(1, worker.user_count)
|
1058
|
-
|
1059
|
-
# Check that all workers have a user count = 1 now
|
1060
|
-
check_rebalanced_equals()
|
1061
|
-
|
1062
|
-
# Simulate that some workers are missing by "killing" them abrutly
|
1063
|
-
for i in range(3):
|
1064
|
-
workers[i].greenlet.kill(block=True)
|
1065
|
-
|
1066
|
-
@retry(AssertionError, tries=10, delay=1)
|
1067
|
-
def check_master_worker_missing_count():
|
1068
|
-
self.assertEqual(3, len(master.clients.missing))
|
1069
|
-
|
1070
|
-
# Check that master detected the missing workers
|
1071
|
-
check_master_worker_missing_count()
|
1072
|
-
|
1073
|
-
@retry(AssertionError, tries=10, delay=1)
|
1074
|
-
def check_remaing_worker_new_user_count():
|
1075
|
-
for i in range(3, 6):
|
1076
|
-
self.assertEqual(2, workers[i].user_count)
|
1077
|
-
|
1078
|
-
# Check that remaining workers have a new count of user due to rebalancing.
|
1079
|
-
check_remaing_worker_new_user_count()
|
1080
|
-
sleep(1)
|
1081
|
-
|
1082
|
-
# Finally quit and check states of remaining workers.
|
1083
|
-
master.quit()
|
1084
|
-
# make sure users are killed on remaining workers
|
1085
|
-
for i in range(3, 6):
|
1086
|
-
self.assertEqual(0, workers[i].user_count)
|
1087
|
-
|
1088
|
-
# check that stats are present in master
|
1089
|
-
self.assertGreater(
|
1090
|
-
master_env.runner.stats.total.num_requests,
|
1091
|
-
20,
|
1092
|
-
"For some reason the master node's stats has not come in",
|
1093
|
-
)
|
1094
|
-
|
1095
|
-
def test_distributed_run_with_custom_args(self):
|
1096
|
-
"""
|
1097
|
-
Full integration test that starts both a MasterRunner and three WorkerRunner instances
|
1098
|
-
and makes sure that their stats is sent to the Master.
|
1099
|
-
"""
|
1100
|
-
|
1101
|
-
class TestUser(User):
|
1102
|
-
wait_time = constant(0.1)
|
1103
|
-
|
1104
|
-
@task
|
1105
|
-
def incr_stats(self):
|
1106
|
-
self.environment.events.request.fire(
|
1107
|
-
request_type="GET",
|
1108
|
-
name=self.environment.parsed_options.my_str_argument,
|
1109
|
-
response_time=self.environment.parsed_options.my_int_argument,
|
1110
|
-
response_length=666,
|
1111
|
-
exception=None,
|
1112
|
-
context={},
|
1113
|
-
)
|
1114
|
-
|
1115
|
-
@locust.events.init_command_line_parser.add_listener
|
1116
|
-
def _(parser, **kw):
|
1117
|
-
parser.add_argument("--my-int-argument", type=int)
|
1118
|
-
parser.add_argument("--my-str-argument", type=str, default="NOOOO")
|
1119
|
-
|
1120
|
-
with mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=0.3):
|
1121
|
-
# start a Master runner
|
1122
|
-
master_env = Environment(user_classes=[TestUser])
|
1123
|
-
master = master_env.create_master_runner("*", 0)
|
1124
|
-
master_env.parsed_options = parse_options(
|
1125
|
-
[
|
1126
|
-
"--my-int-argument",
|
1127
|
-
"42",
|
1128
|
-
"--my-str-argument",
|
1129
|
-
"cool-string",
|
1130
|
-
]
|
1131
|
-
)
|
1132
|
-
sleep(0)
|
1133
|
-
# start 3 Worker runners
|
1134
|
-
workers = []
|
1135
|
-
for i in range(3):
|
1136
|
-
worker_env = Environment(user_classes=[TestUser])
|
1137
|
-
worker = worker_env.create_worker_runner("127.0.0.1", master.server.port)
|
1138
|
-
workers.append(worker)
|
1139
|
-
|
1140
|
-
# give workers time to connect
|
1141
|
-
sleep(0.1)
|
1142
|
-
# issue start command that should trigger TestUsers to be spawned in the Workers
|
1143
|
-
master.start(6, spawn_rate=1000)
|
1144
|
-
sleep(0.1)
|
1145
|
-
# check that worker nodes have started locusts
|
1146
|
-
for worker in workers:
|
1147
|
-
self.assertEqual(2, worker.user_count)
|
1148
|
-
# give time for users to generate stats, and stats to be sent to master
|
1149
|
-
sleep(1)
|
1150
|
-
master.quit()
|
1151
|
-
# make sure users are killed
|
1152
|
-
for worker in workers:
|
1153
|
-
self.assertEqual(0, worker.user_count)
|
1154
|
-
|
1155
|
-
self.assertEqual(master_env.runner.stats.total.max_response_time, 42)
|
1156
|
-
self.assertEqual(master_env.runner.stats.get("cool-string", "GET").avg_response_time, 42)
|
1157
|
-
|
1158
|
-
def test_spawning_complete_and_test_stop_event(self):
|
1159
|
-
class TestUser(User):
|
1160
|
-
wait_time = constant(0.1)
|
1161
|
-
|
1162
|
-
@task
|
1163
|
-
def my_task(l):
|
1164
|
-
pass
|
1165
|
-
|
1166
|
-
with mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=0.3):
|
1167
|
-
# start a Master runner
|
1168
|
-
master_env = Environment(user_classes=[TestUser])
|
1169
|
-
test_stop_count = {"master": 0, "worker": 0}
|
1170
|
-
spawning_complete_count = {"master": 0, "worker": 0}
|
1171
|
-
|
1172
|
-
@master_env.events.test_stop.add_listener
|
1173
|
-
def _(*args, **kwargs):
|
1174
|
-
test_stop_count["master"] += 1
|
1175
|
-
|
1176
|
-
@master_env.events.spawning_complete.add_listener
|
1177
|
-
def _(*args, **kwargs):
|
1178
|
-
spawning_complete_count["master"] += 1
|
1179
|
-
|
1180
|
-
master = master_env.create_master_runner("*", 0)
|
1181
|
-
sleep(0)
|
1182
|
-
# start a Worker runner
|
1183
|
-
worker_env = Environment(user_classes=[TestUser])
|
1184
|
-
|
1185
|
-
@worker_env.events.test_stop.add_listener
|
1186
|
-
def _(*args, **kwargs):
|
1187
|
-
test_stop_count["worker"] += 1
|
1188
|
-
|
1189
|
-
@worker_env.events.spawning_complete.add_listener
|
1190
|
-
def _(*args, **kwargs):
|
1191
|
-
spawning_complete_count["worker"] += 1
|
1192
|
-
|
1193
|
-
worker = worker_env.create_worker_runner("127.0.0.1", master.server.port)
|
1194
|
-
|
1195
|
-
# give worker time to connect
|
1196
|
-
sleep(0.1)
|
1197
|
-
# issue start command that should trigger TestUsers to be spawned in the Workers
|
1198
|
-
master.start(3, spawn_rate=1)
|
1199
|
-
sleep(3)
|
1200
|
-
# check that worker nodes have started locusts
|
1201
|
-
self.assertEqual(3, worker.user_count)
|
1202
|
-
# give time for users to generate stats, and stats to be sent to master
|
1203
|
-
sleep(0.1)
|
1204
|
-
master_env.events.quitting.fire(environment=master_env, reverse=True)
|
1205
|
-
master.quit()
|
1206
|
-
sleep(0.1)
|
1207
|
-
# make sure users are killed
|
1208
|
-
self.assertEqual(0, worker.user_count)
|
1209
|
-
|
1210
|
-
# check the spwaning_complete and test_stop events were called one time in master and one time in worker
|
1211
|
-
self.assertEqual(
|
1212
|
-
1,
|
1213
|
-
test_stop_count["master"],
|
1214
|
-
"The test_stop event was not called exactly one time in the master node",
|
1215
|
-
)
|
1216
|
-
self.assertEqual(
|
1217
|
-
1,
|
1218
|
-
test_stop_count["worker"],
|
1219
|
-
"The test_stop event was not called exactly one time in the worker node",
|
1220
|
-
)
|
1221
|
-
self.assertEqual(
|
1222
|
-
1,
|
1223
|
-
spawning_complete_count["master"],
|
1224
|
-
"The spawning_complete event was not called exactly one time in the master node",
|
1225
|
-
)
|
1226
|
-
self.assertEqual(
|
1227
|
-
1,
|
1228
|
-
spawning_complete_count["worker"],
|
1229
|
-
"The spawning_complete event was not called exactly one time in the worker node",
|
1230
|
-
)
|
1231
|
-
|
1232
|
-
def test_distributed_shape(self):
|
1233
|
-
"""
|
1234
|
-
Full integration test that starts both a MasterRunner and three WorkerRunner instances
|
1235
|
-
and tests a basic LoadTestShape with scaling up and down users
|
1236
|
-
"""
|
1237
|
-
|
1238
|
-
class TestUser(User):
|
1239
|
-
@task
|
1240
|
-
def my_task(self):
|
1241
|
-
pass
|
1242
|
-
|
1243
|
-
class TestShape(LoadTestShape):
|
1244
|
-
def tick(self):
|
1245
|
-
run_time = self.get_run_time()
|
1246
|
-
if run_time < 2:
|
1247
|
-
return 9, 9
|
1248
|
-
elif run_time < 4:
|
1249
|
-
return 21, 21
|
1250
|
-
elif run_time < 6:
|
1251
|
-
return 3, 21
|
1252
|
-
else:
|
1253
|
-
return None
|
1254
|
-
|
1255
|
-
with mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=0.3):
|
1256
|
-
test_shape = TestShape()
|
1257
|
-
master_env = Environment(user_classes=[TestUser], shape_class=test_shape)
|
1258
|
-
master_env.shape_class.reset_time()
|
1259
|
-
master = master_env.create_master_runner("*", 0)
|
1260
|
-
|
1261
|
-
workers = []
|
1262
|
-
for i in range(3):
|
1263
|
-
worker_env = Environment(user_classes=[TestUser])
|
1264
|
-
worker = worker_env.create_worker_runner("127.0.0.1", master.server.port)
|
1265
|
-
workers.append(worker)
|
1266
|
-
|
1267
|
-
# Give workers time to connect
|
1268
|
-
sleep(0.1)
|
1269
|
-
|
1270
|
-
# Start a shape test
|
1271
|
-
master.start_shape()
|
1272
|
-
sleep(1)
|
1273
|
-
|
1274
|
-
# Ensure workers have connected and started the correct amount of users
|
1275
|
-
for worker in workers:
|
1276
|
-
self.assertEqual(3, worker.user_count, "Shape test has not reached stage 1")
|
1277
|
-
self.assertEqual(
|
1278
|
-
9, test_shape.get_current_user_count(), "Shape is not seeing stage 1 runner user count correctly"
|
1279
|
-
)
|
1280
|
-
self.assertDictEqual(master.reported_user_classes_count, {"TestUser": 9})
|
1281
|
-
|
1282
|
-
# Ensure new stage with more users has been reached
|
1283
|
-
sleep(2)
|
1284
|
-
for worker in workers:
|
1285
|
-
self.assertEqual(7, worker.user_count, "Shape test has not reached stage 2")
|
1286
|
-
self.assertEqual(
|
1287
|
-
21, test_shape.get_current_user_count(), "Shape is not seeing stage 2 runner user count correctly"
|
1288
|
-
)
|
1289
|
-
self.assertDictEqual(master.reported_user_classes_count, {"TestUser": 21})
|
1290
|
-
|
1291
|
-
# Ensure new stage with less users has been reached
|
1292
|
-
sleep(2)
|
1293
|
-
for worker in workers:
|
1294
|
-
self.assertEqual(1, worker.user_count, "Shape test has not reached stage 3")
|
1295
|
-
self.assertEqual(
|
1296
|
-
3, test_shape.get_current_user_count(), "Shape is not seeing stage 3 runner user count correctly"
|
1297
|
-
)
|
1298
|
-
self.assertDictEqual(master.reported_user_classes_count, {"TestUser": 3})
|
1299
|
-
|
1300
|
-
# Ensure test stops at the end
|
1301
|
-
sleep(2)
|
1302
|
-
for worker in workers:
|
1303
|
-
self.assertEqual(0, worker.user_count, "Shape test has not stopped")
|
1304
|
-
self.assertEqual(
|
1305
|
-
0, test_shape.get_current_user_count(), "Shape is not seeing stopped runner user count correctly"
|
1306
|
-
)
|
1307
|
-
self.assertDictEqual(master.reported_user_classes_count, {"TestUser": 0})
|
1308
|
-
|
1309
|
-
self.assertEqual("stopped", master.state)
|
1310
|
-
|
1311
|
-
@unittest.skip(reason="a little flaky since #2465, so disabled for now")
|
1312
|
-
def test_distributed_shape_with_fixed_users(self):
|
1313
|
-
"""
|
1314
|
-
Full integration test that starts both a MasterRunner and three WorkerRunner instances
|
1315
|
-
and tests a basic LoadTestShape with scaling up and down users with 'fixed count' users
|
1316
|
-
"""
|
1317
|
-
|
1318
|
-
class TestUser(User):
|
1319
|
-
@task
|
1320
|
-
def my_task(self):
|
1321
|
-
pass
|
1322
|
-
|
1323
|
-
class FixedUser1(User):
|
1324
|
-
fixed_count = 1
|
1325
|
-
|
1326
|
-
@task
|
1327
|
-
def my_task(self):
|
1328
|
-
pass
|
1329
|
-
|
1330
|
-
class FixedUser2(User):
|
1331
|
-
fixed_count = 11
|
1332
|
-
|
1333
|
-
@task
|
1334
|
-
def my_task(self):
|
1335
|
-
pass
|
1336
|
-
|
1337
|
-
class TestShape(LoadTestShape):
|
1338
|
-
def tick(self):
|
1339
|
-
run_time = self.get_run_time()
|
1340
|
-
if run_time < 1:
|
1341
|
-
return 12, 12
|
1342
|
-
elif run_time < 2:
|
1343
|
-
return 36, 24
|
1344
|
-
elif run_time < 3:
|
1345
|
-
return 12, 24
|
1346
|
-
else:
|
1347
|
-
return None
|
1348
|
-
|
1349
|
-
with mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=0.3):
|
1350
|
-
test_shape = TestShape()
|
1351
|
-
master_env = Environment(user_classes=[TestUser, FixedUser1, FixedUser2], shape_class=test_shape)
|
1352
|
-
master_env.shape_class.reset_time()
|
1353
|
-
master = master_env.create_master_runner("*", 0)
|
1354
|
-
|
1355
|
-
workers = []
|
1356
|
-
for _ in range(3):
|
1357
|
-
worker_env = Environment(user_classes=[TestUser, FixedUser1, FixedUser2])
|
1358
|
-
worker = worker_env.create_worker_runner("127.0.0.1", master.server.port)
|
1359
|
-
workers.append(worker)
|
1360
|
-
|
1361
|
-
# Give workers time to connect
|
1362
|
-
sleep(0.1)
|
1363
|
-
|
1364
|
-
# Start a shape test
|
1365
|
-
master.start_shape()
|
1366
|
-
sleep(1)
|
1367
|
-
|
1368
|
-
# Ensure workers have connected and started the correct amount of users (fixed is spawn first)
|
1369
|
-
for worker in workers:
|
1370
|
-
self.assertEqual(4, worker.user_count, "Shape test has not reached stage 1")
|
1371
|
-
self.assertEqual(
|
1372
|
-
12, test_shape.get_current_user_count(), "Shape is not seeing stage 1 runner user count correctly"
|
1373
|
-
)
|
1374
|
-
self.assertDictEqual(master.reported_user_classes_count, {"FixedUser1": 1, "FixedUser2": 11, "TestUser": 0})
|
1375
|
-
|
1376
|
-
# Ensure new stage with more users has been reached
|
1377
|
-
sleep(1)
|
1378
|
-
for worker in workers:
|
1379
|
-
self.assertEqual(12, worker.user_count, "Shape test has not reached stage 2")
|
1380
|
-
self.assertEqual(
|
1381
|
-
36, test_shape.get_current_user_count(), "Shape is not seeing stage 2 runner user count correctly"
|
1382
|
-
)
|
1383
|
-
self.assertDictEqual(
|
1384
|
-
master.reported_user_classes_count, {"FixedUser1": 1, "FixedUser2": 11, "TestUser": 24}
|
1385
|
-
)
|
1386
|
-
|
1387
|
-
# Ensure new stage with less users has been reached
|
1388
|
-
# and expected count of the fixed users is present
|
1389
|
-
sleep(1)
|
1390
|
-
for worker in workers:
|
1391
|
-
self.assertEqual(4, worker.user_count, "Shape test has not reached stage 3")
|
1392
|
-
self.assertEqual(
|
1393
|
-
12, test_shape.get_current_user_count(), "Shape is not seeing stage 3 runner user count correctly"
|
1394
|
-
)
|
1395
|
-
self.assertDictEqual(master.reported_user_classes_count, {"FixedUser1": 1, "FixedUser2": 11, "TestUser": 0})
|
1396
|
-
|
1397
|
-
# Ensure test stops at the end
|
1398
|
-
sleep(0.5)
|
1399
|
-
for worker in workers:
|
1400
|
-
self.assertEqual(0, worker.user_count, "Shape test has not stopped")
|
1401
|
-
self.assertEqual(
|
1402
|
-
0, test_shape.get_current_user_count(), "Shape is not seeing stopped runner user count correctly"
|
1403
|
-
)
|
1404
|
-
self.assertDictEqual(master.reported_user_classes_count, {"FixedUser1": 0, "FixedUser2": 0, "TestUser": 0})
|
1405
|
-
|
1406
|
-
try:
|
1407
|
-
with gevent.Timeout(3.0):
|
1408
|
-
while master.state != STATE_STOPPED:
|
1409
|
-
sleep(0.1)
|
1410
|
-
finally:
|
1411
|
-
self.assertEqual(STATE_STOPPED, master.state)
|
1412
|
-
|
1413
|
-
@unittest.skip(reason="takes way too long (~45s)")
|
1414
|
-
def test_distributed_shape_with_stop_timeout(self):
|
1415
|
-
"""
|
1416
|
-
Full integration test that starts both a MasterRunner and five WorkerRunner instances
|
1417
|
-
and tests a basic LoadTestShape with scaling up and down users
|
1418
|
-
"""
|
1419
|
-
|
1420
|
-
class TestUser1(User):
|
1421
|
-
def start(self, group: Group):
|
1422
|
-
gevent.sleep(0.5)
|
1423
|
-
return super().start(group)
|
1424
|
-
|
1425
|
-
@task
|
1426
|
-
def my_task(self):
|
1427
|
-
gevent.sleep(0)
|
1428
|
-
|
1429
|
-
class TestUser2(User):
|
1430
|
-
def start(self, group: Group):
|
1431
|
-
gevent.sleep(0.5)
|
1432
|
-
return super().start(group)
|
1433
|
-
|
1434
|
-
@task
|
1435
|
-
def my_task(self):
|
1436
|
-
gevent.sleep(600)
|
1437
|
-
|
1438
|
-
class TestUser3(User):
|
1439
|
-
def start(self, group: Group):
|
1440
|
-
gevent.sleep(0.5)
|
1441
|
-
return super().start(group)
|
1442
|
-
|
1443
|
-
@task
|
1444
|
-
def my_task(self):
|
1445
|
-
gevent.sleep(600)
|
1446
|
-
|
1447
|
-
class TestShape(LoadTestShape):
|
1448
|
-
def tick(self):
|
1449
|
-
run_time = self.get_run_time()
|
1450
|
-
if run_time < 10:
|
1451
|
-
return 15, 3
|
1452
|
-
elif run_time < 30:
|
1453
|
-
return 5, 10
|
1454
|
-
else:
|
1455
|
-
return None
|
1456
|
-
|
1457
|
-
locust_worker_additional_wait_before_ready_after_stop = 5
|
1458
|
-
with (
|
1459
|
-
mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=0.3),
|
1460
|
-
patch_env(
|
1461
|
-
"LOCUST_WORKER_ADDITIONAL_WAIT_BEFORE_READY_AFTER_STOP",
|
1462
|
-
str(locust_worker_additional_wait_before_ready_after_stop),
|
1463
|
-
),
|
1464
|
-
):
|
1465
|
-
stop_timeout = 5
|
1466
|
-
master_env = Environment(
|
1467
|
-
user_classes=[TestUser1, TestUser2, TestUser3], shape_class=TestShape(), stop_timeout=stop_timeout
|
1468
|
-
)
|
1469
|
-
master_env.shape_class.reset_time()
|
1470
|
-
master = master_env.create_master_runner("*", 0)
|
1471
|
-
|
1472
|
-
workers = []
|
1473
|
-
for i in range(5):
|
1474
|
-
worker_env = Environment(user_classes=[TestUser1, TestUser2, TestUser3])
|
1475
|
-
worker = worker_env.create_worker_runner("127.0.0.1", master.server.port)
|
1476
|
-
workers.append(worker)
|
1477
|
-
|
1478
|
-
# Give workers time to connect
|
1479
|
-
sleep(0.1)
|
1480
|
-
|
1481
|
-
self.assertEqual(STATE_INIT, master.state)
|
1482
|
-
self.assertEqual(5, len(master.clients.ready))
|
1483
|
-
|
1484
|
-
# Re-order `workers` so that it is sorted by `id`.
|
1485
|
-
# This is required because the dispatch is done
|
1486
|
-
# on the sorted workers.
|
1487
|
-
workers = sorted(workers, key=lambda w: w.client_id)
|
1488
|
-
|
1489
|
-
# Start a shape test
|
1490
|
-
master.start_shape()
|
1491
|
-
|
1492
|
-
# First stage
|
1493
|
-
ts = time.time()
|
1494
|
-
while master.state != STATE_SPAWNING:
|
1495
|
-
self.assertTrue(time.time() - ts <= 1, master.state)
|
1496
|
-
sleep()
|
1497
|
-
sleep(5 - (time.time() - ts)) # runtime = 5s
|
1498
|
-
ts = time.time()
|
1499
|
-
while master.state != STATE_RUNNING:
|
1500
|
-
self.assertTrue(time.time() - ts <= 1, master.state)
|
1501
|
-
sleep()
|
1502
|
-
self.assertEqual(STATE_RUNNING, master.state)
|
1503
|
-
w1 = {"TestUser1": 1, "TestUser2": 1, "TestUser3": 1}
|
1504
|
-
w2 = {"TestUser1": 1, "TestUser2": 1, "TestUser3": 1}
|
1505
|
-
w3 = {"TestUser1": 1, "TestUser2": 1, "TestUser3": 1}
|
1506
|
-
w4 = {"TestUser1": 1, "TestUser2": 1, "TestUser3": 1}
|
1507
|
-
w5 = {"TestUser1": 1, "TestUser2": 1, "TestUser3": 1}
|
1508
|
-
self.assertDictEqual(w1, workers[0].user_classes_count)
|
1509
|
-
self.assertDictEqual(w2, workers[1].user_classes_count)
|
1510
|
-
self.assertDictEqual(w3, workers[2].user_classes_count)
|
1511
|
-
self.assertDictEqual(w4, workers[3].user_classes_count)
|
1512
|
-
self.assertDictEqual(w5, workers[4].user_classes_count)
|
1513
|
-
self.assertDictEqual(w1, master.clients[workers[0].client_id].user_classes_count)
|
1514
|
-
self.assertDictEqual(w2, master.clients[workers[1].client_id].user_classes_count)
|
1515
|
-
self.assertDictEqual(w3, master.clients[workers[2].client_id].user_classes_count)
|
1516
|
-
self.assertDictEqual(w4, master.clients[workers[3].client_id].user_classes_count)
|
1517
|
-
self.assertDictEqual(w5, master.clients[workers[4].client_id].user_classes_count)
|
1518
|
-
sleep(5 - (time.time() - ts)) # runtime = 10s
|
1519
|
-
|
1520
|
-
# Fourth stage
|
1521
|
-
ts = time.time()
|
1522
|
-
while master.state != STATE_SPAWNING:
|
1523
|
-
self.assertTrue(time.time() - ts <= 1, master.state)
|
1524
|
-
sleep()
|
1525
|
-
sleep(5 - (time.time() - ts)) # runtime = 15s
|
1526
|
-
|
1527
|
-
# Fourth stage - Excess TestUser1 have been stopped but
|
1528
|
-
# TestUser2/TestUser3 have not reached stop timeout yet, so
|
1529
|
-
# their number are unchanged
|
1530
|
-
ts = time.time()
|
1531
|
-
while master.state != STATE_RUNNING:
|
1532
|
-
self.assertTrue(time.time() - ts <= 1, master.state)
|
1533
|
-
sleep()
|
1534
|
-
delta = time.time() - ts
|
1535
|
-
w1 = {"TestUser1": 1, "TestUser2": 1, "TestUser3": 1}
|
1536
|
-
w2 = {"TestUser1": 0, "TestUser2": 1, "TestUser3": 1}
|
1537
|
-
w3 = {"TestUser1": 0, "TestUser2": 1, "TestUser3": 1}
|
1538
|
-
w4 = {"TestUser1": 1, "TestUser2": 1, "TestUser3": 1}
|
1539
|
-
w5 = {"TestUser1": 0, "TestUser2": 1, "TestUser3": 1}
|
1540
|
-
self.assertDictEqual(w1, workers[0].user_classes_count)
|
1541
|
-
self.assertDictEqual(w2, workers[1].user_classes_count)
|
1542
|
-
self.assertDictEqual(w3, workers[2].user_classes_count)
|
1543
|
-
self.assertDictEqual(w4, workers[3].user_classes_count)
|
1544
|
-
self.assertDictEqual(w5, workers[4].user_classes_count)
|
1545
|
-
self.assertDictEqual(w1, master.clients[workers[0].client_id].user_classes_count)
|
1546
|
-
self.assertDictEqual(w2, master.clients[workers[1].client_id].user_classes_count)
|
1547
|
-
self.assertDictEqual(w3, master.clients[workers[2].client_id].user_classes_count)
|
1548
|
-
self.assertDictEqual(w4, master.clients[workers[3].client_id].user_classes_count)
|
1549
|
-
self.assertDictEqual(w5, master.clients[workers[4].client_id].user_classes_count)
|
1550
|
-
sleep(1 - delta) # runtime = 16s
|
1551
|
-
|
1552
|
-
# Fourth stage - All users are now at the desired number
|
1553
|
-
ts = time.time()
|
1554
|
-
while master.state != STATE_RUNNING:
|
1555
|
-
self.assertTrue(time.time() - ts <= 1, master.state)
|
1556
|
-
sleep()
|
1557
|
-
delta = time.time() - ts
|
1558
|
-
w1 = {"TestUser1": 1, "TestUser2": 0, "TestUser3": 0}
|
1559
|
-
w2 = {"TestUser1": 0, "TestUser2": 1, "TestUser3": 0}
|
1560
|
-
w3 = {"TestUser1": 0, "TestUser2": 0, "TestUser3": 1}
|
1561
|
-
w4 = {"TestUser1": 1, "TestUser2": 0, "TestUser3": 0}
|
1562
|
-
w5 = {"TestUser1": 0, "TestUser2": 1, "TestUser3": 0}
|
1563
|
-
self.assertDictEqual(w1, workers[0].user_classes_count)
|
1564
|
-
self.assertDictEqual(w2, workers[1].user_classes_count)
|
1565
|
-
self.assertDictEqual(w3, workers[2].user_classes_count)
|
1566
|
-
self.assertDictEqual(w4, workers[3].user_classes_count)
|
1567
|
-
self.assertDictEqual(w5, workers[4].user_classes_count)
|
1568
|
-
self.assertDictEqual(w1, master.clients[workers[0].client_id].user_classes_count)
|
1569
|
-
self.assertDictEqual(w2, master.clients[workers[1].client_id].user_classes_count)
|
1570
|
-
self.assertDictEqual(w3, master.clients[workers[2].client_id].user_classes_count)
|
1571
|
-
self.assertDictEqual(w4, master.clients[workers[3].client_id].user_classes_count)
|
1572
|
-
self.assertDictEqual(w5, master.clients[workers[4].client_id].user_classes_count)
|
1573
|
-
sleep(10 - delta) # runtime = 26s
|
1574
|
-
|
1575
|
-
# Sleep stop_timeout and make sure the test has stopped
|
1576
|
-
sleep(5) # runtime = 31s
|
1577
|
-
self.assertEqual(STATE_STOPPING, master.state)
|
1578
|
-
sleep(stop_timeout) # runtime = 36s
|
1579
|
-
|
1580
|
-
# We wait for "stop_timeout" seconds to let the workers reconnect as "ready" with the master.
|
1581
|
-
# The reason for waiting an additional "stop_timeout" when we already waited for "stop_timeout"
|
1582
|
-
# above is that when a worker receives the stop message, it can take up to "stop_timeout"
|
1583
|
-
# for the worker to send the "client_stopped" message then an additional "stop_timeout" seconds
|
1584
|
-
# to send the "client_ready" message.
|
1585
|
-
ts = time.time()
|
1586
|
-
while len(master.clients.ready) != len(workers):
|
1587
|
-
self.assertTrue(
|
1588
|
-
time.time() - ts <= stop_timeout + locust_worker_additional_wait_before_ready_after_stop,
|
1589
|
-
f"expected {len(workers)} workers to be ready but only {len(master.clients.ready)} workers are",
|
1590
|
-
)
|
1591
|
-
sleep()
|
1592
|
-
sleep(1)
|
1593
|
-
|
1594
|
-
# Check that no users are running
|
1595
|
-
w1 = {"TestUser1": 0, "TestUser2": 0, "TestUser3": 0}
|
1596
|
-
w2 = {"TestUser1": 0, "TestUser2": 0, "TestUser3": 0}
|
1597
|
-
w3 = {"TestUser1": 0, "TestUser2": 0, "TestUser3": 0}
|
1598
|
-
w4 = {"TestUser1": 0, "TestUser2": 0, "TestUser3": 0}
|
1599
|
-
w5 = {"TestUser1": 0, "TestUser2": 0, "TestUser3": 0}
|
1600
|
-
self.assertDictEqual(w1, workers[0].user_classes_count)
|
1601
|
-
self.assertDictEqual(w2, workers[1].user_classes_count)
|
1602
|
-
self.assertDictEqual(w3, workers[2].user_classes_count)
|
1603
|
-
self.assertDictEqual(w4, workers[3].user_classes_count)
|
1604
|
-
self.assertDictEqual(w5, workers[4].user_classes_count)
|
1605
|
-
self.assertDictEqual(w1, master.clients[workers[0].client_id].user_classes_count)
|
1606
|
-
self.assertDictEqual(w2, master.clients[workers[1].client_id].user_classes_count)
|
1607
|
-
self.assertDictEqual(w3, master.clients[workers[2].client_id].user_classes_count)
|
1608
|
-
self.assertDictEqual(w4, master.clients[workers[3].client_id].user_classes_count)
|
1609
|
-
self.assertDictEqual(w5, master.clients[workers[4].client_id].user_classes_count)
|
1610
|
-
|
1611
|
-
ts = time.time()
|
1612
|
-
while master.state != STATE_STOPPED:
|
1613
|
-
self.assertTrue(time.time() - ts <= 5, master.state)
|
1614
|
-
sleep()
|
1615
|
-
|
1616
|
-
master.stop()
|
1617
|
-
|
1618
|
-
@unittest.skip(reason="takes a lot of time and has randomness to it")
|
1619
|
-
def test_distributed_shape_fuzzy_test(self):
|
1620
|
-
"""
|
1621
|
-
Incredibility useful test to find issues with dispatch logic. This test allowed to find
|
1622
|
-
multiple small corner cases with the new dispatch logic of locust v2.
|
1623
|
-
|
1624
|
-
The test is disabled by default because it takes a lot of time to run and has randomness to it.
|
1625
|
-
However, it is advised to run it a few times (you can run it in parallel) when modifying the dispatch logic.
|
1626
|
-
"""
|
1627
|
-
|
1628
|
-
class BaseUser(User):
|
1629
|
-
@task
|
1630
|
-
def my_task(self):
|
1631
|
-
gevent.sleep(600)
|
1632
|
-
|
1633
|
-
class TestUser01(BaseUser):
|
1634
|
-
pass
|
1635
|
-
|
1636
|
-
class TestUser02(BaseUser):
|
1637
|
-
pass
|
1638
|
-
|
1639
|
-
class TestUser03(BaseUser):
|
1640
|
-
pass
|
1641
|
-
|
1642
|
-
class TestUser04(BaseUser):
|
1643
|
-
pass
|
1644
|
-
|
1645
|
-
class TestUser05(BaseUser):
|
1646
|
-
pass
|
1647
|
-
|
1648
|
-
class TestUser06(BaseUser):
|
1649
|
-
pass
|
1650
|
-
|
1651
|
-
class TestUser07(BaseUser):
|
1652
|
-
pass
|
1653
|
-
|
1654
|
-
class TestUser08(BaseUser):
|
1655
|
-
pass
|
1656
|
-
|
1657
|
-
class TestUser09(BaseUser):
|
1658
|
-
pass
|
1659
|
-
|
1660
|
-
class TestUser10(BaseUser):
|
1661
|
-
pass
|
1662
|
-
|
1663
|
-
class TestUser11(BaseUser):
|
1664
|
-
pass
|
1665
|
-
|
1666
|
-
class TestUser12(BaseUser):
|
1667
|
-
pass
|
1668
|
-
|
1669
|
-
class TestUser13(BaseUser):
|
1670
|
-
pass
|
1671
|
-
|
1672
|
-
class TestUser14(BaseUser):
|
1673
|
-
pass
|
1674
|
-
|
1675
|
-
class TestUser15(BaseUser):
|
1676
|
-
pass
|
1677
|
-
|
1678
|
-
class TestShape(LoadTestShape):
|
1679
|
-
def __init__(self):
|
1680
|
-
super().__init__()
|
1681
|
-
|
1682
|
-
self.stages = []
|
1683
|
-
runtime = 0
|
1684
|
-
for _ in range(100):
|
1685
|
-
runtime += random.uniform(3, 15)
|
1686
|
-
self.stages.append((runtime, random.randint(1, 100), random.uniform(0.1, 10)))
|
1687
|
-
|
1688
|
-
def tick(self):
|
1689
|
-
run_time = self.get_run_time()
|
1690
|
-
for stage in self.stages:
|
1691
|
-
if run_time < stage[0]:
|
1692
|
-
return stage[1], stage[2]
|
1693
|
-
|
1694
|
-
user_classes = [
|
1695
|
-
TestUser01,
|
1696
|
-
TestUser02,
|
1697
|
-
TestUser03,
|
1698
|
-
TestUser04,
|
1699
|
-
TestUser05,
|
1700
|
-
TestUser06,
|
1701
|
-
TestUser07,
|
1702
|
-
TestUser08,
|
1703
|
-
TestUser09,
|
1704
|
-
TestUser10,
|
1705
|
-
TestUser11,
|
1706
|
-
TestUser12,
|
1707
|
-
TestUser13,
|
1708
|
-
TestUser14,
|
1709
|
-
TestUser15,
|
1710
|
-
]
|
1711
|
-
|
1712
|
-
chosen_user_classes = random.sample(user_classes, k=random.randint(1, len(user_classes)))
|
1713
|
-
|
1714
|
-
for user_class in chosen_user_classes:
|
1715
|
-
user_class.weight = random.uniform(1, 20)
|
1716
|
-
|
1717
|
-
locust_worker_additional_wait_before_ready_after_stop = 5
|
1718
|
-
with (
|
1719
|
-
mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=0.3),
|
1720
|
-
patch_env(
|
1721
|
-
"LOCUST_WORKER_ADDITIONAL_WAIT_BEFORE_READY_AFTER_STOP",
|
1722
|
-
str(locust_worker_additional_wait_before_ready_after_stop),
|
1723
|
-
),
|
1724
|
-
):
|
1725
|
-
stop_timeout = 5
|
1726
|
-
master_env = Environment(
|
1727
|
-
user_classes=chosen_user_classes, shape_class=TestShape(), stop_timeout=stop_timeout
|
1728
|
-
)
|
1729
|
-
master_env.shape_class.reset_time()
|
1730
|
-
master = master_env.create_master_runner("*", 0)
|
1731
|
-
|
1732
|
-
workers = []
|
1733
|
-
for i in range(random.randint(1, 30)):
|
1734
|
-
worker_env = Environment(user_classes=chosen_user_classes)
|
1735
|
-
worker = worker_env.create_worker_runner("127.0.0.1", master.server.port)
|
1736
|
-
workers.append(worker)
|
1737
|
-
|
1738
|
-
# Give workers time to connect
|
1739
|
-
sleep(0.1)
|
1740
|
-
|
1741
|
-
self.assertEqual(STATE_INIT, master.state)
|
1742
|
-
self.assertEqual(len(workers), len(master.clients.ready))
|
1743
|
-
|
1744
|
-
# Start a shape test
|
1745
|
-
master.start_shape()
|
1746
|
-
|
1747
|
-
ts = time.time()
|
1748
|
-
while master.state != STATE_STOPPED:
|
1749
|
-
self.assertTrue(time.time() - ts <= master_env.shape_class.stages[-1][0] + 60, master.state)
|
1750
|
-
print(
|
1751
|
-
f"{time.time() - ts:.2f}/{master_env.shape_class.stages[-1][0]:.2f} | {master.state} | {sum(master.reported_user_classes_count.values()):.0f} | "
|
1752
|
-
+ json.dumps(dict(sorted(master.reported_user_classes_count.items(), key=itemgetter(0))))
|
1753
|
-
)
|
1754
|
-
sleep(1)
|
1755
|
-
|
1756
|
-
master.stop()
|
1757
|
-
|
1758
|
-
def test_distributed_shape_stop_and_restart(self):
|
1759
|
-
"""
|
1760
|
-
Test stopping and then restarting a LoadTestShape
|
1761
|
-
"""
|
1762
|
-
|
1763
|
-
class TestUser(User):
|
1764
|
-
@task
|
1765
|
-
def my_task(self):
|
1766
|
-
pass
|
1767
|
-
|
1768
|
-
class TestShape(LoadTestShape):
|
1769
|
-
def tick(self):
|
1770
|
-
run_time = self.get_run_time()
|
1771
|
-
if run_time < 10:
|
1772
|
-
return 4, 4
|
1773
|
-
else:
|
1774
|
-
return None
|
1775
|
-
|
1776
|
-
with mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=0.3):
|
1777
|
-
master_env = Environment(user_classes=[TestUser], shape_class=TestShape())
|
1778
|
-
master_env.shape_class.reset_time()
|
1779
|
-
master = master_env.create_master_runner("*", 0)
|
1780
|
-
|
1781
|
-
workers = []
|
1782
|
-
for i in range(2):
|
1783
|
-
worker_env = Environment(user_classes=[TestUser])
|
1784
|
-
worker = worker_env.create_worker_runner("127.0.0.1", master.server.port)
|
1785
|
-
workers.append(worker)
|
1786
|
-
|
1787
|
-
# Give workers time to connect
|
1788
|
-
sleep(0.1)
|
1789
|
-
|
1790
|
-
# Start a shape test and ensure workers have connected and started the correct amount of users
|
1791
|
-
master.start_shape()
|
1792
|
-
sleep(1)
|
1793
|
-
for worker in workers:
|
1794
|
-
self.assertEqual(2, worker.user_count, "Shape test has not started correctly")
|
1795
|
-
|
1796
|
-
# Stop the test and ensure all user count is 0
|
1797
|
-
master.stop()
|
1798
|
-
sleep(1)
|
1799
|
-
for worker in workers:
|
1800
|
-
self.assertEqual(0, worker.user_count, "Shape test has not stopped")
|
1801
|
-
|
1802
|
-
# Then restart the test again and ensure workers have connected and started the correct amount of users
|
1803
|
-
master.start_shape()
|
1804
|
-
sleep(1)
|
1805
|
-
for worker in workers:
|
1806
|
-
self.assertEqual(2, worker.user_count, "Shape test has not started again correctly")
|
1807
|
-
master.stop()
|
1808
|
-
|
1809
|
-
def test_distributed_stop_with_stopping_state(self):
|
1810
|
-
"""
|
1811
|
-
Test stopping state when workers have stopped and now ready for a next test
|
1812
|
-
"""
|
1813
|
-
|
1814
|
-
class TestUser(User):
|
1815
|
-
@task
|
1816
|
-
def my_task(self):
|
1817
|
-
pass
|
1818
|
-
|
1819
|
-
with mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=0.3):
|
1820
|
-
master_env = Environment(user_classes=[TestUser])
|
1821
|
-
master = master_env.create_master_runner("*", 0)
|
1822
|
-
|
1823
|
-
workers = []
|
1824
|
-
for i in range(3):
|
1825
|
-
worker_env = Environment(user_classes=[TestUser])
|
1826
|
-
worker = worker_env.create_worker_runner("127.0.0.1", master.server.port)
|
1827
|
-
workers.append(worker)
|
1828
|
-
|
1829
|
-
for worker in workers:
|
1830
|
-
worker.send_message("client_stopped", None)
|
1831
|
-
|
1832
|
-
sleep(1)
|
1833
|
-
for worker in workers:
|
1834
|
-
self.assertEqual(STATE_INIT, worker.state, "Worker sent a client_stopped, should be ready once stopped")
|
1835
|
-
self.assertEqual(STATE_STOPPED, master.state)
|
1836
|
-
|
1837
|
-
def test_distributed_shape_statuses_transition(self):
|
1838
|
-
"""
|
1839
|
-
Full integration test that starts both a MasterRunner and five WorkerRunner instances
|
1840
|
-
The goal of this test is to validate the status on the master is correctly transitioned for each of the
|
1841
|
-
test phases.
|
1842
|
-
"""
|
1843
|
-
|
1844
|
-
class TestUser1(User):
|
1845
|
-
@task
|
1846
|
-
def my_task(self):
|
1847
|
-
gevent.sleep(600)
|
1848
|
-
|
1849
|
-
class TestShape(LoadTestShape):
|
1850
|
-
def tick(self):
|
1851
|
-
run_time = self.get_run_time()
|
1852
|
-
if run_time < 5:
|
1853
|
-
return 5, 2.5
|
1854
|
-
elif run_time < 10:
|
1855
|
-
return 10, 2.5
|
1856
|
-
elif run_time < 15:
|
1857
|
-
return 15, 2.5
|
1858
|
-
else:
|
1859
|
-
return None
|
1860
|
-
|
1861
|
-
locust_worker_additional_wait_before_ready_after_stop = 2
|
1862
|
-
with (
|
1863
|
-
mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=0.3),
|
1864
|
-
patch_env(
|
1865
|
-
"LOCUST_WORKER_ADDITIONAL_WAIT_BEFORE_READY_AFTER_STOP",
|
1866
|
-
str(locust_worker_additional_wait_before_ready_after_stop),
|
1867
|
-
),
|
1868
|
-
):
|
1869
|
-
master_env = Environment(user_classes=[TestUser1], shape_class=TestShape())
|
1870
|
-
|
1871
|
-
master_env.shape_class.reset_time()
|
1872
|
-
master = master_env.create_master_runner("*", 0)
|
1873
|
-
|
1874
|
-
workers = []
|
1875
|
-
for i in range(5):
|
1876
|
-
worker_env = Environment(user_classes=[TestUser1])
|
1877
|
-
worker = worker_env.create_worker_runner("127.0.0.1", master.server.port)
|
1878
|
-
workers.append(worker)
|
1879
|
-
|
1880
|
-
# Give workers time to connect
|
1881
|
-
sleep(0.1)
|
1882
|
-
|
1883
|
-
self.assertEqual(STATE_INIT, master.state)
|
1884
|
-
self.assertEqual(5, len(master.clients.ready))
|
1885
|
-
|
1886
|
-
statuses = []
|
1887
|
-
|
1888
|
-
ts = time.perf_counter()
|
1889
|
-
|
1890
|
-
master.start_shape()
|
1891
|
-
|
1892
|
-
while master.state != STATE_STOPPED:
|
1893
|
-
# +5s buffer to let master stop
|
1894
|
-
self.assertTrue(
|
1895
|
-
time.perf_counter() - ts <= 30 + locust_worker_additional_wait_before_ready_after_stop + 5,
|
1896
|
-
master.state,
|
1897
|
-
)
|
1898
|
-
statuses.append((time.perf_counter() - ts, master.state, master.user_count))
|
1899
|
-
sleep(0.1)
|
1900
|
-
|
1901
|
-
self.assertEqual(statuses[0][1], STATE_INIT)
|
1902
|
-
|
1903
|
-
stage = 1
|
1904
|
-
tolerance = 1 # in s
|
1905
|
-
for (t1, state1, user_count1), (t2, state2, user_count2) in zip(statuses[:-1], statuses[1:]):
|
1906
|
-
if state1 == STATE_SPAWNING and state2 == STATE_RUNNING and stage == 1:
|
1907
|
-
self.assertTrue(2.5 - tolerance <= t2 <= 2.5 + tolerance)
|
1908
|
-
elif state1 == STATE_RUNNING and state2 == STATE_SPAWNING and stage == 1:
|
1909
|
-
self.assertTrue(5 - tolerance <= t2 <= 5 + tolerance)
|
1910
|
-
stage += 1
|
1911
|
-
elif state1 == STATE_SPAWNING and state2 == STATE_RUNNING and stage == 2:
|
1912
|
-
self.assertTrue(7.5 - tolerance <= t2 <= 7.5 + tolerance)
|
1913
|
-
elif state1 == STATE_RUNNING and state2 == STATE_SPAWNING and stage == 2:
|
1914
|
-
self.assertTrue(10 - tolerance <= t2 <= 10 + tolerance)
|
1915
|
-
stage += 1
|
1916
|
-
elif state1 == STATE_SPAWNING and state2 == STATE_RUNNING and stage == 3:
|
1917
|
-
self.assertTrue(12.5 - tolerance <= t2 <= 12.5 + tolerance)
|
1918
|
-
elif state1 == STATE_RUNNING and state2 == STATE_SPAWNING and stage == 3:
|
1919
|
-
self.assertTrue(15 - tolerance <= t2 <= 15 + tolerance)
|
1920
|
-
stage += 1
|
1921
|
-
elif state1 == STATE_RUNNING and state2 == STATE_STOPPED and stage == 3:
|
1922
|
-
self.assertTrue(15 - tolerance <= t2 <= 15 + tolerance)
|
1923
|
-
|
1924
|
-
def test_swarm_endpoint_is_non_blocking(self):
|
1925
|
-
class TestUser1(User):
|
1926
|
-
@task
|
1927
|
-
def my_task(self):
|
1928
|
-
gevent.sleep(600)
|
1929
|
-
|
1930
|
-
class TestUser2(User):
|
1931
|
-
@task
|
1932
|
-
def my_task(self):
|
1933
|
-
gevent.sleep(600)
|
1934
|
-
|
1935
|
-
with mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=0.3):
|
1936
|
-
master_env = Environment(user_classes=[TestUser1, TestUser2])
|
1937
|
-
master = master_env.create_master_runner("*", 0)
|
1938
|
-
web_ui = master_env.create_web_ui("127.0.0.1", 0)
|
1939
|
-
|
1940
|
-
workers = []
|
1941
|
-
for i in range(2):
|
1942
|
-
worker_env = Environment(user_classes=[TestUser1, TestUser2])
|
1943
|
-
worker = worker_env.create_worker_runner("127.0.0.1", master.server.port)
|
1944
|
-
workers.append(worker)
|
1945
|
-
|
1946
|
-
# Give workers time to connect
|
1947
|
-
sleep(0.1)
|
1948
|
-
|
1949
|
-
self.assertEqual(STATE_INIT, master.state)
|
1950
|
-
self.assertEqual(len(master.clients.ready), len(workers))
|
1951
|
-
|
1952
|
-
ts = time.perf_counter()
|
1953
|
-
response = requests.post(
|
1954
|
-
f"http://127.0.0.1:{web_ui.server.server_port}/swarm",
|
1955
|
-
data={"user_count": 20, "spawn_rate": 5, "host": "https://localhost"},
|
1956
|
-
)
|
1957
|
-
self.assertEqual(200, response.status_code)
|
1958
|
-
self.assertTrue(0 <= time.perf_counter() - ts <= 1, "swarm endpoint is blocking")
|
1959
|
-
|
1960
|
-
ts = time.perf_counter()
|
1961
|
-
while master.state != STATE_RUNNING:
|
1962
|
-
self.assertTrue(time.perf_counter() - ts <= 4, master.state)
|
1963
|
-
gevent.sleep(0.1)
|
1964
|
-
|
1965
|
-
self.assertTrue(3 <= time.perf_counter() - ts <= 5)
|
1966
|
-
|
1967
|
-
self.assertEqual(master.user_count, 20)
|
1968
|
-
|
1969
|
-
master.stop()
|
1970
|
-
web_ui.stop()
|
1971
|
-
|
1972
|
-
def test_can_call_stop_endpoint_if_currently_swarming(self):
|
1973
|
-
class TestUser1(User):
|
1974
|
-
@task
|
1975
|
-
def my_task(self):
|
1976
|
-
gevent.sleep(600)
|
1977
|
-
|
1978
|
-
class TestUser2(User):
|
1979
|
-
@task
|
1980
|
-
def my_task(self):
|
1981
|
-
gevent.sleep(600)
|
1982
|
-
|
1983
|
-
with mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=0.3):
|
1984
|
-
stop_timeout = 5
|
1985
|
-
master_env = Environment(user_classes=[TestUser1, TestUser2], stop_timeout=stop_timeout)
|
1986
|
-
master = master_env.create_master_runner("*", 0)
|
1987
|
-
web_ui = master_env.create_web_ui("127.0.0.1", 0)
|
1988
|
-
|
1989
|
-
workers = []
|
1990
|
-
for i in range(2):
|
1991
|
-
worker_env = Environment(user_classes=[TestUser1, TestUser2])
|
1992
|
-
worker = worker_env.create_worker_runner("127.0.0.1", master.server.port)
|
1993
|
-
workers.append(worker)
|
1994
|
-
|
1995
|
-
# Give workers time to connect
|
1996
|
-
sleep(0.1)
|
1997
|
-
|
1998
|
-
self.assertEqual(STATE_INIT, master.state)
|
1999
|
-
self.assertEqual(len(master.clients.ready), len(workers))
|
2000
|
-
|
2001
|
-
ts = time.perf_counter()
|
2002
|
-
response = requests.post(
|
2003
|
-
f"http://127.0.0.1:{web_ui.server.server_port}/swarm",
|
2004
|
-
data={"user_count": 20, "spawn_rate": 1, "host": "https://localhost"},
|
2005
|
-
)
|
2006
|
-
self.assertEqual(200, response.status_code)
|
2007
|
-
self.assertTrue(0 <= time.perf_counter() - ts <= 1, "swarm endpoint is blocking")
|
2008
|
-
|
2009
|
-
gevent.sleep(5)
|
2010
|
-
|
2011
|
-
self.assertEqual(master.state, STATE_SPAWNING)
|
2012
|
-
self.assertLessEqual(master.user_count, 10)
|
2013
|
-
|
2014
|
-
ts = time.perf_counter()
|
2015
|
-
response = requests.get(
|
2016
|
-
f"http://127.0.0.1:{web_ui.server.server_port}/stop",
|
2017
|
-
)
|
2018
|
-
self.assertEqual(200, response.status_code)
|
2019
|
-
self.assertTrue(stop_timeout <= time.perf_counter() - ts <= stop_timeout + 5, "stop endpoint took too long")
|
2020
|
-
|
2021
|
-
ts = time.perf_counter()
|
2022
|
-
while master.state != STATE_STOPPED:
|
2023
|
-
self.assertTrue(time.perf_counter() - ts <= 2)
|
2024
|
-
gevent.sleep(0.1)
|
2025
|
-
|
2026
|
-
self.assertLessEqual(master.user_count, 0)
|
2027
|
-
|
2028
|
-
master.stop()
|
2029
|
-
web_ui.stop()
|
2030
|
-
|
2031
|
-
def test_target_user_count_is_set_before_ramp_up(self):
|
2032
|
-
"""Test for https://github.com/locustio/locust/issues/1883"""
|
2033
|
-
|
2034
|
-
class MyUser1(User):
|
2035
|
-
wait_time = constant(0)
|
2036
|
-
|
2037
|
-
@task
|
2038
|
-
def my_task(self):
|
2039
|
-
pass
|
2040
|
-
|
2041
|
-
with mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=0.3):
|
2042
|
-
# start a Master runner
|
2043
|
-
master_env = Environment(user_classes=[MyUser1])
|
2044
|
-
master = master_env.create_master_runner("*", 0)
|
2045
|
-
|
2046
|
-
test_start_event_fired = [False]
|
2047
|
-
|
2048
|
-
@master_env.events.test_start.add_listener
|
2049
|
-
def on_test_start(*args, **kwargs):
|
2050
|
-
test_start_event_fired[0] = True
|
2051
|
-
self.assertEqual(master.target_user_count, 3)
|
2052
|
-
|
2053
|
-
sleep(0)
|
2054
|
-
|
2055
|
-
# start 1 worker runner
|
2056
|
-
worker_env = Environment(user_classes=[MyUser1])
|
2057
|
-
worker = worker_env.create_worker_runner("127.0.0.1", master.server.port)
|
2058
|
-
|
2059
|
-
# give worker time to connect
|
2060
|
-
sleep(0.1)
|
2061
|
-
|
2062
|
-
gevent.spawn(master.start, 3, spawn_rate=1)
|
2063
|
-
|
2064
|
-
sleep(1)
|
2065
|
-
|
2066
|
-
self.assertEqual(master.target_user_count, 3)
|
2067
|
-
self.assertEqual(master.user_count, 1)
|
2068
|
-
# However, target_user_classes_count is only updated at the end of the ramp-up/ramp-down
|
2069
|
-
# due to the way it is implemented.
|
2070
|
-
self.assertDictEqual({}, master.target_user_classes_count)
|
2071
|
-
|
2072
|
-
sleep(2)
|
2073
|
-
|
2074
|
-
self.assertEqual(master.target_user_count, 3)
|
2075
|
-
self.assertEqual(master.user_count, 3)
|
2076
|
-
self.assertDictEqual({"MyUser1": 3}, master.target_user_classes_count)
|
2077
|
-
|
2078
|
-
master.quit()
|
2079
|
-
|
2080
|
-
# make sure users are killed
|
2081
|
-
self.assertEqual(0, worker.user_count)
|
2082
|
-
|
2083
|
-
self.assertTrue(test_start_event_fired[0])
|
2084
|
-
|
2085
|
-
def test_long_running_test_start_is_run_to_completion_on_worker(self):
|
2086
|
-
"""Test for https://github.com/locustio/locust/issues/1986"""
|
2087
|
-
|
2088
|
-
class MyUser1(User):
|
2089
|
-
wait_time = constant(0)
|
2090
|
-
|
2091
|
-
@task
|
2092
|
-
def my_task(self):
|
2093
|
-
pass
|
2094
|
-
|
2095
|
-
with mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=0.3):
|
2096
|
-
master_env = Environment(user_classes=[MyUser1])
|
2097
|
-
master = master_env.create_master_runner("*", 0)
|
2098
|
-
|
2099
|
-
sleep(0)
|
2100
|
-
|
2101
|
-
# start 1 worker runner
|
2102
|
-
worker_env = Environment(user_classes=[MyUser1])
|
2103
|
-
worker = worker_env.create_worker_runner("127.0.0.1", master.server.port)
|
2104
|
-
|
2105
|
-
test_start_exec_count = 0
|
2106
|
-
|
2107
|
-
@worker_env.events.test_start.add_listener
|
2108
|
-
def on_test_start(*_, **__):
|
2109
|
-
nonlocal test_start_exec_count
|
2110
|
-
test_start_exec_count += 1
|
2111
|
-
sleep(3)
|
2112
|
-
|
2113
|
-
# give worker time to connect
|
2114
|
-
sleep(0.1)
|
2115
|
-
|
2116
|
-
gevent.spawn(master.start, 3, spawn_rate=1)
|
2117
|
-
|
2118
|
-
t0 = time.perf_counter()
|
2119
|
-
while master.user_count != 3:
|
2120
|
-
self.assertLessEqual(time.perf_counter() - t0, 5, "Expected 3 users to be spawned")
|
2121
|
-
sleep(0.1)
|
2122
|
-
|
2123
|
-
master.quit()
|
2124
|
-
|
2125
|
-
# make sure users are killed
|
2126
|
-
self.assertEqual(0, worker.user_count)
|
2127
|
-
|
2128
|
-
self.assertEqual(test_start_exec_count, 1)
|
2129
|
-
|
2130
|
-
def test_heartbeat_event(self) -> None:
|
2131
|
-
"""
|
2132
|
-
Tests that heartbeat event is fired during a test
|
2133
|
-
"""
|
2134
|
-
|
2135
|
-
class TestUser(User):
|
2136
|
-
wait_time = constant(0.1)
|
2137
|
-
|
2138
|
-
@task
|
2139
|
-
def noop(self) -> None:
|
2140
|
-
pass
|
2141
|
-
|
2142
|
-
with mock.patch("locust.runners.HEARTBEAT_INTERVAL", new=1):
|
2143
|
-
# start a Master runner
|
2144
|
-
master_env = Environment(user_classes=[TestUser])
|
2145
|
-
worker_connect_events = []
|
2146
|
-
timestamp_start: list[float] = [time.time() + 3600.0]
|
2147
|
-
|
2148
|
-
def on_connect(client_id: str) -> None:
|
2149
|
-
worker_connect_events.append(client_id)
|
2150
|
-
timestamp_start[0] = time.time()
|
2151
|
-
|
2152
|
-
master_env.events.worker_connect.add_listener(on_connect)
|
2153
|
-
master = master_env.create_master_runner("*", 0)
|
2154
|
-
sleep(0)
|
2155
|
-
worker_env = Environment(user_classes=[TestUser])
|
2156
|
-
worker: WorkerRunner = worker_env.create_worker_runner("127.0.0.1", master.server.port)
|
2157
|
-
|
2158
|
-
with (
|
2159
|
-
mock.patch.object(
|
2160
|
-
worker.environment.events.heartbeat_received,
|
2161
|
-
"fire",
|
2162
|
-
wraps=worker.environment.events.heartbeat_received.fire,
|
2163
|
-
) as worker_heartbeat_received_mock,
|
2164
|
-
mock.patch.object(
|
2165
|
-
master.environment.events.heartbeat_sent,
|
2166
|
-
"fire",
|
2167
|
-
wraps=master.environment.events.heartbeat_sent.fire,
|
2168
|
-
) as master_heartbeat_sent_mock,
|
2169
|
-
):
|
2170
|
-
# give workers time to connect
|
2171
|
-
sleep(0.1)
|
2172
|
-
# issue start command that should trigger TestUsers to be spawned in the Workers
|
2173
|
-
master.start(2, spawn_rate=2)
|
2174
|
-
sleep(0.1)
|
2175
|
-
# check that worker nodes have started locusts
|
2176
|
-
self.assertEqual(2, worker.user_count)
|
2177
|
-
|
2178
|
-
# give time for nodes to send and receive 5 heartbeats, HEARTBEAT_INTERVAL mocked to 1 second, so
|
2179
|
-
# sleep 5 seconds - 1 second that represents the overhead from connecting
|
2180
|
-
sleep(5 - 1)
|
2181
|
-
master.quit()
|
2182
|
-
|
2183
|
-
# make sure users are killed
|
2184
|
-
self.assertEqual(0, worker.user_count)
|
2185
|
-
# make sure events happened correctly
|
2186
|
-
self.assertIn(worker.client_id, worker_connect_events)
|
2187
|
-
|
2188
|
-
timestamp_stop = time.time()
|
2189
|
-
|
2190
|
-
self.assertEqual(worker_heartbeat_received_mock.call_count, 5)
|
2191
|
-
self.assertEqual(master_heartbeat_sent_mock.call_count, 5)
|
2192
|
-
|
2193
|
-
for call_args, call_kwargs in [
|
2194
|
-
*worker_heartbeat_received_mock.call_args_list,
|
2195
|
-
*master_heartbeat_sent_mock.call_args_list,
|
2196
|
-
]:
|
2197
|
-
self.assertEqual(call_args, ()) # args
|
2198
|
-
self.assertEqual(call_kwargs, {"client_id": worker.client_id, "timestamp": mock.ANY}) # kwargs
|
2199
|
-
self.assertGreaterEqual(call_kwargs["timestamp"], timestamp_start[0])
|
2200
|
-
self.assertLessEqual(call_kwargs["timestamp"], timestamp_stop)
|
2201
|
-
|
2202
|
-
def test_usage_monitor_event(self) -> None:
|
2203
|
-
"""
|
2204
|
-
Tests that usage_monitor event is fired during a test
|
2205
|
-
"""
|
2206
|
-
|
2207
|
-
class TestUser(User):
|
2208
|
-
wait_time = constant(0.1)
|
2209
|
-
|
2210
|
-
@task
|
2211
|
-
def noop(self) -> None:
|
2212
|
-
pass
|
2213
|
-
|
2214
|
-
with mock.patch("locust.runners.CPU_MONITOR_INTERVAL", new=1):
|
2215
|
-
# start a Master runner
|
2216
|
-
master_env = Environment(user_classes=[TestUser])
|
2217
|
-
worker_connect_events = []
|
2218
|
-
|
2219
|
-
def on_connect(client_id: str) -> None:
|
2220
|
-
worker_connect_events.append(client_id)
|
2221
|
-
|
2222
|
-
master_env.events.worker_connect.add_listener(on_connect)
|
2223
|
-
master = master_env.create_master_runner("*", 0)
|
2224
|
-
sleep(0)
|
2225
|
-
worker_env = Environment(user_classes=[TestUser])
|
2226
|
-
worker: WorkerRunner = worker_env.create_worker_runner("127.0.0.1", master.server.port)
|
2227
|
-
|
2228
|
-
with (
|
2229
|
-
mock.patch.object(
|
2230
|
-
worker.environment.events.usage_monitor, "fire", wraps=worker.environment.events.usage_monitor.fire
|
2231
|
-
) as worker_usage_monitor_mock,
|
2232
|
-
mock.patch.object(
|
2233
|
-
master.environment.events.usage_monitor, "fire", wraps=master.environment.events.usage_monitor.fire
|
2234
|
-
) as master_usage_monitor_mock,
|
2235
|
-
):
|
2236
|
-
# give workers time to connect
|
2237
|
-
sleep(0.1)
|
2238
|
-
# issue start command that should trigger TestUsers to be spawned in the Workers
|
2239
|
-
master.start(2, spawn_rate=2)
|
2240
|
-
sleep(0.1)
|
2241
|
-
# check that worker nodes have started locusts
|
2242
|
-
self.assertEqual(2, worker.user_count)
|
2243
|
-
|
2244
|
-
# give time for nodes to send 5 usage_monitor events, CPU_MONITOR_INTERVAL mocked to 1 second, so
|
2245
|
-
# sleep 5 seconds
|
2246
|
-
sleep(5)
|
2247
|
-
master.quit()
|
2248
|
-
|
2249
|
-
# make sure users are killed
|
2250
|
-
self.assertEqual(0, worker.user_count)
|
2251
|
-
# make sure events happened correctly
|
2252
|
-
self.assertIn(worker.client_id, worker_connect_events)
|
2253
|
-
|
2254
|
-
self.assertEqual(worker_usage_monitor_mock.call_count, 5)
|
2255
|
-
self.assertEqual(master_usage_monitor_mock.call_count, 5)
|
2256
|
-
|
2257
|
-
for call_args, call_kwargs in master_usage_monitor_mock:
|
2258
|
-
self.assertEqual(call_args, ()) # args
|
2259
|
-
self.assertEqual(
|
2260
|
-
call_kwargs, {"environment": master_env, "cpu_usage": mock.ANY, "memory_usage": mock.ANY}
|
2261
|
-
) # kwargs
|
2262
|
-
self.assertTrue(isinstance(call_kwargs["cpu_usage"], float))
|
2263
|
-
self.assertTrue(isinstance(call_kwargs["memory_usage"], int))
|
2264
|
-
|
2265
|
-
for call_args, call_kwargs in worker_usage_monitor_mock:
|
2266
|
-
self.assertEqual(call_args, ()) # args
|
2267
|
-
self.assertEqual(
|
2268
|
-
call_kwargs, {"environment": worker_env, "cpu_usage": mock.ANY, "memory_usage": mock.ANY}
|
2269
|
-
) # kwargs
|
2270
|
-
self.assertTrue(isinstance(call_kwargs["cpu_usage"], float))
|
2271
|
-
self.assertTrue(isinstance(call_kwargs["memory_usage"], int))
|
2272
|
-
|
2273
|
-
|
2274
|
-
class TestMasterRunner(LocustRunnerTestCase):
|
2275
|
-
def setUp(self):
|
2276
|
-
super().setUp()
|
2277
|
-
self.environment = Environment(events=locust.events, catch_exceptions=False)
|
2278
|
-
|
2279
|
-
def tearDown(self):
|
2280
|
-
super().tearDown()
|
2281
|
-
|
2282
|
-
def get_runner(self, user_classes=None):
|
2283
|
-
if user_classes is not None:
|
2284
|
-
self.environment.user_classes = user_classes
|
2285
|
-
return self.environment.create_master_runner("*", 5557)
|
2286
|
-
|
2287
|
-
def test_worker_connect(self):
|
2288
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
2289
|
-
master = self.get_runner()
|
2290
|
-
server.mocked_send(Message("client_ready", __version__, "zeh_fake_client1"))
|
2291
|
-
self.assertEqual(1, len(master.clients))
|
2292
|
-
self.assertTrue(
|
2293
|
-
"zeh_fake_client1" in master.clients, "Could not find fake client in master instance's clients dict"
|
2294
|
-
)
|
2295
|
-
server.mocked_send(Message("client_ready", __version__, "zeh_fake_client2"))
|
2296
|
-
server.mocked_send(Message("client_ready", __version__, "zeh_fake_client3"))
|
2297
|
-
server.mocked_send(Message("client_ready", __version__, "zeh_fake_client4"))
|
2298
|
-
self.assertEqual(4, len(master.clients))
|
2299
|
-
|
2300
|
-
server.mocked_send(Message("quit", None, "zeh_fake_client3"))
|
2301
|
-
self.assertEqual(3, len(master.clients))
|
2302
|
-
|
2303
|
-
def test_worker_connect_with_special_versions(self):
|
2304
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
2305
|
-
master = self.get_runner()
|
2306
|
-
server.mocked_send(Message("client_ready", None, "1.x_style_client_should_not_be_allowed"))
|
2307
|
-
self.assertEqual(1, len(self.mocked_log.error))
|
2308
|
-
self.assertEqual(0, len(master.clients))
|
2309
|
-
server.mocked_send(Message("client_ready", "abcd", "other_version_mismatch_should_just_give_a_warning"))
|
2310
|
-
self.assertEqual(1, len(self.mocked_log.warning))
|
2311
|
-
self.assertEqual(1, len(master.clients))
|
2312
|
-
server.mocked_send(Message("client_ready", -1, "version_check_bypass_should_not_warn"))
|
2313
|
-
self.assertEqual(1, len(self.mocked_log.warning))
|
2314
|
-
self.assertEqual(2, len(master.clients))
|
2315
|
-
server.mocked_send(
|
2316
|
-
Message("client_ready", __version__ + "1", "difference_in_patch_version_should_not_warn")
|
2317
|
-
)
|
2318
|
-
self.assertEqual(3, len(master.clients))
|
2319
|
-
self.assertEqual(1, len(self.mocked_log.warning))
|
2320
|
-
|
2321
|
-
def test_worker_stats_report_median(self):
|
2322
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
2323
|
-
master = self.get_runner()
|
2324
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client"))
|
2325
|
-
|
2326
|
-
master.stats.get("/", "GET").log(100, 23455)
|
2327
|
-
master.stats.get("/", "GET").log(800, 23455)
|
2328
|
-
master.stats.get("/", "GET").log(700, 23455)
|
2329
|
-
|
2330
|
-
data = {"user_count": 1}
|
2331
|
-
self.environment.events.report_to_master.fire(client_id="fake_client", data=data)
|
2332
|
-
master.stats.clear_all()
|
2333
|
-
|
2334
|
-
server.mocked_send(Message("stats", data, "fake_client"))
|
2335
|
-
s = master.stats.get("/", "GET")
|
2336
|
-
self.assertEqual(700, s.median_response_time)
|
2337
|
-
|
2338
|
-
def test_worker_stats_report_with_none_response_times(self):
|
2339
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
2340
|
-
master = self.get_runner()
|
2341
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client"))
|
2342
|
-
|
2343
|
-
master.stats.get("/mixed", "GET").log(0, 23455)
|
2344
|
-
master.stats.get("/mixed", "GET").log(800, 23455)
|
2345
|
-
master.stats.get("/mixed", "GET").log(700, 23455)
|
2346
|
-
master.stats.get("/mixed", "GET").log(None, 23455)
|
2347
|
-
master.stats.get("/mixed", "GET").log(None, 23455)
|
2348
|
-
master.stats.get("/mixed", "GET").log(None, 23455)
|
2349
|
-
master.stats.get("/mixed", "GET").log(None, 23455)
|
2350
|
-
master.stats.get("/onlyNone", "GET").log(None, 23455)
|
2351
|
-
|
2352
|
-
data = {"user_count": 1}
|
2353
|
-
self.environment.events.report_to_master.fire(client_id="fake_client", data=data)
|
2354
|
-
master.stats.clear_all()
|
2355
|
-
|
2356
|
-
server.mocked_send(Message("stats", data, "fake_client"))
|
2357
|
-
s1 = master.stats.get("/mixed", "GET")
|
2358
|
-
self.assertEqual(700, s1.median_response_time)
|
2359
|
-
self.assertEqual(500, s1.avg_response_time)
|
2360
|
-
s2 = master.stats.get("/onlyNone", "GET")
|
2361
|
-
self.assertEqual(0, s2.median_response_time)
|
2362
|
-
self.assertEqual(0, s2.avg_response_time)
|
2363
|
-
|
2364
|
-
def test_master_marks_downed_workers_as_missing(self):
|
2365
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
2366
|
-
master = self.get_runner()
|
2367
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client"))
|
2368
|
-
sleep(6)
|
2369
|
-
# print(master.clients['fake_client'].__dict__)
|
2370
|
-
assert master.clients["fake_client"].state == STATE_MISSING
|
2371
|
-
|
2372
|
-
def test_last_worker_quitting_stops_test(self):
|
2373
|
-
class TestUser(User):
|
2374
|
-
@task
|
2375
|
-
def my_task(self):
|
2376
|
-
gevent.sleep(600)
|
2377
|
-
|
2378
|
-
with (
|
2379
|
-
mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server,
|
2380
|
-
patch_env("LOCUST_WAIT_FOR_WORKERS_REPORT_AFTER_RAMP_UP", "0.1"),
|
2381
|
-
):
|
2382
|
-
master = self.get_runner(user_classes=[TestUser])
|
2383
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client1"))
|
2384
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client2"))
|
2385
|
-
|
2386
|
-
master.start(1, 2)
|
2387
|
-
server.mocked_send(Message("spawning", None, "fake_client1"))
|
2388
|
-
server.mocked_send(Message("spawning", None, "fake_client2"))
|
2389
|
-
|
2390
|
-
server.mocked_send(Message("quit", None, "fake_client1"))
|
2391
|
-
sleep(0.1)
|
2392
|
-
self.assertEqual(1, len(master.clients.all))
|
2393
|
-
self.assertNotEqual(STATE_STOPPED, master.state, "Not all workers quit but test stopped anyway.")
|
2394
|
-
|
2395
|
-
server.mocked_send(Message("quit", None, "fake_client2"))
|
2396
|
-
sleep(0.1)
|
2397
|
-
self.assertEqual(0, len(master.clients.all))
|
2398
|
-
self.assertEqual(STATE_STOPPED, master.state, "All workers quit but test didn't stop.")
|
2399
|
-
|
2400
|
-
@mock.patch("locust.runners.HEARTBEAT_INTERVAL", new=0.1)
|
2401
|
-
def test_last_worker_missing_stops_test(self):
|
2402
|
-
class TestUser(User):
|
2403
|
-
@task
|
2404
|
-
def my_task(self):
|
2405
|
-
gevent.sleep(600)
|
2406
|
-
|
2407
|
-
with (
|
2408
|
-
mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server,
|
2409
|
-
patch_env("LOCUST_WAIT_FOR_WORKERS_REPORT_AFTER_RAMP_UP", "0.1"),
|
2410
|
-
):
|
2411
|
-
master = self.get_runner(user_classes=[TestUser])
|
2412
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client1"))
|
2413
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client2"))
|
2414
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client3"))
|
2415
|
-
|
2416
|
-
master.start(3, 3)
|
2417
|
-
server.mocked_send(Message("spawning", None, "fake_client1"))
|
2418
|
-
server.mocked_send(Message("spawning", None, "fake_client2"))
|
2419
|
-
server.mocked_send(Message("spawning", None, "fake_client3"))
|
2420
|
-
|
2421
|
-
sleep(0.2)
|
2422
|
-
server.mocked_send(
|
2423
|
-
Message(
|
2424
|
-
"heartbeat",
|
2425
|
-
{"state": STATE_RUNNING, "current_cpu_usage": 50, "current_memory_usage": 200, "count": 1},
|
2426
|
-
"fake_client1",
|
2427
|
-
)
|
2428
|
-
)
|
2429
|
-
server.mocked_send(
|
2430
|
-
Message(
|
2431
|
-
"heartbeat",
|
2432
|
-
{"state": STATE_RUNNING, "current_cpu_usage": 50, "current_memory_usage": 200, "count": 1},
|
2433
|
-
"fake_client2",
|
2434
|
-
)
|
2435
|
-
)
|
2436
|
-
server.mocked_send(
|
2437
|
-
Message(
|
2438
|
-
"heartbeat",
|
2439
|
-
{"state": STATE_RUNNING, "current_cpu_usage": 50, "current_memory_usage": 200, "count": 1},
|
2440
|
-
"fake_client3",
|
2441
|
-
)
|
2442
|
-
)
|
2443
|
-
|
2444
|
-
sleep(0.2)
|
2445
|
-
self.assertEqual(0, len(master.clients.missing))
|
2446
|
-
self.assertEqual(3, master.worker_count)
|
2447
|
-
self.assertNotIn(
|
2448
|
-
master.state, [STATE_STOPPED, STATE_STOPPING], "Not all workers went missing but test stopped anyway."
|
2449
|
-
)
|
2450
|
-
|
2451
|
-
server.mocked_send(
|
2452
|
-
Message(
|
2453
|
-
"heartbeat",
|
2454
|
-
{"state": STATE_RUNNING, "current_cpu_usage": 50, "current_memory_usage": 200, "count": 1},
|
2455
|
-
"fake_client1",
|
2456
|
-
)
|
2457
|
-
)
|
2458
|
-
|
2459
|
-
sleep(0.4)
|
2460
|
-
self.assertEqual(2, len(master.clients.missing))
|
2461
|
-
self.assertEqual(1, master.worker_count)
|
2462
|
-
self.assertNotIn(
|
2463
|
-
master.state, [STATE_STOPPED, STATE_STOPPING], "Not all workers went missing but test stopped anyway."
|
2464
|
-
)
|
2465
|
-
|
2466
|
-
sleep(0.2)
|
2467
|
-
self.assertEqual(3, len(master.clients.missing))
|
2468
|
-
self.assertEqual(0, master.worker_count)
|
2469
|
-
self.assertEqual(STATE_STOPPED, master.state, "All workers went missing but test didn't stop.")
|
2470
|
-
|
2471
|
-
@mock.patch("locust.runners.HEARTBEAT_INTERVAL", new=0.1)
|
2472
|
-
@mock.patch("locust.runners.HEARTBEAT_DEAD_INTERNAL", new=-3)
|
2473
|
-
def test_worker_missing_after_heartbeat_dead_interval(self):
|
2474
|
-
class TestUser(User):
|
2475
|
-
@task
|
2476
|
-
def my_task(self):
|
2477
|
-
gevent.sleep(600)
|
2478
|
-
|
2479
|
-
with (
|
2480
|
-
mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server,
|
2481
|
-
patch_env("LOCUST_WAIT_FOR_WORKERS_REPORT_AFTER_RAMP_UP", "0.1"),
|
2482
|
-
):
|
2483
|
-
master = self.get_runner(user_classes=[TestUser])
|
2484
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client1"))
|
2485
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client2"))
|
2486
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client3"))
|
2487
|
-
|
2488
|
-
master.start(3, 3)
|
2489
|
-
server.mocked_send(Message("spawning", None, "fake_client1"))
|
2490
|
-
server.mocked_send(Message("spawning", None, "fake_client2"))
|
2491
|
-
server.mocked_send(Message("spawning", None, "fake_client3"))
|
2492
|
-
|
2493
|
-
sleep(0.1)
|
2494
|
-
server.mocked_send(
|
2495
|
-
Message(
|
2496
|
-
"heartbeat",
|
2497
|
-
{"state": STATE_RUNNING, "current_cpu_usage": 50, "current_memory_usage": 200, "count": 1},
|
2498
|
-
"fake_client1",
|
2499
|
-
)
|
2500
|
-
)
|
2501
|
-
server.mocked_send(
|
2502
|
-
Message(
|
2503
|
-
"heartbeat",
|
2504
|
-
{"state": STATE_RUNNING, "current_cpu_usage": 50, "current_memory_usage": 200, "count": 1},
|
2505
|
-
"fake_client2",
|
2506
|
-
)
|
2507
|
-
)
|
2508
|
-
server.mocked_send(
|
2509
|
-
Message(
|
2510
|
-
"heartbeat",
|
2511
|
-
{"state": STATE_RUNNING, "current_cpu_usage": 50, "current_memory_usage": 200, "count": 1},
|
2512
|
-
"fake_client3",
|
2513
|
-
)
|
2514
|
-
)
|
2515
|
-
|
2516
|
-
sleep(0.1)
|
2517
|
-
# initially all workers are in active state
|
2518
|
-
self.assertEqual(0, len(master.clients.missing))
|
2519
|
-
self.assertEqual(3, master.worker_count)
|
2520
|
-
server.mocked_send(
|
2521
|
-
Message(
|
2522
|
-
"heartbeat",
|
2523
|
-
{"state": STATE_RUNNING, "current_cpu_usage": 50, "current_memory_usage": 200, "count": 1},
|
2524
|
-
"fake_client1",
|
2525
|
-
)
|
2526
|
-
)
|
2527
|
-
|
2528
|
-
server.mocked_send(
|
2529
|
-
Message(
|
2530
|
-
"heartbeat",
|
2531
|
-
{"state": STATE_RUNNING, "current_cpu_usage": 50, "current_memory_usage": 200, "count": 1},
|
2532
|
-
"fake_client2",
|
2533
|
-
)
|
2534
|
-
)
|
2535
|
-
|
2536
|
-
sleep(0.6)
|
2537
|
-
# 4 intervals are passed since all 3 heart beats all workers are in missing state
|
2538
|
-
self.assertEqual(3, len(master.clients.missing))
|
2539
|
-
self.assertEqual(0, master.worker_count)
|
2540
|
-
|
2541
|
-
server.mocked_send(
|
2542
|
-
Message(
|
2543
|
-
"heartbeat",
|
2544
|
-
{"state": STATE_RUNNING, "current_cpu_usage": 50, "current_memory_usage": 200, "count": 1},
|
2545
|
-
"fake_client1",
|
2546
|
-
)
|
2547
|
-
)
|
2548
|
-
|
2549
|
-
server.mocked_send(
|
2550
|
-
Message(
|
2551
|
-
"heartbeat",
|
2552
|
-
{"state": STATE_RUNNING, "current_cpu_usage": 50, "current_memory_usage": 200, "count": 1},
|
2553
|
-
"fake_client2",
|
2554
|
-
)
|
2555
|
-
)
|
2556
|
-
sleep(0.2)
|
2557
|
-
# heartbeat received from two workers so they are active, for fake_client3 HEARTBEAT_DEAD_INTERNAL has been breached, so it will be removed from worker list
|
2558
|
-
self.assertEqual(0, len(master.clients.missing))
|
2559
|
-
self.assertEqual(2, master.worker_count)
|
2560
|
-
master.stop()
|
2561
|
-
|
2562
|
-
def test_master_total_stats(self):
|
2563
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
2564
|
-
master = self.get_runner()
|
2565
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client"))
|
2566
|
-
stats = RequestStats()
|
2567
|
-
stats.log_request("GET", "/1", 100, 3546)
|
2568
|
-
stats.log_request("GET", "/1", 800, 56743)
|
2569
|
-
stats2 = RequestStats()
|
2570
|
-
stats2.log_request("GET", "/2", 700, 2201)
|
2571
|
-
server.mocked_send(
|
2572
|
-
Message(
|
2573
|
-
"stats",
|
2574
|
-
{
|
2575
|
-
"stats": stats.serialize_stats(),
|
2576
|
-
"stats_total": stats.total.serialize(),
|
2577
|
-
"errors": stats.serialize_errors(),
|
2578
|
-
"user_count": 1,
|
2579
|
-
},
|
2580
|
-
"fake_client",
|
2581
|
-
)
|
2582
|
-
)
|
2583
|
-
server.mocked_send(
|
2584
|
-
Message(
|
2585
|
-
"stats",
|
2586
|
-
{
|
2587
|
-
"stats": stats2.serialize_stats(),
|
2588
|
-
"stats_total": stats2.total.serialize(),
|
2589
|
-
"errors": stats2.serialize_errors(),
|
2590
|
-
"user_count": 2,
|
2591
|
-
},
|
2592
|
-
"fake_client",
|
2593
|
-
)
|
2594
|
-
)
|
2595
|
-
self.assertEqual(700, master.stats.total.median_response_time)
|
2596
|
-
|
2597
|
-
def test_master_total_stats_with_none_response_times(self):
|
2598
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
2599
|
-
master = self.get_runner()
|
2600
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client"))
|
2601
|
-
stats = RequestStats()
|
2602
|
-
stats.log_request("GET", "/1", 100, 3546)
|
2603
|
-
stats.log_request("GET", "/1", 800, 56743)
|
2604
|
-
stats.log_request("GET", "/1", None, 56743)
|
2605
|
-
stats2 = RequestStats()
|
2606
|
-
stats2.log_request("GET", "/2", 700, 2201)
|
2607
|
-
stats2.log_request("GET", "/2", None, 2201)
|
2608
|
-
stats3 = RequestStats()
|
2609
|
-
stats3.log_request("GET", "/3", None, 2201)
|
2610
|
-
server.mocked_send(
|
2611
|
-
Message(
|
2612
|
-
"stats",
|
2613
|
-
{
|
2614
|
-
"stats": stats.serialize_stats(),
|
2615
|
-
"stats_total": stats.total.serialize(),
|
2616
|
-
"errors": stats.serialize_errors(),
|
2617
|
-
"user_count": 1,
|
2618
|
-
},
|
2619
|
-
"fake_client",
|
2620
|
-
)
|
2621
|
-
)
|
2622
|
-
server.mocked_send(
|
2623
|
-
Message(
|
2624
|
-
"stats",
|
2625
|
-
{
|
2626
|
-
"stats": stats2.serialize_stats(),
|
2627
|
-
"stats_total": stats2.total.serialize(),
|
2628
|
-
"errors": stats2.serialize_errors(),
|
2629
|
-
"user_count": 2,
|
2630
|
-
},
|
2631
|
-
"fake_client",
|
2632
|
-
)
|
2633
|
-
)
|
2634
|
-
server.mocked_send(
|
2635
|
-
Message(
|
2636
|
-
"stats",
|
2637
|
-
{
|
2638
|
-
"stats": stats3.serialize_stats(),
|
2639
|
-
"stats_total": stats3.total.serialize(),
|
2640
|
-
"errors": stats3.serialize_errors(),
|
2641
|
-
"user_count": 2,
|
2642
|
-
},
|
2643
|
-
"fake_client",
|
2644
|
-
)
|
2645
|
-
)
|
2646
|
-
self.assertEqual(700, master.stats.total.median_response_time)
|
2647
|
-
|
2648
|
-
def test_master_current_response_times(self):
|
2649
|
-
start_time = 1
|
2650
|
-
with mock.patch("time.time") as mocked_time:
|
2651
|
-
mocked_time.return_value = start_time
|
2652
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
2653
|
-
master = self.get_runner()
|
2654
|
-
self.environment.stats.reset_all()
|
2655
|
-
mocked_time.return_value += 1.0234
|
2656
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client"))
|
2657
|
-
stats = RequestStats()
|
2658
|
-
stats.log_request("GET", "/1", 100, 3546)
|
2659
|
-
stats.log_request("GET", "/1", 800, 56743)
|
2660
|
-
server.mocked_send(
|
2661
|
-
Message(
|
2662
|
-
"stats",
|
2663
|
-
{
|
2664
|
-
"stats": stats.serialize_stats(),
|
2665
|
-
"stats_total": stats.total.get_stripped_report(),
|
2666
|
-
"errors": stats.serialize_errors(),
|
2667
|
-
"user_count": 1,
|
2668
|
-
},
|
2669
|
-
"fake_client",
|
2670
|
-
)
|
2671
|
-
)
|
2672
|
-
mocked_time.return_value += 1
|
2673
|
-
stats2 = RequestStats()
|
2674
|
-
stats2.log_request("GET", "/2", 400, 2201)
|
2675
|
-
server.mocked_send(
|
2676
|
-
Message(
|
2677
|
-
"stats",
|
2678
|
-
{
|
2679
|
-
"stats": stats2.serialize_stats(),
|
2680
|
-
"stats_total": stats2.total.get_stripped_report(),
|
2681
|
-
"errors": stats2.serialize_errors(),
|
2682
|
-
"user_count": 2,
|
2683
|
-
},
|
2684
|
-
"fake_client",
|
2685
|
-
)
|
2686
|
-
)
|
2687
|
-
mocked_time.return_value += 4
|
2688
|
-
self.assertEqual(400, master.stats.total.get_current_response_time_percentile(0.5))
|
2689
|
-
self.assertEqual(800, master.stats.total.get_current_response_time_percentile(0.95))
|
2690
|
-
|
2691
|
-
# let 10 second pass, do some more requests, send it to the master and make
|
2692
|
-
# sure the current response time percentiles only accounts for these new requests
|
2693
|
-
mocked_time.return_value += 10.10023
|
2694
|
-
stats.log_request("GET", "/1", 20, 1)
|
2695
|
-
stats.log_request("GET", "/1", 30, 1)
|
2696
|
-
stats.log_request("GET", "/1", 3000, 1)
|
2697
|
-
server.mocked_send(
|
2698
|
-
Message(
|
2699
|
-
"stats",
|
2700
|
-
{
|
2701
|
-
"stats": stats.serialize_stats(),
|
2702
|
-
"stats_total": stats.total.get_stripped_report(),
|
2703
|
-
"errors": stats.serialize_errors(),
|
2704
|
-
"user_count": 2,
|
2705
|
-
},
|
2706
|
-
"fake_client",
|
2707
|
-
)
|
2708
|
-
)
|
2709
|
-
self.assertEqual(30, master.stats.total.get_current_response_time_percentile(0.5))
|
2710
|
-
self.assertEqual(3000, master.stats.total.get_current_response_time_percentile(0.95))
|
2711
|
-
|
2712
|
-
@mock.patch("locust.runners.HEARTBEAT_INTERVAL", new=600)
|
2713
|
-
def test_rebalance_locust_users_on_worker_connect(self):
|
2714
|
-
class TestUser(User):
|
2715
|
-
@task
|
2716
|
-
def my_task(self):
|
2717
|
-
pass
|
2718
|
-
|
2719
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
2720
|
-
master = self.get_runner(user_classes=[TestUser])
|
2721
|
-
server.mocked_send(Message("client_ready", __version__, "zeh_fake_client1"))
|
2722
|
-
self.assertEqual(1, len(master.clients))
|
2723
|
-
self.assertTrue(
|
2724
|
-
"zeh_fake_client1" in master.clients, "Could not find fake client in master instance's clients dict"
|
2725
|
-
)
|
2726
|
-
|
2727
|
-
master.start(100, 20)
|
2728
|
-
self.assertEqual(7, len(server.get_messages()))
|
2729
|
-
spawn_messages = server.get_messages("spawn")
|
2730
|
-
for i, msg in enumerate(server.get_messages("spawn")):
|
2731
|
-
self.assertDictEqual({"TestUser": int((i + 1) * 20)}, msg.data["user_classes_count"])
|
2732
|
-
|
2733
|
-
# Normally, this attribute would be updated when the
|
2734
|
-
# master receives the report from the worker.
|
2735
|
-
master.clients["zeh_fake_client1"].user_classes_count = {"TestUser": 100}
|
2736
|
-
|
2737
|
-
# let another worker connect
|
2738
|
-
server.mocked_send(Message("client_ready", __version__, "zeh_fake_client2"))
|
2739
|
-
self.assertEqual(2, len(master.clients))
|
2740
|
-
sleep(0.1) # give time for messages to be sent to clients
|
2741
|
-
spawn_messages = server.get_messages("spawn")
|
2742
|
-
self.assertEqual({"TestUser": 50}, spawn_messages[-1].data["user_classes_count"])
|
2743
|
-
self.assertEqual({"TestUser": 50}, spawn_messages[-2].data["user_classes_count"])
|
2744
|
-
|
2745
|
-
def test_sends_spawn_data_to_ready_running_spawning_workers(self):
|
2746
|
-
"""Sends spawn job to running, ready, or spawning workers"""
|
2747
|
-
|
2748
|
-
class TestUser(User):
|
2749
|
-
@task
|
2750
|
-
def my_task(self):
|
2751
|
-
pass
|
2752
|
-
|
2753
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
2754
|
-
master = self.get_runner(user_classes=[TestUser])
|
2755
|
-
master.clients[1] = WorkerNode("1")
|
2756
|
-
master.clients[2] = WorkerNode("2")
|
2757
|
-
master.clients[3] = WorkerNode("3")
|
2758
|
-
master.clients[1].state = STATE_INIT
|
2759
|
-
master.clients[2].state = STATE_SPAWNING
|
2760
|
-
master.clients[3].state = STATE_RUNNING
|
2761
|
-
master.start(user_count=5, spawn_rate=5)
|
2762
|
-
self.assertEqual(3, len(server.get_messages("spawn")))
|
2763
|
-
self.assertEqual(3, len(server.get_messages("spawning_complete")))
|
2764
|
-
|
2765
|
-
def test_start_event(self):
|
2766
|
-
"""
|
2767
|
-
Tests that test_start event is fired
|
2768
|
-
"""
|
2769
|
-
|
2770
|
-
class TestUser(User):
|
2771
|
-
@task
|
2772
|
-
def my_task(self):
|
2773
|
-
pass
|
2774
|
-
|
2775
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
2776
|
-
master = self.get_runner(user_classes=[TestUser])
|
2777
|
-
|
2778
|
-
run_count = [0]
|
2779
|
-
|
2780
|
-
@self.environment.events.test_start.add_listener
|
2781
|
-
def on_test_start(*a, **kw):
|
2782
|
-
run_count[0] += 1
|
2783
|
-
|
2784
|
-
for i in range(5):
|
2785
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client%i" % i))
|
2786
|
-
|
2787
|
-
master.start(7, 7)
|
2788
|
-
self.assertEqual(15, len(server.get_messages()))
|
2789
|
-
self.assertEqual(1, run_count[0])
|
2790
|
-
|
2791
|
-
# change number of users and check that test_start isn't fired again
|
2792
|
-
master.start(7, 7)
|
2793
|
-
self.assertEqual(1, run_count[0])
|
2794
|
-
|
2795
|
-
# stop and start to make sure test_start is fired again
|
2796
|
-
master.stop()
|
2797
|
-
master.start(3, 3)
|
2798
|
-
self.assertEqual(2, run_count[0])
|
2799
|
-
|
2800
|
-
master.quit()
|
2801
|
-
|
2802
|
-
def test_stop_event(self):
|
2803
|
-
"""
|
2804
|
-
Tests that test_stop event is fired
|
2805
|
-
"""
|
2806
|
-
|
2807
|
-
class TestUser(User):
|
2808
|
-
@task
|
2809
|
-
def my_task(self):
|
2810
|
-
pass
|
2811
|
-
|
2812
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
2813
|
-
master = self.get_runner(user_classes=[TestUser])
|
2814
|
-
|
2815
|
-
@self.environment.events.test_stopping.add_listener
|
2816
|
-
def on_test_stopping(*_, **__):
|
2817
|
-
self.runner_stopping = True
|
2818
|
-
|
2819
|
-
@self.environment.events.test_stop.add_listener
|
2820
|
-
def on_test_stop(*_, **__):
|
2821
|
-
self.runner_stopped = True
|
2822
|
-
|
2823
|
-
for i in range(5):
|
2824
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client%i" % i))
|
2825
|
-
|
2826
|
-
master.start(7, 7)
|
2827
|
-
self.assertEqual(15, len(server.get_messages()))
|
2828
|
-
master.stop()
|
2829
|
-
self.assertTrue(self.runner_stopping)
|
2830
|
-
self.assertTrue(self.runner_stopped)
|
2831
|
-
|
2832
|
-
self.reset_state()
|
2833
|
-
for i in range(5):
|
2834
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client%i" % i))
|
2835
|
-
master.start(7, 7)
|
2836
|
-
master.stop()
|
2837
|
-
master.quit()
|
2838
|
-
self.assertTrue(self.runner_stopping)
|
2839
|
-
self.assertTrue(self.runner_stopped)
|
2840
|
-
|
2841
|
-
def test_stop_event_quit(self):
|
2842
|
-
"""
|
2843
|
-
Tests that test_stop event is fired when quit() is called directly
|
2844
|
-
"""
|
2845
|
-
|
2846
|
-
class TestUser(User):
|
2847
|
-
@task
|
2848
|
-
def my_task(self):
|
2849
|
-
pass
|
2850
|
-
|
2851
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
2852
|
-
master = self.get_runner(user_classes=[TestUser])
|
2853
|
-
|
2854
|
-
@self.environment.events.test_stopping.add_listener
|
2855
|
-
def on_test_stopping(*_, **__):
|
2856
|
-
self.runner_stopping = True
|
2857
|
-
|
2858
|
-
@self.environment.events.test_stop.add_listener
|
2859
|
-
def on_test_stop(*_, **__):
|
2860
|
-
self.runner_stopped = True
|
2861
|
-
|
2862
|
-
for i in range(5):
|
2863
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client%i" % i))
|
2864
|
-
|
2865
|
-
master.start(7, 7)
|
2866
|
-
self.assertEqual(15, len(server.get_messages()))
|
2867
|
-
master.quit()
|
2868
|
-
self.assertTrue(self.runner_stopping)
|
2869
|
-
self.assertTrue(self.runner_stopped)
|
2870
|
-
|
2871
|
-
def test_spawn_zero_locusts(self):
|
2872
|
-
class MyTaskSet(TaskSet):
|
2873
|
-
@task
|
2874
|
-
def my_task(self):
|
2875
|
-
pass
|
2876
|
-
|
2877
|
-
class MyTestUser(User):
|
2878
|
-
tasks = [MyTaskSet]
|
2879
|
-
wait_time = constant(0.1)
|
2880
|
-
|
2881
|
-
environment = Environment(user_classes=[MyTestUser])
|
2882
|
-
runner = LocalRunner(environment)
|
2883
|
-
|
2884
|
-
timeout = gevent.Timeout(2.0)
|
2885
|
-
timeout.start()
|
2886
|
-
|
2887
|
-
try:
|
2888
|
-
runner.start(0, 1, wait=True)
|
2889
|
-
runner.spawning_greenlet.join()
|
2890
|
-
except gevent.Timeout:
|
2891
|
-
self.fail("Got Timeout exception. A locust seems to have been spawned, even though 0 was specified.")
|
2892
|
-
finally:
|
2893
|
-
timeout.cancel()
|
2894
|
-
|
2895
|
-
def test_spawn_uneven_locusts(self):
|
2896
|
-
"""
|
2897
|
-
Tests that we can accurately spawn a certain number of locusts, even if it's not an
|
2898
|
-
even number of the connected workers
|
2899
|
-
"""
|
2900
|
-
|
2901
|
-
class TestUser(User):
|
2902
|
-
@task
|
2903
|
-
def my_task(self):
|
2904
|
-
pass
|
2905
|
-
|
2906
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
2907
|
-
master = self.get_runner(user_classes=[TestUser])
|
2908
|
-
|
2909
|
-
for i in range(5):
|
2910
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client%i" % i))
|
2911
|
-
|
2912
|
-
master.start(7, 7)
|
2913
|
-
self.assertEqual(15, len(server.outbox))
|
2914
|
-
|
2915
|
-
num_users = sum(sum(msg.data["user_classes_count"].values()) for msg in server.get_messages("spawn"))
|
2916
|
-
self.assertEqual(7, num_users)
|
2917
|
-
|
2918
|
-
def test_spawn_fewer_locusts_than_workers(self):
|
2919
|
-
class TestUser(User):
|
2920
|
-
@task
|
2921
|
-
def my_task(self):
|
2922
|
-
pass
|
2923
|
-
|
2924
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
2925
|
-
master = self.get_runner(user_classes=[TestUser])
|
2926
|
-
|
2927
|
-
for i in range(5):
|
2928
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client%i" % i))
|
2929
|
-
|
2930
|
-
master.start(2, 2)
|
2931
|
-
self.assertEqual(15, len(server.outbox))
|
2932
|
-
|
2933
|
-
num_users = sum(sum(msg.data["user_classes_count"].values()) for msg in server.get_messages("spawn"))
|
2934
|
-
|
2935
|
-
self.assertEqual(2, num_users, "Total number of locusts that would have been spawned is not 2")
|
2936
|
-
|
2937
|
-
def test_spawn_correct_worker_indexes(self):
|
2938
|
-
"""
|
2939
|
-
Tests that workers would receive a monotonic sequence of ordinal IDs.
|
2940
|
-
"""
|
2941
|
-
|
2942
|
-
class TestUser(User):
|
2943
|
-
@task
|
2944
|
-
def my_task(self):
|
2945
|
-
pass
|
2946
|
-
|
2947
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
2948
|
-
master = self.get_runner(user_classes=[TestUser])
|
2949
|
-
|
2950
|
-
USERS_COUNT = 5
|
2951
|
-
|
2952
|
-
for i in range(USERS_COUNT):
|
2953
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client%i" % i))
|
2954
|
-
|
2955
|
-
master.start(USERS_COUNT, USERS_COUNT)
|
2956
|
-
self.assertEqual(USERS_COUNT * 3, len(server.outbox))
|
2957
|
-
|
2958
|
-
indexes = []
|
2959
|
-
for msg in server.get_messages("ack"):
|
2960
|
-
indexes.append(msg.data["index"])
|
2961
|
-
self.assertEqual(USERS_COUNT, len(indexes), "Total number of locusts/workers is not 5")
|
2962
|
-
|
2963
|
-
indexes.sort()
|
2964
|
-
for i in range(USERS_COUNT):
|
2965
|
-
self.assertEqual(indexes[i], i, "Worker index mismatch")
|
2966
|
-
|
2967
|
-
def test_custom_shape_scale_interval(self):
|
2968
|
-
class MyUser(User):
|
2969
|
-
@task
|
2970
|
-
def my_task(self):
|
2971
|
-
pass
|
2972
|
-
|
2973
|
-
class TestShape(LoadTestShape):
|
2974
|
-
def __init__(self):
|
2975
|
-
super().__init__()
|
2976
|
-
self._users_num = [1, 1, 1, 2, 2, 3, 3, 3, 4]
|
2977
|
-
self._index = 0
|
2978
|
-
|
2979
|
-
def tick(self):
|
2980
|
-
if self._index >= len(self._users_num):
|
2981
|
-
return None
|
2982
|
-
users_num = self._users_num[self._index]
|
2983
|
-
self._index += 1
|
2984
|
-
return users_num, users_num
|
2985
|
-
|
2986
|
-
self.environment.shape_class = TestShape()
|
2987
|
-
|
2988
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
2989
|
-
master = self.get_runner(user_classes=[MyUser])
|
2990
|
-
for i in range(5):
|
2991
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client%i" % i))
|
2992
|
-
|
2993
|
-
# Start the shape_worker
|
2994
|
-
self.environment.shape_class.reset_time()
|
2995
|
-
master.start_shape()
|
2996
|
-
|
2997
|
-
# Wait for shape_worker to update user_count
|
2998
|
-
sleep(0.5)
|
2999
|
-
num_users = sum(sum(msg.data["user_classes_count"].values()) for msg in server.get_messages("spawn"))
|
3000
|
-
self.assertEqual(
|
3001
|
-
1, num_users, "Total number of users in first stage of shape test is not 1: %i" % num_users
|
3002
|
-
)
|
3003
|
-
|
3004
|
-
# Wait for shape_worker to update user_count again
|
3005
|
-
sleep(1.5)
|
3006
|
-
num_users = sum(sum(msg.data["user_classes_count"].values()) for msg in server.get_messages("spawn"))
|
3007
|
-
self.assertEqual(
|
3008
|
-
1, num_users, "Total number of users in second stage of shape test is not 1: %i" % num_users
|
3009
|
-
)
|
3010
|
-
|
3011
|
-
# Wait for shape_worker to update user_count few times but not reach the end yet
|
3012
|
-
sleep(2.5)
|
3013
|
-
num_users = sum(sum(msg.data["user_classes_count"].values()) for msg in server.get_messages("spawn"))
|
3014
|
-
self.assertEqual(
|
3015
|
-
3, num_users, "Total number of users in second stage of shape test is not 3: %i" % num_users
|
3016
|
-
)
|
3017
|
-
|
3018
|
-
# Wait to ensure shape_worker has stopped the test
|
3019
|
-
sleep(3)
|
3020
|
-
self.assertEqual("stopped", master.state, "The test has not been stopped by the shape class")
|
3021
|
-
|
3022
|
-
def test_custom_shape_scale_up(self):
|
3023
|
-
class MyUser(User):
|
3024
|
-
@task
|
3025
|
-
def my_task(self):
|
3026
|
-
pass
|
3027
|
-
|
3028
|
-
class TestShape(LoadTestShape):
|
3029
|
-
def tick(self):
|
3030
|
-
run_time = self.get_run_time()
|
3031
|
-
if run_time < 2:
|
3032
|
-
return 1, 1
|
3033
|
-
elif run_time < 4:
|
3034
|
-
return 2, 2
|
3035
|
-
else:
|
3036
|
-
return None
|
3037
|
-
|
3038
|
-
self.environment.shape_class = TestShape()
|
3039
|
-
|
3040
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
3041
|
-
master = self.get_runner(user_classes=[MyUser])
|
3042
|
-
for i in range(5):
|
3043
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client%i" % i))
|
3044
|
-
|
3045
|
-
# Start the shape_worker
|
3046
|
-
self.environment.shape_class.reset_time()
|
3047
|
-
master.start_shape()
|
3048
|
-
sleep(0.5)
|
3049
|
-
|
3050
|
-
# Wait for shape_worker to update user_count
|
3051
|
-
num_users = sum(sum(msg.data["user_classes_count"].values()) for msg in server.get_messages("spawn"))
|
3052
|
-
self.assertEqual(
|
3053
|
-
1, num_users, "Total number of users in first stage of shape test is not 1: %i" % num_users
|
3054
|
-
)
|
3055
|
-
|
3056
|
-
# Wait for shape_worker to update user_count again
|
3057
|
-
sleep(2)
|
3058
|
-
num_users = sum(sum(msg.data["user_classes_count"].values()) for msg in server.get_messages("spawn"))
|
3059
|
-
self.assertEqual(
|
3060
|
-
3, num_users, "Total number of users in second stage of shape test is not 3: %i" % num_users
|
3061
|
-
)
|
3062
|
-
|
3063
|
-
# Wait to ensure shape_worker has stopped the test
|
3064
|
-
sleep(3)
|
3065
|
-
self.assertEqual("stopped", master.state, "The test has not been stopped by the shape class")
|
3066
|
-
|
3067
|
-
def test_custom_shape_scale_down(self):
|
3068
|
-
class MyUser(User):
|
3069
|
-
@task
|
3070
|
-
def my_task(self):
|
3071
|
-
pass
|
3072
|
-
|
3073
|
-
class TestShape(LoadTestShape):
|
3074
|
-
def tick(self):
|
3075
|
-
run_time = self.get_run_time()
|
3076
|
-
if run_time < 2:
|
3077
|
-
return 5, 5
|
3078
|
-
elif run_time < 4:
|
3079
|
-
return 1, 5
|
3080
|
-
else:
|
3081
|
-
return None
|
3082
|
-
|
3083
|
-
self.environment.shape_class = TestShape()
|
3084
|
-
|
3085
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
3086
|
-
master = self.get_runner(user_classes=[MyUser])
|
3087
|
-
for i in range(5):
|
3088
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client%i" % i))
|
3089
|
-
|
3090
|
-
# Start the shape_worker
|
3091
|
-
self.environment.shape_class.reset_time()
|
3092
|
-
master.start_shape()
|
3093
|
-
sleep(0.5)
|
3094
|
-
|
3095
|
-
# Wait for shape_worker to update user_count
|
3096
|
-
num_users = sum(sum(msg.data["user_classes_count"].values()) for msg in server.get_messages("spawn"))
|
3097
|
-
self.assertEqual(
|
3098
|
-
5, num_users, "Total number of users in first stage of shape test is not 5: %i" % num_users
|
3099
|
-
)
|
3100
|
-
|
3101
|
-
# Wait for shape_worker to update user_count again
|
3102
|
-
sleep(2)
|
3103
|
-
msgs = defaultdict(dict)
|
3104
|
-
for msg in server.get_messages("spawn"):
|
3105
|
-
msgs[msg.node_id][msg.data["timestamp"]] = sum(msg.data["user_classes_count"].values())
|
3106
|
-
# Count users for the last received messages
|
3107
|
-
num_users = sum(v[max(v.keys())] for v in msgs.values())
|
3108
|
-
self.assertEqual(
|
3109
|
-
1, num_users, "Total number of users in second stage of shape test is not 1: %i" % num_users
|
3110
|
-
)
|
3111
|
-
|
3112
|
-
# Wait to ensure shape_worker has stopped the test
|
3113
|
-
sleep(3)
|
3114
|
-
self.assertEqual("stopped", master.state, "The test has not been stopped by the shape class")
|
3115
|
-
|
3116
|
-
def test_exception_in_task(self):
|
3117
|
-
class MyUser(User):
|
3118
|
-
@task
|
3119
|
-
def will_error(self):
|
3120
|
-
raise HeyAnException(":(")
|
3121
|
-
|
3122
|
-
self.environment.user_classes = [MyUser]
|
3123
|
-
runner = self.environment.create_local_runner()
|
3124
|
-
|
3125
|
-
l = MyUser(self.environment)
|
3126
|
-
|
3127
|
-
self.assertRaises(HeyAnException, l.run)
|
3128
|
-
self.assertRaises(HeyAnException, l.run)
|
3129
|
-
self.assertEqual(1, len(runner.exceptions))
|
3130
|
-
|
3131
|
-
hash_key, exception = runner.exceptions.popitem()
|
3132
|
-
self.assertIn("traceback", exception)
|
3133
|
-
self.assertIn("HeyAnException", exception["traceback"])
|
3134
|
-
self.assertEqual(2, exception["count"])
|
3135
|
-
|
3136
|
-
def test_exception_is_caught(self):
|
3137
|
-
"""Test that exceptions are stored, and execution continues"""
|
3138
|
-
|
3139
|
-
class MyTaskSet(TaskSet):
|
3140
|
-
def __init__(self, *a, **kw):
|
3141
|
-
super().__init__(*a, **kw)
|
3142
|
-
self._task_queue = deque([self.will_error, self.will_stop])
|
3143
|
-
|
3144
|
-
@task(1)
|
3145
|
-
def will_error(self):
|
3146
|
-
raise HeyAnException(":(")
|
3147
|
-
|
3148
|
-
@task(1)
|
3149
|
-
def will_stop(self):
|
3150
|
-
raise StopUser()
|
3151
|
-
|
3152
|
-
class MyUser(User):
|
3153
|
-
wait_time = constant(0.01)
|
3154
|
-
tasks = [MyTaskSet]
|
3155
|
-
|
3156
|
-
# set config to catch exceptions in locust users
|
3157
|
-
self.environment.catch_exceptions = True
|
3158
|
-
self.environment.user_classes = [MyUser]
|
3159
|
-
runner = LocalRunner(self.environment)
|
3160
|
-
l = MyUser(self.environment)
|
3161
|
-
|
3162
|
-
# make sure HeyAnException isn't raised
|
3163
|
-
l.run()
|
3164
|
-
l.run()
|
3165
|
-
# make sure we got two entries in the error log
|
3166
|
-
self.assertEqual(2, len(self.mocked_log.error))
|
3167
|
-
|
3168
|
-
# make sure exception was stored
|
3169
|
-
self.assertEqual(1, len(runner.exceptions))
|
3170
|
-
hash_key, exception = runner.exceptions.popitem()
|
3171
|
-
self.assertTrue("traceback" in exception)
|
3172
|
-
self.assertTrue("HeyAnException" in exception["traceback"])
|
3173
|
-
self.assertEqual(2, exception["count"])
|
3174
|
-
|
3175
|
-
def test_master_reset_connection(self):
|
3176
|
-
"""Test that connection will be reset when network issues found"""
|
3177
|
-
with mock.patch("locust.runners.FALLBACK_INTERVAL", new=0.1):
|
3178
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc(raise_on_close=False)) as server:
|
3179
|
-
master = self.get_runner()
|
3180
|
-
self.assertEqual(0, len(master.clients))
|
3181
|
-
server.mocked_send(Message("client_ready", NETWORK_BROKEN, "fake_client"))
|
3182
|
-
self.assertTrue(master.connection_broken)
|
3183
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client"))
|
3184
|
-
sleep(1)
|
3185
|
-
self.assertFalse(master.connection_broken)
|
3186
|
-
self.assertEqual(1, len(master.clients))
|
3187
|
-
master.quit()
|
3188
|
-
|
3189
|
-
def test_reset_connection_after_RPCError(self):
|
3190
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc(raise_on_close=False)) as server:
|
3191
|
-
master = self.get_runner()
|
3192
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client"))
|
3193
|
-
sleep(0.2)
|
3194
|
-
self.assertFalse(master.connection_broken)
|
3195
|
-
self.assertEqual(1, len(master.clients))
|
3196
|
-
|
3197
|
-
# Trigger RPCError
|
3198
|
-
server.mocked_send(Message("lets_trigger_RPCError", NETWORK_BROKEN, "fake_client"))
|
3199
|
-
self.assertTrue(master.connection_broken)
|
3200
|
-
sleep(1)
|
3201
|
-
self.assertFalse(master.connection_broken)
|
3202
|
-
master.quit()
|
3203
|
-
|
3204
|
-
def test_attributes_populated_when_calling_start(self):
|
3205
|
-
class MyUser1(User):
|
3206
|
-
@task
|
3207
|
-
def my_task(self):
|
3208
|
-
pass
|
3209
|
-
|
3210
|
-
class MyUser2(User):
|
3211
|
-
@task
|
3212
|
-
def my_task(self):
|
3213
|
-
pass
|
3214
|
-
|
3215
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
3216
|
-
master = self.get_runner(user_classes=[MyUser1, MyUser2])
|
3217
|
-
|
3218
|
-
server.mocked_send(Message("client_ready", __version__, "fake_client1"))
|
3219
|
-
|
3220
|
-
master.start(7, 7)
|
3221
|
-
self.assertEqual({"MyUser1": 4, "MyUser2": 3}, master.target_user_classes_count)
|
3222
|
-
self.assertEqual(7, master.target_user_count)
|
3223
|
-
self.assertEqual(7, master.spawn_rate)
|
3224
|
-
|
3225
|
-
master.start(10, 10)
|
3226
|
-
self.assertEqual({"MyUser1": 5, "MyUser2": 5}, master.target_user_classes_count)
|
3227
|
-
self.assertEqual(10, master.target_user_count)
|
3228
|
-
self.assertEqual(10, master.spawn_rate)
|
3229
|
-
|
3230
|
-
master.start(1, 3)
|
3231
|
-
self.assertEqual({"MyUser1": 1, "MyUser2": 0}, master.target_user_classes_count)
|
3232
|
-
self.assertEqual(1, master.target_user_count)
|
3233
|
-
self.assertEqual(3, master.spawn_rate)
|
3234
|
-
|
3235
|
-
def test_custom_message_send(self):
|
3236
|
-
class MyUser(User):
|
3237
|
-
wait_time = constant(1)
|
3238
|
-
|
3239
|
-
@task
|
3240
|
-
def my_task(self):
|
3241
|
-
pass
|
3242
|
-
|
3243
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
3244
|
-
master = self.get_runner()
|
3245
|
-
for i in range(5):
|
3246
|
-
master.clients[i] = WorkerNode(str(i))
|
3247
|
-
master.send_message("test_custom_msg", {"test_data": 123})
|
3248
|
-
|
3249
|
-
messages = server.get_messages()
|
3250
|
-
|
3251
|
-
self.assertEqual(5, len(messages))
|
3252
|
-
for msg in messages:
|
3253
|
-
self.assertEqual("test_custom_msg", msg.type)
|
3254
|
-
self.assertEqual(123, msg.data["test_data"])
|
3255
|
-
|
3256
|
-
def test_custom_message_receive(self):
|
3257
|
-
class MyUser(User):
|
3258
|
-
wait_time = constant(1)
|
3259
|
-
|
3260
|
-
@task
|
3261
|
-
def my_task(self):
|
3262
|
-
pass
|
3263
|
-
|
3264
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
3265
|
-
test_custom_msg = [False]
|
3266
|
-
test_custom_msg_data = [{}]
|
3267
|
-
|
3268
|
-
def on_custom_msg(msg, **kw):
|
3269
|
-
test_custom_msg[0] = True
|
3270
|
-
test_custom_msg_data[0] = msg.data
|
3271
|
-
|
3272
|
-
master = self.get_runner()
|
3273
|
-
master.register_message("test_custom_msg", on_custom_msg)
|
3274
|
-
|
3275
|
-
server.mocked_send(Message("test_custom_msg", {"test_data": 123}, "dummy_id"))
|
3276
|
-
|
3277
|
-
self.assertTrue(test_custom_msg[0])
|
3278
|
-
self.assertEqual(123, test_custom_msg_data[0]["test_data"])
|
3279
|
-
|
3280
|
-
def test_undefined_custom_message_receive(self):
|
3281
|
-
class MyUser(User):
|
3282
|
-
wait_time = constant(1)
|
3283
|
-
|
3284
|
-
@task
|
3285
|
-
def my_task(self):
|
3286
|
-
pass
|
3287
|
-
|
3288
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
3289
|
-
test_custom_msg = [False]
|
3290
|
-
|
3291
|
-
def on_custom_msg(msg, **kw):
|
3292
|
-
test_custom_msg[0] = True
|
3293
|
-
|
3294
|
-
master = self.get_runner()
|
3295
|
-
master.register_message("test_custom_msg", on_custom_msg)
|
3296
|
-
|
3297
|
-
server.mocked_send(Message("unregistered_custom_msg", {}, "dummy_id"))
|
3298
|
-
|
3299
|
-
self.assertFalse(test_custom_msg[0])
|
3300
|
-
self.assertEqual(1, len(self.mocked_log.warning))
|
3301
|
-
msg = self.mocked_log.warning[0]
|
3302
|
-
self.assertIn("Unknown message type received from worker", msg)
|
3303
|
-
|
3304
|
-
def test_wait_for_workers_report_after_ramp_up(self):
|
3305
|
-
def assert_cache_hits():
|
3306
|
-
self.assertEqual(master._wait_for_workers_report_after_ramp_up.cache_info().hits, 0)
|
3307
|
-
master._wait_for_workers_report_after_ramp_up()
|
3308
|
-
self.assertEqual(master._wait_for_workers_report_after_ramp_up.cache_info().hits, 1)
|
3309
|
-
|
3310
|
-
master = self.get_runner()
|
3311
|
-
|
3312
|
-
master._wait_for_workers_report_after_ramp_up.cache_clear()
|
3313
|
-
self.assertEqual(master._wait_for_workers_report_after_ramp_up(), 1.0)
|
3314
|
-
assert_cache_hits()
|
3315
|
-
|
3316
|
-
master._wait_for_workers_report_after_ramp_up.cache_clear()
|
3317
|
-
with patch_env("LOCUST_WAIT_FOR_WORKERS_REPORT_AFTER_RAMP_UP", "5.7"):
|
3318
|
-
self.assertEqual(master._wait_for_workers_report_after_ramp_up(), 5.7)
|
3319
|
-
assert_cache_hits()
|
3320
|
-
|
3321
|
-
master._wait_for_workers_report_after_ramp_up.cache_clear()
|
3322
|
-
with (
|
3323
|
-
mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=1.5),
|
3324
|
-
patch_env("LOCUST_WAIT_FOR_WORKERS_REPORT_AFTER_RAMP_UP", "5.7 * WORKER_REPORT_INTERVAL"),
|
3325
|
-
):
|
3326
|
-
self.assertEqual(master._wait_for_workers_report_after_ramp_up(), 5.7 * 1.5)
|
3327
|
-
assert_cache_hits()
|
3328
|
-
|
3329
|
-
master._wait_for_workers_report_after_ramp_up.cache_clear()
|
3330
|
-
|
3331
|
-
def test_master_discard_first_client_ready(self):
|
3332
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
3333
|
-
server.mocked_send(Message("client_ready", __version__, "dummy_client"))
|
3334
|
-
# discard first client_ready msg
|
3335
|
-
server.queue.get()
|
3336
|
-
master = self.get_runner()
|
3337
|
-
server.mocked_send(Message("client_ready", __version__, "dummy_client"))
|
3338
|
-
|
3339
|
-
messages = server.get_messages()
|
3340
|
-
|
3341
|
-
self.assertEqual(1, len(master.clients))
|
3342
|
-
self.assertEqual("ack", messages[0].type)
|
3343
|
-
self.assertEqual(1, len(messages))
|
3344
|
-
self.assertEqual(0, messages[0].data["index"])
|
3345
|
-
|
3346
|
-
def test_worker_sends_bad_message_to_master(self):
|
3347
|
-
"""
|
3348
|
-
Validate master sends reconnect message to worker when it receives a bad message.
|
3349
|
-
"""
|
3350
|
-
|
3351
|
-
class TestUser(User):
|
3352
|
-
@task
|
3353
|
-
def my_task(self):
|
3354
|
-
pass
|
3355
|
-
|
3356
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
3357
|
-
master = self.get_runner(user_classes=[TestUser])
|
3358
|
-
server.mocked_send(Message("client_ready", __version__, "zeh_fake_client1"))
|
3359
|
-
self.assertEqual(1, len(master.clients))
|
3360
|
-
self.assertTrue(
|
3361
|
-
"zeh_fake_client1" in master.clients, "Could not find fake client in master instance's clients dict"
|
3362
|
-
)
|
3363
|
-
|
3364
|
-
master.start(10, 10)
|
3365
|
-
sleep(0.1)
|
3366
|
-
server.mocked_send(Message("stats", BAD_MESSAGE, "zeh_fake_client1"))
|
3367
|
-
messages = server.get_messages()
|
3368
|
-
self.assertEqual(5, len(messages))
|
3369
|
-
|
3370
|
-
# Expected message order in outbox: ack, spawn, reconnect, ack
|
3371
|
-
self.assertEqual(
|
3372
|
-
"reconnect", messages[3].type, "Master didn't send worker reconnect message when expected."
|
3373
|
-
)
|
3374
|
-
|
3375
|
-
def test_worker_sends_unrecognized_message_to_master(self):
|
3376
|
-
"""
|
3377
|
-
Validate master ignores message from worker when it cannot parse address info.
|
3378
|
-
"""
|
3379
|
-
|
3380
|
-
class TestUser(User):
|
3381
|
-
@task
|
3382
|
-
def my_task(self):
|
3383
|
-
pass
|
3384
|
-
|
3385
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
3386
|
-
master = self.get_runner(user_classes=[TestUser])
|
3387
|
-
server.mocked_send(Message("client_ready", __version__, "zeh_fake_client1"))
|
3388
|
-
self.assertEqual(1, len(master.clients))
|
3389
|
-
self.assertTrue(
|
3390
|
-
"zeh_fake_client1" in master.clients, "Could not find fake client in master instance's clients dict"
|
3391
|
-
)
|
3392
|
-
|
3393
|
-
master.start(10, 10)
|
3394
|
-
sleep(0.1)
|
3395
|
-
server.mocked_send(Message("stats", UNRECOGNIZED_MESSAGE, "zeh_fake_client1"))
|
3396
|
-
self.assertEqual(3, len(server.get_messages()))
|
3397
|
-
|
3398
|
-
def test_unknown_host_sends_message_to_master(self):
|
3399
|
-
"""
|
3400
|
-
Validate master ignores message that is sent from unknown host
|
3401
|
-
"""
|
3402
|
-
|
3403
|
-
class TestUser(User):
|
3404
|
-
@task
|
3405
|
-
def my_task(self):
|
3406
|
-
pass
|
3407
|
-
|
3408
|
-
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
|
3409
|
-
master = self.get_runner(user_classes=[TestUser])
|
3410
|
-
server.mocked_send(Message("client_ready", __version__, "zeh_fake_client1"))
|
3411
|
-
self.assertEqual(1, len(master.clients))
|
3412
|
-
self.assertTrue(
|
3413
|
-
"zeh_fake_client1" in master.clients, "Could not find fake client in master instance's clients dict"
|
3414
|
-
)
|
3415
|
-
|
3416
|
-
master.start(10, 10)
|
3417
|
-
sleep(0.1)
|
3418
|
-
server.mocked_send(Message("stats", UNRECOGNIZED_HOST_MESSAGE, "unknown_host"))
|
3419
|
-
self.assertEqual(3, len(server.get_messages()))
|
3420
|
-
|
3421
|
-
|
3422
|
-
class TestWorkerRunner(LocustTestCase):
|
3423
|
-
def setUp(self):
|
3424
|
-
super().setUp()
|
3425
|
-
# self._report_to_master_event_handlers = [h for h in events.report_to_master._handlers]
|
3426
|
-
|
3427
|
-
def tearDown(self):
|
3428
|
-
# events.report_to_master._handlers = self._report_to_master_event_handlers
|
3429
|
-
super().tearDown()
|
3430
|
-
|
3431
|
-
def get_runner(self, client, environment=None, user_classes=None, auto_connect=True):
|
3432
|
-
if auto_connect:
|
3433
|
-
client.mocked_send(Message("ack", {"index": 0}, "dummy_client_id"))
|
3434
|
-
if environment is None:
|
3435
|
-
environment = self.environment
|
3436
|
-
user_classes = user_classes or []
|
3437
|
-
environment.user_classes = user_classes
|
3438
|
-
return WorkerRunner(environment, master_host="localhost", master_port=5557)
|
3439
|
-
|
3440
|
-
def test_worker_stop_timeout(self):
|
3441
|
-
class MyTestUser(User):
|
3442
|
-
_test_state = 0
|
3443
|
-
|
3444
|
-
@task
|
3445
|
-
def the_task(self):
|
3446
|
-
MyTestUser._test_state = 1
|
3447
|
-
gevent.sleep(0.2)
|
3448
|
-
MyTestUser._test_state = 2
|
3449
|
-
|
3450
|
-
with mock.patch("locust.rpc.rpc.Client", mocked_rpc()) as client:
|
3451
|
-
worker = self.get_runner(environment=Environment(), user_classes=[MyTestUser], client=client)
|
3452
|
-
messages = client.get_messages()
|
3453
|
-
self.assertEqual(1, len(messages))
|
3454
|
-
self.assertEqual("client_ready", messages[0].type)
|
3455
|
-
client.mocked_send(
|
3456
|
-
Message(
|
3457
|
-
"spawn",
|
3458
|
-
{
|
3459
|
-
"timestamp": 1605538584,
|
3460
|
-
"user_classes_count": {"MyTestUser": 1},
|
3461
|
-
"host": "",
|
3462
|
-
"stop_timeout": 1,
|
3463
|
-
"parsed_options": {},
|
3464
|
-
},
|
3465
|
-
"dummy_client_id",
|
3466
|
-
)
|
3467
|
-
)
|
3468
|
-
self.assertTrue(client.get_messages("spawning"))
|
3469
|
-
# wait for worker to spawn locusts
|
3470
|
-
# self.assertIn("spawning", [m.type for m in messages])
|
3471
|
-
worker.spawning_greenlet.join()
|
3472
|
-
self.assertEqual(1, len(worker.user_greenlets))
|
3473
|
-
# check that locust has started running
|
3474
|
-
gevent.sleep(0.01)
|
3475
|
-
self.assertEqual(1, MyTestUser._test_state)
|
3476
|
-
# send stop message
|
3477
|
-
client.mocked_send(Message("stop", None, "dummy_client_id"))
|
3478
|
-
worker.user_greenlets.join()
|
3479
|
-
# check that locust user got to finish
|
3480
|
-
self.assertEqual(2, MyTestUser._test_state)
|
3481
|
-
|
3482
|
-
def test_worker_without_stop_timeout(self):
|
3483
|
-
class MyTestUser(User):
|
3484
|
-
_test_state = 0
|
3485
|
-
|
3486
|
-
@task
|
3487
|
-
def the_task(self):
|
3488
|
-
MyTestUser._test_state = 1
|
3489
|
-
gevent.sleep(0.2)
|
3490
|
-
MyTestUser._test_state = 2
|
3491
|
-
|
3492
|
-
with mock.patch("locust.rpc.rpc.Client", mocked_rpc()) as client:
|
3493
|
-
worker = self.get_runner(environment=Environment(), user_classes=[MyTestUser], client=client)
|
3494
|
-
messages = client.get_messages()
|
3495
|
-
self.assertEqual(1, len(messages))
|
3496
|
-
self.assertEqual("client_ready", messages[0].type)
|
3497
|
-
client.mocked_send(
|
3498
|
-
Message(
|
3499
|
-
"spawn",
|
3500
|
-
{
|
3501
|
-
"timestamp": 1605538584,
|
3502
|
-
"user_classes_count": {"MyTestUser": 1},
|
3503
|
-
"host": "",
|
3504
|
-
"stop_timeout": None,
|
3505
|
-
"parsed_options": {},
|
3506
|
-
},
|
3507
|
-
"dummy_client_id",
|
3508
|
-
)
|
3509
|
-
)
|
3510
|
-
# print("outbox:", client.outbox)
|
3511
|
-
# wait for worker to spawn locusts
|
3512
|
-
self.assertTrue(client.get_messages("spawning"))
|
3513
|
-
worker.spawning_greenlet.join()
|
3514
|
-
self.assertEqual(1, len(worker.user_greenlets))
|
3515
|
-
# check that locust has started running
|
3516
|
-
gevent.sleep(0.01)
|
3517
|
-
self.assertEqual(1, MyTestUser._test_state)
|
3518
|
-
# send stop message
|
3519
|
-
client.mocked_send(Message("stop", None, "dummy_client_id"))
|
3520
|
-
worker.user_greenlets.join()
|
3521
|
-
# check that locust user did not get to finish
|
3522
|
-
self.assertEqual(1, MyTestUser._test_state)
|
3523
|
-
|
3524
|
-
def test_spawn_message_with_older_timestamp_is_rejected(self):
|
3525
|
-
class MyUser(User):
|
3526
|
-
wait_time = constant(1)
|
3527
|
-
|
3528
|
-
def start(self, group: Group):
|
3529
|
-
# We do this so that the spawning does not finish
|
3530
|
-
# too quickly
|
3531
|
-
gevent.sleep(0.1)
|
3532
|
-
return super().start(group)
|
3533
|
-
|
3534
|
-
@task
|
3535
|
-
def my_task(self):
|
3536
|
-
pass
|
3537
|
-
|
3538
|
-
with mock.patch("locust.rpc.rpc.Client", mocked_rpc()) as client:
|
3539
|
-
worker = self.get_runner(environment=Environment(), user_classes=[MyUser], client=client)
|
3540
|
-
|
3541
|
-
client.mocked_send(
|
3542
|
-
Message(
|
3543
|
-
"spawn",
|
3544
|
-
{
|
3545
|
-
"timestamp": 1605538584,
|
3546
|
-
"user_classes_count": {"MyUser": 10},
|
3547
|
-
"host": "",
|
3548
|
-
"stop_timeout": None,
|
3549
|
-
"parsed_options": {},
|
3550
|
-
},
|
3551
|
-
"dummy_client_id",
|
3552
|
-
)
|
3553
|
-
)
|
3554
|
-
sleep(0.6)
|
3555
|
-
self.assertEqual(STATE_SPAWNING, worker.state)
|
3556
|
-
worker.spawning_greenlet.join()
|
3557
|
-
self.assertEqual(10, worker.user_count)
|
3558
|
-
|
3559
|
-
# Send same timestamp as the first message
|
3560
|
-
client.mocked_send(
|
3561
|
-
Message(
|
3562
|
-
"spawn",
|
3563
|
-
{
|
3564
|
-
"timestamp": 1605538584,
|
3565
|
-
"user_classes_count": {"MyUser": 9},
|
3566
|
-
"host": "",
|
3567
|
-
"stop_timeout": None,
|
3568
|
-
"parsed_options": {},
|
3569
|
-
},
|
3570
|
-
"dummy_client_id",
|
3571
|
-
)
|
3572
|
-
)
|
3573
|
-
worker.spawning_greenlet.join()
|
3574
|
-
# Still 10 users
|
3575
|
-
self.assertEqual(10, worker.user_count)
|
3576
|
-
|
3577
|
-
# Send older timestamp than the first message
|
3578
|
-
client.mocked_send(
|
3579
|
-
Message(
|
3580
|
-
"spawn",
|
3581
|
-
{
|
3582
|
-
"timestamp": 1605538583,
|
3583
|
-
"user_classes_count": {"MyUser": 2},
|
3584
|
-
"host": "",
|
3585
|
-
"stop_timeout": None,
|
3586
|
-
"parsed_options": {},
|
3587
|
-
},
|
3588
|
-
"dummy_client_id",
|
3589
|
-
)
|
3590
|
-
)
|
3591
|
-
worker.spawning_greenlet.join()
|
3592
|
-
# Still 10 users
|
3593
|
-
self.assertEqual(10, worker.user_count)
|
3594
|
-
|
3595
|
-
# Send newer timestamp than the first message
|
3596
|
-
client.mocked_send(
|
3597
|
-
Message(
|
3598
|
-
"spawn",
|
3599
|
-
{
|
3600
|
-
"timestamp": 1605538585,
|
3601
|
-
"user_classes_count": {"MyUser": 2},
|
3602
|
-
"host": "",
|
3603
|
-
"stop_timeout": None,
|
3604
|
-
"parsed_options": {},
|
3605
|
-
},
|
3606
|
-
"dummy_client_id",
|
3607
|
-
)
|
3608
|
-
)
|
3609
|
-
worker.spawning_greenlet.join()
|
3610
|
-
self.assertEqual(2, worker.user_count)
|
3611
|
-
|
3612
|
-
worker.quit()
|
3613
|
-
|
3614
|
-
def test_worker_messages_sent_to_master(self):
|
3615
|
-
"""
|
3616
|
-
Ensure that worker includes both "user_count" and "user_classes_count"
|
3617
|
-
when reporting to the master.
|
3618
|
-
"""
|
3619
|
-
|
3620
|
-
class MyUser(User):
|
3621
|
-
wait_time = constant(1)
|
3622
|
-
|
3623
|
-
def start(self, group: Group):
|
3624
|
-
# We do this so that the spawning does not finish
|
3625
|
-
# too quickly
|
3626
|
-
gevent.sleep(0.1)
|
3627
|
-
return super().start(group)
|
3628
|
-
|
3629
|
-
@task
|
3630
|
-
def my_task(self):
|
3631
|
-
pass
|
3632
|
-
|
3633
|
-
with mock.patch("locust.rpc.rpc.Client", mocked_rpc()) as client:
|
3634
|
-
worker = self.get_runner(environment=Environment(), user_classes=[MyUser], client=client)
|
3635
|
-
|
3636
|
-
client.mocked_send(
|
3637
|
-
Message(
|
3638
|
-
"spawn",
|
3639
|
-
{
|
3640
|
-
"timestamp": 1605538584,
|
3641
|
-
"user_classes_count": {"MyUser": 10},
|
3642
|
-
"host": "",
|
3643
|
-
"stop_timeout": None,
|
3644
|
-
"parsed_options": {},
|
3645
|
-
},
|
3646
|
-
"dummy_client_id",
|
3647
|
-
)
|
3648
|
-
)
|
3649
|
-
sleep(0.6)
|
3650
|
-
self.assertEqual(STATE_SPAWNING, worker.state)
|
3651
|
-
worker.spawning_greenlet.join()
|
3652
|
-
self.assertEqual(10, worker.user_count)
|
3653
|
-
|
3654
|
-
sleep(2)
|
3655
|
-
|
3656
|
-
message = client.get_messages("stats")[-1]
|
3657
|
-
self.assertIsNotNone(message)
|
3658
|
-
self.assertIn("user_count", message.data)
|
3659
|
-
self.assertIn("user_classes_count", message.data)
|
3660
|
-
self.assertEqual(message.data["user_count"], 10)
|
3661
|
-
self.assertEqual(message.data["user_classes_count"]["MyUser"], 10)
|
3662
|
-
|
3663
|
-
message = client.get_messages("spawning_complete")[0]
|
3664
|
-
self.assertIsNotNone(message)
|
3665
|
-
self.assertIn("user_count", message.data)
|
3666
|
-
self.assertIn("user_classes_count", message.data)
|
3667
|
-
self.assertEqual(message.data["user_count"], 10)
|
3668
|
-
self.assertEqual(message.data["user_classes_count"]["MyUser"], 10)
|
3669
|
-
|
3670
|
-
worker.quit()
|
3671
|
-
|
3672
|
-
def test_worker_heartbeat_messages_sent_to_master(self):
|
3673
|
-
"""
|
3674
|
-
Validate content of the heartbeat payload sent to the master.
|
3675
|
-
"""
|
3676
|
-
|
3677
|
-
class MyUser(User):
|
3678
|
-
wait_time = constant(1)
|
3679
|
-
|
3680
|
-
@task
|
3681
|
-
def my_task(self):
|
3682
|
-
pass
|
3683
|
-
|
3684
|
-
with mock.patch("locust.rpc.rpc.Client", mocked_rpc()) as client:
|
3685
|
-
worker = self.get_runner(environment=Environment(), user_classes=[MyUser], client=client)
|
3686
|
-
|
3687
|
-
t0 = time.perf_counter()
|
3688
|
-
while len(client.get_messages("heartbeat")) == 0:
|
3689
|
-
self.assertLessEqual(time.perf_counter() - t0, 3)
|
3690
|
-
sleep(0.1)
|
3691
|
-
|
3692
|
-
message = client.get_messages("heartbeat")[-1]
|
3693
|
-
self.assertEqual(len(message.data), 3)
|
3694
|
-
self.assertIn("state", message.data)
|
3695
|
-
self.assertIn("current_cpu_usage", message.data)
|
3696
|
-
self.assertIn("current_memory_usage", message.data)
|
3697
|
-
|
3698
|
-
worker.quit()
|
3699
|
-
|
3700
|
-
def test_reset_rpc_connection_to_master(self):
|
3701
|
-
"""
|
3702
|
-
Validate worker resets RPC connection to master on "reconnect" message.
|
3703
|
-
"""
|
3704
|
-
|
3705
|
-
class MyUser(User):
|
3706
|
-
wait_time = constant(1)
|
3707
|
-
|
3708
|
-
@task
|
3709
|
-
def my_task(self):
|
3710
|
-
pass
|
3711
|
-
|
3712
|
-
with mock.patch("locust.rpc.rpc.Client", mocked_rpc(raise_on_close=False)) as client:
|
3713
|
-
worker = self.get_runner(environment=Environment(), user_classes=[MyUser], client=client)
|
3714
|
-
client.mocked_send(
|
3715
|
-
Message(
|
3716
|
-
"spawn",
|
3717
|
-
{
|
3718
|
-
"timestamp": 1605538584,
|
3719
|
-
"user_classes_count": {"MyUser": 10},
|
3720
|
-
"host": "",
|
3721
|
-
"stop_timeout": None,
|
3722
|
-
"parsed_options": {},
|
3723
|
-
},
|
3724
|
-
"dummy_client_id",
|
3725
|
-
)
|
3726
|
-
)
|
3727
|
-
sleep(0.6)
|
3728
|
-
self.assertEqual(STATE_RUNNING, worker.state)
|
3729
|
-
with self.assertLogs("locust.runners") as capture:
|
3730
|
-
with mock.patch("locust.rpc.rpc.Client.close") as close:
|
3731
|
-
client.mocked_send(
|
3732
|
-
Message(
|
3733
|
-
"reconnect",
|
3734
|
-
None,
|
3735
|
-
"dummy_client_id",
|
3736
|
-
)
|
3737
|
-
)
|
3738
|
-
sleep(0)
|
3739
|
-
worker.spawning_greenlet.join()
|
3740
|
-
worker.quit()
|
3741
|
-
close.assert_called_once()
|
3742
|
-
self.assertIn(
|
3743
|
-
"WARNING:locust.runners:Received reconnect message from master. Resetting RPC connection.",
|
3744
|
-
capture.output,
|
3745
|
-
)
|
3746
|
-
|
3747
|
-
def test_change_user_count_during_spawning(self):
|
3748
|
-
class MyUser(User):
|
3749
|
-
wait_time = constant(1)
|
3750
|
-
|
3751
|
-
def start(self, group: Group):
|
3752
|
-
# We do this so that the spawning does not finish
|
3753
|
-
# too quickly
|
3754
|
-
gevent.sleep(0.1)
|
3755
|
-
return super().start(group)
|
3756
|
-
|
3757
|
-
@task
|
3758
|
-
def my_task(self):
|
3759
|
-
pass
|
3760
|
-
|
3761
|
-
with mock.patch("locust.rpc.rpc.Client", mocked_rpc()) as client:
|
3762
|
-
worker = self.get_runner(environment=Environment(), user_classes=[MyUser], client=client)
|
3763
|
-
|
3764
|
-
client.mocked_send(
|
3765
|
-
Message(
|
3766
|
-
"spawn",
|
3767
|
-
{
|
3768
|
-
"timestamp": 1605538584,
|
3769
|
-
"user_classes_count": {"MyUser": 10},
|
3770
|
-
"host": "",
|
3771
|
-
"stop_timeout": None,
|
3772
|
-
"parsed_options": {},
|
3773
|
-
},
|
3774
|
-
"dummy_client_id",
|
3775
|
-
)
|
3776
|
-
)
|
3777
|
-
sleep(0.6)
|
3778
|
-
self.assertEqual(STATE_SPAWNING, worker.state)
|
3779
|
-
client.mocked_send(
|
3780
|
-
Message(
|
3781
|
-
"spawn",
|
3782
|
-
{
|
3783
|
-
"timestamp": 1605538585,
|
3784
|
-
"user_classes_count": {"MyUser": 9},
|
3785
|
-
"host": "",
|
3786
|
-
"stop_timeout": None,
|
3787
|
-
"parsed_options": {},
|
3788
|
-
},
|
3789
|
-
"dummy_client_id",
|
3790
|
-
)
|
3791
|
-
)
|
3792
|
-
sleep(0)
|
3793
|
-
worker.spawning_greenlet.join()
|
3794
|
-
self.assertEqual(9, len(worker.user_greenlets))
|
3795
|
-
worker.quit()
|
3796
|
-
|
3797
|
-
def test_computed_properties(self):
|
3798
|
-
class MyUser1(User):
|
3799
|
-
wait_time = constant(1)
|
3800
|
-
|
3801
|
-
@task
|
3802
|
-
def my_task(self):
|
3803
|
-
pass
|
3804
|
-
|
3805
|
-
class MyUser2(User):
|
3806
|
-
wait_time = constant(1)
|
3807
|
-
|
3808
|
-
@task
|
3809
|
-
def my_task(self):
|
3810
|
-
pass
|
3811
|
-
|
3812
|
-
with mock.patch("locust.rpc.rpc.Client", mocked_rpc()) as client:
|
3813
|
-
worker = self.get_runner(environment=Environment(), user_classes=[MyUser1, MyUser2], client=client)
|
3814
|
-
|
3815
|
-
client.mocked_send(
|
3816
|
-
Message(
|
3817
|
-
"spawn",
|
3818
|
-
{
|
3819
|
-
"timestamp": 1605538584,
|
3820
|
-
"user_classes_count": {"MyUser1": 10, "MyUser2": 10},
|
3821
|
-
"host": "",
|
3822
|
-
"stop_timeout": None,
|
3823
|
-
"parsed_options": {},
|
3824
|
-
},
|
3825
|
-
"dummy_client_id",
|
3826
|
-
)
|
3827
|
-
)
|
3828
|
-
worker.spawning_greenlet.join()
|
3829
|
-
self.assertDictEqual(worker.user_classes_count, {"MyUser1": 10, "MyUser2": 10})
|
3830
|
-
self.assertDictEqual(worker.target_user_classes_count, {"MyUser1": 10, "MyUser2": 10})
|
3831
|
-
self.assertEqual(worker.target_user_count, 20)
|
3832
|
-
|
3833
|
-
client.mocked_send(
|
3834
|
-
Message(
|
3835
|
-
"spawn",
|
3836
|
-
{
|
3837
|
-
"timestamp": 1605538585,
|
3838
|
-
"user_classes_count": {"MyUser1": 1, "MyUser2": 2},
|
3839
|
-
"host": "",
|
3840
|
-
"stop_timeout": None,
|
3841
|
-
"parsed_options": {},
|
3842
|
-
},
|
3843
|
-
"dummy_client_id",
|
3844
|
-
)
|
3845
|
-
)
|
3846
|
-
worker.spawning_greenlet.join()
|
3847
|
-
self.assertDictEqual(worker.user_classes_count, {"MyUser1": 1, "MyUser2": 2})
|
3848
|
-
self.assertDictEqual(worker.target_user_classes_count, {"MyUser1": 1, "MyUser2": 2})
|
3849
|
-
self.assertEqual(worker.target_user_count, 3)
|
3850
|
-
|
3851
|
-
worker.quit()
|
3852
|
-
|
3853
|
-
def test_custom_message_send(self):
|
3854
|
-
class MyUser(User):
|
3855
|
-
wait_time = constant(1)
|
3856
|
-
|
3857
|
-
@task
|
3858
|
-
def my_task(self):
|
3859
|
-
pass
|
3860
|
-
|
3861
|
-
with mock.patch("locust.rpc.rpc.Client", mocked_rpc()) as client:
|
3862
|
-
worker = self.get_runner(environment=Environment(), user_classes=[MyUser], client=client)
|
3863
|
-
client.outbox.clear()
|
3864
|
-
worker.send_message("test_custom_msg", {"test_data": 123})
|
3865
|
-
messages = client.get_messages()
|
3866
|
-
self.assertEqual("test_custom_msg", messages[0].type)
|
3867
|
-
self.assertEqual(123, messages[0].data["test_data"])
|
3868
|
-
worker.quit()
|
3869
|
-
|
3870
|
-
def test_custom_message_receive(self):
|
3871
|
-
class MyUser(User):
|
3872
|
-
wait_time = constant(1)
|
3873
|
-
|
3874
|
-
@task
|
3875
|
-
def my_task(self):
|
3876
|
-
pass
|
3877
|
-
|
3878
|
-
with mock.patch("locust.rpc.rpc.Client", mocked_rpc()) as client:
|
3879
|
-
test_custom_msg = [False]
|
3880
|
-
test_custom_msg_data = [{}]
|
3881
|
-
|
3882
|
-
def on_custom_msg(msg, **kw):
|
3883
|
-
test_custom_msg[0] = True
|
3884
|
-
test_custom_msg_data[0] = msg.data
|
3885
|
-
|
3886
|
-
worker = self.get_runner(environment=Environment(), user_classes=[MyUser], client=client)
|
3887
|
-
worker.register_message("test_custom_msg", on_custom_msg)
|
3888
|
-
|
3889
|
-
client.mocked_send(Message("test_custom_msg", {"test_data": 123}, "dummy_client_id"))
|
3890
|
-
|
3891
|
-
self.assertTrue(test_custom_msg[0])
|
3892
|
-
self.assertEqual(123, test_custom_msg_data[0]["test_data"])
|
3893
|
-
worker.quit()
|
3894
|
-
|
3895
|
-
def test_undefined_custom_message_receive(self):
|
3896
|
-
class MyUser(User):
|
3897
|
-
wait_time = constant(1)
|
3898
|
-
|
3899
|
-
@task
|
3900
|
-
def my_task(self):
|
3901
|
-
pass
|
3902
|
-
|
3903
|
-
with mock.patch("locust.rpc.rpc.Client", mocked_rpc()) as client:
|
3904
|
-
test_custom_msg = [False]
|
3905
|
-
|
3906
|
-
def on_custom_msg(msg, **kw):
|
3907
|
-
test_custom_msg[0] = True
|
3908
|
-
|
3909
|
-
worker = self.get_runner(environment=Environment(), user_classes=[MyUser], client=client)
|
3910
|
-
worker.register_message("test_custom_msg", on_custom_msg)
|
3911
|
-
|
3912
|
-
client.mocked_send(Message("unregistered_custom_msg", {}, "dummy_id"))
|
3913
|
-
|
3914
|
-
self.assertFalse(test_custom_msg[0])
|
3915
|
-
self.assertEqual(1, len(self.mocked_log.warning))
|
3916
|
-
msg = self.mocked_log.warning[0]
|
3917
|
-
self.assertIn("Unknown message type received", msg)
|
3918
|
-
|
3919
|
-
def test_start_event(self):
|
3920
|
-
class MyTestUser(User):
|
3921
|
-
_test_state = 0
|
3922
|
-
|
3923
|
-
@task
|
3924
|
-
def the_task(self):
|
3925
|
-
MyTestUser._test_state = 1
|
3926
|
-
gevent.sleep(0.2)
|
3927
|
-
MyTestUser._test_state = 2
|
3928
|
-
|
3929
|
-
with mock.patch("locust.rpc.rpc.Client", mocked_rpc()) as client:
|
3930
|
-
environment = Environment()
|
3931
|
-
run_count = [0]
|
3932
|
-
|
3933
|
-
@environment.events.test_start.add_listener
|
3934
|
-
def on_test_start(*args, **kw):
|
3935
|
-
run_count[0] += 1
|
3936
|
-
|
3937
|
-
worker = self.get_runner(environment=environment, user_classes=[MyTestUser], client=client)
|
3938
|
-
messages = client.get_messages()
|
3939
|
-
self.assertEqual(1, len(messages))
|
3940
|
-
self.assertEqual("client_ready", messages[0].type)
|
3941
|
-
client.mocked_send(
|
3942
|
-
Message(
|
3943
|
-
"spawn",
|
3944
|
-
{
|
3945
|
-
"timestamp": 1605538585,
|
3946
|
-
"user_classes_count": {"MyTestUser": 1},
|
3947
|
-
"spawn_rate": 1,
|
3948
|
-
"num_users": 1,
|
3949
|
-
"host": "",
|
3950
|
-
"stop_timeout": None,
|
3951
|
-
"parsed_options": {},
|
3952
|
-
},
|
3953
|
-
"dummy_client_id",
|
3954
|
-
)
|
3955
|
-
)
|
3956
|
-
# wait for worker to spawn locusts
|
3957
|
-
self.assertTrue(client.get_messages("spawning"))
|
3958
|
-
worker.spawning_greenlet.join()
|
3959
|
-
self.assertEqual(1, len(worker.user_greenlets))
|
3960
|
-
self.assertEqual(1, run_count[0])
|
3961
|
-
|
3962
|
-
# check that locust has started running
|
3963
|
-
gevent.sleep(0.01)
|
3964
|
-
self.assertEqual(1, MyTestUser._test_state)
|
3965
|
-
|
3966
|
-
# change number of users and check that test_start isn't fired again
|
3967
|
-
client.mocked_send(
|
3968
|
-
Message(
|
3969
|
-
"spawn",
|
3970
|
-
{
|
3971
|
-
"timestamp": 1605538586,
|
3972
|
-
"user_classes_count": {"MyTestUser": 1},
|
3973
|
-
"spawn_rate": 1,
|
3974
|
-
"num_users": 1,
|
3975
|
-
"host": "",
|
3976
|
-
"stop_timeout": None,
|
3977
|
-
"parsed_options": {},
|
3978
|
-
},
|
3979
|
-
"dummy_client_id",
|
3980
|
-
)
|
3981
|
-
)
|
3982
|
-
self.assertEqual(1, run_count[0])
|
3983
|
-
|
3984
|
-
# stop and start to make sure test_start is fired again
|
3985
|
-
client.mocked_send(Message("stop", None, "dummy_client_id"))
|
3986
|
-
client.mocked_send(
|
3987
|
-
Message(
|
3988
|
-
"spawn",
|
3989
|
-
{
|
3990
|
-
"timestamp": 1605538587,
|
3991
|
-
"user_classes_count": {"MyTestUser": 1},
|
3992
|
-
"spawn_rate": 1,
|
3993
|
-
"num_users": 1,
|
3994
|
-
"host": "",
|
3995
|
-
"stop_timeout": None,
|
3996
|
-
"parsed_options": {},
|
3997
|
-
},
|
3998
|
-
"dummy_client_id",
|
3999
|
-
)
|
4000
|
-
)
|
4001
|
-
gevent.sleep(0.01)
|
4002
|
-
self.assertEqual(2, run_count[0])
|
4003
|
-
|
4004
|
-
client.mocked_send(Message("stop", None, "dummy_client_id"))
|
4005
|
-
|
4006
|
-
def test_stop_event(self):
|
4007
|
-
class MyTestUser(User):
|
4008
|
-
_test_state = 0
|
4009
|
-
|
4010
|
-
@task
|
4011
|
-
def the_task(self):
|
4012
|
-
MyTestUser._test_state = 1
|
4013
|
-
gevent.sleep(0.2)
|
4014
|
-
MyTestUser._test_state = 2
|
4015
|
-
|
4016
|
-
with mock.patch("locust.rpc.rpc.Client", mocked_rpc()) as client:
|
4017
|
-
environment = Environment()
|
4018
|
-
run_count = [0]
|
4019
|
-
|
4020
|
-
@environment.events.test_stop.add_listener
|
4021
|
-
def on_test_stop(*args, **kw):
|
4022
|
-
run_count[0] += 1
|
4023
|
-
|
4024
|
-
worker = self.get_runner(environment=environment, user_classes=[MyTestUser], client=client)
|
4025
|
-
messages = client.get_messages()
|
4026
|
-
self.assertEqual(1, len(messages))
|
4027
|
-
self.assertEqual("client_ready", messages[0].type)
|
4028
|
-
client.mocked_send(
|
4029
|
-
Message(
|
4030
|
-
"spawn",
|
4031
|
-
{
|
4032
|
-
"timestamp": 1605538585,
|
4033
|
-
"user_classes_count": {"MyTestUser": 1},
|
4034
|
-
"spawn_rate": 1,
|
4035
|
-
"num_users": 1,
|
4036
|
-
"host": "",
|
4037
|
-
"stop_timeout": None,
|
4038
|
-
"parsed_options": {},
|
4039
|
-
},
|
4040
|
-
"dummy_client_id",
|
4041
|
-
)
|
4042
|
-
)
|
4043
|
-
|
4044
|
-
# wait for worker to spawn locusts
|
4045
|
-
self.assertTrue(client.get_messages("spawning"))
|
4046
|
-
worker.spawning_greenlet.join()
|
4047
|
-
self.assertEqual(1, len(worker.user_greenlets))
|
4048
|
-
|
4049
|
-
# check that locust has started running
|
4050
|
-
gevent.sleep(0.01)
|
4051
|
-
self.assertEqual(1, MyTestUser._test_state)
|
4052
|
-
|
4053
|
-
# stop and make sure test_stop is fired
|
4054
|
-
client.mocked_send(Message("stop", None, "dummy_client_id"))
|
4055
|
-
gevent.sleep(0.01)
|
4056
|
-
self.assertEqual(1, run_count[0])
|
4057
|
-
|
4058
|
-
# stop while stopped and make sure the event isn't fired again
|
4059
|
-
client.mocked_send(Message("stop", None, "dummy_client_id"))
|
4060
|
-
gevent.sleep(0.01)
|
4061
|
-
self.assertEqual(1, run_count[0])
|
4062
|
-
|
4063
|
-
# start and stop to check that the event is fired again
|
4064
|
-
client.mocked_send(
|
4065
|
-
Message(
|
4066
|
-
"spawn",
|
4067
|
-
{
|
4068
|
-
"timestamp": 1605538586,
|
4069
|
-
"user_classes_count": {"MyTestUser": 1},
|
4070
|
-
"spawn_rate": 1,
|
4071
|
-
"num_users": 1,
|
4072
|
-
"host": "",
|
4073
|
-
"stop_timeout": None,
|
4074
|
-
"parsed_options": {},
|
4075
|
-
},
|
4076
|
-
"dummy_client_id",
|
4077
|
-
)
|
4078
|
-
)
|
4079
|
-
client.mocked_send(Message("stop", None, "dummy_client_id"))
|
4080
|
-
gevent.sleep(0.01)
|
4081
|
-
self.assertEqual(2, run_count[0])
|
4082
|
-
|
4083
|
-
def test_worker_connect_success(self):
|
4084
|
-
class MyTestUser(User):
|
4085
|
-
@task
|
4086
|
-
def the_task(self):
|
4087
|
-
pass
|
4088
|
-
|
4089
|
-
with mock.patch("locust.runners.CONNECT_TIMEOUT", new=1):
|
4090
|
-
with mock.patch("locust.rpc.rpc.Client", mocked_rpc()) as client:
|
4091
|
-
worker = self.get_runner(environment=Environment(), user_classes=[MyTestUser], client=client)
|
4092
|
-
messages = client.get_messages()
|
4093
|
-
|
4094
|
-
self.assertEqual("client_ready", messages[0].type)
|
4095
|
-
self.assertEqual(1, len(messages))
|
4096
|
-
self.assertTrue(worker.connected)
|
4097
|
-
|
4098
|
-
def test_worker_connect_failure(self):
|
4099
|
-
class MyTestUser(User):
|
4100
|
-
@task
|
4101
|
-
def the_task(self):
|
4102
|
-
pass
|
4103
|
-
|
4104
|
-
with mock.patch("locust.runners.CONNECT_TIMEOUT", new=0.01):
|
4105
|
-
with mock.patch("locust.runners.CONNECT_RETRY_COUNT", new=1):
|
4106
|
-
with mock.patch("locust.rpc.rpc.Client", mocked_rpc()) as client:
|
4107
|
-
with self.assertRaises(ConnectionError):
|
4108
|
-
self.get_runner(
|
4109
|
-
environment=Environment(), user_classes=[MyTestUser], client=client, auto_connect=False
|
4110
|
-
)
|
4111
|
-
self.assertEqual(2, len(client.outbox))
|
4112
|
-
|
4113
|
-
def test_send_logs(self):
|
4114
|
-
class MyUser(User):
|
4115
|
-
wait_time = constant(1)
|
4116
|
-
|
4117
|
-
@task
|
4118
|
-
def my_task(self):
|
4119
|
-
pass
|
4120
|
-
|
4121
|
-
with mock.patch("locust.rpc.rpc.Client", mocked_rpc()) as client:
|
4122
|
-
short_time = 0.05
|
4123
|
-
|
4124
|
-
log_handler = LogReader()
|
4125
|
-
log_handler.name = "log_reader"
|
4126
|
-
log_handler.setLevel(logging.INFO)
|
4127
|
-
logger = logging.getLogger("root")
|
4128
|
-
logger.addHandler(log_handler)
|
4129
|
-
log_line = "some log info"
|
4130
|
-
logger.info(log_line)
|
4131
|
-
|
4132
|
-
worker = self.get_runner(environment=Environment(), user_classes=[MyUser], client=client)
|
4133
|
-
|
4134
|
-
gevent.sleep(short_time)
|
4135
|
-
|
4136
|
-
messages = client.get_messages()
|
4137
|
-
|
4138
|
-
self.assertEqual("logs", messages[3].type)
|
4139
|
-
self.assertEqual(log_line, messages[3].data.get("logs", [])[0])
|
4140
|
-
self.assertEqual(worker.client_id, messages[3].data.get("worker_id"))
|
4141
|
-
worker.quit()
|
4142
|
-
|
4143
|
-
def test_quit_worker_logs(self):
|
4144
|
-
class MyUser(User):
|
4145
|
-
wait_time = constant(1)
|
4146
|
-
|
4147
|
-
@task
|
4148
|
-
def my_task(self):
|
4149
|
-
pass
|
4150
|
-
|
4151
|
-
with mock.patch("locust.rpc.rpc.Client", mocked_rpc()) as client:
|
4152
|
-
short_time = 0.05
|
4153
|
-
|
4154
|
-
log_handler = LogReader()
|
4155
|
-
log_handler.name = "log_reader"
|
4156
|
-
log_handler.setLevel(logging.INFO)
|
4157
|
-
logger = logging.getLogger("root")
|
4158
|
-
logger.addHandler(log_handler)
|
4159
|
-
log_line = "spamming log"
|
4160
|
-
|
4161
|
-
for _ in range(11):
|
4162
|
-
logger.info(log_line)
|
4163
|
-
|
4164
|
-
worker = self.get_runner(environment=Environment(), user_classes=[MyUser], client=client)
|
4165
|
-
|
4166
|
-
gevent.sleep(short_time)
|
4167
|
-
|
4168
|
-
message = client.get_messages("logs")[0]
|
4169
|
-
|
4170
|
-
self.assertEqual(
|
4171
|
-
"The worker attempted to send more than 10 log lines in one interval. Further log sending was disabled for this worker.",
|
4172
|
-
message.data.get("logs", [])[-1],
|
4173
|
-
)
|
4174
|
-
self.assertEqual(worker.client_id, message.data.get("worker_id"))
|
4175
|
-
worker.quit()
|
4176
|
-
logger.removeHandler(log_handler)
|
4177
|
-
|
4178
|
-
|
4179
|
-
class TestMessageSerializing(unittest.TestCase):
|
4180
|
-
def test_message_serialize(self):
|
4181
|
-
msg = Message("client_ready", __version__, "my_id")
|
4182
|
-
rebuilt = Message.unserialize(msg.serialize())
|
4183
|
-
self.assertEqual(msg.type, rebuilt.type)
|
4184
|
-
self.assertEqual(msg.data, rebuilt.data)
|
4185
|
-
self.assertEqual(msg.node_id, rebuilt.node_id)
|
4186
|
-
|
4187
|
-
|
4188
|
-
class TestStopTimeout(LocustTestCase):
|
4189
|
-
def test_stop_timeout(self):
|
4190
|
-
short_time = 0.05
|
4191
|
-
|
4192
|
-
class MyTaskSet(TaskSet):
|
4193
|
-
@task
|
4194
|
-
def my_task(self):
|
4195
|
-
MyTaskSet.state = "first"
|
4196
|
-
gevent.sleep(short_time)
|
4197
|
-
MyTaskSet.state = "second" # should only run when run time + stop_timeout is > short_time
|
4198
|
-
gevent.sleep(short_time)
|
4199
|
-
MyTaskSet.state = "third" # should only run when run time + stop_timeout is > short_time * 2
|
4200
|
-
|
4201
|
-
class MyTestUser(User):
|
4202
|
-
tasks = [MyTaskSet]
|
4203
|
-
|
4204
|
-
environment = Environment(user_classes=[MyTestUser])
|
4205
|
-
runner = environment.create_local_runner()
|
4206
|
-
runner.start(1, 1, wait=False)
|
4207
|
-
gevent.sleep(short_time / 2)
|
4208
|
-
runner.quit()
|
4209
|
-
self.assertEqual("first", MyTaskSet.state)
|
4210
|
-
|
4211
|
-
# exit with timeout
|
4212
|
-
environment = Environment(user_classes=[MyTestUser], stop_timeout=short_time / 2)
|
4213
|
-
runner = environment.create_local_runner()
|
4214
|
-
runner.start(1, 1, wait=False)
|
4215
|
-
gevent.sleep(short_time)
|
4216
|
-
runner.quit()
|
4217
|
-
self.assertEqual("second", MyTaskSet.state)
|
4218
|
-
|
4219
|
-
# allow task iteration to complete, with some margin
|
4220
|
-
environment = Environment(user_classes=[MyTestUser], stop_timeout=short_time * 3)
|
4221
|
-
runner = environment.create_local_runner()
|
4222
|
-
runner.start(1, 1, wait=False)
|
4223
|
-
gevent.sleep(short_time)
|
4224
|
-
timeout = gevent.Timeout(short_time * 2)
|
4225
|
-
timeout.start()
|
4226
|
-
try:
|
4227
|
-
runner.quit()
|
4228
|
-
runner.greenlet.join()
|
4229
|
-
except gevent.Timeout:
|
4230
|
-
self.fail("Got Timeout exception. Some locusts must have kept running after iteration finish")
|
4231
|
-
finally:
|
4232
|
-
timeout.cancel()
|
4233
|
-
self.assertEqual("third", MyTaskSet.state)
|
4234
|
-
|
4235
|
-
def test_stop_timeout_during_on_start(self):
|
4236
|
-
short_time = 0.05
|
4237
|
-
|
4238
|
-
class MyTaskSet(TaskSet):
|
4239
|
-
finished_on_start = False
|
4240
|
-
my_task_run = False
|
4241
|
-
|
4242
|
-
def on_start(self):
|
4243
|
-
gevent.sleep(short_time)
|
4244
|
-
MyTaskSet.finished_on_start = True
|
4245
|
-
|
4246
|
-
@task
|
4247
|
-
def my_task(self):
|
4248
|
-
MyTaskSet.my_task_run = True
|
4249
|
-
|
4250
|
-
class MyTestUser(User):
|
4251
|
-
tasks = [MyTaskSet]
|
4252
|
-
|
4253
|
-
environment = create_environment([MyTestUser], mocked_options())
|
4254
|
-
environment.stop_timeout = short_time
|
4255
|
-
runner = environment.create_local_runner()
|
4256
|
-
runner.start(1, 1)
|
4257
|
-
gevent.sleep(short_time / 2)
|
4258
|
-
runner.quit()
|
4259
|
-
|
4260
|
-
self.assertTrue(MyTaskSet.finished_on_start)
|
4261
|
-
self.assertFalse(MyTaskSet.my_task_run)
|
4262
|
-
|
4263
|
-
def test_stop_timeout_exit_during_wait(self):
|
4264
|
-
short_time = 0.05
|
4265
|
-
|
4266
|
-
class MyTaskSet(TaskSet):
|
4267
|
-
@task
|
4268
|
-
def my_task(self):
|
4269
|
-
pass
|
4270
|
-
|
4271
|
-
class MyTestUser(User):
|
4272
|
-
tasks = [MyTaskSet]
|
4273
|
-
wait_time = constant(1)
|
4274
|
-
|
4275
|
-
environment = Environment(user_classes=[MyTestUser], stop_timeout=short_time)
|
4276
|
-
runner = environment.create_local_runner()
|
4277
|
-
runner.start(1, 1)
|
4278
|
-
gevent.sleep(short_time) # sleep to make sure locust has had time to start waiting
|
4279
|
-
timeout = gevent.Timeout(short_time)
|
4280
|
-
timeout.start()
|
4281
|
-
try:
|
4282
|
-
runner.quit()
|
4283
|
-
runner.greenlet.join()
|
4284
|
-
except gevent.Timeout:
|
4285
|
-
self.fail("Got Timeout exception. Waiting locusts should stop immediately, even when using stop_timeout.")
|
4286
|
-
finally:
|
4287
|
-
timeout.cancel()
|
4288
|
-
|
4289
|
-
def test_stop_timeout_with_interrupt(self):
|
4290
|
-
short_time = 0.05
|
4291
|
-
|
4292
|
-
class MySubTaskSet(TaskSet):
|
4293
|
-
@task
|
4294
|
-
def a_task(self):
|
4295
|
-
gevent.sleep(0)
|
4296
|
-
self.interrupt(reschedule=True)
|
4297
|
-
|
4298
|
-
class MyTaskSet(TaskSet):
|
4299
|
-
tasks = [MySubTaskSet]
|
4300
|
-
|
4301
|
-
class MyTestUser(User):
|
4302
|
-
tasks = [MyTaskSet]
|
4303
|
-
|
4304
|
-
environment = create_environment([MyTestUser], mocked_options())
|
4305
|
-
environment.stop_timeout = short_time
|
4306
|
-
runner = environment.create_local_runner()
|
4307
|
-
runner.start(1, 1, wait=True)
|
4308
|
-
gevent.sleep(0)
|
4309
|
-
timeout = gevent.Timeout(short_time)
|
4310
|
-
timeout.start()
|
4311
|
-
try:
|
4312
|
-
runner.quit()
|
4313
|
-
runner.greenlet.join()
|
4314
|
-
except gevent.Timeout:
|
4315
|
-
self.fail("Got Timeout exception. Interrupted locusts should exit immediately during stop_timeout.")
|
4316
|
-
finally:
|
4317
|
-
timeout.cancel()
|
4318
|
-
|
4319
|
-
def test_stop_timeout_with_interrupt_no_reschedule(self):
|
4320
|
-
state = [0]
|
4321
|
-
|
4322
|
-
class MySubTaskSet(TaskSet):
|
4323
|
-
@task
|
4324
|
-
def a_task(self):
|
4325
|
-
gevent.sleep(0.1)
|
4326
|
-
state[0] = 1
|
4327
|
-
self.interrupt(reschedule=False)
|
4328
|
-
|
4329
|
-
class MyTestUser(User):
|
4330
|
-
tasks = [MySubTaskSet]
|
4331
|
-
wait_time = constant(3)
|
4332
|
-
|
4333
|
-
options = mocked_options()
|
4334
|
-
options.stop_timeout = 0.3
|
4335
|
-
environment = create_environment([MyTestUser], options)
|
4336
|
-
runner = environment.create_local_runner()
|
4337
|
-
runner.start(1, 1, wait=True)
|
4338
|
-
gevent.sleep(0)
|
4339
|
-
timeout = gevent.Timeout(0.11)
|
4340
|
-
timeout.start()
|
4341
|
-
try:
|
4342
|
-
runner.quit()
|
4343
|
-
runner.greenlet.join()
|
4344
|
-
except gevent.Timeout:
|
4345
|
-
self.fail("Got Timeout exception. Interrupted locusts should exit immediately during stop_timeout.")
|
4346
|
-
finally:
|
4347
|
-
timeout.cancel()
|
4348
|
-
self.assertEqual(1, state[0])
|
4349
|
-
|
4350
|
-
def test_kill_locusts_with_stop_timeout(self):
|
4351
|
-
short_time = 0.05
|
4352
|
-
|
4353
|
-
class MyTaskSet(TaskSet):
|
4354
|
-
@task
|
4355
|
-
def my_task(self):
|
4356
|
-
MyTaskSet.state = "first"
|
4357
|
-
gevent.sleep(short_time)
|
4358
|
-
MyTaskSet.state = "second" # should only run when run time + stop_timeout is > short_time
|
4359
|
-
gevent.sleep(short_time)
|
4360
|
-
MyTaskSet.state = "third" # should only run when run time + stop_timeout is > short_time * 2
|
4361
|
-
|
4362
|
-
class MyTestUser(User):
|
4363
|
-
tasks = [MyTaskSet]
|
4364
|
-
|
4365
|
-
environment = create_environment([MyTestUser], mocked_options())
|
4366
|
-
runner = environment.create_local_runner()
|
4367
|
-
runner.start(1, 1)
|
4368
|
-
gevent.sleep(short_time / 2)
|
4369
|
-
runner.stop_users({MyTestUser.__name__: 1})
|
4370
|
-
self.assertEqual("first", MyTaskSet.state)
|
4371
|
-
runner.quit()
|
4372
|
-
environment.runner = None
|
4373
|
-
|
4374
|
-
environment.stop_timeout = short_time / 2 # exit with timeout
|
4375
|
-
runner = environment.create_local_runner()
|
4376
|
-
runner.start(1, 1)
|
4377
|
-
gevent.sleep(short_time)
|
4378
|
-
runner.stop_users({MyTestUser.__name__: 1})
|
4379
|
-
self.assertEqual("second", MyTaskSet.state)
|
4380
|
-
runner.quit()
|
4381
|
-
environment.runner = None
|
4382
|
-
|
4383
|
-
environment.stop_timeout = short_time * 3 # allow task iteration to complete, with some margin
|
4384
|
-
runner = environment.create_local_runner()
|
4385
|
-
runner.start(1, 1)
|
4386
|
-
gevent.sleep(short_time)
|
4387
|
-
timeout = gevent.Timeout(short_time * 2)
|
4388
|
-
timeout.start()
|
4389
|
-
try:
|
4390
|
-
runner.stop_users({MyTestUser.__name__: 1})
|
4391
|
-
runner.user_greenlets.join()
|
4392
|
-
except gevent.Timeout:
|
4393
|
-
self.fail("Got Timeout exception. Some locusts must have kept running after iteration finish")
|
4394
|
-
finally:
|
4395
|
-
timeout.cancel()
|
4396
|
-
self.assertEqual("third", MyTaskSet.state)
|
4397
|
-
|
4398
|
-
def test_users_can_call_runner_quit_with_stop_timeout(self):
|
4399
|
-
class BaseUser(User):
|
4400
|
-
wait_time = constant(1)
|
4401
|
-
|
4402
|
-
@task
|
4403
|
-
def trigger(self):
|
4404
|
-
self.environment.runner.quit()
|
4405
|
-
|
4406
|
-
runner = Environment(user_classes=[BaseUser]).create_local_runner()
|
4407
|
-
runner.environment.stop_timeout = 1
|
4408
|
-
runner.spawn_users({BaseUser.__name__: 1}, wait=False)
|
4409
|
-
timeout = gevent.Timeout(0.5)
|
4410
|
-
timeout.start()
|
4411
|
-
try:
|
4412
|
-
runner.greenlet.join()
|
4413
|
-
except gevent.Timeout:
|
4414
|
-
self.fail("Got Timeout exception, runner must have hung somehow.")
|
4415
|
-
finally:
|
4416
|
-
timeout.cancel()
|
4417
|
-
|
4418
|
-
def test_gracefully_handle_exceptions_in_listener(self):
|
4419
|
-
class MyUser(User):
|
4420
|
-
wait_time = constant(1)
|
4421
|
-
|
4422
|
-
@task
|
4423
|
-
def my_task(self):
|
4424
|
-
pass
|
4425
|
-
|
4426
|
-
test_stop_run = [0]
|
4427
|
-
environment = Environment(user_classes=[MyUser])
|
4428
|
-
|
4429
|
-
def on_test_stop_ok(*args, **kwargs):
|
4430
|
-
test_stop_run[0] += 1
|
4431
|
-
|
4432
|
-
def on_test_stop_fail(*args, **kwargs):
|
4433
|
-
assert False
|
4434
|
-
|
4435
|
-
environment.events.test_stop.add_listener(on_test_stop_ok)
|
4436
|
-
environment.events.test_stop.add_listener(on_test_stop_fail)
|
4437
|
-
environment.events.test_stop.add_listener(on_test_stop_ok)
|
4438
|
-
|
4439
|
-
runner = LocalRunner(environment)
|
4440
|
-
runner.start(user_count=3, spawn_rate=3, wait=False)
|
4441
|
-
self.assertEqual(0, test_stop_run[0])
|
4442
|
-
runner.stop()
|
4443
|
-
self.assertEqual(2, test_stop_run[0])
|
4444
|
-
|
4445
|
-
def test_stop_timeout_with_ramp_down(self):
|
4446
|
-
class MyTaskSet(TaskSet):
|
4447
|
-
@task
|
4448
|
-
def my_task(self):
|
4449
|
-
gevent.sleep(1)
|
4450
|
-
|
4451
|
-
class MyTestUser(User):
|
4452
|
-
tasks = [MyTaskSet]
|
4453
|
-
|
4454
|
-
environment = Environment(user_classes=[MyTestUser], stop_timeout=2)
|
4455
|
-
runner = environment.create_local_runner()
|
4456
|
-
|
4457
|
-
# Start load test, wait for users to start, then trigger ramp down
|
4458
|
-
ts = time.perf_counter()
|
4459
|
-
runner.start(10, 10, wait=False)
|
4460
|
-
runner.spawning_greenlet.join()
|
4461
|
-
delta = time.perf_counter() - ts
|
4462
|
-
self.assertTrue(
|
4463
|
-
0 <= delta <= 0.05, f"Expected user count to increase to 10 instantaneously, instead it took {delta:f}"
|
4464
|
-
)
|
4465
|
-
self.assertTrue(
|
4466
|
-
runner.user_count == 10, "User count has not decreased correctly to 2, it is : %i" % runner.user_count
|
4467
|
-
)
|
4468
|
-
|
4469
|
-
ts = time.perf_counter()
|
4470
|
-
runner.start(2, 4, wait=False)
|
4471
|
-
runner.spawning_greenlet.join()
|
4472
|
-
delta = time.perf_counter() - ts
|
4473
|
-
self.assertTrue(2 <= delta <= 2.05, f"Expected user count to decrease to 2 in 2s, instead it took {delta:f}")
|
4474
|
-
self.assertTrue(
|
4475
|
-
runner.user_count == 2, "User count has not decreased correctly to 2, it is : %i" % runner.user_count
|
4476
|
-
)
|