From 1fea5c399574636c94e3c295cf6187ff668626b1 Mon Sep 17 00:00:00 2001 From: Christopher Grebs Date: Wed, 14 Aug 2013 09:34:09 -0700 Subject: [PATCH 1/2] Merged gevent/zmq updates, ported to requests >= 1.2 * Updated gevent/zmq libraries * Switched to unittest2 discovery * Fixed some deprecated code * Removed requirements.txt files, everything is in setup.py * Fixed test_average test being not executed --- .gitignore | 1 + .travis.yml | 2 - locust/__init__.py | 2 +- locust/clients.py | 74 +++++++++++++------------ locust/core.py | 8 +-- locust/main.py | 2 +- locust/rpc/socketrpc.py | 6 +-- locust/rpc/zmqrpc.py | 2 +- locust/runners.py | 14 +++-- locust/stats.py | 2 +- locust/test/requirements.txt | 1 - locust/test/runtests.py | 1 + locust/test/test_average.py | 93 ++++++++++++++++---------------- locust/test/test_client.py | 7 +-- locust/test/test_locust_class.py | 10 ++-- locust/test/test_stats.py | 7 ++- locust/test/testcases.py | 7 +-- requirements.txt | 5 -- setup.py | 26 +++++++-- 19 files changed, 141 insertions(+), 129 deletions(-) delete mode 100644 locust/test/requirements.txt delete mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore index a7461bd31c..b987c739a0 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ dist/** *.ipr .vagrant build/ +.coverage diff --git a/.travis.yml b/.travis.yml index e08ca58d78..f9ca87101e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,5 @@ python: # command to install dependencies install: - sudo apt-get install -y libevent-dev - - pip install -r requirements.txt --use-mirrors - - pip install -r locust/test/requirements.txt # command to run tests script: python setup.py test diff --git a/locust/__init__.py b/locust/__init__.py index 5703fb8c9b..72386d9bff 100644 --- a/locust/__init__.py +++ b/locust/__init__.py @@ -1,4 +1,4 @@ from core import Locust, TaskSet, WebLocust, SubLocust, task from exception import InterruptTaskSet, ResponseError, RescheduleTaskImmediately -version = "0.6.2" +version = "0.7.0" diff --git a/locust/clients.py b/locust/clients.py index 7f01b39ea1..f2317f5836 100644 --- a/locust/clients.py +++ b/locust/clients.py @@ -5,7 +5,7 @@ from urlparse import urlparse, urlunparse import requests -from requests import Response +from requests import Response, Request from requests.packages.urllib3.response import HTTPResponse from requests.auth import HTTPBasicAuth from requests.exceptions import (RequestException, ConnectionError, HTTPError, @@ -17,6 +17,19 @@ absolute_http_url_regexp = re.compile(r"^https?://", re.I) +def timedelta_to_ms(td): + "python 2.7 has a total_seconds method for timedelta objects. This is here for py<2.7 compat." + return int((td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**3) + + +class LocustResponse(Response): + + def raise_for_status(self): + if hasattr(self, 'error') and self.error: + raise self.error + Response.raise_for_status(self) + + class HttpSession(requests.Session): """ Class for performing web requests and holding (session-) cookies between requests (in order @@ -41,6 +54,8 @@ class HttpSession(requests.Session): and then mark it as successful even if the response code was not (i.e 500 or 404). """ def __init__(self, base_url, *args, **kwargs): + requests.Session.__init__(self, *args, **kwargs) + self.base_url = base_url # Check for basic authentication @@ -53,19 +68,7 @@ def __init__(self, base_url, *args, **kwargs): # remove username and password from the base_url self.base_url = urlunparse((parsed_url.scheme, netloc, parsed_url.path, parsed_url.params, parsed_url.query, parsed_url.fragment)) # configure requests to use basic auth - kwargs["auth"] = HTTPBasicAuth(parsed_url.username, parsed_url.password) - - # requests config - config = { - "max_retries": 0, - "keep_alive": False, - "safe_mode": True, - } - if "config" in kwargs: - config.update(kwargs["config"]) - kwargs["config"] = config - - super(HttpSession, self).__init__(*args, **kwargs) + self.auth = HTTPBasicAuth(parsed_url.username, parsed_url.password) def _build_url(self, path): """ prepend url with hostname unless it's already an absolute URL """ @@ -98,7 +101,7 @@ def request(self, method, url, name=None, catch_response=False, **kwargs): :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy. :param return_response: (optional) If False, an un-sent Request object will returned. :param config: (optional) A configuration dictionary. See ``request.defaults`` for allowed keys and their default values. - :param prefetch: (optional) whether to immediately download the response content. Defaults to ``True``. + :param stream: (optional) whether to immediately download the response content. Defaults to ``False``. :param verify: (optional) if ``True``, the SSL cert will be verified. A CA_BUNDLE path can also be provided. :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. """ @@ -110,26 +113,22 @@ def request(self, method, url, name=None, catch_response=False, **kwargs): request_meta = {} # set up pre_request hook for attaching meta data to the request object - def on_pre_request(request): - request_meta["method"] = request.method - request_meta["name"] = name or request.path_url - request_meta["start_time"] = time.time() - - kwargs["hooks"] = {"pre_request":on_pre_request} + request_meta["start_time"] = time.time() - # make the request using a wrapper that works around a bug in python-requests causing - # safe_mode to not work when making requests through Session instances response = self._send_request_safe_mode(method, url, **kwargs) + request_meta["method"] = response.request.method + request_meta["name"] = name or response.request.path_url + # record the consumed time - request_meta["response_time"] = int((time.time() - request_meta["start_time"]) * 1000) + request_meta["response_time"] = timedelta_to_ms(response.elapsed) - # get the length of the content, but if the argument prefetch is set to False, we take + # get the length of the content, but if the argument stream is set to True, we take # the size from the content-length header, in order to not trigger fetching of the body - if kwargs.get("prefetch", True): - request_meta["content_size"] = len(response.content or "") - else: + if kwargs.get("stream", False): request_meta["content_size"] = int(response.headers.get("content-length") or 0) + else: + request_meta["content_size"] = len(response.content or "") if catch_response: response.locust_request_meta = request_meta @@ -137,7 +136,7 @@ def on_pre_request(request): else: try: response.raise_for_status() - except RequestException, e: + except RequestException as e: events.request_failure.fire(request_meta["method"], request_meta["name"], request_meta["response_time"], e, None) else: events.request_success.fire( @@ -152,23 +151,22 @@ def _send_request_safe_mode(self, method, url, **kwargs): """ Send an HTTP request, and catch any exception that might occur due to connection problems. - This is equivalent of python-requests' safe_mode, which due to a bug, does currently *not* - work together with Sessions. Once the issue is fixed in python-requests, this method should - be removed. See: https://github.com/kennethreitz/requests/issues/888 + Safe mode has been removed from requests 1.x. """ try: - return super(HttpSession, self).request(method, url, **kwargs) + return requests.Session.request(self, method, url, **kwargs) except (MissingSchema, InvalidSchema, InvalidURL): raise - except (RequestException, ConnectionError, HTTPError, - socket.timeout, socket.gaierror) as e: - r = Response() + except RequestException as e: + r = LocustResponse() r.error = e r.raw = HTTPResponse() # otherwise, tests fail r.status_code = 0 # with this status_code, content returns None + r.request = Request(method, url).prepare() return r -class ResponseContextManager(requests.Response): + +class ResponseContextManager(LocustResponse): """ A Response class that also acts as a context manager that provides the ability to manually control if an HTTP request should be marked as successful or a failure in Locust's statistics @@ -201,7 +199,7 @@ def __exit__(self, exc, value, traceback): else: try: self.raise_for_status() - except requests.exceptions.RequestException, e: + except requests.exceptions.RequestException as e: self.failure(e) else: self.success() diff --git a/locust/core.py b/locust/core.py index ccd04e13b4..da05f73c10 100644 --- a/locust/core.py +++ b/locust/core.py @@ -102,7 +102,7 @@ def run(self): self.task_set(self).run() except StopLocust: pass - except (RescheduleTask, RescheduleTaskImmediately), e: + except (RescheduleTask, RescheduleTaskImmediately) as e: raise LocustError, LocustError("A task inside a Locust class' main TaskSet (`%s.task_set` of type `%s`) seems to have called interrupt() or raised an InterruptTaskSet exception. The interrupt() function is used to hand over execution to a parent TaskSet, and should never be called in the main TaskSet which a Locust class' task_set attribute points to." % (type(self).__name__, self.task_set.__name__)), sys.exc_info()[2] @@ -253,7 +253,7 @@ def run(self, *args, **kwargs): self.wait() else: self.wait() - except InterruptTaskSet, e: + except InterruptTaskSet as e: if e.reschedule: raise RescheduleTaskImmediately, e, sys.exc_info()[2] else: @@ -262,7 +262,7 @@ def run(self, *args, **kwargs): raise except GreenletExit: raise - except Exception, e: + except Exception as e: events.locust_error.fire(self, e, sys.exc_info()[2]) sys.stderr.write("\n" + traceback.format_exc()) self.wait() @@ -273,7 +273,7 @@ def execute_next_task(self): def execute_task(self, task, *args, **kwargs): # check if the function is a method bound to the current locust, and if so, don't pass self as first argument - if hasattr(task, "im_self") and task.im_self == self: + if hasattr(task, "im_self") and task.__self__ == self: # task is a bound method on self task(*args, **kwargs) elif hasattr(task, "tasks") and issubclass(task, TaskSet): diff --git a/locust/main.py b/locust/main.py index 859b5c4e7f..4f6c86a439 100644 --- a/locust/main.py +++ b/locust/main.py @@ -396,7 +396,7 @@ def sig_term_handler(): logger.info("Starting Locust %s" % version) main_greenlet.join() shutdown(0) - except KeyboardInterrupt, e: + except KeyboardInterrupt as e: shutdown(0) if __name__ == '__main__': diff --git a/locust/rpc/socketrpc.py b/locust/rpc/socketrpc.py index ba96204049..35e88f9d66 100644 --- a/locust/rpc/socketrpc.py +++ b/locust/rpc/socketrpc.py @@ -25,7 +25,7 @@ def _send_obj(sock, msg): packed = struct.pack('!i', len(data)) + data try: sock.sendall(packed) - except Exception, e: + except Exception as e: try: sock.close() except: @@ -52,7 +52,7 @@ def handle(): try: while True: self.command_queue.put_nowait(_recv_obj(sock)) - except Exception, e: + except Exception as e: try: sock.close() except: @@ -99,7 +99,7 @@ def handle_slave(sock): try: while True: self.event_queue.put_nowait(_recv_obj(sock)) - except Exception, e: + except Exception as e: logger.info("Slave disconnected") slaves.remove(sock) if self.slave_index == len(slaves) and len(slaves) > 0: diff --git a/locust/rpc/zmqrpc.py b/locust/rpc/zmqrpc.py index ddc14c384c..caee4ad33b 100644 --- a/locust/rpc/zmqrpc.py +++ b/locust/rpc/zmqrpc.py @@ -1,5 +1,4 @@ import zmq.green as zmq - from .protocol import Message @@ -20,6 +19,7 @@ def recv(self): data = self.receiver.recv() return Message.unserialize(data) + class Client(object): def __init__(self, host): context = zmq.Context() diff --git a/locust/runners.py b/locust/runners.py index a7d0437fd8..c97a57b331 100644 --- a/locust/runners.py +++ b/locust/runners.py @@ -242,7 +242,7 @@ def running(self): self.server = rpc.Server() self.greenlet = Group() - self.greenlet.spawn(self.client_listener).link_exception(self.noop) + self.greenlet.spawn(self.client_listener).link_exception(receiver=self.noop) # listener that gathers info on how many locust users the slaves has spawned def on_slave_report(client_id, data): @@ -254,6 +254,9 @@ def on_quitting(): self.quit() events.quitting += on_quitting + def noop(self, *args, **kw): + pass + @property def user_count(self): return sum([c.user_count for c in self.clients.itervalues()]) @@ -325,15 +328,20 @@ def slave_count(self): return len(self.clients.ready) + len(self.clients.hatching) + len(self.clients.running) class SlaveLocustRunner(DistributedLocustRunner): + + def noop(self, *args, **kw): + pass + def __init__(self, *args, **kwargs): super(SlaveLocustRunner, self).__init__(*args, **kwargs) self.client_id = socket.gethostname() + "_" + md5(str(time() + random.randint(0,10000))).hexdigest() self.client = rpc.Client(self.master_host) self.greenlet = Group() - self.greenlet.spawn(self.worker).link_exception(self.noop) + + self.greenlet.spawn(self.worker).link_exception(receiver=self.noop) self.client.send(Message("client_ready", None, self.client_id)) - self.greenlet.spawn(self.stats_reporter).link_exception(self.noop) + self.greenlet.spawn(self.stats_reporter).link_exception(receiver=self.noop) # register listener for when all locust users have hatched, and report it to the master node def on_hatch_complete(count): diff --git a/locust/stats.py b/locust/stats.py index f0c67a60fe..03e246b4b7 100644 --- a/locust/stats.py +++ b/locust/stats.py @@ -304,7 +304,7 @@ def get_stripped_report(self): def __str__(self): try: - fail_percent = (self.num_failures/float(self.num_requests))*100 + fail_percent = (self.num_failures/float(self.num_requests + self.num_failures))*100 except ZeroDivisionError: fail_percent = 0 diff --git a/locust/test/requirements.txt b/locust/test/requirements.txt deleted file mode 100644 index 0e202168a3..0000000000 --- a/locust/test/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -mock==1.0.0 diff --git a/locust/test/runtests.py b/locust/test/runtests.py index 139dd57193..b71e845728 100644 --- a/locust/test/runtests.py +++ b/locust/test/runtests.py @@ -9,6 +9,7 @@ from test_taskratio import TestTaskRatio from test_client import TestHttpSession from test_web import TestWebUI +from test_average import MovingAverageTest if __name__ == '__main__': unittest.main() diff --git a/locust/test/test_average.py b/locust/test/test_average.py index 02874ae022..170d7831f7 100644 --- a/locust/test/test_average.py +++ b/locust/test/test_average.py @@ -1,72 +1,71 @@ import unittest -from core import Locust +from locust.core import Locust, TaskSet +from testcases import WebserverTestCase -class MyLocust(Locust): - def _sleep(self, seconds): - """ Bypass actual sleeping - """ - pass -class MovingAverageTest(unittest.TestCase): +class MovingAverageTest(WebserverTestCase): """ This is not a unit test per se because it may fail ocassionally, which is perfectly ok since it deals with randomness. - It is more for testing the expected behavior if using self.avg_wait in a Locust class and for feedback on the algorithm used. + It is more for testing the expected behavior if using self.avg_wait in a taskset class and for feedback on the algorithm used. """ - def _setupLocust(self, min_wait, avg_wait, max_wait): - locust = MyLocust() - locust.min_wait = min_wait - locust.avg_wait = avg_wait - locust.max_wait = max_wait - return locust + def get_task_set(self, min_wait, avg_wait, max_wait): + class MyLocust(Locust): + host = 'http://127.0.0.1:%s' % self.port + min_wait = 1 + max_wait = 1 + + taskset = TaskSet(MyLocust()) + taskset._sleep = lambda seconds: None + taskset.min_wait = min_wait + taskset.avg_wait = avg_wait + taskset.max_wait = max_wait + return taskset - def _wait(self, locust, count): + def _wait(self, taskset, count): for x in xrange(count): - locust.wait() + taskset.wait() - def _get_deviation(self, locust): - return abs(locust._avg_wait - locust.avg_wait) + def _get_deviation(self, taskset): + return abs(taskset._avg_wait - taskset.avg_wait) - def _get_max_deviation(self, locust, percent): - return locust.avg_wait * (percent / 100.0) + def _get_max_deviation(self, taskset, percent): + return taskset.avg_wait * (percent / 100.0) - def _deviation(self, locust, percentage): - deviation = self._get_deviation(locust) - max_deviation = self._get_max_deviation(locust, percentage) - print "Deviation: %.3f ms (%1.3f%%), max: %s ms (%s%%)" % (deviation, deviation / locust.avg_wait * 100.0, max_deviation, percentage) + def _deviation(self, taskset, percentage): + deviation = self._get_deviation(taskset) + max_deviation = self._get_max_deviation(taskset, percentage) + print "Deviation: %.3f ms (%1.3f%%), max: %s ms (%s%%)" % (deviation, deviation / taskset.avg_wait * 100.0, max_deviation, percentage) return deviation, max_deviation, percentage - def _dump_stats(self, locust): - print "Num waits: %d Wanted Average: %s Actual Average: %s" % (locust._avg_wait_ctr, locust.avg_wait, locust._avg_wait) + def _dump_stats(self, taskset): + print "Num waits: %d Wanted Average: %s Actual Average: %s" % (taskset._avg_wait_ctr, taskset.avg_wait, taskset._avg_wait) - def _assertion(self, locust, deviation, max_deviation, percentage): - self._dump_stats(locust) + def _assertion(self, taskset, deviation, max_deviation, percentage): + self._dump_stats(taskset) self.assertTrue(deviation < max_deviation, msg="Deviation not within %s%% of wanted average" % percentage) def test_moving_average_100000(self): - locust = self._setupLocust(3000, 140000, 20 * 60 * 1000) # 3 seconds, 140 seconds, 20 minutes - print "Large" - self._wait(locust, 100000) - (deviation, max_deviation, percentage) = self._deviation(locust, 1.0) - self._assertion(locust, deviation, max_deviation, percentage) + taskset = self.get_task_set(3000, 140000, 20 * 60 * 1000) # 3 seconds, 140 seconds, 20 minutes + self._wait(taskset, 100000) + (deviation, max_deviation, percentage) = self._deviation(taskset, 1.0) + self._assertion(taskset, deviation, max_deviation, percentage) def test_moving_average_100(self): # This test is actually expected to fail sometimes - locust = self._setupLocust(3000, 140000, 20 * 60 * 1000) # 3 seconds, 140 seconds, 20 minutes - print "Small" - self._wait(locust, 100) - (deviation, max_deviation, percentage) = self._deviation(locust, 5.0) - self._assertion(locust, deviation, max_deviation, percentage) + taskset = self.get_task_set(3000, 140000, 20 * 60 * 1000) # 3 seconds, 140 seconds, 20 minutes + self._wait(taskset, 100) + (deviation, max_deviation, percentage) = self._deviation(taskset, 5.0) + self._assertion(taskset, deviation, max_deviation, percentage) def test_omit_average(self): - locust = self._setupLocust(3000, None, 20 * 60 * 1000) # 3 seconds, None, 20 minutes - print "Omitted" - self.assertEquals(None, locust.avg_wait) - self.assertEquals(0, locust._avg_wait) - self.assertEquals(0, locust._avg_wait_ctr) - self._wait(locust, 100000) - self.assertEquals(None, locust.avg_wait) - self.assertEquals(0, locust._avg_wait) - self.assertEquals(0, locust._avg_wait_ctr) + taskset = self.get_task_set(3000, None, 20 * 60 * 1000) # 3 seconds, None, 20 minutes + self.assertEquals(None, taskset.avg_wait) + self.assertEquals(0, taskset._avg_wait) + self.assertEquals(0, taskset._avg_wait_ctr) + self._wait(taskset, 100000) + self.assertEquals(None, taskset.avg_wait) + self.assertEquals(0, taskset._avg_wait) + self.assertEquals(0, taskset._avg_wait_ctr) if __name__ == '__main__': diff --git a/locust/test/test_client.py b/locust/test/test_client.py index 74fd60fd75..a700a4e7bc 100644 --- a/locust/test/test_client.py +++ b/locust/test/test_client.py @@ -11,15 +11,10 @@ def test_get(self): r = s.get("/ultra_fast") self.assertEqual(200, r.status_code) - def test_session_config(self): - s = HttpSession("http://127.0.0.1:%i" % self.port) - self.assertFalse(s.config.get("keep_alive")) - self.assertEqual(0, s.config.get("max_retries")) - def test_connection_error(self): s = HttpSession("http://localhost:1") r = s.get("/", timeout=0.1) - self.assertFalse(r) + self.assertEqual(r.status_code, 0) self.assertEqual(None, r.content) self.assertRaises(RequestException, r.raise_for_status) diff --git a/locust/test/test_locust_class.py b/locust/test/test_locust_class.py index 66c4f8ae5c..c49c20a338 100644 --- a/locust/test/test_locust_class.py +++ b/locust/test/test_locust_class.py @@ -151,7 +151,7 @@ def t2(self): l = MySubTaskSet(self.locust) self.assertEqual(2, len(l.tasks)) - self.assertEqual([t1, MySubTaskSet.t2.im_func], l.tasks) + self.assertEqual([t1, MySubTaskSet.t2.__func__], l.tasks) def test_task_decorator_with_or_without_argument(self): class MyTaskSet(TaskSet): @@ -257,7 +257,7 @@ class MyLocust2(Locust): try: l.run() - except LocustError, e: + except LocustError as e: self.assertTrue("MyLocust" in e.args[0], "MyLocust should have been referred to in the exception message") self.assertTrue("MyTaskSet" in e.args[0], "MyTaskSet should have been referred to in the exception message") except: @@ -265,7 +265,7 @@ class MyLocust2(Locust): try: l2.run() - except LocustError, e: + except LocustError as e: self.assertTrue("MyLocust2" in e.args[0], "MyLocust2 should have been referred to in the exception message") self.assertTrue("MyTaskSet2" in e.args[0], "MyTaskSet2 should have been referred to in the exception message") except: @@ -484,7 +484,7 @@ class MyLocust(Locust): host = "http://127.0.0.1:1" l = MyLocust() with l.client.get("/", catch_response=True) as r: - self.assertFalse(r) + self.assertEqual(r.status_code, 0) self.assertEqual(None, r.content) r.success() self.assertEqual(1, self.num_success) @@ -495,7 +495,7 @@ class MyLocust(Locust): host = "http://127.0.0.1:1" l = MyLocust() with l.client.get("/", catch_response=True) as r: - self.assertFalse(r) + self.assertEqual(r.status_code, 0) self.assertEqual(None, r.content) r.success() self.assertEqual(1, self.num_success) diff --git a/locust/test/test_stats.py b/locust/test/test_stats.py index d9a3bea245..d7c97f3b08 100644 --- a/locust/test/test_stats.py +++ b/locust/test/test_stats.py @@ -130,12 +130,12 @@ class MyLocust(Locust): r = l.client.get(path) self.assertEqual(global_stats.get(path, "GET").avg_content_length, len("This response does not have content-length in the header")) - def test_request_stats_no_content_length_no_prefetch(self): + def test_request_stats_no_content_length_streaming(self): class MyLocust(Locust): host = "http://127.0.0.1:%i" % self.port l = MyLocust() path = "/no_content_length" - r = l.client.get(path, prefetch=False) + r = l.client.get(path, stream=True) self.assertEqual(0, global_stats.get(path, "GET").avg_content_length) def test_request_stats_named_endpoint(self): @@ -160,7 +160,7 @@ class MyLocust(Locust): locust = MyLocust() response = locust.client.get("/", timeout=0.1) - self.assertFalse(response) + self.assertEqual(response.status_code, 0) self.assertEqual(1, global_stats.get("/", "GET").num_failures) self.assertEqual(0, global_stats.get("/", "GET").num_requests) @@ -225,4 +225,3 @@ def test_get_task_ratio_dict_total(self): self.assertEqual(0.25, ratio["MyTaskSet"]["tasks"]["MySubTaskSet"]["ratio"]) self.assertEqual(0.125, ratio["MyTaskSet"]["tasks"]["MySubTaskSet"]["tasks"]["task1"]["ratio"]) self.assertEqual(0.125, ratio["MyTaskSet"]["tasks"]["MySubTaskSet"]["tasks"]["task2"]["ratio"]) - \ No newline at end of file diff --git a/locust/test/testcases.py b/locust/test/testcases.py index b971b9f58c..43497f8fb5 100644 --- a/locust/test/testcases.py +++ b/locust/test/testcases.py @@ -1,8 +1,8 @@ -import random +import base64 import gevent import gevent.pywsgi +import random import unittest -import base64 from copy import copy from StringIO import StringIO @@ -49,7 +49,7 @@ def failed_request(): return "This response failed", 500 @app.route("/redirect") -def redirect(): +def do_redirect(): return redirect("/ultra_fast") @app.route("/basic_auth") @@ -102,4 +102,5 @@ def setUp(self): def tearDown(self): super(WebserverTestCase, self).tearDown() + self._web_server.stop_accepting() self._web_server.stop() diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 3aa2e0e8e5..0000000000 --- a/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -greenlet -gevent>=0.13 -Flask>=0.8 -requests==0.14.2 -msgpack-python==0.3.0 diff --git a/setup.py b/setup.py index 0d5e06caa3..b96c2e825b 100644 --- a/setup.py +++ b/setup.py @@ -1,9 +1,26 @@ # encoding: utf-8 -from setuptools import setup, find_packages +from setuptools import setup, find_packages, Command import sys, os -version = '0.6.2' +version = '0.7.0' + + +class Unit2Discover(Command): + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + import sys, subprocess + basecmd = ['unit2', 'discover'] + errno = subprocess.call(basecmd) + raise SystemExit(errno) + setup( name='locustio', @@ -27,11 +44,12 @@ packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), include_package_data=True, zip_safe=False, - install_requires=["gevent>=0.13", "flask>=0.8", "requests==0.14.2", "msgpack-python==0.3.0"], + install_requires=["gevent>=0.13", "flask>=0.8", "requests>=1.2", "msgpack-python==0.3.0"], + tests_require=['unittest2', 'mock', 'pyzmq'], entry_points={ 'console_scripts': [ 'locust = locust.main:main', ] }, - test_suite='locust.test.runtests', + test_suite='unittest2.collector', ) From 83b246f6bfb105940e6dc35ac63cf9946cfcb669 Mon Sep 17 00:00:00 2001 From: Christopher Grebs Date: Wed, 14 Aug 2013 09:37:57 -0700 Subject: [PATCH 2/2] Add setup.cfg to work with nose tests --- setup.cfg | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 setup.cfg diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000000..abcfaf9f89 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,6 @@ +[nosetests] +match = ^test +nocapture = 1 +cover-package = locust +with-coverage = 1 +cover-erase = 1 \ No newline at end of file