[tor-commits] [stem/master] Use assertRaisesRegexp to check exception messages

atagar at torproject.org atagar at torproject.org
Thu Mar 30 04:18:02 UTC 2017


commit 2f6544d56a2f46a001a55a639e4fa4b996b0e9f7
Author: Damian Johnson <atagar at torproject.org>
Date:   Sat Mar 11 23:12:55 2017 -0800

    Use assertRaisesRegexp to check exception messages
    
    Interesting! New tests used assertRaisesRegexp which is a much nicer method of
    asserting what an exception message is. Using this throughout our codebase.
---
 test/integ/control/controller.py                  | 24 +++++--------
 test/integ/process.py                             | 23 ++++++------
 test/integ/util/system.py                         |  6 +---
 test/unit/control/controller.py                   | 15 +++-----
 test/unit/descriptor/extrainfo_descriptor.py      | 10 +++---
 test/unit/descriptor/microdescriptor.py           |  7 ++--
 test/unit/descriptor/networkstatus/document_v3.py |  9 ++---
 test/unit/descriptor/server_descriptor.py         | 14 +++-----
 test/unit/manual.py                               | 43 +++++++----------------
 test/unit/response/add_onion.py                   | 16 +++------
 test/unit/util/proc.py                            |  9 ++---
 11 files changed, 56 insertions(+), 120 deletions(-)

diff --git a/test/integ/control/controller.py b/test/integ/control/controller.py
index a1f155b..f5c91d7 100644
--- a/test/integ/control/controller.py
+++ b/test/integ/control/controller.py
@@ -581,13 +581,11 @@ class TestController(unittest.TestCase):
     runner = test.runner.get_runner()
 
     with runner.get_tor_controller() as controller:
+      # try creating a service with an invalid ports
+
       for ports in (4567890, [4567, 4567890], {4567: '-:4567'}):
-        try:
-          # try creating a service with an invalid port
-          response = controller.create_ephemeral_hidden_service(ports)
-          self.fail("we should've raised a stem.ProtocolError")
-        except stem.ProtocolError as exc:
-          self.assertEqual("ADD_ONION response didn't have an OK status: Invalid VIRTPORT/TARGET", str(exc))
+        exc_msg = "ADD_ONION response didn't have an OK status: Invalid VIRTPORT/TARGET"
+        self.assertRaisesRegexp(stem.ProtocolError, exc_msg, controller.create_ephemeral_hidden_service, ports)
 
       response = controller.create_ephemeral_hidden_service(4567)
       self.assertEqual([response.service_id], controller.list_ephemeral_hidden_services())
@@ -654,11 +652,8 @@ class TestController(unittest.TestCase):
     runner = test.runner.get_runner()
 
     with runner.get_tor_controller() as controller:
-      try:
-        controller.create_ephemeral_hidden_service(4567, basic_auth = {})
-        self.fail('ADD_ONION should fail when using basic auth without any clients')
-      except stem.ProtocolError as exc:
-        self.assertEqual("ADD_ONION response didn't have an OK status: No auth clients specified", str(exc))
+      exc_msg = "ADD_ONION response didn't have an OK status: No auth clients specified"
+      self.assertRaisesRegexp(stem.ProtocolError, exc_msg, controller.create_ephemeral_hidden_service, 4567, basic_auth = {})
 
   @require_controller
   @require_version(Requirement.ADD_ONION)
@@ -1266,11 +1261,8 @@ class TestController(unittest.TestCase):
 
       # try to fetch something that doesn't exist
 
-      try:
-        desc = controller.get_hidden_service_descriptor('m4cfuk6qp4lpu2g3')
-        self.fail("Didn't expect m4cfuk6qp4lpu2g3.onion to exist, but provided: %s" % desc)
-      except stem.DescriptorUnavailable as exc:
-        self.assertEqual('No running hidden service at m4cfuk6qp4lpu2g3.onion', str(exc))
+      exc_msg = 'No running hidden service at m4cfuk6qp4lpu2g3.onion'
+      self.assertRaisesRegexp(stem.DescriptorUnavailable, exc_msg, controller.get_hidden_service_descriptor, 'm4cfuk6qp4lpu2g3')
 
       # ... but shouldn't fail if we have a default argument or aren't awaiting the descriptor
 
diff --git a/test/integ/process.py b/test/integ/process.py
index 19d6750..3f7ac65 100644
--- a/test/integ/process.py
+++ b/test/integ/process.py
@@ -356,18 +356,17 @@ class TestProcess(unittest.TestCase):
     #   [warn] Failed to parse/validate config: Failed to bind one of the listener ports.
     #   [err] Reading config failed--see warnings above.
 
