def test_spawn_zero_locusts(self): class MyTaskSet(TaskSet): @task def my_task(self): pass class MyTestLocust(Locust): tasks = [MyTaskSet] wait_time = constant(0.1) environment = Environment(options=mocked_options()) runner = LocalLocustRunner(environment, [MyTestLocust]) timeout = gevent.Timeout(2.0) timeout.start() try: runner.start(0, 1, wait=True) runner.hatching_greenlet.join() except gevent.Timeout: self.fail( "Got Timeout exception. A locust seems to have been spawned, even though 0 was specified." ) finally: timeout.cancel()
def test_spawn_zero_locusts(self): class MyTaskSet(TaskSet): @task def my_task(self): pass class MyTestLocust(Locust): task_set = MyTaskSet min_wait = 100 max_wait = 100 runner = LocalLocustRunner([MyTestLocust], self.options) timeout = gevent.Timeout(2.0) timeout.start() try: runner.start_hatching(0, 1, wait=True) runner.greenlet.join() except gevent.Timeout: self.fail( "Got Timeout exception. A locust seems to have been spawned, even though 0 was specified." ) finally: timeout.cancel()
def test_taskset_setup_method_exception(self): class User(Locust): setup_run_count = 0 task_run_count = 0 locust_error_count = 0 wait_time = constant(1) @task class task_set(TaskSet): def setup(self): User.setup_run_count += 1 raise Exception("some exception") @task def my_task(self): User.task_run_count += 1 environment = Environment(options=mocked_options()) def on_locust_error(*args, **kwargs): User.locust_error_count += 1 environment.events.locust_error.add_listener(on_locust_error) runner = LocalLocustRunner(environment, locust_classes=[User]) runner.start(locust_count=3, hatch_rate=3, wait=False) runner.hatching_greenlet.get(timeout=3) self.assertEqual(1, User.setup_run_count) self.assertEqual(1, User.locust_error_count) self.assertEqual(3, User.task_run_count)
def test_taskset_setup_method_exception(self): class User(Locust): setup_run_count = 0 task_run_count = 0 locust_error_count = 0 wait_time = constant(1) class task_set(TaskSet): def setup(self): User.setup_run_count += 1 raise Exception("some exception") @task def my_task(self): User.task_run_count += 1 def on_locust_error(*args, **kwargs): User.locust_error_count += 1 events.locust_error += on_locust_error runner = LocalLocustRunner([User], mocked_options()) runner.start_hatching(locust_count=3, hatch_rate=3, wait=False) runner.hatching_greenlet.get(timeout=3) self.assertEqual(1, User.setup_run_count) self.assertEqual(1, User.locust_error_count) self.assertEqual(3, User.task_run_count)
def test_exception_is_catched(self): """ Test that exceptions are stored, and execution continues """ class HeyAnException(Exception): pass class MyTaskSet(TaskSet): def __init__(self, *a, **kw): super(MyTaskSet, self).__init__(*a, **kw) self._task_queue = [ { "callable": self.will_error, "args": [], "kwargs": {} }, { "callable": self.will_stop, "args": [], "kwargs": {} }, ] @task(1) def will_error(self): raise HeyAnException(":(") @task(1) def will_stop(self): self.interrupt() class MyLocust(Locust): min_wait = 10 max_wait = 10 task_set = MyTaskSet runner = LocalLocustRunner([MyLocust], self.options) l = MyLocust() # supress stderr with mock.patch("sys.stderr") as mocked: l.task_set._task_queue = [ l.task_set.will_error, l.task_set.will_stop ] self.assertRaises(LocustError, l.run) # make sure HeyAnException isn't raised l.task_set._task_queue = [ l.task_set.will_error, l.task_set.will_stop ] self.assertRaises(LocustError, l.run) # make sure HeyAnException isn't raised self.assertEqual(2, len(mocked.method_calls)) # make sure exception was stored self.assertEqual(1, len(runner.exceptions)) hash_key, exception = runner.exceptions.popitem() self.assertTrue("traceback" in exception) self.assertTrue("HeyAnException" in exception["traceback"]) self.assertEqual(2, exception["count"])
def test_start_event(self): class User(Locust): wait_time = constant(1) task_run_count = 0 @task def my_task(self): User.task_run_count += 1 test_start_run = [0] environment = Environment(options=mocked_options()) def on_test_start(*args, **kwargs): test_start_run[0] += 1 environment.events.test_start.add_listener(on_test_start) runner = LocalLocustRunner(environment, locust_classes=[User]) runner.start(locust_count=3, hatch_rate=3, wait=False) runner.hatching_greenlet.get(timeout=3) self.assertEqual(1, test_start_run[0]) self.assertEqual(3, User.task_run_count)
def test_stop_timeout_with_interrupt_no_reschedule(self): state = [0] class MySubTaskSet(TaskSet): @task def a_task(self): gevent.sleep(0.1) state[0] = 1 self.interrupt(reschedule=False) class MyTestLocust(Locust): tasks = [MySubTaskSet] wait_time = constant(3) environment = create_environment(mocked_options()) environment.stop_timeout = 0.3 runner = LocalLocustRunner(environment, [MyTestLocust]) runner.start(1, 1, wait=True) gevent.sleep(0) timeout = gevent.Timeout(0.11) timeout.start() try: runner.quit() runner.greenlet.join() except gevent.Timeout: self.fail( "Got Timeout exception. Interrupted locusts should exit immediately during stop_timeout." ) finally: timeout.cancel() self.assertEqual(1, state[0])
def test_stop_timeout_during_on_start(self): short_time = 0.05 class MyTaskSet(TaskSet): finished_on_start = False my_task_run = False def on_start(self): gevent.sleep(short_time) MyTaskSet.finished_on_start = True @task def my_task(self): MyTaskSet.my_task_run = True class MyTestLocust(Locust): tasks = [MyTaskSet] wait_time = constant(0) environment = create_environment(mocked_options()) environment.stop_timeout = short_time runner = LocalLocustRunner(environment, [MyTestLocust]) runner.start(1, 1) gevent.sleep(short_time / 2) runner.quit() self.assertTrue(MyTaskSet.finished_on_start) self.assertFalse(MyTaskSet.my_task_run)
def test_user_count_in_csv_history_stats(self): start_time = int(time.time()) class TestUser(Locust): wait_time = constant(10) @task def t(self): self.environment.runner.stats.log_request("GET", "/", 10, 10) runner = LocalLocustRunner(self.environment, [TestUser]) runner.start(3, 5) # spawn a user every 0.2 second gevent.sleep(0.1) greenlet = gevent.spawn(stats_writer, self.environment, self.STATS_BASE_NAME, full_history=True) gevent.sleep(0.6) gevent.kill(greenlet) runner.stop() with open(self.STATS_HISTORY_FILENAME) as f: reader = csv.DictReader(f) rows = [r for r in reader] self.assertEqual(6, len(rows)) for i in range(3): row = rows.pop(0) self.assertEqual("%i" % (i + 1), row["User count"]) self.assertEqual("/", row["Name"]) self.assertEqual("%i" % (i + 1), row["# requests"]) self.assertGreaterEqual(int(row["Timestamp"]), start_time) row = rows.pop(0) self.assertEqual("%i" % (i + 1), row["User count"]) self.assertEqual("Aggregated", row["Name"]) self.assertEqual("%i" % (i + 1), row["# requests"]) self.assertGreaterEqual(int(row["Timestamp"]), start_time)
def test_no_reset_stats(self): class User(Locust): wait_time = constant(0) @task class task_set(TaskSet): @task def my_task(self): self.locust.environment.events.request_success.fire( request_type="GET", name="/test", response_time=666, response_length=1337, ) sleep(2) environment = Environment(reset_stats=False, options=mocked_options()) runner = LocalLocustRunner(environment, locust_classes=[User]) runner.start(locust_count=6, hatch_rate=12, wait=False) sleep(0.25) self.assertGreaterEqual( runner.stats.get("/test", "GET").num_requests, 3) sleep(0.3) self.assertEqual(6, runner.stats.get("/test", "GET").num_requests) runner.quit()
def test_stop_timeout_during_on_start(self): short_time = 0.05 class MyTaskSet(TaskSet): finished_on_start = False my_task_run = False def on_start(self): gevent.sleep(short_time) MyTaskSet.finished_on_start = True @task def my_task(self): MyTaskSet.my_task_run = True class MyTestLocust(Locust): task_set = MyTaskSet min_wait = 0 max_wait = 0 options = mocked_options() options.stop_timeout = short_time runner = LocalLocustRunner([MyTestLocust], options) runner.start_hatching(1, 1) gevent.sleep(short_time / 2) runner.quit() self.assertTrue(MyTaskSet.finished_on_start) self.assertFalse(MyTaskSet.my_task_run)
def test_stop_timeout_exit_during_wait(self): short_time = 0.05 class MyTaskSet(TaskSet): @task def my_task(self): pass class MyTestLocust(Locust): task_set = MyTaskSet wait_time = between(1, 1) options = mocked_options() options.stop_timeout = short_time runner = LocalLocustRunner([MyTestLocust], options) runner.start_hatching(1, 1) gevent.sleep(short_time) # sleep to make sure locust has had time to start waiting timeout = gevent.Timeout(short_time) timeout.start() try: runner.quit() runner.greenlet.join() except gevent.Timeout: self.fail("Got Timeout exception. Waiting locusts should stop immediately, even when using stop_timeout.") finally: timeout.cancel()
def test_stop_timeout_with_interrupt(self): short_time = 0.05 class MySubTaskSet(TaskSet): @task def a_task(self): gevent.sleep(0) self.interrupt(reschedule=True) class MyTaskSet(TaskSet): tasks = [MySubTaskSet] class MyTestLocust(Locust): task_set = MyTaskSet options = mocked_options() options.stop_timeout = short_time runner = LocalLocustRunner([MyTestLocust], options) runner.start_hatching(1, 1) gevent.sleep(0) timeout = gevent.Timeout(short_time) timeout.start() try: runner.quit() runner.greenlet.join() except gevent.Timeout: self.fail("Got Timeout exception. Interrupted locusts should exit immediately during stop_timeout.") finally: timeout.cancel()
def test_change_user_count_during_hatching(self): class User(Locust): wait_time = constant(1) @task def my_task(self): pass environment = Environment(options=mocked_options()) runner = LocalLocustRunner(environment, [User]) runner.start(locust_count=10, hatch_rate=5, wait=False) sleep(0.6) runner.start(locust_count=5, hatch_rate=5, wait=False) runner.hatching_greenlet.join() self.assertEqual(5, len(runner.locusts)) runner.quit()
def test_change_user_count_during_hatching(self): class User(Locust): wait_time = constant(1) class task_set(TaskSet): @task def my_task(self): pass runner = LocalLocustRunner([User], mocked_options()) runner.start_hatching(locust_count=10, hatch_rate=5, wait=False) sleep(0.6) runner.start_hatching(locust_count=5, hatch_rate=5, wait=False) runner.hatching_greenlet.join() self.assertEqual(5, len(runner.locusts)) runner.quit()
def test_exception_is_catched(self): """ Test that exceptions are stored, and execution continues """ class HeyAnException(Exception): pass class MyTaskSet(TaskSet): def __init__(self, *a, **kw): super(MyTaskSet, self).__init__(*a, **kw) self._task_queue = [ { "callable": self.will_error, "args": [], "kwargs": {} }, { "callable": self.will_stop, "args": [], "kwargs": {} }, ] @task(1) def will_error(self): raise HeyAnException(":(") @task(1) def will_stop(self): self.interrupt() class MyLocust(Locust): wait_time = constant(0.01) task_set = MyTaskSet runner = LocalLocustRunner(self.environment, [MyLocust]) l = MyLocust(self.environment) l.task_set._task_queue = [l.task_set.will_error, l.task_set.will_stop] self.assertRaises(LocustError, l.run) # make sure HeyAnException isn't raised l.task_set._task_queue = [l.task_set.will_error, l.task_set.will_stop] self.assertRaises(LocustError, l.run) # make sure HeyAnException isn't raised # make sure we got two entries in the error log self.assertEqual(2, len(self.mocked_log.error)) # make sure exception was stored self.assertEqual(1, len(runner.exceptions)) hash_key, exception = runner.exceptions.popitem() self.assertTrue("traceback" in exception) self.assertTrue("HeyAnException" in exception["traceback"]) self.assertEqual(2, exception["count"])
def test_spawn_zero_locusts(self): class MyTaskSet(TaskSet): @task def my_task(self): pass class MyTestLocust(Locust): task_set = MyTaskSet min_wait = 100 max_wait = 100 runner = LocalLocustRunner([MyTestLocust], self.options) timeout = gevent.Timeout(2.0) timeout.start() try: runner.start_hatching(0, 1, wait=True) runner.greenlet.join() except gevent.Timeout: self.fail("Got Timeout exception. A locust seems to have been spawned, even though 0 was specified.") finally: timeout.cancel()
def _run_locust(locust_classes, master): config_options(master=master) print('*** Starting locust: {}:{} ***'.format(options.web_host, options.port)) if options.master: runners.locust_runner = MasterLocustRunner(locust_classes, options) else: runners.locust_runner = LocalLocustRunner(locust_classes, options) logging.info("Starting web monitor at http://%s:%s" % (options.web_host or "0.0.0.0", options.port)) main_greenlet = gevent.spawn(locust_web.start, locust_classes, options) stats_printer_greenlet = None def shutdown(code=0): """ Shut down locust by firing quitting event, printing/writing stats and exiting """ logging.info("Shutting down (exit code %s), bye." % code) if stats_printer_greenlet is not None: stats_printer_greenlet.kill(block=False) logging.info("Cleaning up runner...") if runners.locust_runner is not None: runners.locust_runner.quit() logging.info("Running teardowns...") events.quitting.fire(reverse=True) print_stats(runners.locust_runner.stats, current=False) print_percentile_stats(runners.locust_runner.stats) if options.csvfilebase: write_stat_csvs(options.csvfilebase, options.stats_history_enabled) print_error_report() sys.exit(code) # install SIGTERM handler def sig_term_handler(): logging.info("Got SIGTERM signal") shutdown(0) gevent.signal_handler(signal.SIGTERM, sig_term_handler) try: logging.info("Starting Locust...") main_greenlet.join() code = 0 lr = runners.locust_runner if len(lr.errors) or len(lr.exceptions) or lr.cpu_log_warning(): code = options.exit_code_on_error shutdown(code=code) except KeyboardInterrupt: shutdown(0)
def bootstrap(test_id): test_runner = get_runner(test_id) if test_runner is None: parser, options, arguments = parse_options() tests_in_locust_folder = find_all_test_in_folder( '/Users/jaumepinyol/Documents/locust/locust/tests') for test_in_folder in tests_in_locust_folder: docstring, locusts = tests_in_locust_folder[test_in_folder][ "locust"] if arguments: missing = set(arguments) - set(locusts.keys()) if missing: logger.error("Unknown Locust(s): %s\n" % (", ".join(missing))) sys.exit(1) else: names = set(arguments) & set(locusts.keys()) tests_in_locust_folder[test_in_folder]["locust"] = [ locusts[n] for n in names ] else: tests_in_locust_folder[test_in_folder][ "locust"] = locusts.values() runners.locust_runner = LocalLocustRunner(tests_in_locust_folder, options) test_runner = get_runner(test_id) changed = False if test_runner.selected_locust != test_id: changed = test_runner.set_selected_locust(test_id) is_distributed = isinstance(test_runner, MasterLocustRunner) if is_distributed: slave_count = test_runner.slave_count else: slave_count = 0 files = runners.locust_runner.files return render_template("test.html", state=test_runner.state, is_distributed=is_distributed, slave_count=slave_count, user_count=test_runner.user_count, version=version, files=files, selected=test_runner.selected_locust, changed=changed)
def start_pressure_test(test_data): global locust_runner_id, host locust_runner_id = test_data["test_id"] host = test_data["host"] locust_count = test_data["locust_count"] hatch_rate = test_data["hatch_rate"] arser, options, arguments = parse_options() WebsiteUser = decorator(test_data=test_data) runners.locust_runner = LocalLocustRunner([WebsiteUser], options) runners.locust_runner.start_hatching(locust_count=locust_count, hatch_rate=hatch_rate) update_running_status(DJANGO_GET_UPDATE_RUNNING_STATUS_URL, locust_runner_id) gevent.spawn_later(test_data["run_time"], time_limit_stop, locust_runner_id)
def test_stop_event_stop_and_quit(self): class User(Locust): wait_time = constant(1) @task def my_task(self): pass test_stop_run = [0] environment = Environment(options=mocked_options()) def on_test_stop(*args, **kwargs): test_stop_run[0] += 1 environment.events.test_stop.add_listener(on_test_stop) runner = LocalLocustRunner(environment, locust_classes=[User]) runner.start(locust_count=3, hatch_rate=3, wait=False) self.assertEqual(0, test_stop_run[0]) runner.stop() runner.quit() self.assertEqual(1, test_stop_run[0])
def test_exception_in_task(self): class MyLocust(Locust): @task def will_error(self): raise HeyAnException(":(") runner = LocalLocustRunner(self.environment, [MyLocust]) l = MyLocust(self.environment) self.assertRaises(HeyAnException, l.run) self.assertRaises(HeyAnException, l.run) self.assertEqual(1, len(runner.exceptions)) hash_key, exception = runner.exceptions.popitem() self.assertTrue("traceback" in exception) self.assertTrue("HeyAnException" in exception["traceback"]) self.assertEqual(2, exception["count"])
def launch(classname, base_url, n_clients, rate, n_requests=None, timeout=600): """ Launches the tests :param: classname: class inherited from HttpLocust defining the test :param: base_url: server and port of the url to request :param: n_clients: Number of concurrent users :param: rate: The rate per second in which clients are spawned :param: n_requests: Total number of requests to be done :param: timeout: Stop testing after the specified amount of seconds """ options = Namespace(**{ 'host': base_url, 'num_clients': n_clients, 'hatch_rate': rate, 'num_requests': n_requests if n_requests else n_clients * 10, 'run_time': timeout, 'no_web': True, 'no_reset_stats': True, 'csvfilebase': os.path.join(get_or_create_output_dir(), get_test_calling()) }) setup_logging('INFO', None) runners.locust_runner = LocalLocustRunner([classname], options) # spawn client spawning/hatching greenlets: runners.locust_runner.start_hatching(wait=True) main_greenlet = runners.locust_runner.greenlet spawn_run_time_limit_greenlet(options) if options.csvfilebase: gevent.spawn(stats_writer, options.csvfilebase) try: main_greenlet.join() code = 0 if len(runners.locust_runner.errors): code = 1 shutdown(options, code=code) except KeyboardInterrupt: shutdown(options, 0)
def test_exception_in_task(self): class HeyAnException(Exception): pass class MyLocust(Locust): class task_set(TaskSet): @task def will_error(self): raise HeyAnException(":(") runner = LocalLocustRunner([MyLocust], self.options) l = MyLocust() l._catch_exceptions = False self.assertRaises(HeyAnException, l.run) self.assertRaises(HeyAnException, l.run) self.assertEqual(1, len(runner.exceptions)) hash_key, exception = runner.exceptions.popitem() self.assertTrue("traceback" in exception) self.assertTrue("HeyAnException" in exception["traceback"]) self.assertEqual(2, exception["count"])
def test_cpu_warning(self): _monitor_interval = runners.CPU_MONITOR_INTERVAL runners.CPU_MONITOR_INTERVAL = 2.0 try: class CpuLocust(Locust): wait_time = constant(0) class task_set(TaskSet): @task def cpu_task(self): for i in range(1000000): _ = 3 / 2 runner = LocalLocustRunner([CpuLocust], mocked_options()) self.assertFalse(runner.cpu_warning_emitted) runner.spawn_locusts(1, wait=False) sleep(2.5) runner.quit() self.assertTrue(runner.cpu_warning_emitted) finally: runners.CPU_MONITOR_INTERVAL = _monitor_interval
def test_cpu_warning(self): _monitor_interval = runners.CPU_MONITOR_INTERVAL runners.CPU_MONITOR_INTERVAL = 2.0 try: class CpuLocust(Locust): wait_time = constant(0.001) @task def cpu_task(self): for i in range(1000000): _ = 3 / 2 environment = Environment( options=mocked_options(), ) runner = LocalLocustRunner(environment, [CpuLocust]) self.assertFalse(runner.cpu_warning_emitted) runner.spawn_locusts(1, 1, wait=False) sleep(2.5) runner.quit() self.assertTrue(runner.cpu_warning_emitted) finally: runners.CPU_MONITOR_INTERVAL = _monitor_interval
wait_time = between(1, 3) host = "https://docs.locust.io" class task_set(TaskSet): @task def my_task(self): self.client.get("/") @task def task_404(self): self.client.get("/non-existing-path") # setup Environment and Runner env = Environment() runner = LocalLocustRunner(environment=env, locust_classes=[User]) # start a WebUI instance web_ui = WebUI(environment=env) gevent.spawn(lambda: web_ui.start("127.0.0.1", 8089)) # TODO: fix #def on_request_success(request_type, name, response_time, response_length, **kwargs): # report_to_grafana("%_%s" % (request_type, name), response_time) #env.events.request_succes.add_listener(on_request_success) # start a greenlet that periodically outputs the current stats gevent.spawn(stats_printer(runner.stats)) # start the test runner.start(1, hatch_rate=10) # wait for the greenlets (indefinitely)
def test_kill_locusts_with_stop_timeout(self): short_time = 0.05 class MyTaskSet(TaskSet): @task def my_task(self): MyTaskSet.state = "first" gevent.sleep(short_time) MyTaskSet.state = "second" # should only run when run time + stop_timeout is > short_time gevent.sleep(short_time) MyTaskSet.state = "third" # should only run when run time + stop_timeout is > short_time * 2 class MyTestLocust(Locust): task_set = MyTaskSet wait_time = constant(0) options = mocked_options() runner = LocalLocustRunner([MyTestLocust], options) runner.start_hatching(1, 1) gevent.sleep(short_time / 2) runner.kill_locusts(1) self.assertEqual("first", MyTaskSet.state) runner.quit() options.stop_timeout = short_time / 2 # exit with timeout runner = LocalLocustRunner([MyTestLocust], options) runner.start_hatching(1, 1) gevent.sleep(short_time) runner.kill_locusts(1) self.assertEqual("second", MyTaskSet.state) runner.quit() options.stop_timeout = short_time * 3 # allow task iteration to complete, with some margin runner = LocalLocustRunner([MyTestLocust], options) runner.start_hatching(1, 1) gevent.sleep(short_time) timeout = gevent.Timeout(short_time * 2) timeout.start() try: runner.kill_locusts(1) runner.locusts.join() except gevent.Timeout: self.fail("Got Timeout exception. Some locusts must have kept runnining after iteration finish") finally: timeout.cancel() self.assertEqual("third", MyTaskSet.state)
def main(): parser, options = parse_options() # setup logging if not options.skip_log_setup: setup_logging(options.loglevel, options.logfile) logger = logging.getLogger(__name__) locust_path = get_locust_path() if options.web: web_port = options.web_port logger.info('Running easy-locust web: 0.0.0.0:{}'.format(web_port)) init_app(port=web_port) sys.exit(0) if options.demo: if not locust_path: logger.error( '''Cannot locate Python path, make sure it is in right place. If windows add it to sys PATH, if linux make sure python is installed in /usr/local/lib/''') sys.exit(1) pt_demo_path = os.path.join(locust_path, 'demo', 'demo_pressuretest.xls') pt_demo_path_json = os.path.join(locust_path, 'demo', 'demo_locustfile.json') pt_new_demo = os.path.join(os.getcwd(), 'PtDemo.xls') pt_new_demo_json = os.path.join(os.getcwd(), 'demo.json') shutil.copyfile(pt_demo_path, pt_new_demo) shutil.copyfile(pt_demo_path_json, pt_new_demo_json) sys.exit(0) if options.xlsfile: pt_file = options.xlsfile if not (pt_file.endswith('.xls') or pt_file.endswith('.json')): logger.error( "PressureTest file must be end with '.xls' or '.json' and see --help for available options." ) sys.exit(1) if not os.path.isfile(pt_file): logger.error('PressureTest file is not exist, please check it.') sys.exit(1) _status = generate_locust_file(pt_file) if not _status: sys.exit(1) sys.exit(0) locustfile = find_locustfile(options.locustfile) if not locustfile: logger.error( "Could not find any locustfile! Ensure file ends in '.py' and see --help for available options." ) sys.exit(1) if locustfile == "locust.py" or locustfile == "locust.xls" or locustfile == "locust.json": logger.error( "The locustfile must not be named `locust.py` or `locust.xls` or `locust.json`. " "Please rename the file and try again.") sys.exit(1) docstring, locusts = load_locustfile(locustfile) if options.list_commands: console_logger.info("Available Locusts:") for name in locusts: console_logger.info(" " + name) sys.exit(0) if not locusts: logger.error("No Locust class found!") sys.exit(1) # make sure specified Locust exists if options.locust_classes: missing = set(options.locust_classes) - set(locusts.keys()) if missing: logger.error("Unknown Locust(s): %s\n" % (", ".join(missing))) sys.exit(1) else: names = set(options.locust_classes) & set(locusts.keys()) locust_classes = [locusts[n] for n in names] else: # list() call is needed to consume the dict_view object in Python 3 locust_classes = list(locusts.values()) if options.show_task_ratio: console_logger.info("\n Task ratio per locust class") console_logger.info("-" * 80) print_task_ratio(locust_classes) console_logger.info("\n Total task ratio") console_logger.info("-" * 80) print_task_ratio(locust_classes, total=True) sys.exit(0) if options.show_task_ratio_json: from json import dumps task_data = { "per_class": get_task_ratio_dict(locust_classes), "total": get_task_ratio_dict(locust_classes, total=True) } console_logger.info(dumps(task_data)) sys.exit(0) if options.run_time: if not options.no_web: logger.error( "The --run-time argument can only be used together with --no-web" ) sys.exit(1) try: options.run_time = parse_timespan(options.run_time) except ValueError: logger.error( "Valid --run-time formats are: 20, 20s, 3m, 2h, 1h20m, 3h30m10s, etc." ) sys.exit(1) def spawn_run_time_limit_greenlet(): logger.info("Run time limit set to %s seconds" % options.run_time) def timelimit_stop(): logger.info("Time limit reached. Stopping Locust.") runners.locust_runner.quit() gevent.spawn_later(options.run_time, timelimit_stop) if options.step_time: if not options.step_load: logger.error( "The --step-time argument can only be used together with --step-load" ) sys.exit(1) try: options.step_time = parse_timespan(options.step_time) except ValueError: logger.error( "Valid --step-time formats are: 20, 20s, 3m, 2h, 1h20m, 3h30m10s, etc." ) sys.exit(1) if options.master: # Add -d for automatically run slaves if options.distribute: ptpy = locustfile if options.locustfile.endswith('.xls'): _type = 'xls' pt_s = PtExcel(options.locustfile) master_ip, pt_slave_info = pt_s.pt_slave() else: _type = 'dict' with open(options.locustfile, 'r') as f: _d = json.load(f, encoding='utf-8') master_ip = _d.get('master_ip') pt_slave_info = _d.get('slaves') if master_ip == '': logger.error( 'master IP cannot be None if you use --distribute') sys.exit(1) if options.boomer: locust_cli_slave = 'nohup ./client_v1 --web --master-host={masteIP} > /dev/null 2>&1 &'.format( masteIP=master_ip) targets_dict, file_list = gen_boomer_client_json( options.locustfile) boomer_client_file = os.path.join(locust_path, 'boomer_client', 'client_v1') file_list.append(boomer_client_file) thread_pool = [] try: for slave in pt_slave_info: if _type == 'xls': slave_ip, slave_username, slave_password = slave else: slave_ip, slave_username, slave_password = slave[ 'ip'], slave['username'], slave['password'] _t = Thread(target=pt_slave_boomer, args=(slave_ip, slave_username, slave_password, file_list, locust_cli_slave, targets_dict)) logger.info('Prepare slave {}'.format(slave_ip)) thread_pool.append(_t) _t.start() for each_t in thread_pool: each_t.join() file_list.pop() for each in file_list: os.remove(each) except KeyboardInterrupt: pass except Exception as e: logger.error( 'Something happened, collect Exceptions here: {}'. format(e)) else: try: locust_cli_slave = 'nohup locust -f /root/locust_client.py --slave --master-host={masteIP} > /dev/null 2>&1 &'\ .format(masteIP=master_ip) thread_pool = [] for slave in pt_slave_info: if _type == 'xls': slave_ip, slave_username, slave_password = slave else: slave_ip, slave_username, slave_password = slave[ 'ip'], slave['username'], slave['password'] _t = Thread(target=pt_slave, args=(slave_ip, slave_username, slave_password, ptpy, locust_cli_slave)) logger.info('Prepare slave {}'.format(slave_ip)) thread_pool.append(_t) _t.start() for each_t in thread_pool: each_t.join() except KeyboardInterrupt: pass except Exception as e: logger.error( 'Something happened, collect Exceptions here: {}'. format(e)) runners.locust_runner = MasterLocustRunner(locust_classes, options) else: runners.locust_runner = LocalLocustRunner(locust_classes, options) # main_greenlet is pointing to runners.locust_runner.greenlet by default, it will point the web greenlet later if in web mode main_greenlet = runners.locust_runner.greenlet if options.no_web: if options.master: while len(runners.locust_runner.clients.ready ) < options.expect_slaves: logging.info( "Waiting for slaves to be ready, %s of %s connected", len(runners.locust_runner.clients.ready), options.expect_slaves) time.sleep(1) if options.step_time: runners.locust_runner.start_stepload(options.num_clients, options.hatch_rate, options.step_clients, options.step_time) else: runners.locust_runner.start_hatching(options.num_clients, options.hatch_rate) # make locusts are spawned time.sleep(1) else: # spawn web greenlet logger.info("Starting web monitor at http://%s:%s" % (options.web_host or "*", options.port)) main_greenlet = gevent.spawn(web.start, locust_classes, options) if options.run_time: spawn_run_time_limit_greenlet() stats_printer_greenlet = None if not options.only_summary and (options.print_stats or options.no_web): # spawn stats printing greenlet stats_printer_greenlet = gevent.spawn(stats_printer) if options.csvfilebase: gevent.spawn(stats_writer, options.csvfilebase, options.stats_history_enabled) def shutdown(code=0): """ Shut down locust by firing quitting event, printing/writing stats and exiting """ logger.info("Shutting down (exit code %s), bye." % code) if stats_printer_greenlet is not None: stats_printer_greenlet.kill(block=False) logger.info("Cleaning up runner...") if runners.locust_runner is not None: runners.locust_runner.quit() logger.info("Running teardowns...") events.quitting.fire(reverse=True) print_stats(runners.locust_runner.stats, current=False) print_percentile_stats(runners.locust_runner.stats) if options.csvfilebase: write_stat_csvs(options.csvfilebase, options.stats_history_enabled) print_error_report() sys.exit(code) # install SIGTERM handler def sig_term_handler(): logger.info("Got SIGTERM signal") shutdown(0) gevent.signal(signal.SIGTERM, sig_term_handler) try: logger.info("Starting Locust %s" % version) main_greenlet.join() code = 0 if len(runners.locust_runner.errors) or len( runners.locust_runner.exceptions): code = options.exit_code_on_error shutdown(code=code) except KeyboardInterrupt: shutdown(0)
def main(): parser, options, arguments = parse_options() # setup logging if options.show_version: print("Locust %s" % (version,)) sys.exit(0) locustfile = find_locustfile(options.locustfile) if not locustfile: logger.error("Could not find any locustfile! Ensure file ends in '.py' and see --help for available options.") sys.exit(1) docstring, locusts = load_locustfile(locustfile) if options.list_commands: logger.info("Available Locusts:") for name in locusts: logger.info(" " + name) sys.exit(0) if not locusts: logger.error("No Locust class found!") sys.exit(1) # make sure specified Locust exists if arguments: missing = set(arguments) - set(locusts.keys()) if missing: logger.error("Unknown Locust(s): %s\n" % (", ".join(missing))) sys.exit(1) else: names = set(arguments) & set(locusts.keys()) locust_classes = [locusts[n] for n in names] else: locust_classes = locusts.values() if options.show_task_ratio: logger.info("\n Task ratio per locust class") logger.info( "-" * 80) print_task_ratio(locust_classes) logger.info("\n Total task ratio") logger.info("-" * 80) print_task_ratio(locust_classes, total=True) sys.exit(0) if options.show_task_ratio_json: from json import dumps task_data = { "per_class": get_task_ratio_dict(locust_classes), "total": get_task_ratio_dict(locust_classes, total=True) } logger.info(dumps(task_data)) sys.exit(0) # if --master is set, make sure --no-web isn't set if options.master and options.no_web: logger.error("Locust can not run distributed with the web interface disabled (do not use --no-web and --master together)") sys.exit(0) if not options.no_web and not options.slave: # spawn web greenlet logger.info("Starting web monitor at %s:%s" % (options.web_host or "*", options.port)) main_greenlet = gevent.spawn(web.start, locust_classes, options) if not options.master and not options.slave: runners.locust_runner = LocalLocustRunner(locust_classes, options) # spawn client spawning/hatching greenlet if options.no_web: runners.locust_runner.start_hatching(wait=True) main_greenlet = runners.locust_runner.greenlet elif options.master: runners.locust_runner = MasterLocustRunner(locust_classes, options) elif options.slave: try: runners.locust_runner = SlaveLocustRunner(locust_classes, options) main_greenlet = runners.locust_runner.greenlet except socket.error as e: logger.error("Failed to connect to the Locust master: %s", e) sys.exit(-1) if not options.only_summary and (options.print_stats or (options.no_web and not options.slave)): # spawn stats printing greenlet gevent.spawn(stats_printer) def shutdown(code=0): """ Shut down locust by firing quitting event, printing stats and exiting """ logger.info("Shutting down (exit code %s), bye." % code) events.quitting.fire() print_stats(runners.locust_runner.request_stats) print_percentile_stats(runners.locust_runner.request_stats) print_error_report() sys.exit(code) # install SIGTERM handler def sig_term_handler(): logger.info("Got SIGTERM signal") shutdown(0) gevent.signal(signal.SIGTERM, sig_term_handler) try: logger.info("Starting Locust %s" % version) main_greenlet.join() code = 0 if len(runners.locust_runner.errors): code = 1 shutdown(code=code) except KeyboardInterrupt as e: shutdown(0)
def test_runner_reference_on_environment(self): env = Environment() runner = LocalLocustRunner(environment=env, locust_classes=[]) self.assertEqual(env, runner.environment)