commit f38ad0811290b51b99475de28628895a663e7dec Author: Damian Johnson atagar@torproject.org Date: Sun Jan 29 14:40:27 2012 -0800
Refactoring ControlMessage integ tests
Besides the normal cleanup I'm pushing most of the skipTest checks into the runner module. The advantage of this is that it simplifies tests a little more and allows a standardized skip message rather than having each module provide their own for the same thing. --- test/integ/connection/authentication.py | 4 +- test/integ/connection/connect.py | 4 +- test/integ/connection/protocolinfo.py | 4 +- test/integ/socket/control_message.py | 146 +++++++++++++------------------ test/integ/version.py | 13 +-- test/runner.py | 54 ++++++++--- 6 files changed, 106 insertions(+), 119 deletions(-)
diff --git a/test/integ/connection/authentication.py b/test/integ/connection/authentication.py index cc36c66..0762b23 100644 --- a/test/integ/connection/authentication.py +++ b/test/integ/connection/authentication.py @@ -78,9 +78,7 @@ def _get_auth_failure_message(auth_type):
class TestAuthenticate(unittest.TestCase): def setUp(self): - # none of these tests apply if there's no control connection - if not test.runner.get_runner().is_accessible(): - self.skipTest("(no connection)") + test.runner.require_control(self)
def test_authenticate_general(self): """ diff --git a/test/integ/connection/connect.py b/test/integ/connection/connect.py index ca3cf41..9b6337a 100644 --- a/test/integ/connection/connect.py +++ b/test/integ/connection/connect.py @@ -11,9 +11,7 @@ import test.runner
class TestConnect(unittest.TestCase): def setUp(self): - # none of these tests apply if there's no control connection - if not test.runner.get_runner().is_accessible(): - self.skipTest("(no connection)") + test.runner.require_control(self)
# prevents the function from printing to the real stdout self.original_stdout = sys.stdout diff --git a/test/integ/connection/protocolinfo.py b/test/integ/connection/protocolinfo.py index a0e1699..b572aee 100644 --- a/test/integ/connection/protocolinfo.py +++ b/test/integ/connection/protocolinfo.py @@ -26,9 +26,7 @@ def filter_system_call(prefixes):
class TestProtocolInfo(unittest.TestCase): def setUp(self): - if not test.runner.get_runner().is_accessible(): - self.skipTest("(no connection)") - + test.runner.require_control(self) mocking.mock(stem.util.proc.is_available, mocking.return_false()) mocking.mock(stem.util.system.is_available, mocking.return_true())
diff --git a/test/integ/socket/control_message.py b/test/integ/socket/control_message.py index ec99edb..ce25c8f 100644 --- a/test/integ/socket/control_message.py +++ b/test/integ/socket/control_message.py @@ -3,7 +3,6 @@ Integration tests for the stem.socket.ControlMessage class. """
import re -import socket import unittest
import stem.socket @@ -11,19 +10,19 @@ import stem.version import test.runner
class TestControlMessage(unittest.TestCase): + def setUp(self): + test.runner.require_control(self) + def test_unestablished_socket(self): """ Checks message parsing when we have a valid but unauthenticated socket. """
- runner = test.runner.get_runner() - if not runner.is_accessible(): self.skipTest("(no control socket)") - # If an unauthenticated connection gets a message besides AUTHENTICATE or # PROTOCOLINFO then tor will give an 'Authentication required.' message and # hang up.
- control_socket = runner.get_tor_socket(False) + control_socket = test.runner.get_runner().get_tor_socket(False) control_socket.send("GETINFO version")
auth_required_response = control_socket.recv() @@ -63,36 +62,26 @@ class TestControlMessage(unittest.TestCase): Parses the response for a command which doesn't exist. """
- runner = test.runner.get_runner() - if not runner.is_accessible(): self.skipTest("(no control socket)") - control_socket = runner.get_tor_socket() - - control_socket.send("blarg") - unrecognized_command_response = control_socket.recv() - self.assertEquals('Unrecognized command "blarg"', str(unrecognized_command_response)) - self.assertEquals(['Unrecognized command "blarg"'], list(unrecognized_command_response)) - self.assertEquals('510 Unrecognized command "blarg"\r\n', unrecognized_command_response.raw_content()) - self.assertEquals([('510', ' ', 'Unrecognized command "blarg"')], unrecognized_command_response.content()) - - control_socket.close() + with test.runner.get_runner().get_tor_socket() as control_socket: + control_socket.send("blarg") + unrecognized_command_response = control_socket.recv() + self.assertEquals('Unrecognized command "blarg"', str(unrecognized_command_response)) + self.assertEquals(['Unrecognized command "blarg"'], list(unrecognized_command_response)) + self.assertEquals('510 Unrecognized command "blarg"\r\n', unrecognized_command_response.raw_content()) + self.assertEquals([('510', ' ', 'Unrecognized command "blarg"')], unrecognized_command_response.content())
def test_invalid_getinfo(self): """ Parses the response for a GETINFO query which doesn't exist. """
- runner = test.runner.get_runner() - if not runner.is_accessible(): self.skipTest("(no control socket)") - control_socket = runner.get_tor_socket() - - control_socket.send("GETINFO blarg") - unrecognized_key_response = control_socket.recv() - self.assertEquals('Unrecognized key "blarg"', str(unrecognized_key_response)) - self.assertEquals(['Unrecognized key "blarg"'], list(unrecognized_key_response)) - self.assertEquals('552 Unrecognized key "blarg"\r\n', unrecognized_key_response.raw_content()) - self.assertEquals([('552', ' ', 'Unrecognized key "blarg"')], unrecognized_key_response.content()) - - control_socket.close() + with test.runner.get_runner().get_tor_socket() as control_socket: + control_socket.send("GETINFO blarg") + unrecognized_key_response = control_socket.recv() + self.assertEquals('Unrecognized key "blarg"', str(unrecognized_key_response)) + self.assertEquals(['Unrecognized key "blarg"'], list(unrecognized_key_response)) + self.assertEquals('552 Unrecognized key "blarg"\r\n', unrecognized_key_response.raw_content()) + self.assertEquals([('552', ' ', 'Unrecognized key "blarg"')], unrecognized_key_response.content())
def test_getinfo_config_file(self): """ @@ -101,30 +90,22 @@ class TestControlMessage(unittest.TestCase):
runner = test.runner.get_runner() torrc_dst = runner.get_torrc_path() - if not runner.is_accessible(): self.skipTest("(no control socket)") - control_socket = runner.get_tor_socket() - - control_socket.send("GETINFO config-file") - config_file_response = control_socket.recv() - self.assertEquals("config-file=%s\nOK" % torrc_dst, str(config_file_response)) - self.assertEquals(["config-file=%s" % torrc_dst, "OK"], list(config_file_response)) - self.assertEquals("250-config-file=%s\r\n250 OK\r\n" % torrc_dst, config_file_response.raw_content()) - self.assertEquals([("250", "-", "config-file=%s" % torrc_dst), ("250", " ", "OK")], config_file_response.content())
- control_socket.close() + with runner.get_tor_socket() as control_socket: + control_socket.send("GETINFO config-file") + config_file_response = control_socket.recv() + self.assertEquals("config-file=%s\nOK" % torrc_dst, str(config_file_response)) + self.assertEquals(["config-file=%s" % torrc_dst, "OK"], list(config_file_response)) + self.assertEquals("250-config-file=%s\r\n250 OK\r\n" % torrc_dst, config_file_response.raw_content()) + self.assertEquals([("250", "-", "config-file=%s" % torrc_dst), ("250", " ", "OK")], config_file_response.content())
def test_getinfo_config_text(self): """ Parses the 'GETINFO config-text' response. """
+ test.runner.require_version(self, stem.version.Requirement.GETINFO_CONFIG_TEXT) runner = test.runner.get_runner() - req_version = stem.version.Requirement.GETINFO_CONFIG_TEXT - - if not runner.is_accessible(): - self.skipTest("(no control socket)") - elif runner.get_tor_version() < req_version: - self.skipTest("(requires %s)" % req_version)
# We can't be certain of the order, and there may be extra config-text # entries as per... @@ -140,50 +121,43 @@ class TestControlMessage(unittest.TestCase): if line and not line.startswith("#"): torrc_contents.append(line)
- control_socket = runner.get_tor_socket() - control_socket.send("GETINFO config-text") - config_text_response = control_socket.recv() - - # the response should contain two entries, the first being a data response - self.assertEqual(2, len(list(config_text_response))) - self.assertEqual("OK", list(config_text_response)[1]) - self.assertEqual(("250", " ", "OK"), config_text_response.content()[1]) - self.assertTrue(config_text_response.raw_content().startswith("250+config-text=\r\n")) - self.assertTrue(config_text_response.raw_content().endswith("\r\n.\r\n250 OK\r\n")) - self.assertTrue(str(config_text_response).startswith("config-text=\n")) - self.assertTrue(str(config_text_response).endswith("\nOK")) - - for torrc_entry in torrc_contents: - self.assertTrue("\n%s\n" % torrc_entry in str(config_text_response)) - self.assertTrue(torrc_entry in list(config_text_response)[0]) - self.assertTrue("%s\r\n" % torrc_entry in config_text_response.raw_content()) - self.assertTrue("%s" % torrc_entry in config_text_response.content()[0][2]) - - control_socket.close() + with runner.get_tor_socket() as control_socket: + control_socket.send("GETINFO config-text") + config_text_response = control_socket.recv() + + # the response should contain two entries, the first being a data response + self.assertEqual(2, len(list(config_text_response))) + self.assertEqual("OK", list(config_text_response)[1]) + self.assertEqual(("250", " ", "OK"), config_text_response.content()[1]) + self.assertTrue(config_text_response.raw_content().startswith("250+config-text=\r\n")) + self.assertTrue(config_text_response.raw_content().endswith("\r\n.\r\n250 OK\r\n")) + self.assertTrue(str(config_text_response).startswith("config-text=\n")) + self.assertTrue(str(config_text_response).endswith("\nOK")) + + for torrc_entry in torrc_contents: + self.assertTrue("\n%s\n" % torrc_entry in str(config_text_response)) + self.assertTrue(torrc_entry in list(config_text_response)[0]) + self.assertTrue("%s\r\n" % torrc_entry in config_text_response.raw_content()) + self.assertTrue("%s" % torrc_entry in config_text_response.content()[0][2])
def test_bw_event(self): """ - Issues 'SETEVENTS BW' and parses a few events. + Issues 'SETEVENTS BW' and parses a couple events. """
- runner = test.runner.get_runner() - if not runner.is_accessible(): self.skipTest("(no control socket)") - control_socket = runner.get_tor_socket() - - control_socket.send("SETEVENTS BW") - setevents_response = control_socket.recv() - self.assertEquals("OK", str(setevents_response)) - self.assertEquals(["OK"], list(setevents_response)) - self.assertEquals("250 OK\r\n", setevents_response.raw_content()) - self.assertEquals([("250", " ", "OK")], setevents_response.content()) - - # Tor will emit a BW event once per second. Parsing two of them. - - for _ in range(2): - bw_event = control_socket.recv() - self.assertTrue(re.match("BW [0-9]+ [0-9]+", str(bw_event))) - self.assertTrue(re.match("650 BW [0-9]+ [0-9]+\r\n", bw_event.raw_content())) - self.assertEquals(("650", " "), bw_event.content()[0][:2]) - - control_socket.close() + with test.runner.get_runner().get_tor_socket() as control_socket: + control_socket.send("SETEVENTS BW") + setevents_response = control_socket.recv() + self.assertEquals("OK", str(setevents_response)) + self.assertEquals(["OK"], list(setevents_response)) + self.assertEquals("250 OK\r\n", setevents_response.raw_content()) + self.assertEquals([("250", " ", "OK")], setevents_response.content()) + + # Tor will emit a BW event once per second. Parsing two of them. + + for _ in range(2): + bw_event = control_socket.recv() + self.assertTrue(re.match("BW [0-9]+ [0-9]+", str(bw_event))) + self.assertTrue(re.match("650 BW [0-9]+ [0-9]+\r\n", bw_event.raw_content())) + self.assertEquals(("650", " "), bw_event.content()[0][:2])
diff --git a/test/integ/version.py b/test/integ/version.py index e344ef8..833cbd2 100644 --- a/test/integ/version.py +++ b/test/integ/version.py @@ -35,11 +35,9 @@ class TestVersion(unittest.TestCase): test instance provides. """
- runner = test.runner.get_runner() - - if not runner.is_accessible(): - self.skipTest("(no connection)") + test.runner.require_control(self)
+ runner = test.runner.get_runner() system_tor_version = stem.version.get_system_tor_version(runner.get_tor_command()) self.assertEquals(runner.get_tor_version(), system_tor_version)
@@ -49,12 +47,9 @@ class TestVersion(unittest.TestCase): we can parse it. """
- runner = test.runner.get_runner() - - if not runner.is_accessible(): - self.skipTest("(no connection)") + test.runner.require_control(self)
- control_socket = runner.get_tor_socket() + control_socket = test.runner.get_runner().get_tor_socket() control_socket.send("GETINFO version") version_response = control_socket.recv() control_socket.close() diff --git a/test/runner.py b/test/runner.py index 0e71ade..3cf5542 100644 --- a/test/runner.py +++ b/test/runner.py @@ -6,6 +6,8 @@ about the tor test instance they're running against. RunnerStopped - Runner doesn't have an active tor instance TorInaccessable - Tor can't be queried for the information
+require_control - skips the test unless tor provides a controller endpoint +require_version - skips the test unless we meet a tor version requirement exercise_socket - Does a basic sanity check that a control socket can be used
get_runner - Singleton for fetching our runtime context. @@ -86,13 +88,36 @@ class TorInaccessable(Exception): "Raised when information is needed from tor but the instance we have is inaccessable" pass
+def require_control(test_case): + """ + Skips the test unless tor provides an endpoint for controllers to attach to. + + Arguments: + test_case (unittest.TestCase) - test being ran + """ + + if not test.runner.get_runner().is_accessible(): + test_case.skipTest("(no connection)") + +def require_version(test_case, req_version): + """ + Skips the test unless we meet the required version. + + Arguments: + test_case (unittest.TestCase) - test being ran + req_version (stem.version.Version) - required tor version for the test + """ + + if get_runner().get_tor_version() < req_version: + test_case.skipTest("(requires %s)" % req_version) + def exercise_socket(test_case, control_socket): """ Checks that we can now use the socket by issuing a 'GETINFO config-file' query.
Arguments: - test_case (unittest.TestCase) - unit testing case being ran + test_case (unittest.TestCase) - test being ran control_socket (stem.socket.ControlSocket) - socket to be tested """
@@ -380,22 +405,21 @@ class Runner:
Returns: stem.version.Version for our test instance - - Raises: - TorInaccessable if this can't be determined """
- # TODO: replace with higher level functions when we've completed a basic - # controller class - - control_socket = self.get_tor_socket() - - control_socket.send("GETINFO version") - version_response = control_socket.recv() - control_socket.close() - - tor_version = list(version_response)[0][8:] - return stem.version.Version(tor_version) + try: + # TODO: replace with higher level functions when we've completed a basic + # controller class + control_socket = self.get_tor_socket() + + control_socket.send("GETINFO version") + version_response = control_socket.recv() + control_socket.close() + + tor_version = list(version_response)[0][8:] + return stem.version.Version(tor_version) + except TorInaccessable: + return stem.version.get_system_tor_version(self.get_tor_command())
def get_tor_command(self): """