-    try:
-      stem.process.launch_tor_with_config(
-        tor_cmd = test.runner.get_runner().get_tor_command(),
-        config = {
-          'SocksPort': '2777',
-          'ControlPort': '2777',
-          'DataDirectory': self.data_directory,
-        },
-      )
-      self.fail("We should abort when there's an identical SocksPort and ControlPort")
-    except OSError as exc:
-      self.assertEqual('Process terminated: Failed to bind one of the listener ports.', str(exc))
+    self.assertRaisesRegexp(
+      OSError,
+      'Process terminated: Failed to bind one of the listener ports.',
+      stem.process.launch_tor_with_config,
+      tor_cmd = test.runner.get_runner().get_tor_command(),
+      config = {
+        'SocksPort': '2777',
+        'ControlPort': '2777',
+        'DataDirectory': self.data_directory,
+      },
+    )
 
   @only_run_once
   def test_launch_tor_with_timeout(self):
diff --git a/test/integ/util/system.py b/test/integ/util/system.py
index b6e66a0..be04393 100644
--- a/test/integ/util/system.py
+++ b/test/integ/util/system.py
@@ -564,11 +564,7 @@ class TestSystem(unittest.TestCase):
     self.assertEqual(os.path.join(home_dir, 'foo'), stem.util.system.expand_path('~%s/foo' % username))
 
   def test_call_timeout(self):
-    try:
-      stem.util.system.call('sleep 1', timeout = 0.001)
-      self.fail("sleep should've timed out")
-    except stem.util.system.CallTimeoutError as exc:
-      self.assertEqual("Process didn't finish after 0.0 seconds", str(exc))
+    self.assertRaisesRegexp(stem.util.system.CallTimeoutError, "Process didn't finish after 0.0 seconds", stem.util.system.call, 'sleep 1', timeout = 0.001)
 
   def test_call_time_tracked(self):
     """
diff --git a/test/unit/control/controller.py b/test/unit/control/controller.py
index 0c82a94..47ac9d2 100644
--- a/test/unit/control/controller.py
+++ b/test/unit/control/controller.py
@@ -442,12 +442,8 @@ class TestControl(unittest.TestCase):
 
     get_info_mock.side_effect = ControllerError('nope, too bad')
 
-    try:
-      self.controller.get_network_status()
-      self.fail("We should've raised an exception")
-    except ControllerError as exc:
-      self.assertEqual('Unable to determine our own fingerprint: nope, too bad', str(exc))
-
+    exc_msg = 'Unable to determine our own fingerprint: nope, too bad'
+    self.assertRaisesRegexp(ControllerError, exc_msg, self.controller.get_network_status)
     self.assertEqual('boom', self.controller.get_network_status(default = 'boom'))
 
     # successful request
@@ -469,11 +465,8 @@ class TestControl(unittest.TestCase):
 
     get_info_mock.side_effect = InvalidArguments(None, 'GETINFO request contained unrecognized keywords: ns/id/5AC9C5AA75BA1F18D8459B326B4B8111A856D290')
 
-    try:
-      self.controller.get_network_status('5AC9C5AA75BA1F18D8459B326B4B8111A856D290')
-      self.fail("We should've raised an exception")
-    except DescriptorUnavailable as exc:
-      self.assertEqual("Tor was unable to provide the descriptor for '5AC9C5AA75BA1F18D8459B326B4B8111A856D290'", str(exc))
+    exc_msg = "Tor was unable to provide the descriptor for '5AC9C5AA75BA1F18D8459B326B4B8111A856D290'"
+    self.assertRaisesRegexp(DescriptorUnavailable, exc_msg, self.controller.get_network_status, '5AC9C5AA75BA1F18D8459B326B4B8111A856D290')
 
   @patch('stem.control.Controller.get_info')
   def test_get_network_status(self, get_info_mock):
diff --git a/test/unit/descriptor/extrainfo_descriptor.py b/test/unit/descriptor/extrainfo_descriptor.py
index 79d0b5c..cab3440 100644
--- a/test/unit/descriptor/extrainfo_descriptor.py
+++ b/test/unit/descriptor/extrainfo_descriptor.py
@@ -3,6 +3,7 @@ Unit tests for stem.descriptor.extrainfo_descriptor.
 """
 
 import datetime
+import re
 import unittest
 
 import stem.descriptor
