commit c4c998f6201dae0123c068c3e081a26d6af8a99b
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Nov 5 14:10:07 2011 -0700
Better organizing unit tests for stem.types
---
run_tests.py | 12 +-
test/unit/__init__.py | 2 +-
test/unit/message.py | 189 ------------------------------------
test/unit/types.py | 29 ------
test/unit/types/__init__.py | 6 +
test/unit/types/control_line.py | 29 ++++++
test/unit/types/control_message.py | 189 ++++++++++++++++++++++++++++++++++++
test/unit/types/version.py | 121 +++++++++++++++++++++++
test/unit/version.py | 121 -----------------------
9 files changed, 352 insertions(+), 346 deletions(-)
diff --git a/run_tests.py b/run_tests.py
index 15a6c92..c0731d9 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -10,9 +10,9 @@ import time
import getopt
import unittest
import test.runner
-import test.unit.message
-import test.unit.version
-import test.unit.types
+import test.unit.types.control_message
+import test.unit.types.control_line
+import test.unit.types.version
import test.integ.message
import test.integ.system
@@ -23,9 +23,9 @@ OPT_EXPANDED = ["unit", "integ", "config=", "targets=", "help"]
DIVIDER = "=" * 70
# (name, class) tuples for all of our unit and integration tests
-UNIT_TESTS = (("stem.types.ControlMessage", test.unit.message.TestMessageFunctions),
- ("stem.types.Version", test.unit.version.TestVerionFunctions),
- ("stem.types.get_entry", test.unit.types.TestGetEntry),
+UNIT_TESTS = (("stem.types.ControlMessage", test.unit.types.control_message.TestControlMessage),
+ ("stem.types.ControlLine", test.unit.types.control_line.TestGetEntry),
+ ("stem.types.Version", test.unit.types.version.TestVerion),
)
INTEG_TESTS = (("stem.types.ControlMessage", test.integ.message.TestMessageFunctions),
diff --git a/test/unit/__init__.py b/test/unit/__init__.py
index 52a6924..3c9e3b6 100644
--- a/test/unit/__init__.py
+++ b/test/unit/__init__.py
@@ -2,5 +2,5 @@
Unit tests for the stem library.
"""
-__all__ = ["message", "version"]
+__all__ = []
diff --git a/test/unit/message.py b/test/unit/message.py
deleted file mode 100644
index 851b394..0000000
--- a/test/unit/message.py
+++ /dev/null
@@ -1,189 +0,0 @@
-"""
-Unit tests for the types.ControlMessage parsing and class.
-"""
-
-import socket
-import StringIO
-import unittest
-import stem.types
-
-OK_REPLY = "250 OK\r\n"
-
-EVENT_BW = "650 BW 32326 2856\r\n"
-EVENT_CIRC_TIMEOUT = "650 CIRC 5 FAILED PURPOSE=GENERAL REASON=TIMEOUT\r\n"
-EVENT_CIRC_LAUNCHED = "650 CIRC 9 LAUNCHED PURPOSE=GENERAL\r\n"
-EVENT_CIRC_EXTENDED = "650 CIRC 5 EXTENDED $A200F527C82C59A25CCA44884B49D3D65B122652=faktor PURPOSE=MEASURE_TIMEOUT\r\n"
-
-GETINFO_VERSION = """250-version=0.2.2.23-alpha (git-b85eb949b528f4d7)
-250 OK
-""".replace("\n", "\r\n")
-
-GETINFO_INFONAMES = """250+info/names=
-accounting/bytes -- Number of bytes read/written so far in the accounting interval.
-accounting/bytes-left -- Number of bytes left to write/read so far in the accounting interval.
-accounting/enabled -- Is accounting currently enabled?
-accounting/hibernating -- Are we hibernating or awake?
-stream-status -- List of current streams.
-version -- The current version of Tor.
-.
-250 OK
-""".replace("\n", "\r\n")
-
-class TestMessageFunctions(unittest.TestCase):
- """
- Tests methods and functions related to 'types.ControlMessage'. This uses
- StringIO to make 'files' to mock socket input.
- """
-
- def test_ok_response(self):
- """
- Checks the basic 'OK' response that we get for most commands.
- """
-
- message = self.assert_message_parses(OK_REPLY)
- self.assertEquals("OK", str(message))
-
- contents = message.content()
- self.assertEquals(1, len(contents))
- self.assertEquals(("250", " ", "OK"), contents[0])
-
- def test_event_response(self):
- """
- Checks parsing of actual events.
- """
-
- # BW event
- message = self.assert_message_parses(EVENT_BW)
- self.assertEquals("BW 32326 2856", str(message))
-
- contents = message.content()
- self.assertEquals(1, len(contents))
- self.assertEquals(("650", " ", "BW 32326 2856"), contents[0])
-
- # few types of CIRC events
- for circ_content in (EVENT_CIRC_TIMEOUT, EVENT_CIRC_LAUNCHED, EVENT_CIRC_EXTENDED):
- message = self.assert_message_parses(circ_content)
- self.assertEquals(circ_content[4:-2], str(message))
-
- contents = message.content()
- self.assertEquals(1, len(contents))
- self.assertEquals(("650", " ", str(message)), contents[0])
-
- def test_getinfo_response(self):
- """
- Checks parsing of actual GETINFO responses.
- """
-
- # GETINFO version (basic single-line results)
- message = self.assert_message_parses(GETINFO_VERSION)
- self.assertEquals(2, len(list(message)))
- self.assertEquals(2, len(str(message).split("\n")))
-
- # manually checks the contents
- contents = message.content()
- self.assertEquals(2, len(contents))
- self.assertEquals(("250", "-", "version=0.2.2.23-alpha (git-b85eb949b528f4d7)"), contents[0])
- self.assertEquals(("250", " ", "OK"), contents[1])
-
- # GETINFO info/names (data entry)
- message = self.assert_message_parses(GETINFO_INFONAMES)
- self.assertEquals(2, len(list(message)))
- self.assertEquals(8, len(str(message).split("\n")))
-
- # manually checks the contents
- contents = message.content()
- self.assertEquals(2, len(contents))
-
- first_entry = (contents[0][0], contents[0][1], contents[0][2][:contents[0][2].find("\n")])
- self.assertEquals(("250", "+", "info/names="), first_entry)
- self.assertEquals(("250", " ", "OK"), contents[1])
-
- def test_no_crlf(self):
- """
- Checks that we get a ProtocolError when we don't have both a carrage
- returna and newline for line endings. This doesn't really check for
- newlines (since that's what readline would break on), but not the end of
- the world.
- """
-
- # Replaces each of the CRLF entries with just LF, confirming that this
- # causes a parsing error. This should test line endings for both data
- # entry parsing and non-data.
-
- infonames_lines = [line + "\n" for line in GETINFO_INFONAMES.split("\n")[:-1]]
-
- for i in range(len(infonames_lines)):
- # replace the CRLF for the line
- infonames_lines[i] = infonames_lines[i].rstrip("\r\n") + "\n"
- test_socket_file = StringIO.StringIO("".join(infonames_lines))
- self.assertRaises(stem.types.ProtocolError, stem.types.read_message, test_socket_file)
-
- # puts the CRLF back
- infonames_lines[i] = infonames_lines[i].rstrip("\n") + "\r\n"
-
- # sanity check the above test isn't broken due to leaving infonames_lines
- # with invalid data
-
- self.assert_message_parses("".join(infonames_lines))
-
- def test_malformed_prefix(self):
- """
- Checks parsing for responses where the header is missing a digit or divider.
- """
-
- for i in range(len(EVENT_BW)):
- # makes test input with that character missing or replaced
- removal_test_input = EVENT_BW[:i] + EVENT_BW[i + 1:]
- replacement_test_input = EVENT_BW[:i] + "#" + EVENT_BW[i + 1:]
-
- if i < 4 or i >= (len(EVENT_BW) - 2):
- # dropping the character should cause an error if...
- # - this is part of the message prefix
- # - this is disrupting the line ending
-
- self.assertRaises(stem.types.ProtocolError, stem.types.read_message, StringIO.StringIO(removal_test_input))
- self.assertRaises(stem.types.ProtocolError, stem.types.read_message, StringIO.StringIO(replacement_test_input))
- else:
- # otherwise the data will be malformed, but this goes undetected
- self.assert_message_parses(removal_test_input)
- self.assert_message_parses(replacement_test_input)
-
- def test_disconnected_socket(self):
- """
- Tests when the read function is given a file derived from a disconnected
- socket.
- """
-
- control_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- control_socket_file = control_socket.makefile()
- self.assertRaises(stem.types.ControlSocketClosed, stem.types.read_message, control_socket_file)
-
- def assert_message_parses(self, controller_reply):
- """
- Performs some basic sanity checks that a reply mirrors its parsed result.
-
- Returns:
- types.ControlMessage for the given input
- """
-
- message = stem.types.read_message(StringIO.StringIO(controller_reply))
-
- # checks that the raw_content equals the input value
- self.assertEqual(controller_reply, message.raw_content())
-
- # checks that the contents match the input
- message_lines = str(message).split("\n")
- controller_lines = controller_reply.split("\r\n")
- controller_lines.pop() # the ControlMessage won't have a trailing newline
-
- while controller_lines:
- line = controller_lines.pop(0)
-
- # mismatching lines with just a period are probably data termination
- if line == "." and (not message_lines or line != message_lines[0]):
- continue
-
- self.assertTrue(line.endswith(message_lines.pop(0)))
-
- return message
-
diff --git a/test/unit/types.py b/test/unit/types.py
deleted file mode 100644
index 9c06ab2..0000000
--- a/test/unit/types.py
+++ /dev/null
@@ -1,29 +0,0 @@
-"""
-Unit tests for the types.get_entry function.
-"""
-
-import unittest
-import stem.types
-
-class TestGetEntry(unittest.TestCase):
- """
- Tests the types.get_entry function.
- """
-
- def test_examples(self):
- """
- Checks that the examples from the pydoc are correct.
- """
-
- example_input = 'hello there random person'
- example_result = (None, "hello", "there random person")
- self.assertEquals(stem.types.get_entry(example_input), example_result)
-
- example_input = 'version="0.1.2.3"'
- example_result = ("version", "0.1.2.3", "")
- self.assertEquals(stem.types.get_entry(example_input, True, True), example_result)
-
- example_input = r'"this has a \" and \\ in it" foo=bar more_data'
- example_result = (None, r'this has a " and \ in it', "foo=bar more_data")
- self.assertEquals(stem.types.get_entry(example_input, False, True, True), example_result)
-
diff --git a/test/unit/types/__init__.py b/test/unit/types/__init__.py
new file mode 100644
index 0000000..d472371
--- /dev/null
+++ b/test/unit/types/__init__.py
@@ -0,0 +1,6 @@
+"""
+Unit tests for stem.types.
+"""
+
+__all__ = ["control_message", "control_line", "version"]
+
diff --git a/test/unit/types/control_line.py b/test/unit/types/control_line.py
new file mode 100644
index 0000000..9c06ab2
--- /dev/null
+++ b/test/unit/types/control_line.py
@@ -0,0 +1,29 @@
+"""
+Unit tests for the types.get_entry function.
+"""
+
+import unittest
+import stem.types
+
+class TestGetEntry(unittest.TestCase):
+ """
+ Tests the types.get_entry function.
+ """
+
+ def test_examples(self):
+ """
+ Checks that the examples from the pydoc are correct.
+ """
+
+ example_input = 'hello there random person'
+ example_result = (None, "hello", "there random person")
+ self.assertEquals(stem.types.get_entry(example_input), example_result)
+
+ example_input = 'version="0.1.2.3"'
+ example_result = ("version", "0.1.2.3", "")
+ self.assertEquals(stem.types.get_entry(example_input, True, True), example_result)
+
+ example_input = r'"this has a \" and \\ in it" foo=bar more_data'
+ example_result = (None, r'this has a " and \ in it', "foo=bar more_data")
+ self.assertEquals(stem.types.get_entry(example_input, False, True, True), example_result)
+
diff --git a/test/unit/types/control_message.py b/test/unit/types/control_message.py
new file mode 100644
index 0000000..563a0bf
--- /dev/null
+++ b/test/unit/types/control_message.py
@@ -0,0 +1,189 @@
+"""
+Unit tests for the types.ControlMessage parsing and class.
+"""
+
+import socket
+import StringIO
+import unittest
+import stem.types
+
+OK_REPLY = "250 OK\r\n"
+
+EVENT_BW = "650 BW 32326 2856\r\n"
+EVENT_CIRC_TIMEOUT = "650 CIRC 5 FAILED PURPOSE=GENERAL REASON=TIMEOUT\r\n"
+EVENT_CIRC_LAUNCHED = "650 CIRC 9 LAUNCHED PURPOSE=GENERAL\r\n"
+EVENT_CIRC_EXTENDED = "650 CIRC 5 EXTENDED $A200F527C82C59A25CCA44884B49D3D65B122652=faktor PURPOSE=MEASURE_TIMEOUT\r\n"
+
+GETINFO_VERSION = """250-version=0.2.2.23-alpha (git-b85eb949b528f4d7)
+250 OK
+""".replace("\n", "\r\n")
+
+GETINFO_INFONAMES = """250+info/names=
+accounting/bytes -- Number of bytes read/written so far in the accounting interval.
+accounting/bytes-left -- Number of bytes left to write/read so far in the accounting interval.
+accounting/enabled -- Is accounting currently enabled?
+accounting/hibernating -- Are we hibernating or awake?
+stream-status -- List of current streams.
+version -- The current version of Tor.
+.
+250 OK
+""".replace("\n", "\r\n")
+
+class TestControlMessage(unittest.TestCase):
+ """
+ Tests methods and functions related to 'types.ControlMessage'. This uses
+ StringIO to make 'files' to mock socket input.
+ """
+
+ def test_ok_response(self):
+ """
+ Checks the basic 'OK' response that we get for most commands.
+ """
+
+ message = self.assert_message_parses(OK_REPLY)
+ self.assertEquals("OK", str(message))
+
+ contents = message.content()
+ self.assertEquals(1, len(contents))
+ self.assertEquals(("250", " ", "OK"), contents[0])
+
+ def test_event_response(self):
+ """
+ Checks parsing of actual events.
+ """
+
+ # BW event
+ message = self.assert_message_parses(EVENT_BW)
+ self.assertEquals("BW 32326 2856", str(message))
+
+ contents = message.content()
+ self.assertEquals(1, len(contents))
+ self.assertEquals(("650", " ", "BW 32326 2856"), contents[0])
+
+ # few types of CIRC events
+ for circ_content in (EVENT_CIRC_TIMEOUT, EVENT_CIRC_LAUNCHED, EVENT_CIRC_EXTENDED):
+ message = self.assert_message_parses(circ_content)
+ self.assertEquals(circ_content[4:-2], str(message))
+
+ contents = message.content()
+ self.assertEquals(1, len(contents))
+ self.assertEquals(("650", " ", str(message)), contents[0])
+
+ def test_getinfo_response(self):
+ """
+ Checks parsing of actual GETINFO responses.
+ """
+
+ # GETINFO version (basic single-line results)
+ message = self.assert_message_parses(GETINFO_VERSION)
+ self.assertEquals(2, len(list(message)))
+ self.assertEquals(2, len(str(message).split("\n")))
+
+ # manually checks the contents
+ contents = message.content()
+ self.assertEquals(2, len(contents))
+ self.assertEquals(("250", "-", "version=0.2.2.23-alpha (git-b85eb949b528f4d7)"), contents[0])
+ self.assertEquals(("250", " ", "OK"), contents[1])
+
+ # GETINFO info/names (data entry)
+ message = self.assert_message_parses(GETINFO_INFONAMES)
+ self.assertEquals(2, len(list(message)))
+ self.assertEquals(8, len(str(message).split("\n")))
+
+ # manually checks the contents
+ contents = message.content()
+ self.assertEquals(2, len(contents))
+
+ first_entry = (contents[0][0], contents[0][1], contents[0][2][:contents[0][2].find("\n")])
+ self.assertEquals(("250", "+", "info/names="), first_entry)
+ self.assertEquals(("250", " ", "OK"), contents[1])
+
+ def test_no_crlf(self):
+ """
+ Checks that we get a ProtocolError when we don't have both a carrage
+ returna and newline for line endings. This doesn't really check for
+ newlines (since that's what readline would break on), but not the end of
+ the world.
+ """
+
+ # Replaces each of the CRLF entries with just LF, confirming that this
+ # causes a parsing error. This should test line endings for both data
+ # entry parsing and non-data.
+
+ infonames_lines = [line + "\n" for line in GETINFO_INFONAMES.split("\n")[:-1]]
+
+ for i in range(len(infonames_lines)):
+ # replace the CRLF for the line
+ infonames_lines[i] = infonames_lines[i].rstrip("\r\n") + "\n"
+ test_socket_file = StringIO.StringIO("".join(infonames_lines))
+ self.assertRaises(stem.types.ProtocolError, stem.types.read_message, test_socket_file)
+
+ # puts the CRLF back
+ infonames_lines[i] = infonames_lines[i].rstrip("\n") + "\r\n"
+
+ # sanity check the above test isn't broken due to leaving infonames_lines
+ # with invalid data
+
+ self.assert_message_parses("".join(infonames_lines))
+
+ def test_malformed_prefix(self):
+ """
+ Checks parsing for responses where the header is missing a digit or divider.
+ """
+
+ for i in range(len(EVENT_BW)):
+ # makes test input with that character missing or replaced
+ removal_test_input = EVENT_BW[:i] + EVENT_BW[i + 1:]
+ replacement_test_input = EVENT_BW[:i] + "#" + EVENT_BW[i + 1:]
+
+ if i < 4 or i >= (len(EVENT_BW) - 2):
+ # dropping the character should cause an error if...
+ # - this is part of the message prefix
+ # - this is disrupting the line ending
+
+ self.assertRaises(stem.types.ProtocolError, stem.types.read_message, StringIO.StringIO(removal_test_input))
+ self.assertRaises(stem.types.ProtocolError, stem.types.read_message, StringIO.StringIO(replacement_test_input))
+ else:
+ # otherwise the data will be malformed, but this goes undetected
+ self.assert_message_parses(removal_test_input)
+ self.assert_message_parses(replacement_test_input)
+
+ def test_disconnected_socket(self):
+ """
+ Tests when the read function is given a file derived from a disconnected
+ socket.
+ """
+
+ control_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ control_socket_file = control_socket.makefile()
+ self.assertRaises(stem.types.ControlSocketClosed, stem.types.read_message, control_socket_file)
+
+ def assert_message_parses(self, controller_reply):
+ """
+ Performs some basic sanity checks that a reply mirrors its parsed result.
+
+ Returns:
+ types.ControlMessage for the given input
+ """
+
+ message = stem.types.read_message(StringIO.StringIO(controller_reply))
+
+ # checks that the raw_content equals the input value
+ self.assertEqual(controller_reply, message.raw_content())
+
+ # checks that the contents match the input
+ message_lines = str(message).split("\n")
+ controller_lines = controller_reply.split("\r\n")
+ controller_lines.pop() # the ControlMessage won't have a trailing newline
+
+ while controller_lines:
+ line = controller_lines.pop(0)
+
+ # mismatching lines with just a period are probably data termination
+ if line == "." and (not message_lines or line != message_lines[0]):
+ continue
+
+ self.assertTrue(line.endswith(message_lines.pop(0)))
+
+ return message
+
diff --git a/test/unit/types/version.py b/test/unit/types/version.py
new file mode 100644
index 0000000..fb04641
--- /dev/null
+++ b/test/unit/types/version.py
@@ -0,0 +1,121 @@
+"""
+Unit tests for the types.Version parsing and class.
+"""
+
+import unittest
+import stem.types
+
+class TestVerion(unittest.TestCase):
+ """
+ Tests methods and functions related to 'types.Version'.
+ """
+
+ def test_parsing(self):
+ """
+ Tests parsing by the Version class constructor.
+ """
+
+ # valid versions with various number of compontents to the version
+ version = stem.types.Version("0.1.2.3-tag")
+ self.assert_versions_match(version, 0, 1, 2, 3, "tag")
+
+ version = stem.types.Version("0.1.2.3")
+ self.assert_versions_match(version, 0, 1, 2, 3, None)
+
+ version = stem.types.Version("0.1.2-tag")
+ self.assert_versions_match(version, 0, 1, 2, None, "tag")
+
+ version = stem.types.Version("0.1.2")
+ self.assert_versions_match(version, 0, 1, 2, None, None)
+
+ # checks an empty tag
+ version = stem.types.Version("0.1.2.3-")
+ self.assert_versions_match(version, 0, 1, 2, 3, "")
+
+ version = stem.types.Version("0.1.2-")
+ self.assert_versions_match(version, 0, 1, 2, None, "")
+
+ # checks invalid version strings
+ self.assertRaises(ValueError, stem.types.Version, "")
+ self.assertRaises(ValueError, stem.types.Version, "1.2.3.4nodash")
+ self.assertRaises(ValueError, stem.types.Version, "1.2.3.a")
+ self.assertRaises(ValueError, stem.types.Version, "1.2.a.4")
+ self.assertRaises(ValueError, stem.types.Version, "12.3")
+ self.assertRaises(ValueError, stem.types.Version, "1.-2.3")
+
+ def test_comparison(self):
+ """
+ Tests comparision between Version instances.
+ """
+
+ # check for basic incrementing in each portion
+ self.assert_version_is_greater("1.1.2.3-tag", "0.1.2.3-tag")
+ self.assert_version_is_greater("0.2.2.3-tag", "0.1.2.3-tag")
+ self.assert_version_is_greater("0.1.3.3-tag", "0.1.2.3-tag")
+ self.assert_version_is_greater("0.1.2.4-tag", "0.1.2.3-tag")
+ self.assert_version_is_greater("0.1.2.3-ugg", "0.1.2.3-tag")
+ self.assert_version_is_equal("0.1.2.3-tag", "0.1.2.3-tag")
+
+ # checks that a missing patch level equals zero
+ self.assert_version_is_equal("0.1.2", "0.1.2.0")
+ self.assert_version_is_equal("0.1.2-tag", "0.1.2.0-tag")
+
+ # checks for missing patch or status
+ self.assert_version_is_greater("0.1.2.3-tag", "0.1.2.3")
+ self.assert_version_is_greater("0.1.2.3-tag", "0.1.2-tag")
+ self.assert_version_is_greater("0.1.2.3-tag", "0.1.2")
+
+ self.assert_version_is_equal("0.1.2.3", "0.1.2.3")
+ self.assert_version_is_equal("0.1.2", "0.1.2")
+
+ def test_string(self):
+ """
+ Tests the Version -> string conversion.
+ """
+
+ # checks conversion with various numbers of arguments
+
+ self.assert_string_matches("0.1.2.3-tag")
+ self.assert_string_matches("0.1.2.3")
+ self.assert_string_matches("0.1.2")
+
+ def assert_versions_match(self, version, major, minor, micro, patch, status):
+ """
+ Asserts that the values for a types.Version instance match the given
+ values.
+ """
+
+ self.assertEqual(version.major, major)
+ self.assertEqual(version.minor, minor)
+ self.assertEqual(version.micro, micro)
+ self.assertEqual(version.patch, patch)
+ self.assertEqual(version.status, status)
+
+ def assert_version_is_greater(self, first_version, second_version):
+ """
+ Asserts that the parsed version of the first version is greate than the
+ second (also checking the inverse).
+ """
+
+ version1 = stem.types.Version(first_version)
+ version2 = stem.types.Version(second_version)
+ self.assertEqual(version1 > version2, True)
+ self.assertEqual(version1 < version2, False)
+
+ def assert_version_is_equal(self, first_version, second_version):
+ """
+ Asserts that the parsed version of the first version equals the second.
+ """
+
+ version1 = stem.types.Version(first_version)
+ version2 = stem.types.Version(second_version)
+ self.assertEqual(version1, version2)
+
+ def assert_string_matches(self, version):
+ """
+ Parses the given version string then checks that its string representation
+ matches the input.
+ """
+
+ self.assertEqual(version, str(stem.types.Version(version)))
+
diff --git a/test/unit/version.py b/test/unit/version.py
deleted file mode 100644
index 3200d05..0000000
--- a/test/unit/version.py
+++ /dev/null
@@ -1,121 +0,0 @@
-"""
-Unit tests for the types.Version parsing and class.
-"""
-
-import unittest
-import stem.types
-
-class TestVerionFunctions(unittest.TestCase):
- """
- Tests methods and functions related to 'types.Version'.
- """
-
- def test_parsing(self):
- """
- Tests parsing by the Version class constructor.
- """
-
- # valid versions with various number of compontents to the version
- version = stem.types.Version("0.1.2.3-tag")
- self.assert_versions_match(version, 0, 1, 2, 3, "tag")
-
- version = stem.types.Version("0.1.2.3")
- self.assert_versions_match(version, 0, 1, 2, 3, None)
-
- version = stem.types.Version("0.1.2-tag")
- self.assert_versions_match(version, 0, 1, 2, None, "tag")
-
- version = stem.types.Version("0.1.2")
- self.assert_versions_match(version, 0, 1, 2, None, None)
-
- # checks an empty tag
- version = stem.types.Version("0.1.2.3-")
- self.assert_versions_match(version, 0, 1, 2, 3, "")
-
- version = stem.types.Version("0.1.2-")
- self.assert_versions_match(version, 0, 1, 2, None, "")
-
- # checks invalid version strings
- self.assertRaises(ValueError, stem.types.Version, "")
- self.assertRaises(ValueError, stem.types.Version, "1.2.3.4nodash")
- self.assertRaises(ValueError, stem.types.Version, "1.2.3.a")
- self.assertRaises(ValueError, stem.types.Version, "1.2.a.4")
- self.assertRaises(ValueError, stem.types.Version, "12.3")
- self.assertRaises(ValueError, stem.types.Version, "1.-2.3")
-
- def test_comparison(self):
- """
- Tests comparision between Version instances.
- """
-
- # check for basic incrementing in each portion
- self.assert_version_is_greater("1.1.2.3-tag", "0.1.2.3-tag")
- self.assert_version_is_greater("0.2.2.3-tag", "0.1.2.3-tag")
- self.assert_version_is_greater("0.1.3.3-tag", "0.1.2.3-tag")
- self.assert_version_is_greater("0.1.2.4-tag", "0.1.2.3-tag")
- self.assert_version_is_greater("0.1.2.3-ugg", "0.1.2.3-tag")
- self.assert_version_is_equal("0.1.2.3-tag", "0.1.2.3-tag")
-
- # checks that a missing patch level equals zero
- self.assert_version_is_equal("0.1.2", "0.1.2.0")
- self.assert_version_is_equal("0.1.2-tag", "0.1.2.0-tag")
-
- # checks for missing patch or status
- self.assert_version_is_greater("0.1.2.3-tag", "0.1.2.3")
- self.assert_version_is_greater("0.1.2.3-tag", "0.1.2-tag")
- self.assert_version_is_greater("0.1.2.3-tag", "0.1.2")
-
- self.assert_version_is_equal("0.1.2.3", "0.1.2.3")
- self.assert_version_is_equal("0.1.2", "0.1.2")
-
- def test_string(self):
- """
- Tests the Version -> string conversion.
- """
-
- # checks conversion with various numbers of arguments
-
- self.assert_string_matches("0.1.2.3-tag")
- self.assert_string_matches("0.1.2.3")
- self.assert_string_matches("0.1.2")
-
- def assert_versions_match(self, version, major, minor, micro, patch, status):
- """
- Asserts that the values for a types.Version instance match the given
- values.
- """
-
- self.assertEqual(version.major, major)
- self.assertEqual(version.minor, minor)
- self.assertEqual(version.micro, micro)
- self.assertEqual(version.patch, patch)
- self.assertEqual(version.status, status)
-
- def assert_version_is_greater(self, first_version, second_version):
- """
- Asserts that the parsed version of the first version is greate than the
- second (also checking the inverse).
- """
-
- version1 = stem.types.Version(first_version)
- version2 = stem.types.Version(second_version)
- self.assertEqual(version1 > version2, True)
- self.assertEqual(version1 < version2, False)
-
- def assert_version_is_equal(self, first_version, second_version):
- """
- Asserts that the parsed version of the first version equals the second.
- """
-
- version1 = stem.types.Version(first_version)
- version2 = stem.types.Version(second_version)
- self.assertEqual(version1, version2)
-
- def assert_string_matches(self, version):
- """
- Parses the given version string then checks that its string representation
- matches the input.
- """
-
- self.assertEqual(version, str(stem.types.Version(version)))
-