locust 2.29.2.dev32__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 (42) hide show
  1. locust/_version.py +6 -2
  2. locust/contrib/fasthttp.py +1 -1
  3. locust/dispatch.py +7 -6
  4. locust/stats.py +4 -17
  5. {locust-2.29.2.dev32.dist-info → locust-2.29.2.dev42.dist-info}/METADATA +31 -26
  6. locust-2.29.2.dev42.dist-info/RECORD +49 -0
  7. locust-2.29.2.dev42.dist-info/WHEEL +4 -0
  8. locust-2.29.2.dev42.dist-info/entry_points.txt +3 -0
  9. locust/test/__init__.py +0 -15
  10. locust/test/fake_module1_for_env_test.py +0 -7
  11. locust/test/fake_module2_for_env_test.py +0 -7
  12. locust/test/mock_locustfile.py +0 -56
  13. locust/test/mock_logging.py +0 -28
  14. locust/test/test_debugging.py +0 -39
  15. locust/test/test_dispatch.py +0 -4170
  16. locust/test/test_env.py +0 -283
  17. locust/test/test_fasthttp.py +0 -785
  18. locust/test/test_http.py +0 -325
  19. locust/test/test_interruptable_task.py +0 -48
  20. locust/test/test_load_locustfile.py +0 -228
  21. locust/test/test_locust_class.py +0 -831
  22. locust/test/test_log.py +0 -237
  23. locust/test/test_main.py +0 -2264
  24. locust/test/test_old_wait_api.py +0 -0
  25. locust/test/test_parser.py +0 -450
  26. locust/test/test_runners.py +0 -4476
  27. locust/test/test_sequential_taskset.py +0 -157
  28. locust/test/test_stats.py +0 -866
  29. locust/test/test_tags.py +0 -440
  30. locust/test/test_taskratio.py +0 -94
  31. locust/test/test_users.py +0 -69
  32. locust/test/test_util.py +0 -33
  33. locust/test/test_wait_time.py +0 -79
  34. locust/test/test_web.py +0 -1257
  35. locust/test/test_zmqrpc.py +0 -58
  36. locust/test/testcases.py +0 -248
  37. locust/test/util.py +0 -88
  38. locust-2.29.2.dev32.dist-info/RECORD +0 -79
  39. locust-2.29.2.dev32.dist-info/WHEEL +0 -5
  40. locust-2.29.2.dev32.dist-info/entry_points.txt +0 -2
  41. locust-2.29.2.dev32.dist-info/top_level.txt +0 -1
  42. {locust-2.29.2.dev32.dist-info → locust-2.29.2.dev42.dist-info}/LICENSE +0 -0