@@ -172,12 +173,9 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
     """
 
     with open(get_resource('unparseable/extrainfo_nonascii_v3_reqs'), 'rb') as descriptor_file:
-      try:
-        next(stem.descriptor.parse_file(descriptor_file, 'extra-info 1.0', validate = True))
-        self.fail("validation should've raised an exception")
-      except ValueError as exc:
-        expected = "'dirreq-v3-reqs' line had non-ascii content: S?=4026597208,S?=4026597208,S?=4026597208,S?=4026597208,S?=4026597208,S?=4026597208,??=4026591624,6?=4026537520,6?=4026537520,6?=4026537520,us=8"
-        self.assertEqual(expected, str(exc))
+      desc_generator = stem.descriptor.parse_file(descriptor_file, 'extra-info 1.0', validate = True)
+      exc_msg = "'dirreq-v3-reqs' line had non-ascii content: S?=4026597208,S?=4026597208,S?=4026597208,S?=4026597208,S?=4026597208,S?=4026597208,??=4026591624,6?=4026537520,6?=4026537520,6?=4026537520,us=8"
+      self.assertRaisesRegexp(ValueError, re.escape(exc_msg), next, desc_generator)
 
   def test_minimal_extrainfo_descriptor(self):
     """
diff --git a/test/unit/descriptor/microdescriptor.py b/test/unit/descriptor/microdescriptor.py
index 9b3fb2d..f9350d2 100644
--- a/test/unit/descriptor/microdescriptor.py
+++ b/test/unit/descriptor/microdescriptor.py
@@ -204,8 +204,5 @@ class TestMicrodescriptor(unittest.TestCase):
     desc = Microdescriptor(desc_text)
     self.assertEqual({}, desc.identifiers)
 
-    try:
-      Microdescriptor(desc_text, validate = True)
-      self.fail('constructor validation should fail')
-    except ValueError as exc:
-      self.assertEqual("There can only be one 'id' line per a key type, but 'rsa1024' appeared multiple times", str(exc))
+    exc_msg = "There can only be one 'id' line per a key type, but 'rsa1024' appeared multiple times"
+    self.assertRaisesRegexp(ValueError, exc_msg, Microdescriptor, desc_text, validate = True)
diff --git a/test/unit/descriptor/networkstatus/document_v3.py b/test/unit/descriptor/networkstatus/document_v3.py
index e6e5d76..6a2e784 100644
--- a/test/unit/descriptor/networkstatus/document_v3.py
+++ b/test/unit/descriptor/networkstatus/document_v3.py
@@ -4,6 +4,7 @@ Unit tests for the NetworkStatusDocumentV3 of stem.descriptor.networkstatus.
 
 import datetime
 import io
+import re
 import unittest
 
 import stem.descriptor
@@ -1239,15 +1240,9 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
 
     for attr, expected_exception in test_values:
       content = get_directory_authority(attr, content = True)
-
-      try:
-        DirectoryAuthority(content, True)
-        self.fail("validation should've rejected malformed shared randomness attribute")
-      except ValueError as exc:
-        self.assertEqual(expected_exception, str(exc))
+      self.assertRaisesRegexp(ValueError, re.escape(expected_exception), DirectoryAuthority, content, True)
 
       authority = DirectoryAuthority(content, False)
-
       self.assertEqual([], authority.shared_randomness_commitments)
       self.assertEqual(None, authority.shared_randomness_previous_reveal_count)
       self.assertEqual(None, authority.shared_randomness_previous_value)
diff --git a/test/unit/descriptor/server_descriptor.py b/test/unit/descriptor/server_descriptor.py
index 8170532..b43a6f3 100644
--- a/test/unit/descriptor/server_descriptor.py
+++ b/test/unit/descriptor/server_descriptor.py
@@ -681,22 +681,16 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
     Checks a 'proto' line when it's not key=value pairs.
     """
 
-    try:
-      get_relay_server_descriptor({'proto': 'Desc Link=1-4'})
-      self.fail('Did not raise expected exception')
-    except ValueError as exc:
-      self.assertEqual("Protocol entires are expected to be a series of 'key=value' pairs but was: proto Desc Link=1-4", str(exc))
+    exc_msg = "Protocol entires are expected to be a series of 'key=value' pairs but was: proto Desc Link=1-4"
+    self.assertRaisesRegexp(ValueError, exc_msg, get_relay_server_descriptor, {'proto': 'Desc Link=1-4'})
 
   def test_parse_with_non_int_version(self):
     """
     Checks a 'proto' line with non-numeric content.
     """
 
-    try:
-      get_relay_server_descriptor({'proto': 'Desc=hi Link=1-4'})
-      self.fail('Did not raise expected exception')
-    except ValueError as exc:
-      self.assertEqual('Protocol values should be a number or number range, but was: proto Desc=hi Link=1-4', str(exc))
+    exc_msg = 'Protocol values should be a number or number range, but was: proto Desc=hi Link=1-4'
+    self.assertRaisesRegexp(ValueError, exc_msg, get_relay_server_descriptor, {'proto': 'Desc=hi Link=1-4'})
 
   def test_ntor_onion_key(self):
     """
