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