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.
- locust/_version.py +6 -2
- locust/contrib/fasthttp.py +1 -1
- locust/dispatch.py +7 -6
- {locust-2.29.2.dev34.dist-info → locust-2.29.2.dev42.dist-info}/METADATA +31 -26
- {locust-2.29.2.dev34.dist-info → locust-2.29.2.dev42.dist-info}/RECORD +16 -46
- locust-2.29.2.dev42.dist-info/WHEEL +4 -0
- locust-2.29.2.dev42.dist-info/entry_points.txt +3 -0
- locust/test/__init__.py +0 -15
- locust/test/fake_module1_for_env_test.py +0 -7
- locust/test/fake_module2_for_env_test.py +0 -7
- locust/test/mock_locustfile.py +0 -56
- locust/test/mock_logging.py +0 -28
- locust/test/test_debugging.py +0 -39
- locust/test/test_dispatch.py +0 -4170
- locust/test/test_env.py +0 -283
- locust/test/test_fasthttp.py +0 -785
- locust/test/test_http.py +0 -325
- locust/test/test_interruptable_task.py +0 -48
- locust/test/test_load_locustfile.py +0 -228
- locust/test/test_locust_class.py +0 -831
- locust/test/test_log.py +0 -237
- locust/test/test_main.py +0 -2264
- locust/test/test_old_wait_api.py +0 -0
- locust/test/test_parser.py +0 -450
- locust/test/test_runners.py +0 -4476
- locust/test/test_sequential_taskset.py +0 -157
- locust/test/test_stats.py +0 -866
- locust/test/test_tags.py +0 -440
- locust/test/test_taskratio.py +0 -94
- locust/test/test_users.py +0 -69
- locust/test/test_util.py +0 -33
- locust/test/test_wait_time.py +0 -79
- locust/test/test_web.py +0 -1257
- locust/test/test_zmqrpc.py +0 -58
- locust/test/testcases.py +0 -248
- locust/test/util.py +0 -88
- locust-2.29.2.dev34.dist-info/WHEEL +0 -5
- locust-2.29.2.dev34.dist-info/entry_points.txt +0 -2
- locust-2.29.2.dev34.dist-info/top_level.txt +0 -1
- {locust-2.29.2.dev34.dist-info → locust-2.29.2.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("/<html>", 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("Error with special characters{'foo':'bar'}")"', 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])
|