diff --git a/test/unit/manual.py b/test/unit/manual.py
index 671ed93..06e0071 100644
--- a/test/unit/manual.py
+++ b/test/unit/manual.py
@@ -4,6 +4,7 @@ Unit testing for the stem.manual module.
 
 import io
 import os
+import re
 import tempfile
 import unittest
 
@@ -237,30 +238,21 @@ class TestManual(unittest.TestCase):
     self.assertTrue(len(manual.config_options) > 200)
 
   def test_download_man_page_without_arguments(self):
-    try:
-      stem.manual.download_man_page()
-      self.fail('we should fail without a path or file handler')
-    except ValueError as exc:
-      self.assertEqual("Either the path or file_handle we're saving to must be provided", str(exc))
+    exc_msg = "Either the path or file_handle we're saving to must be provided"
+    self.assertRaisesRegexp(ValueError, exc_msg, stem.manual.download_man_page)
 
   @patch('stem.util.system.is_available', Mock(return_value = False))
   def test_download_man_page_requires_a2x(self):
-    try:
-      stem.manual.download_man_page('/tmp/no_such_file')
-      self.fail('we should require a2x to be available')
-    except IOError as exc:
-      self.assertEqual('We require a2x from asciidoc to provide a man page', str(exc))
+    exc_msg = 'We require a2x from asciidoc to provide a man page'
+    self.assertRaisesRegexp(IOError, exc_msg, stem.manual.download_man_page, '/tmp/no_such_file')
 
   @patch('tempfile.mkdtemp', Mock(return_value = '/no/such/path'))
   @patch('shutil.rmtree', Mock())
   @patch('stem.manual.open', Mock(side_effect = IOError('unable to write to file')), create = True)
   @patch('stem.util.system.is_available', Mock(return_value = True))
   def test_download_man_page_when_unable_to_write(self):
-    try:
-      stem.manual.download_man_page('/tmp/no_such_file')
-      self.fail("we shouldn't be able to write to /no/such/path")
-    except IOError as exc:
-      self.assertEqual("Unable to download tor's manual from https://gitweb.torproject.org/tor.git/plain/doc/tor.1.txt to /no/such/path/tor.1.txt: unable to write to file", str(exc))
+    exc_msg = "Unable to download tor's manual from https://gitweb.torproject.org/tor.git/plain/doc/tor.1.txt to /no/such/path/tor.1.txt: unable to write to file"
+    self.assertRaisesRegexp(IOError, re.escape(exc_msg), stem.manual.download_man_page, '/tmp/no_such_file')
 
   @patch('tempfile.mkdtemp', Mock(return_value = '/no/such/path'))
   @patch('shutil.rmtree', Mock())
@@ -268,11 +260,8 @@ class TestManual(unittest.TestCase):
   @patch('stem.util.system.is_available', Mock(return_value = True))
   @patch(URL_OPEN, Mock(side_effect = urllib.URLError('<urlopen error [Errno -2] Name or service not known>')))
   def test_download_man_page_when_download_fails(self):
-    try:
-      stem.manual.download_man_page('/tmp/no_such_file', url = 'https://www.atagar.com/foo/bar')
-      self.fail("downloading from test_invalid_url.org shouldn't work")
-    except IOError as exc:
-      self.assertEqual("Unable to download tor's manual from https://www.atagar.com/foo/bar to /no/such/path/tor.1.txt: <urlopen error <urlopen error [Errno -2] Name or service not known>>", str(exc))
+    exc_msg = "Unable to download tor's manual from https://www.atagar.com/foo/bar to /no/such/path/tor.1.txt: <urlopen error <urlopen error [Errno -2] Name or service not known>>"
+    self.assertRaisesRegexp(IOError, re.escape(exc_msg), stem.manual.download_man_page, '/tmp/no_such_file', url = 'https://www.atagar.com/foo/bar')
 
   @patch('tempfile.mkdtemp', Mock(return_value = '/no/such/path'))
   @patch('shutil.rmtree', Mock())
@@ -281,11 +270,8 @@ class TestManual(unittest.TestCase):
   @patch('stem.util.system.is_available', Mock(return_value = True))
   @patch(URL_OPEN, Mock(return_value = io.BytesIO(b'test content')))
   def test_download_man_page_when_a2x_fails(self):
