Skip to content

Commit

Permalink
Merge pull request #2 from locustio/master
Browse files Browse the repository at this point in the history
sync from locustio
  • Loading branch information
taojy123 authored Aug 25, 2020
2 parents 70e6ef8 + b12aa3d commit c96ef69
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 8 deletions.
14 changes: 12 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## [1.2.2](https://github.com/locustio/locust/tree/1.2.2) (2020-08-22)

[Full Changelog](https://github.com/locustio/locust/compare/1.2.1...1.2.2)

**Merged pull requests:**

- Fix load shape worker in headless. [\#1539](https://github.com/locustio/locust/pull/1539) ([cyberw](https://github.com/cyberw))
- Add test case for stats\_history [\#1538](https://github.com/locustio/locust/pull/1538) ([taojy123](https://github.com/taojy123))
- Update README.md to have full links to images [\#1536](https://github.com/locustio/locust/pull/1536) ([max-rocket-internet](https://github.com/max-rocket-internet))

## [1.2.1](https://github.com/locustio/locust/tree/1.2.1) (2020-08-20)

[Full Changelog](https://github.com/locustio/locust/compare/1.2...1.2.1)
Expand Down Expand Up @@ -1075,6 +1085,7 @@
- Suggest Python version [\#231](https://github.com/locustio/locust/issues/231)
- Changing locustfile.py on master via UI and having master / slave replication [\#209](https://github.com/locustio/locust/issues/209)
- Option to prevent stats from being reset when all locusts are hatched [\#205](https://github.com/locustio/locust/issues/205)
- PUT requests are shown as GET [\#204](https://github.com/locustio/locust/issues/204)
- Cannot simulate one single user [\#178](https://github.com/locustio/locust/issues/178)
- Feature request: Stepped hatch rate [\#168](https://github.com/locustio/locust/issues/168)
- Having a locust "die" or stop after one task [\#161](https://github.com/locustio/locust/issues/161)
Expand Down Expand Up @@ -1302,7 +1313,6 @@
- multiple slaves of different server specs ? [\#210](https://github.com/locustio/locust/issues/210)
- multiple url tests ? [\#208](https://github.com/locustio/locust/issues/208)
- how to get the recorded data [\#206](https://github.com/locustio/locust/issues/206)
- PUT requests are shown as GET [\#204](https://github.com/locustio/locust/issues/204)
- http proxy support [\#203](https://github.com/locustio/locust/issues/203)
- error report is not included in `--logfile` nor is it available for download as a csv [\#202](https://github.com/locustio/locust/issues/202)
- Stats get corrupted when the number of swarm users reaches the objective [\#201](https://github.com/locustio/locust/issues/201)
Expand Down Expand Up @@ -1387,6 +1397,7 @@
- Fixed Docs Homebrew Link [\#143](https://github.com/locustio/locust/pull/143) ([saulshanabrook](https://github.com/saulshanabrook))
- Fix typo [\#132](https://github.com/locustio/locust/pull/132) ([rafax](https://github.com/rafax))
- Fix task ratio [\#125](https://github.com/locustio/locust/pull/125) ([sanga](https://github.com/sanga))
- fix typo in downloadable CSV [\#100](https://github.com/locustio/locust/pull/100) ([sghill](https://github.com/sghill))

## [v0.7](https://github.com/locustio/locust/tree/v0.7) (2014-01-20)

Expand Down Expand Up @@ -1439,7 +1450,6 @@
- fix module and variable name clash \(traceback refers to a mod so it's a ... [\#115](https://github.com/locustio/locust/pull/115) ([sanga](https://github.com/sanga))
- Removes duplicate attribute documentation [\#106](https://github.com/locustio/locust/pull/106) ([djoume](https://github.com/djoume))
- Fixes typo in example code [\#105](https://github.com/locustio/locust/pull/105) ([djoume](https://github.com/djoume))
- fix typo in downloadable CSV [\#100](https://github.com/locustio/locust/pull/100) ([sghill](https://github.com/sghill))
- Documented more 0.7 changes [\#90](https://github.com/locustio/locust/pull/90) ([EnTeQuAk](https://github.com/EnTeQuAk))
- include hostname in log messages [\#89](https://github.com/locustio/locust/pull/89) ([sanga](https://github.com/sanga))
- Cleanups \(deprecated code, unused imports\) [\#88](https://github.com/locustio/locust/pull/88) ([EnTeQuAk](https://github.com/EnTeQuAk))
Expand Down
5 changes: 5 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ Changelog Highlights

For full details of the Locust changelog, please see https://github.com/locustio/locust/blob/master/CHANGELOG.md

1.2.2
=====

* Bug fix (LoadTestShape in headless mode https://github.com/locustio/locust/pull/1539)

1.2.1
=====

Expand Down
2 changes: 1 addition & 1 deletion locust/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from .event import Events
events = Events()

__version__ = "1.2.1"
__version__ = "1.2.2"
__all__ = (
"SequentialTaskSet",
"wait_time",
Expand Down
2 changes: 2 additions & 0 deletions locust/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,8 @@ def timelimit_stop():
# start the test
if options.step_time:
runner.start_stepload(options.num_users, options.spawn_rate, options.step_users, options.step_time)
if environment.shape_class:
environment.runner.start_shape()
else:
runner.start(options.num_users, options.spawn_rate)

Expand Down
6 changes: 5 additions & 1 deletion locust/runners.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ def stop_users(self, user_count, stop_rate=None):

if stop_rate == None or stop_rate >= user_count:
sleep_time = 0
logger.info("Stopping %i users immediately" % (user_count))
logger.info("Stopping %i users" % (user_count))
else:
sleep_time = 1.0 / stop_rate
logger.info("Stopping %i users at rate of %g users/s" % (user_count, stop_rate))
Expand Down Expand Up @@ -525,6 +525,10 @@ def start(self, user_count, spawn_rate):
def stop(self):
if self.state not in [STATE_INIT, STATE_STOPPED, STATE_STOPPING]:
self.state = STATE_STOPPING

if self.environment.shape_class:
self.shape_last_state = None

for client in self.clients.all:
self.server.send_to_client(Message("stop", None, client.id))
self.environment.events.test_stop.fire(environment=self.environment)
Expand Down
2 changes: 0 additions & 2 deletions locust/test/mock_locustfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ class UserSubclass(HttpUser):
class NotUserSubclass():
host = "http://localhost:8000"
class LoadTestShape(LoadTestShape):
pass
'''

class MockedLocustfile:
Expand Down
40 changes: 38 additions & 2 deletions locust/test/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from locust.argument_parser import parse_options
from locust.main import create_environment
from locust.user import HttpUser, User, TaskSet
from .mock_locustfile import mock_locustfile
from .mock_locustfile import mock_locustfile, MOCK_LOUCSTFILE_CONTENT
from .testcases import LocustTestCase
from .util import temporary_file, get_free_tcp_port

Expand Down Expand Up @@ -48,6 +48,7 @@ def test_load_locust_file_from_absolute_path(self):
self.assertIn('UserSubclass', user_classes)
self.assertNotIn('NotUserSubclass', user_classes)
self.assertNotIn('LoadTestShape', user_classes)
self.assertIsNone(shape_class)

def test_load_locust_file_from_relative_path(self):
with mock_locustfile() as mocked:
Expand All @@ -64,7 +65,18 @@ def test_return_docstring_and_user_classes(self):
self.assertIn('UserSubclass', user_classes)
self.assertNotIn('NotUserSubclass', user_classes)
self.assertNotIn('LoadTestShape', user_classes)


def test_with_shape_class(self):
content = MOCK_LOUCSTFILE_CONTENT + '''class LoadTestShape(LoadTestShape):
pass
'''
with mock_locustfile(content=content) as mocked:
docstring, user_classes, shape_class = main.load_locustfile(mocked.file_path)
self.assertEqual("This is a mock locust file for unit testing", docstring)
self.assertIn('UserSubclass', user_classes)
self.assertNotIn('NotUserSubclass', user_classes)
self.assertEqual(shape_class.__class__.__name__, 'LoadTestShape')

def test_create_environment(self):
options = parse_options(args=[
"--host", "https://custom-host",
Expand Down Expand Up @@ -190,6 +202,30 @@ def test_default_headless_spawn_options(self):
).decode("utf-8").strip()
self.assertIn("Spawning 1 users at the rate 1 users/s", output)

def test_default_headless_spawn_options_with_shape(self):
content = MOCK_LOUCSTFILE_CONTENT + '''
class LoadTestShape(LoadTestShape):
def tick(self):
run_time = self.get_run_time()
if run_time < 2:
return (10, 1)
return None
'''
with mock_locustfile(content=content) as mocked:
output = subprocess.check_output(
["locust",
"-f", mocked.file_path,
"--host", "https://test.com/",
"--run-time", "1s",
"--headless"],
stderr=subprocess.STDOUT,
timeout=3,
).decode("utf-8").strip()
self.assertIn("Shape test updating to 10 users at 1.00 spawn rate", output)
self.assertIn("Cleaning up runner...", output)


def test_web_options(self):
port = get_free_tcp_port()
if platform.system() == "Darwin":
Expand Down
51 changes: 51 additions & 0 deletions locust/test/test_runners.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,57 @@ def tick(self):
for worker in workers:
self.assertEqual(0, worker.user_count, "Shape test has not stopped")

def test_distributed_shape_stop_and_restart(self):
"""
Test stopping and then restarting a LoadTestShape
"""
class TestUser(User):
wait_time = constant(0)
@task
def my_task(self):
pass

class TestShape(LoadTestShape):
def tick(self):
run_time = self.get_run_time()
if run_time < 10:
return (4, 4)
else:
return None

with mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=0.3):
master_env = Environment(user_classes=[TestUser], shape_class=TestShape())
master_env.shape_class.reset_time()
master = master_env.create_master_runner("*", 0)

workers = []
for i in range(2):
worker_env = Environment(user_classes=[TestUser])
worker = worker_env.create_worker_runner("127.0.0.1", master.server.port)
workers.append(worker)

# Give workers time to connect
sleep(0.1)

# Start a shape test and ensure workers have connected and started the correct amounf of users
master.start_shape()
sleep(1)
for worker in workers:
self.assertEqual(2, worker.user_count, "Shape test has not started correctly")

# Stop the test and ensure all user count is 0
master.stop()
sleep(1)
for worker in workers:
self.assertEqual(0, worker.user_count, "Shape test has not stopped")

# Then restart the test again and ensure workers have connected and started the correct amounf of users
master.start_shape()
sleep(1)
for worker in workers:
self.assertEqual(2, worker.user_count, "Shape test has not started again correctly")
master.stop()


class TestMasterRunner(LocustTestCase):
def setUp(self):
Expand Down

0 comments on commit c96ef69

Please sign in to comment.