locust 2.18.4.dev6__py3-none-any.whl → 2.18.4.dev17__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.

Potentially problematic release.


This version of locust might be problematic. Click here for more details.

locust/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '2.18.4.dev6'
16
- __version_tuple__ = version_tuple = (2, 18, 4, 'dev6')
15
+ __version__ = version = '2.18.4.dev17'
16
+ __version_tuple__ = version_tuple = (2, 18, 4, 'dev17')
locust/runners.py CHANGED
@@ -333,6 +333,7 @@ class Runner:
333
333
  def shape_worker(self) -> None:
334
334
  logger.info("Shape worker starting")
335
335
  while self.state == STATE_INIT or self.state == STATE_SPAWNING or self.state == STATE_RUNNING:
336
+ shape_adjustment_start = time.time()
336
337
  current_tick = self.environment.shape_class.tick() if self.environment.shape_class is not None else None
337
338
  if current_tick is None:
338
339
  logger.info("Shape test stopping")
@@ -343,9 +344,7 @@ class Runner:
343
344
  self.shape_greenlet = None
344
345
  self.shape_last_tick = None
345
346
  return
346
- elif self.shape_last_tick == current_tick:
347
- gevent.sleep(1)
348
- else:
347
+ elif self.shape_last_tick != current_tick:
349
348
  if len(current_tick) == 2:
350
349
  user_count, spawn_rate = current_tick # type: ignore
351
350
  user_classes = None
@@ -367,6 +366,8 @@ class Runner:
367
366
  # of each load test shape stage.
368
367
  self.start(user_count=user_count, spawn_rate=spawn_rate, user_classes=user_classes)
369
368
  self.shape_last_tick = current_tick
369
+ shape_adjustment_time_ms = time.time() - shape_adjustment_start
370
+ gevent.sleep(max(1 - shape_adjustment_time_ms, 0))
370
371
 
371
372
  def stop(self) -> None:
372
373
  """
locust/stats.py CHANGED
@@ -390,8 +390,8 @@ class StatsEntry:
390
390
 
391
391
  if self.min_response_time is None:
392
392
  self.min_response_time = response_time
393
-
394
- self.min_response_time = min(self.min_response_time, response_time)
393
+ else:
394
+ self.min_response_time = min(self.min_response_time, response_time)
395
395
  self.max_response_time = max(self.max_response_time, response_time)
396
396
 
397
397
  # to avoid to much data that has to be transferred to the master node when
@@ -2707,6 +2707,67 @@ class TestMasterRunner(LocustRunnerTestCase):
2707
2707
  for i in range(USERS_COUNT):
2708
2708
  self.assertEqual(indexes[i], i, "Worker index mismatch")
2709
2709
 
2710
+ def test_custom_shape_scale_interval(self):
2711
+ class MyUser(User):
2712
+ @task
2713
+ def my_task(self):
2714
+ pass
2715
+
2716
+ class TestShape(LoadTestShape):
2717
+ def __init__(self):
2718
+ super().__init__()
2719
+ self._users_num = [1, 1, 1, 2, 2, 3, 3, 3, 4]
2720
+ self._index = 0
2721
+
2722
+ def tick(self):
2723
+ if self._index >= len(self._users_num):
2724
+ return None
2725
+ users_num = self._users_num[self._index]
2726
+ self._index += 1
2727
+ return users_num, users_num
2728
+
2729
+ self.environment.shape_class = TestShape()
2730
+
2731
+ with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
2732
+ master = self.get_runner(user_classes=[MyUser])
2733
+ for i in range(5):
2734
+ server.mocked_send(Message("client_ready", __version__, "fake_client%i" % i))
2735
+
2736
+ # Start the shape_worker
2737
+ self.environment.shape_class.reset_time()
2738
+ master.start_shape()
2739
+
2740
+ # Wait for shape_worker to update user_count
2741
+ sleep(0.5)
2742
+ num_users = sum(
2743
+ sum(msg.data["user_classes_count"].values()) for _, msg in server.outbox if msg.type != "ack"
2744
+ )
2745
+ self.assertEqual(
2746
+ 1, num_users, "Total number of users in first stage of shape test is not 1: %i" % num_users
2747
+ )
2748
+
2749
+ # Wait for shape_worker to update user_count again
2750
+ sleep(1.5)
2751
+ num_users = sum(
2752
+ sum(msg.data["user_classes_count"].values()) for _, msg in server.outbox if msg.type != "ack"
2753
+ )
2754
+ self.assertEqual(
2755
+ 1, num_users, "Total number of users in second stage of shape test is not 1: %i" % num_users
2756
+ )
2757
+
2758
+ # Wait for shape_worker to update user_count few times but not reach the end yet
2759
+ sleep(2.5)
2760
+ num_users = sum(
2761
+ sum(msg.data["user_classes_count"].values()) for _, msg in server.outbox if msg.type != "ack"
2762
+ )
2763
+ self.assertEqual(
2764
+ 3, num_users, "Total number of users in second stage of shape test is not 3: %i" % num_users
2765
+ )
2766
+
2767
+ # Wait to ensure shape_worker has stopped the test
2768
+ sleep(3)
2769
+ self.assertEqual("stopped", master.state, "The test has not been stopped by the shape class")
2770
+
2710
2771
  def test_custom_shape_scale_up(self):
2711
2772
  class MyUser(User):
2712
2773
  @task
locust/web.py CHANGED
@@ -367,6 +367,7 @@ class WebUI:
367
367
  "stats": stats,
368
368
  "errors": errors,
369
369
  "total_rps": 0.0,
370
+ "total_fail_per_sec": 0.0,
370
371
  "fail_ratio": 0.0,
371
372
  "current_response_time_percentile_1": None,
372
373
  "current_response_time_percentile_2": None,
@@ -395,9 +396,11 @@ class WebUI:
395
396
  truncated_stats += [stats[-1]]
396
397
 
397
398
  report = {"stats": truncated_stats, "errors": errors[:500]}
399
+ total_stats = stats[-1]
398
400
 
399
401
  if stats:
400
- report["total_rps"] = stats[len(stats) - 1]["current_rps"]
402
+ report["total_rps"] = total_stats["current_rps"]
403
+ report["total_fail_per_sec"] = total_stats["current_fail_per_sec"]
401
404
  report["fail_ratio"] = environment.runner.stats.total.fail_ratio
402
405
  report[
403
406
  "current_response_time_percentile_1"