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