-    try:
-      stem.manual.download_man_page('/tmp/no_such_file', url = 'https://www.atagar.com/foo/bar')
-      self.fail("downloading from test_invalid_url.org shouldn't work")
-    except IOError as exc:
-      self.assertEqual("Unable to run 'a2x -f manpage /no/such/path/tor.1.txt': call failed", str(exc))
+    exc_msg = "Unable to run 'a2x -f manpage /no/such/path/tor.1.txt': call failed"
+    self.assertRaisesRegexp(IOError, exc_msg, stem.manual.download_man_page, '/tmp/no_such_file', url = 'https://www.atagar.com/foo/bar')
 
   @patch('tempfile.mkdtemp', Mock(return_value = '/no/such/path'))
   @patch('shutil.rmtree', Mock())
@@ -310,11 +296,8 @@ class TestManual(unittest.TestCase):
   @patch('stem.util.system.is_mac', Mock(return_value = False))
   @patch('stem.util.system.call', Mock(side_effect = OSError('man --encoding=ascii -P cat tor returned exit status 16')))
   def test_from_man_when_manual_is_unavailable(self):
-    try:
-      stem.manual.Manual.from_man()
-      self.fail("fetching the manual should fail when it's unavailable")
-    except IOError as exc:
-      self.assertEqual("Unable to run 'man --encoding=ascii -P cat tor': man --encoding=ascii -P cat tor returned exit status 16", str(exc))
+    exc_msg = "Unable to run 'man --encoding=ascii -P cat tor': man --encoding=ascii -P cat tor returned exit status 16"
+    self.assertRaisesRegexp(IOError, exc_msg, stem.manual.Manual.from_man)
 
   @patch('stem.util.system.call', Mock(return_value = []))
   def test_when_man_is_empty(self):
diff --git a/test/unit/response/add_onion.py b/test/unit/response/add_onion.py
index 64e3688..1213f0c 100644
--- a/test/unit/response/add_onion.py
+++ b/test/unit/response/add_onion.py
@@ -95,21 +95,13 @@ class TestAddOnionResponse(unittest.TestCase):
     Checks a response that lack an initial service id.
     """
 
-    try:
-      response = mocking.get_message(WRONG_FIRST_KEY)
-      stem.response.convert('ADD_ONION', response)
-      self.fail("we should've raised a ProtocolError")
-    except stem.ProtocolError as exc:
-      self.assertTrue(str(exc).startswith('ADD_ONION response should start with'))
+    response = mocking.get_message(WRONG_FIRST_KEY)
+    self.assertRaisesRegexp(stem.ProtocolError, 'ADD_ONION response should start with', stem.response.convert, 'ADD_ONION', response)
 
   def test_no_key_type(self):
     """
     Checks a response that's missing the private key type.
     """
 
-    try:
-      response = mocking.get_message(MISSING_KEY_TYPE)
-      stem.response.convert('ADD_ONION', response)
-      self.fail("we should've raised a ProtocolError")
-    except stem.ProtocolError as exc:
-      self.assertTrue(str(exc).startswith('ADD_ONION PrivateKey lines should be of the form'))
+    response = mocking.get_message(MISSING_KEY_TYPE)
+    self.assertRaisesRegexp(stem.ProtocolError, 'ADD_ONION PrivateKey lines should be of the form', stem.response.convert, 'ADD_ONION', response)
diff --git a/test/unit/util/proc.py b/test/unit/util/proc.py
index 72b7131..5f65212 100644
--- a/test/unit/util/proc.py
+++ b/test/unit/util/proc.py
@@ -3,6 +3,7 @@ Unit testing code for the stem.util.proc functions.
 """
 
 import io
+import re
 import unittest
 
 from stem.util import proc
@@ -174,12 +175,8 @@ class TestProc(unittest.TestCase):
     error_msg = "OSError: [Errno 2] No such file or directory: '/proc/2118/fd'"
     listdir_mock.side_effect = OSError(error_msg)
 
-    try:
-      proc.file_descriptors_used(2118)
-      self.fail('We should raise when listdir() fails')
-    except IOError as exc:
-      expected = 'Unable to check number of file descriptors used: %s' % error_msg
-      self.assertEqual(expected, str(exc))
+    exc_msg = 'Unable to check number of file descriptors used: %s' % error_msg
+    self.assertRaisesRegexp(IOError, re.escape(exc_msg), proc.file_descriptors_used, 2118)
 
     # successful calls
 





More information about the tor-commits mailing list