commit c4c998f6201dae0123c068c3e081a26d6af8a99b Author: Damian Johnson atagar@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))) -