locust/test/test_web.py DELETED
@@ -1,1257 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import locust
4
- from locust import LoadTestShape, constant, stats
5
- from locust.argument_parser import get_parser, parse_options
6
- from locust.env import Environment
7
- from locust.log import LogReader
8
- from locust.runners import Runner
9
- from locust.stats import StatsCSVFileWriter
10
- from locust.user import User, task
11
- from locust.web import WebUI
12
-
13
- import copy
14
- import csv
15
- import json
16
- import logging
17
- import os
18
- import re
19
- import textwrap
20
- import traceback
21
- from io import StringIO
22
- from tempfile import NamedTemporaryFile, TemporaryDirectory
23
-
24
- import gevent
25
- import requests
26
- from flask_login import UserMixin
27
- from pyquery import PyQuery as pq
28
-
29
- from ..util.load_locustfile import load_locustfile
30
- from .mock_locustfile import mock_locustfile
31
- from .testcases import LocustTestCase
32
- from .util import create_tls_cert
33
-
34
-
35
- class _HeaderCheckMixin:
36
- def _check_csv_headers(self, headers, exp_fn_prefix):
37
- # Check common headers for csv file download request
38
- self.assertIn("Content-Type", headers)
39
- content_type = headers["Content-Type"]
40
- self.assertIn("text/csv", content_type)
41
-
42
- self.assertIn("Content-disposition", headers)
43
- disposition = headers[
44
- "Content-disposition"
45
- ] # e.g.: 'attachment; filename=requests_full_history_1597586811.5084946.csv'
46
- self.assertIn(exp_fn_prefix, disposition)
47
-
48
-
49
- class TestWebUI(LocustTestCase, _HeaderCheckMixin):
50
- def setUp(self):
51
- super().setUp()
52
-
53
- parser = get_parser(default_config_files=[])
54
- self.environment.parsed_options = parser.parse_args([])
55
- self.stats = self.environment.stats
56
-
57
- self.web_ui = self.environment.create_web_ui("127.0.0.1", 0)
58
- self.web_ui.app.view_functions["request_stats"].clear_cache()
59
- gevent.sleep(0.01)
60
- self.web_port = self.web_ui.server.server_port
61
-
62
- def tearDown(self):
63
- super().tearDown()
64
- self.web_ui.stop()
65
- self.runner.quit()
66
-
67
- def test_web_ui_reference_on_environment(self):
68
- self.assertEqual(self.web_ui, self.environment.web_ui)
69
-
70
- def test_web_ui_no_runner(self):
71
- env = Environment()
72
- web_ui = WebUI(env, "127.0.0.1", 0)
73
- gevent.sleep(0.01)
74
- try:
75
- response = requests.get("http://127.0.0.1:%i/" % web_ui.server.server_port)
76
- self.assertEqual(500, response.status_code)
77
- self.assertEqual("Error: Locust Environment does not have any runner", response.text)
78
- finally:
79
- web_ui.stop()
80
-
81
- def test_index(self):
82
- self.assertEqual(self.web_ui, self.environment.web_ui)
83
-
84
- html_to_option = {
85
- "num_users": ["-u", "100"],
86
- "spawn_rate": ["-r", "10.0"],
87
- }
88
-
89
- response = requests.get("http://127.0.0.1:%i/" % self.web_port)
90
- d = pq(response.content.decode("utf-8"))
91
-
92
- self.assertEqual(200, response.status_code)
93
- self.assertTrue(d("#root"))
94
-
95
- for html_name_to_test in html_to_option.keys():
96
- # Test that setting each spawn option individually populates the corresponding field in the html, and none of the others
97
- self.environment.parsed_options = parse_options(html_to_option[html_name_to_test])
98
-
99
- response = requests.get("http://127.0.0.1:%i/" % self.web_port)
100
- self.assertEqual(200, response.status_code)
101
-
102
- d = pq(response.content.decode("utf-8"))
103
-
104
- self.assertIn(f'"{html_name_to_test}": {html_to_option[html_name_to_test][1]}', str(d("script")))
105
-
106
- def test_index_with_spawn_options(self):
107
- html_to_option = {
108
- "num_users": ["-u", "100"],
109
- "spawn_rate": ["-r", "10.0"],
110
- }
111
-
112
- for html_name_to_test in html_to_option.keys():
113
- self.environment.parsed_options = parse_options(html_to_option[html_name_to_test])
114
-
115
- response = requests.get("http://127.0.0.1:%i/" % self.web_port)
116
- self.assertEqual(200, response.status_code)
117
-
118
- d = pq(response.content.decode("utf-8"))
119
-
120
- self.assertIn(f'"{html_name_to_test}": {html_to_option[html_name_to_test][1]}', str(d))
121
-
122
- def test_stats_no_data(self):
123
- self.assertEqual(200, requests.get("http://127.0.0.1:%i/stats/requests" % self.web_port).status_code)
124
-
125
- def test_stats(self):
126
- self.stats.log_request("GET", "/<html>", 120, 5612)
127
- response = requests.get("http://127.0.0.1:%i/stats/requests" % self.web_port)
128
- self.assertEqual(200, response.status_code)
129
-
130
- data = json.loads(response.text)
131
- self.assertEqual(2, len(data["stats"])) # one entry plus Aggregated
132
- self.assertEqual("/<html>", data["stats"][0]["name"])
133
- self.assertEqual("/&lt;html&gt;", data["stats"][0]["safe_name"])
134
- self.assertEqual("GET", data["stats"][0]["method"])
135
- self.assertEqual(120, data["stats"][0]["avg_response_time"])
136
-
137
- self.assertEqual("Aggregated", data["stats"][1]["name"])
138
- self.assertEqual(1, data["stats"][1]["num_requests"])
139
- self.assertEqual(120, data["stats"][1]["avg_response_time"])
140
-
141
- def test_stats_cache(self):
142
- self.stats.log_request("GET", "/test", 120, 5612)
143
- response = requests.get("http://127.0.0.1:%i/stats/requests" % self.web_port)
144
- self.assertEqual(200, response.status_code)
145
- data = json.loads(response.text)
146
- self.assertEqual(2, len(data["stats"])) # one entry plus Aggregated
147
-
148
- # add another entry
149
- self.stats.log_request("GET", "/test2", 120, 5612)
150
- data = json.loads(requests.get("http://127.0.0.1:%i/stats/requests" % self.web_port).text)
151
- self.assertEqual(2, len(data["stats"])) # old value should be cached now
152
-
153
- self.web_ui.app.view_functions["request_stats"].clear_cache()
154
-
155
- data = json.loads(requests.get("http://127.0.0.1:%i/stats/requests" % self.web_port).text)
156
- self.assertEqual(3, len(data["stats"])) # this should no longer be cached
157
-
158
- def test_stats_rounding(self):
159
- self.stats.log_request("GET", "/test", 1.39764125, 2)
160
- self.stats.log_request("GET", "/test", 999.9764125, 1000)
161
- response = requests.get("http://127.0.0.1:%i/stats/requests" % self.web_port)
162
- self.assertEqual(200, response.status_code)
163
-
164
- data = json.loads(response.text)
165
- self.assertEqual(1, data["stats"][0]["min_response_time"])
166
- self.assertEqual(1000, data["stats"][0]["max_response_time"])
167
-
168
- def test_request_stats_csv(self):
169
- self.stats.log_request("GET", "/test2", 120, 5612)
170
- response = requests.get("http://127.0.0.1:%i/stats/requests/csv" % self.web_port)
171
- self.assertEqual(200, response.status_code)
172
- self._check_csv_headers(response.headers, "requests")
173
-
174
- def test_request_stats_full_history_csv_not_present(self):
175
- self.stats.log_request("GET", "/test2", 120, 5612)
176
- response = requests.get("http://127.0.0.1:%i/stats/requests_full_history/csv" % self.web_port)
177
- self.assertEqual(404, response.status_code)
178
-
179
- def test_failure_stats_csv(self):
180
- self.stats.log_error("GET", "/", Exception("Error1337"))
181
- response = requests.get("http://127.0.0.1:%i/stats/failures/csv" % self.web_port)
182
- self.assertEqual(200, response.status_code)
183
- self._check_csv_headers(response.headers, "failures")
184
-
185
- def test_request_stats_with_errors(self):
186
- self.stats.log_error("GET", "/", Exception("Error with special characters {'foo':'bar'}"))
187
- response = requests.get("http://127.0.0.1:%i/stats/requests" % self.web_port)
188
- self.assertEqual(200, response.status_code)
189
-
190
- # escaped, old school
191
- # self.assertIn(
192
- # '"Exception(&quot;Error with special characters{&#x27;foo&#x27;:&#x27;bar&#x27;}&quot;)"', response.text
193
- # )
194
-
195
- # not html escaping, leave that to the frontend
196
- self.assertIn("\"Exception(\\\"Error with special characters {'foo':'bar'}\\\")", response.text)
197
-
198
- def test_reset_stats(self):
199
- try:
200
- raise Exception("A cool test exception")
201
- except Exception as e:
202
- tb = e.__traceback__
203
- self.runner.log_exception("local", str(e), "".join(traceback.format_tb(tb)))
204
- self.runner.log_exception("local", str(e), "".join(traceback.format_tb(tb)))
205
-
206
- self.stats.log_request("GET", "/test", 120, 5612)
207
- self.stats.log_error("GET", "/", Exception("Error1337"))
208
-
209
- response = requests.get("http://127.0.0.1:%i/stats/reset" % self.web_port)
210
-
211
- self.assertEqual(200, response.status_code)
212
-
213
- self.assertEqual({}, self.stats.errors)
214
- self.assertEqual({}, self.runner.exceptions)
215
-
216
- self.assertEqual(0, self.stats.get("/", "GET").num_requests)
217
- self.assertEqual(0, self.stats.get("/", "GET").num_failures)
218
- self.assertEqual(0, self.stats.get("/test", "GET").num_requests)
219
- self.assertEqual(0, self.stats.get("/test", "GET").num_failures)
220
-
221
- def test_exceptions(self):
222
- try:
223
- raise Exception("A cool test exception")
224
- except Exception as e:
225
- tb = e.__traceback__
226
- self.runner.log_exception("local", str(e), "".join(traceback.format_tb(tb)))
227
- self.runner.log_exception("local", str(e), "".join(traceback.format_tb(tb)))
228
-
229
- response = requests.get("http://127.0.0.1:%i/exceptions" % self.web_port)
230
- self.assertEqual(200, response.status_code)
231
- self.assertIn("A cool test exception", response.text)
232
-
233
- response = requests.get("http://127.0.0.1:%i/stats/requests" % self.web_port)
234
- self.assertEqual(200, response.status_code)
235
-
236
- def test_exceptions_csv(self):
237
- try:
238
- raise Exception("Test exception")
239
- except Exception as e:
240
- tb = e.__traceback__
241
- self.runner.log_exception("local", str(e), "".join(traceback.format_tb(tb)))
242
- self.runner.log_exception("local", str(e), "".join(traceback.format_tb(tb)))
243
-
244
- response = requests.get("http://127.0.0.1:%i/exceptions/csv" % self.web_port)
245
- self.assertEqual(200, response.status_code)
246
- self._check_csv_headers(response.headers, "exceptions")
247
-
248
- reader = csv.reader(StringIO(response.text))
249
- rows = []
250
- for row in reader:
251
- rows.append(row)
252
-
253
- self.assertEqual(2, len(rows))
254
- self.assertEqual("Test exception", rows[1][1])
255
- self.assertEqual(2, int(rows[1][0]), "Exception count should be 2")
256
-
257
- def test_swarm_host_value_specified(self):
258
- class MyUser(User):
259
- wait_time = constant(1)
260
-
261
- @task(1)
262
- def my_task(self):
263
- pass
264
-
265
- self.environment.user_classes = [MyUser]
266
- self.environment.web_ui.parsed_options = parse_options()
267
- response = requests.post(
268
- "http://127.0.0.1:%i/swarm" % self.web_port,
269
- data={"user_count": 5, "spawn_rate": 5, "host": "https://localhost"},
270
- )
271
- self.assertEqual(200, response.status_code)
272
- self.assertEqual("https://localhost", response.json()["host"])
273
- self.assertEqual(self.environment.host, "https://localhost")
274
- # stop
275
- gevent.sleep(1)
276
- response = requests.get("http://127.0.0.1:%i/stop" % self.web_port)
277
- self.assertEqual(response.json()["message"], "Test stopped")
278
- # and swarm again, with new host
279
- gevent.sleep(1)
280
- response = requests.post(
281
- "http://127.0.0.1:%i/swarm" % self.web_port,
282
- data={"user_count": 5, "spawn_rate": 5, "host": "https://localhost/other"},
283
- )
284
- gevent.sleep(1)
285
- self.assertEqual(200, response.status_code)
286
- self.assertEqual("https://localhost/other", response.json()["host"])
287
- self.assertEqual(self.environment.host, "https://localhost/other")
288
-
289
- def test_swarm_userclass_specified(self):
290
- class User1(User):
291
- wait_time = constant(1)
292
-
293
- @task
294
- def t(self):
295
- pass
296
-
297
- class User2(User):
298
- wait_time = constant(1)
299
-
300
- @task
301
- def t(self):
302
- pass
303
-
304
- self.environment.web_ui.userclass_picker_is_active = True
305
- self.environment.available_user_classes = {"User1": User1, "User2": User2}
306
-
307
- response = requests.post(
308
- "http://127.0.0.1:%i/swarm" % self.web_port,
309
- data={
310
- "user_count": 5,
311
- "spawn_rate": 5,
312
- "host": "https://localhost",
313
- "user_classes": "User1",
314
- },
315
- )
316
-
317
- self.assertEqual(200, response.status_code)
318
- self.assertEqual("https://localhost", response.json()["host"])
319
- self.assertEqual(self.environment.host, "https://localhost")
320
- self.assertEqual(["User1"], response.json()["user_classes"])
321
-
322
- # stop
323
- gevent.sleep(1)
324
- response = requests.get("http://127.0.0.1:%i/stop" % self.web_port)
325
- self.assertEqual(response.json()["message"], "Test stopped")
326
-
327
- # and swarm again, with new locustfile
328
- gevent.sleep(1)
329
- response = requests.post(
330
- "http://127.0.0.1:%i/swarm" % self.web_port,
331
- data={
332
- "user_count": 5,
333
- "spawn_rate": 5,
334
- "host": "https://localhost",
335
- "user_classes": "User2",
336
- },
337
- )
338
- gevent.sleep(1)
339
- self.assertEqual(200, response.status_code)
340
- self.assertEqual("https://localhost", response.json()["host"])
341
- self.assertEqual(self.environment.host, "https://localhost")
342
- self.assertEqual(["User2"], response.json()["user_classes"])
343
-
344
- def test_swarm_multiple_userclasses_specified(self):
345
- class User1(User):
346
- wait_time = constant(1)
347
-
348
- @task
349
- def t(self):
350
- pass
351
-
352
- class User2(User):
353
- wait_time = constant(1)
354
-
355
- @task
356
- def t(self):
357
- pass
358
-
359
- self.environment.web_ui.userclass_picker_is_active = True
360
- self.environment.available_user_classes = {"User1": User1, "User2": User2}
361
-
362
- response = requests.post(
363
- "http://127.0.0.1:%i/swarm" % self.web_port,
364
- data={
365
- "user_count": 5,
366
- "spawn_rate": 5,
367
- "host": "https://localhost",
368
- "user_classes": ["User1", "User2"],
369
- },
370
- )
371
-
372
- self.assertEqual(200, response.status_code)
373
- self.assertEqual("https://localhost", response.json()["host"])
374
- self.assertEqual(self.environment.host, "https://localhost")
375
- self.assertListEqual(["User1", "User2"], response.json()["user_classes"])
376
-
377
- self.assertIsNotNone(self.environment.locustfile, "verify locustfile is not empty")
378
- self.assertEqual(self.environment.locustfile, "User1,User2", "Verify locustfile variable used in web ui title")
379
-
380
- # stop
381
- gevent.sleep(1)
382
- response = requests.get("http://127.0.0.1:%i/stop" % self.web_port)
383
- self.assertEqual(response.json()["message"], "Test stopped")
384
-
385
- def test_swarm_updates_parsed_options_when_single_userclass_specified(self):
386
- """
387
- This test validates that environment.parsed_options.user_classes isn't overwritten
388
- when /swarm is hit with 'user_classes' in the data.
389
- """
390
-
391
- class User1(User):
392
- wait_time = constant(1)
393
-
394
- @task
395
- def t(self):
396
- pass
397
-
398
- class User2(User):
399
- wait_time = constant(1)
400
-
401
- @task
402
- def t(self):
403
- pass
404
-
405
- self.environment.web_ui.userclass_picker_is_active = True
406
- self.environment.available_user_classes = {"User1": User1, "User2": User2}
407
-
408
- response = requests.post(
409
- "http://127.0.0.1:%i/swarm" % self.web_port,
410
- data={
411
- "user_count": 5,
412
- "spawn_rate": 5,
413
- "host": "https://localhost",
414
- "user_classes": ["User1"],
415
- },
416
- )
417
- self.assertListEqual(["User1"], response.json()["user_classes"])
418
-
419
- # stop
420
- gevent.sleep(1)
421
- response = requests.get("http://127.0.0.1:%i/stop" % self.web_port)
422
- self.assertEqual(response.json()["message"], "Test stopped")
423
-
424
- # Checking environment.parsed_options.user_classes was updated
425
- self.assertListEqual(self.environment.parsed_options.user_classes, ["User1"])
426
-
427
- def test_swarm_updates_parsed_options_when_multiple_userclasses_specified(self):
428
- """
429
- This test validates that environment.parsed_options.user_classes isn't overwritten
430
- when /swarm is hit with 'user_classes' in the data.
431
- """
432
-
433
- class User1(User):
434
- wait_time = constant(1)
435
-
436
- @task
437
- def t(self):
438
- pass
439
-
440
- class User2(User):
441
- wait_time = constant(1)
442
-
443
- @task
444
- def t(self):
445
- pass
446
-
447
- self.environment.web_ui.userclass_picker_is_active = True
448
- self.environment.available_user_classes = {"User1": User1, "User2": User2}
449
-
450
- response = requests.post(
451
- "http://127.0.0.1:%i/swarm" % self.web_port,
452
- data={
453
- "user_count": 5,
454
- "spawn_rate": 5,
455
- "host": "https://localhost",
456
- "user_classes": ["User1", "User2"],
457
- },
458
- )
459
- self.assertListEqual(["User1", "User2"], response.json()["user_classes"])
460
-
461
- # stop
462
- gevent.sleep(1)
463
- response = requests.get("http://127.0.0.1:%i/stop" % self.web_port)
464
- self.assertEqual(response.json()["message"], "Test stopped")
465
-
466
- # Checking environment.parsed_options.user_classes was updated
467
- self.assertListEqual(self.environment.parsed_options.user_classes, ["User1", "User2"])
468
-
469
- def test_swarm_defaults_to_all_available_userclasses_when_userclass_picker_is_active_and_no_userclass_in_payload(
470
- self,
471
- ):
472
- class User1(User):
473
- wait_time = constant(1)
474
-
475
- @task
476
- def t(self):
477
- pass
478
-
479
- class User2(User):
480
- wait_time = constant(1)
481
-
482
- @task
483
- def t(self):
484
- pass
485
-
486
- self.environment.web_ui.userclass_picker_is_active = True
487
- self.environment.available_user_classes = {"User1": User1, "User2": User2}
488
- response = requests.post(
489
- "http://127.0.0.1:%i/swarm" % self.web_port,
490
- data={
491
- "user_count": 5,
492
- "spawn_rate": 5,
493
- "host": "https://localhost",
494
- },
495
- )
496
-
497
- self.assertEqual(200, response.status_code)
498
- self.assertEqual("https://localhost", response.json()["host"])
499
- self.assertEqual(self.environment.host, "https://localhost")
500
- self.assertListEqual(["User1", "User2"], response.json()["user_classes"])
501
-
502
- # stop
503
- gevent.sleep(1)
504
- response = requests.get("http://127.0.0.1:%i/stop" % self.web_port)
505
- self.assertEqual(response.json()["message"], "Test stopped")
506
-
507
- def test_swarm_uses_pre_selected_user_classes_when_empty_payload_and_test_is_already_running_with_class_picker(
508
- self,
509
- ):
510
- # This test validates that the correct User Classes are used when editing a running test
511
- class User1(User):
512
- wait_time = constant(1)
513
-
514
- @task
515
- def t(self):
516
- pass
517
-
518
- class User2(User):
519
- wait_time = constant(1)
520
-
521
- @task
522
- def t(self):
523
- pass
524
-
525
- self.environment.web_ui.userclass_picker_is_active = True
526
- self.environment.available_user_classes = {"User1": User1, "User2": User2}
527
- response = requests.post(
528
- "http://127.0.0.1:%i/swarm" % self.web_port,
529
- data={
530
- "user_count": 5,
531
- "spawn_rate": 5,
532
- "host": "https://localhost",
533
- "user_classes": ["User1"],
534
- },
535
- )
536
-
537
- self.assertEqual(200, response.status_code)
538
- self.assertEqual("https://localhost", response.json()["host"])
539
- self.assertEqual(self.environment.host, "https://localhost")
540
- self.assertListEqual(["User1"], response.json()["user_classes"])
541
-
542
- # simulating edit running load test
543
- response = requests.post(
544
- "http://127.0.0.1:%i/swarm" % self.web_port,
545
- data={
546
- "user_count": 10,
547
- "spawn_rate": 10,
548
- },
549
- )
550
- self.assertEqual(200, response.status_code)
551
- self.assertEqual("https://localhost", response.json()["host"])
552
- self.assertEqual(self.environment.host, "https://localhost")
553
- self.assertListEqual(["User1"], response.json()["user_classes"])
554
-
555
- # stop
556
- gevent.sleep(1)
557
- response = requests.get("http://127.0.0.1:%i/stop" % self.web_port)
558
- self.assertEqual(response.json()["message"], "Test stopped")
559
-
560
- def test_swarm_error_when_userclass_picker_is_active_but_no_available_userclasses(self):
561
- self.environment.web_ui.userclass_picker_is_active = True
562
- response = requests.post(
563
- "http://127.0.0.1:%i/swarm" % self.web_port,
564
- data={
565
- "user_count": 5,
566
- "spawn_rate": 5,
567
- "host": "https://localhost",
568
- "user_classes": "User1",
569
- },
570
- )
571
-
572
- expected_error_message = "UserClass picker is active but there are no available UserClasses"
573
- self.assertEqual(False, response.json()["success"])
574
- self.assertEqual(expected_error_message, response.json()["message"])
575
-
576
- def test_swarm_shape_class_specified(self):
577
- class User1(User):
578
- wait_time = constant(1)
579
-
580
- @task
581
- def t(self):
582
- pass
583
-
584
- class User2(User):
585
- wait_time = constant(1)
586
-
587
- @task
588
- def t(self):
589
- pass
590
-
591
- class TestShape1(LoadTestShape):
592
- def tick(self):
593
- run_time = self.get_run_time()
594
- if run_time < 10:
595
- return 4, 4
596
- else:
597
- return None
598
-
599
- class TestShape2(LoadTestShape):
600
- def tick(self):
601
- run_time = self.get_run_time()
602
- if run_time < 10:
603
- return 4, 4
604
- else:
605
- return None
606
-
607
- self.environment.web_ui.userclass_picker_is_active = True
608
- self.environment.available_user_classes = {"User1": User1, "User2": User2}
609
- self.environment.available_shape_classes = {"TestShape1": TestShape1(), "TestShape2": TestShape2()}
610
- self.environment.shape_class = TestShape1()
611
-
612
- response = requests.post(
613
- "http://127.0.0.1:%i/swarm" % self.web_port,
614
- data={
615
- "user_count": 5,
616
- "spawn_rate": 5,
617
- "host": "https://localhost",
618
- "user_classes": "User1",
619
- "shape_class": "TestShape2",
620
- },
621
- )
622
-
623
- self.assertEqual(200, response.status_code)
624
- self.assertEqual("https://localhost", response.json()["host"])
625
- self.assertEqual(self.environment.host, "https://localhost")
626
- assert isinstance(self.environment.shape_class, TestShape2)
627
-
628
- # stop
629
- gevent.sleep(1)
630
- response = requests.get("http://127.0.0.1:%i/stop" % self.web_port)
631
- self.assertEqual(response.json()["message"], "Test stopped")
632
-
633
- def test_swarm_shape_class_defaults_to_none_when_userclass_picker_is_active(self):
634
- class User1(User):
635
- wait_time = constant(1)
636
-
637
- @task
638
- def t(self):
639
- pass
640
-
641
- class User2(User):
642
- wait_time = constant(1)
643
-
644
- @task
645
- def t(self):
646
- pass
647
-
648
- class TestShape(LoadTestShape):
649
- def tick(self):
650
- run_time = self.get_run_time()
651
- if run_time < 10:
652
- return 4, 4
653
- else:
654
- return None
655
-
656
- test_shape_instance = TestShape()
657
-
658
- self.environment.web_ui.userclass_picker_is_active = True
659
- self.environment.available_user_classes = {"User1": User1, "User2": User2}
660
- self.environment.available_shape_classes = {"TestShape": test_shape_instance}
661
- self.environment.shape_class = test_shape_instance
662
-
663
- response = requests.post(
664
- "http://127.0.0.1:%i/swarm" % self.web_port,
665
- data={
666
- "user_count": 5,
667
- "spawn_rate": 5,
668
- "host": "https://localhost",
669
- "user_classes": "User1",
670
- },
671
- )
672
-
673
- self.assertEqual(200, response.status_code)
674
- self.assertEqual("https://localhost", response.json()["host"])
675
- self.assertEqual(self.environment.host, "https://localhost")
676
- self.assertIsNone(self.environment.shape_class)
677
-
678
- # stop
679
- gevent.sleep(1)
680
- response = requests.get("http://127.0.0.1:%i/stop" % self.web_port)
681
- self.assertEqual(response.json()["message"], "Test stopped")
682
-
683
- def test_swarm_shape_class_is_updated_when_userclass_picker_is_active(self):
684
- class User1(User):
685
- pass
686
-
687
- class TestShape(LoadTestShape):
688
- def tick(self):
689
- pass
690
-
691
- test_shape_instance = TestShape()
692
-
693
- self.environment.web_ui.userclass_picker_is_active = True
694
- self.environment.available_user_classes = {"User1": User1}
695
- self.environment.available_shape_classes = {"TestShape": test_shape_instance}
696
- self.environment.shape_class = None
697
-
698
- response = requests.post(
699
- "http://127.0.0.1:%i/swarm" % self.web_port,
700
- data={
701
- "user_count": 5,
702
- "spawn_rate": 5,
703
- "host": "https://localhost",
704
- "user_classes": "User1",
705
- "shape_class": "TestShape",
706
- },
707
- )
708
-
709
- self.assertEqual(200, response.status_code)
710
- self.assertEqual(test_shape_instance, self.environment.shape_class)
711
- self.assertIsNotNone(test_shape_instance.runner)
712
-
713
- # stop
714
- gevent.sleep(1)
715
- response = requests.get("http://127.0.0.1:%i/stop" % self.web_port)
716
- self.assertEqual(response.json()["message"], "Test stopped")
717
-
718
- def test_swarm_userclass_shapeclass_ignored_when_userclass_picker_is_inactive(self):
719
- class User1(User):
720
- wait_time = constant(1)
721
-
722
- @task
723
- def t(self):
724
- pass
725
-
726
- class User2(User):
727
- wait_time = constant(1)
728
-
729
- @task
730
- def t(self):
731
- pass
732
-
733
- class TestShape(LoadTestShape):
734
- def tick(self):
735
- run_time = self.get_run_time()
736
- if run_time < 10:
737
- return 4, 4
738
- else:
739
- return None
740
-
741
- self.environment.web_ui.userclass_picker_is_active = False
742
- self.environment.user_classes = [User1, User2]
743
- self.environment.available_user_classes = {"User1": User1, "User2": User2}
744
- self.environment.available_shape_classes = {"TestShape": TestShape()}
745
- self.environment.shape_class = None
746
-
747
- response = requests.post(
748
- "http://127.0.0.1:%i/swarm" % self.web_port,
749
- data={
750
- "user_count": 5,
751
- "spawn_rate": 5,
752
- "host": "https://localhost",
753
- "user_classes": "User1",
754
- "shape_class": "TestShape",
755
- },
756
- )
757
-
758
- self.assertEqual(200, response.status_code)
759
- self.assertEqual("https://localhost", response.json()["host"])
760
- self.assertEqual(self.environment.host, "https://localhost")
761
- self.assertListEqual(self.environment.user_classes, [User1, User2])
762
- self.assertIsNone(self.environment.shape_class)
763
-
764
- # stop
765
- gevent.sleep(1)
766
- response = requests.get("http://127.0.0.1:%i/stop" % self.web_port)
767
- self.assertEqual(response.json()["message"], "Test stopped")
768
-
769
- def test_swarm_custom_argument(self):
770
- my_dict = {}
771
-
772
- class MyUser(User):
773
- host = "http://example.com"
774
- wait_time = constant(1)
775
-
776
- @task(1)
777
- def my_task(self):
778
- my_dict["val"] = self.environment.parsed_options.my_argument
779
-
780
- @locust.events.init_command_line_parser.add_listener
781
- def _(parser, **kw):
782
- parser.add_argument("--my-argument", type=int, help="Give me a number")
783
-
784
- parsed_options = parse_options(args=["--my-argument", "42"])
785
- self.environment.user_classes = [MyUser]
786
- self.environment.parsed_options = parsed_options
787
- self.environment.web_ui.parsed_options = parsed_options
788
- response = requests.post(
789
- "http://127.0.0.1:%i/swarm" % self.web_port,
790
- data={"user_count": 1, "spawn_rate": 1, "host": "", "my_argument": "42"},
791
- )
792
- self.assertEqual(200, response.status_code)
793
- self.assertEqual(my_dict["val"], 42)
794
-
795
- def test_swarm_host_value_not_specified(self):
796
- class MyUser(User):
797
- wait_time = constant(1)
798
-
799
- @task(1)
800
- def my_task(self):
801
- pass
802
-
803
- self.environment.user_classes = [MyUser]
804
- self.environment.web_ui.parsed_options = parse_options()
805
- response = requests.post(
806
- "http://127.0.0.1:%i/swarm" % self.web_port,
807
- data={"user_count": 5, "spawn_rate": 5},
808
- )
809
- self.assertEqual(200, response.status_code)
810
- self.assertEqual(None, response.json()["host"])
811
- self.assertEqual(self.environment.host, None)
812
-
813
- def test_swarm_run_time(self):
814
- class MyUser(User):
815
- wait_time = constant(1)
816
-
817
- @task(1)
818
- def my_task(self):
819
- pass
820
-
821
- self.environment.user_classes = [MyUser]
822
- self.environment.web_ui.parsed_options = parse_options()
823
- response = requests.post(
824
- "http://127.0.0.1:%i/swarm" % self.web_port,
825
- data={"user_count": 5, "spawn_rate": 5, "host": "https://localhost", "run_time": "1s"},
826
- )
827
- self.assertEqual(200, response.status_code)
828
- self.assertEqual("https://localhost", response.json()["host"])
829
- self.assertEqual(self.environment.host, "https://localhost")
830
- self.assertEqual(1, response.json()["run_time"])
831
- # wait for test to run
832
- gevent.sleep(3)
833
- response = requests.get("http://127.0.0.1:%i/stats/requests" % self.web_port)
834
- self.assertEqual("stopped", response.json()["state"])
835
-
836
- def test_swarm_run_time_invalid_input(self):
837
- class MyUser(User):
838
- wait_time = constant(1)
839
-
840
- @task(1)
841
- def my_task(self):
842
- pass
843
-
844
- self.environment.user_classes = [MyUser]
845
- self.environment.web_ui.parsed_options = parse_options()
846
- response = requests.post(
847
- "http://127.0.0.1:%i/swarm" % self.web_port,
848
- data={"user_count": 5, "spawn_rate": 5, "host": "https://localhost", "run_time": "bad"},
849
- )
850
- self.assertEqual(200, response.status_code)
851
- self.assertEqual(False, response.json()["success"])
852
- self.assertEqual(self.environment.host, "https://localhost")
853
- self.assertEqual(
854
- "Valid run_time formats are : 20, 20s, 3m, 2h, 1h20m, 3h30m10s, etc.", response.json()["message"]
855
- )
856
- # verify test was not started
857
- response = requests.get("http://127.0.0.1:%i/stats/requests" % self.web_port)
858
- self.assertEqual("ready", response.json()["state"])
859
- requests.get("http://127.0.0.1:%i/stats/reset" % self.web_port)
860
-
861
- def test_swarm_run_time_empty_input(self):
862
- class MyUser(User):
863
- wait_time = constant(1)
864
-
865
- @task(1)
866
- def my_task(self):
867
- pass
868
-
869
- self.environment.user_classes = [MyUser]
870
- self.environment.web_ui.parsed_options = parse_options()
871
- response = requests.post(
872
- "http://127.0.0.1:%i/swarm" % self.web_port,
873
- data={"user_count": 5, "spawn_rate": 5, "host": "https://localhost", "run_time": ""},
874
- )
875
-
876
- self.assertEqual(200, response.status_code)
877
- self.assertEqual("https://localhost", response.json()["host"])
878
- self.assertEqual(self.environment.host, "https://localhost")
879
-
880
- # verify test is running
881
- gevent.sleep(1)
882
- response = requests.get("http://127.0.0.1:%i/stats/requests" % self.web_port)
883
- self.assertEqual("running", response.json()["state"])
884
-
885
- # stop
886
- response = requests.get("http://127.0.0.1:%i/stop" % self.web_port)
887
-
888
- def test_host_value_from_user_class(self):
889
- class MyUser(User):
890
- host = "http://example.com"
891
-
892
- self.environment.user_classes = [MyUser]
893
- response = requests.get("http://127.0.0.1:%i/" % self.web_port)
894
- self.assertEqual(200, response.status_code)
895
- self.assertIn("http://example.com", response.content.decode("utf-8"))
896
- self.assertNotIn("setting this will override the host on all User classes", response.content.decode("utf-8"))
897
-
898
- def test_host_value_from_multiple_user_classes(self):
899
- class MyUser(User):
900
- host = "http://example.com"
901
-
902
- class MyUser2(User):
903
- host = "http://example.com"
904
-
905
- self.environment.user_classes = [MyUser, MyUser2]
906
- response = requests.get("http://127.0.0.1:%i/" % self.web_port)
907
- self.assertEqual(200, response.status_code)
908
- self.assertIn("http://example.com", response.content.decode("utf-8"))
909
- self.assertNotIn("setting this will override the host on all User classes", response.content.decode("utf-8"))
910
-
911
- def test_host_value_from_multiple_user_classes_different_hosts(self):
912
- class MyUser(User):
913
- host = None
914
-
915
- class MyUser2(User):
916
- host = "http://example.com"
917
-
918
- self.environment.user_classes = [MyUser, MyUser2]
919
- response = requests.get("http://127.0.0.1:%i/" % self.web_port)
920
- self.assertEqual(200, response.status_code)
921
- self.assertNotIn("http://example.com", response.content.decode("utf-8"))
922
-
923
- def test_report_page(self):
924
- self.stats.log_request("GET", "/test", 120, 5612)
925
- r = requests.get("http://127.0.0.1:%i/stats/report" % self.web_port)
926
-
927
- d = pq(r.content.decode("utf-8"))
928
-
929
- self.assertEqual(200, r.status_code)
930
- self.assertIn('"host": "None"', str(d))
931
- self.assertIn('"num_requests": 1', str(d))
932
- self.assertIn('"is_report": true', str(d))
933
- self.assertIn('"show_download_link": true', str(d))
934
-
935
- def test_report_page_empty_stats(self):
936
- r = requests.get("http://127.0.0.1:%i/stats/report" % self.web_port)
937
- self.assertEqual(200, r.status_code)
938
-
939
- def test_report_download(self):
940
- self.stats.log_request("GET", "/test", 120, 5612)
941
- r = requests.get("http://127.0.0.1:%i/stats/report?download=1" % self.web_port)
942
-
943
- d = pq(r.content.decode("utf-8"))
944
-
945
- self.assertEqual(200, r.status_code)
946
- self.assertIn("attachment", r.headers.get("Content-Disposition", ""))
947
- self.assertIn('"show_download_link": false', str(d))
948
-
949
- def test_report_host(self):
950
- self.environment.host = "http://test.com"
951
- self.stats.log_request("GET", "/test", 120, 5612)
952
- r = requests.get("http://127.0.0.1:%i/stats/report" % self.web_port)
953
-
954
- d = pq(r.content.decode("utf-8"))
955
-
956
- self.assertEqual(200, r.status_code)
957
- self.assertIn('"host": "http://test.com"', str(d))
958
-
959
- def test_report_host2(self):
960
- class MyUser(User):
961
- host = "http://test2.com"
962
-
963
- @task
964
- def my_task(self):
965
- pass
966
-
967
- self.environment.host = None
968
- self.environment.user_classes = [MyUser]
969
- self.stats.log_request("GET", "/test", 120, 5612)
970
- r = requests.get("http://127.0.0.1:%i/stats/report" % self.web_port)
971
-
972
- d = pq(r.content.decode("utf-8"))
973
-
974
- self.assertEqual(200, r.status_code)
975
- self.assertIn('"host": "http://test2.com"', str(d))
976
-
977
- def test_report_exceptions(self):
978
- try:
979
- raise Exception("Test exception")
980
- except Exception as e:
981
- tb = e.__traceback__
982
- self.runner.log_exception("local", str(e), "".join(traceback.format_tb(tb)))
983
- self.runner.log_exception("local", str(e), "".join(traceback.format_tb(tb)))
984
- self.stats.log_request("GET", "/test", 120, 5612)
985
- r = requests.get("http://127.0.0.1:%i/stats/report" % self.web_port)
986
-
987
- d = pq(r.content.decode("utf-8"))
988
-
989
- self.assertIn('exceptions_statistics": [{"count": 2', str(d))
990
-
991
- # Prior to 088a98bf8ff4035a0de3becc8cd4e887d618af53, the "nodes" field for each exception in
992
- # "self.runner.exceptions" was accidentally mutated in "get_html_report" to a string.
993
- # This assertion reproduces the issue and it is left there to make sure there's no
994
- # regression in the future.
995
- self.assertTrue(
996
- isinstance(next(iter(self.runner.exceptions.values()))["nodes"], set), "exception object has been mutated"
997
- )
998
-
999
- def test_html_stats_report(self):
1000
- self.environment.locustfile = "locust.py"
1001
- self.environment.host = "http://localhost"
1002
-
1003
- response = requests.get("http://127.0.0.1:%i/stats/report" % self.web_port)
1004
- self.assertEqual(200, response.status_code)
1005
-
1006
- d = pq(response.content.decode("utf-8"))
1007
-
1008
- self.assertTrue(d("#root"))
1009
- self.assertIn('"locustfile": "locust.py"', str(d))
1010
- self.assertIn('"host": "http://localhost"', str(d))
1011
-
1012
- def test_logs(self):
1013
- log_handler = LogReader()
1014
- log_handler.name = "log_reader"
1015
- log_handler.setLevel(logging.INFO)
1016
- logger = logging.getLogger("root")
1017
- logger.addHandler(log_handler)
1018
- log_line = "some log info"
1019
- logger.info(log_line)
1020
-
1021
- response = requests.get("http://127.0.0.1:%i/logs" % self.web_port)
1022
-
1023
- self.assertIn(log_line, response.json().get("master"))
1024
-
1025
- def test_worker_logs(self):
1026
- log_handler = LogReader()
1027
- log_handler.name = "log_reader"
1028
- log_handler.setLevel(logging.INFO)
1029
- logger = logging.getLogger("root")
1030
- logger.addHandler(log_handler)
1031
- log_line = "some log info"
1032
- logger.info(log_line)
1033
-
1034
- worker_id = "123"
1035
- worker_log_line = "worker log"
1036
- self.environment.update_worker_logs({"worker_id": worker_id, "logs": [worker_log_line]})
1037
-
1038
- response = requests.get("http://127.0.0.1:%i/logs" % self.web_port)
1039
-
1040
- self.assertIn(log_line, response.json().get("master"))
1041
- self.assertIn(worker_log_line, response.json().get("workers").get(worker_id))
1042
-
1043
- def test_template_args(self):
1044
- class MyUser(User):
1045
- @task
1046
- def do_something(self):
1047
- self.client.get("/")
1048
-
1049
- host = "http://example.com"
1050
-
1051
- class MyUser2(User):
1052
- host = "http://example.com"
1053
-
1054
- self.environment.user_classes = [MyUser, MyUser2]
1055
- self.environment.available_user_classes = {"User1": MyUser, "User2": MyUser2}
1056
- self.environment.available_user_tasks = {"User1": MyUser.tasks, "User2": MyUser2.tasks}
1057
-
1058
- users = {"User1": MyUser.json(), "User2": MyUser2.json()}
1059
- available_user_tasks = {"User1": ["do_something"], "User2": []}
1060
-
1061
- self.web_ui.update_template_args()
1062
-
1063
- self.assertEqual(self.web_ui.template_args.get("users"), users)
1064
- self.assertEqual(
1065
- self.web_ui.template_args.get("available_user_classes"), sorted(self.environment.available_user_classes)
1066
- )
1067
- self.assertEqual(self.web_ui.template_args.get("available_user_tasks"), available_user_tasks)
1068
-
1069
- def test_update_user_endpoint(self):
1070
- class MyUser(User):
1071
- @task
1072
- def my_task(self):
1073
- pass
1074
-
1075
- @task
1076
- def my_task_2(self):
1077
- pass
1078
-
1079
- host = "http://example.com"
1080
-
1081
- class MyUser2(User):
1082
- host = "http://example.com"
1083
-
1084
- self.environment.user_classes = [MyUser, MyUser2]
1085
- self.environment.available_user_classes = {"User1": MyUser, "User2": MyUser2}
1086
- self.environment.available_user_tasks = {"User1": MyUser.tasks, "User2": MyUser2.tasks}
1087
-
1088
- requests.post(
1089
- "http://127.0.0.1:%i/user" % self.web_port,
1090
- json={"user_class_name": "User1", "host": "http://localhost", "tasks": ["my_task_2"]},
1091
- )
1092
-
1093
- self.assertEqual(
1094
- self.environment.available_user_classes["User1"].json(),
1095
- {"host": "http://localhost", "tasks": ["my_task_2"], "fixed_count": 0, "weight": 1},
1096
- )
1097
-
1098
-
1099
- class TestWebUIAuth(LocustTestCase):
1100
- def setUp(self):
1101
- super().setUp()
1102
-
1103
- parser = get_parser(default_config_files=[])
1104
- self.environment.parsed_options = parser.parse_args(["--web-login"])
1105
-
1106
- self.web_ui = self.environment.create_web_ui("127.0.0.1", 0, web_login=True)
1107
-
1108
- self.web_ui.app.secret_key = "secret!"
1109
- gevent.sleep(0.01)
1110
- self.web_port = self.web_ui.server.server_port
1111
-
1112
- def tearDown(self):
1113
- super().tearDown()
1114
- self.web_ui.stop()
1115
- self.runner.quit()
1116
-
1117
- def test_index_with_web_login_enabled_valid_user(self):
1118
- class User(UserMixin):
1119
- def __init__(self):
1120
- self.username = "test_user"
1121
-
1122
- def get_id(self):
1123
- return self.username
1124
-
1125
- def load_user(id):
1126
- return User()
1127
-
1128
- self.web_ui.login_manager.request_loader(load_user)
1129
-
1130
- response = requests.get("http://127.0.0.1:%i" % self.web_port)
1131
- d = pq(response.content.decode("utf-8"))
1132
-
1133
- self.assertNotIn("authArgs", str(d))
1134
- self.assertIn("templateArgs", str(d))
1135
-
1136
- def test_index_with_web_login_enabled_no_user(self):
1137
- def load_user():
1138
- return None
1139
-
1140
- self.web_ui.login_manager.user_loader(load_user)
1141
-
1142
- response = requests.get("http://127.0.0.1:%i" % self.web_port)
1143
- d = pq(response.content.decode("utf-8"))
1144
-
1145
- # asserts auth page is returned
1146
- self.assertIn("authArgs", str(d))
1147
-
1148
-
1149
- class TestWebUIWithTLS(LocustTestCase):
1150
- def setUp(self):
1151
- super().setUp()
1152
- tls_cert, tls_key = create_tls_cert("127.0.0.1")
1153
- self.tls_cert_file = NamedTemporaryFile(delete=False)
1154
- self.tls_key_file = NamedTemporaryFile(delete=False)
1155
- with open(self.tls_cert_file.name, "w") as f:
1156
- f.write(tls_cert.decode())
1157
- with open(self.tls_key_file.name, "w") as f:
1158
- f.write(tls_key.decode())
1159
-
1160
- parser = get_parser(default_config_files=[])
1161
- options = parser.parse_args(
1162
- [
1163
- "--tls-cert",
1164
- self.tls_cert_file.name,
1165
- "--tls-key",
1166
- self.tls_key_file.name,
1167
- ]
1168
- )
1169
- self.runner = Runner(self.environment)
1170
- self.stats = self.runner.stats
1171
- self.web_ui = self.environment.create_web_ui("127.0.0.1", 0, tls_cert=options.tls_cert, tls_key=options.tls_key)
1172
- gevent.sleep(0.01)
1173
- self.web_port = self.web_ui.server.server_port
1174
-
1175
- def tearDown(self):
1176
- super().tearDown()
1177
- self.web_ui.stop()
1178
- self.runner.quit()
1179
- os.unlink(self.tls_cert_file.name)
1180
- os.unlink(self.tls_key_file.name)
1181
-
1182
- def test_index_with_https(self):
1183
- # Suppress only the single warning from urllib3 needed.
1184
- from urllib3.exceptions import InsecureRequestWarning
1185
-
1186
- requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
1187
- self.assertEqual(200, requests.get("https://127.0.0.1:%i/" % self.web_port, verify=False).status_code)
1188
-
1189
-
1190
- class TestWebUIFullHistory(LocustTestCase, _HeaderCheckMixin):
1191
- STATS_BASE_DIR = "csv_output"
1192
- STATS_BASE_NAME = "web_test"
1193
- STATS_FILENAME = f"{STATS_BASE_NAME}_stats.csv"
1194
- STATS_HISTORY_FILENAME = f"{STATS_BASE_NAME}_stats_history.csv"
1195
- STATS_FAILURES_FILENAME = f"{STATS_BASE_NAME}_failures.csv"
1196
-
1197
- def setUp(self):
1198
- super().setUp()
1199
- self.remove_files_if_exists()
1200
-
1201
- parser = get_parser(default_config_files=[])
1202
- self.environment.parsed_options = parser.parse_args(
1203
- ["--csv", os.path.join(self.STATS_BASE_DIR, self.STATS_BASE_NAME), "--csv-full-history"]
1204
- )
1205
- self.stats = self.environment.stats
1206
- self.stats.CSV_STATS_INTERVAL_SEC = 0.02
1207
-
1208
- locust.stats.CSV_STATS_INTERVAL_SEC = 0.1
1209
- self.stats_csv_writer = StatsCSVFileWriter(
1210
- self.environment, stats.PERCENTILES_TO_REPORT, self.STATS_BASE_NAME, full_history=True
1211
- )
1212
- self.web_ui = self.environment.create_web_ui("127.0.0.1", 0, stats_csv_writer=self.stats_csv_writer)
1213
- self.web_ui.app.view_functions["request_stats"].clear_cache()
1214
- gevent.sleep(0.01)
1215
- self.web_port = self.web_ui.server.server_port
1216
-
1217
- def tearDown(self):
1218
- super().tearDown()
1219
- self.web_ui.stop()
1220
- self.runner.quit()
1221
- self.remove_files_if_exists()
1222
-
1223
- def remove_file_if_exists(self, filename):
1224
- if os.path.exists(filename):
1225
- os.remove(filename)
1226
-
1227
- def remove_files_if_exists(self):
1228
- self.remove_file_if_exists(self.STATS_FILENAME)
1229
- self.remove_file_if_exists(self.STATS_HISTORY_FILENAME)
1230
- self.remove_file_if_exists(self.STATS_FAILURES_FILENAME)
1231
- self.remove_file_if_exists(self.STATS_BASE_DIR)
1232
-
1233
- def test_request_stats_full_history_csv(self):
1234
- self.stats.log_request("GET", "/test", 1.39764125, 2)
1235
- self.stats.log_request("GET", "/test", 999.9764125, 1000)
1236
- self.stats.log_request("GET", "/test2", 120, 5612)
1237
-
1238
- greenlet = gevent.spawn(self.stats_csv_writer.stats_writer)
1239
- gevent.sleep(0.01)
1240
- self.stats_csv_writer.stats_history_flush()
1241
- gevent.kill(greenlet)
1242
-
1243
- response = requests.get("http://127.0.0.1:%i/stats/requests_full_history/csv" % self.web_port)
1244
- self.assertEqual(200, response.status_code)
1245
- self._check_csv_headers(response.headers, "requests_full_history")
1246
- self.assertIn("Content-Length", response.headers)
1247
-
1248
- reader = csv.reader(StringIO(response.text))
1249
- rows = [r for r in reader]
1250
-
1251
- self.assertEqual(4, len(rows))
1252
- self.assertEqual("Timestamp", rows[0][0])
1253
- self.assertEqual("GET", rows[1][2])
1254
- self.assertEqual("/test", rows[1][3])
1255
- self.assertEqual("/test2", rows[2][3])
1256
- self.assertEqual("", rows[3][2])
1257
- self.assertEqual("Aggregated", rows[3][3])