[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [stem/master] Unit tests for minimal vote and missing fields
commit 725e2f9deb1a22b5cfca0242ea79d5c0548a40e3
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Sat Sep 8 10:26:48 2012 -0700
Unit tests for minimal vote and missing fields
Unit tests for a couple important use cases and lots 'o fixes for the issues
they uncovered. As mentioned earlier the 'validate' attribute took the wrong
meaning in this parser so valid content errors and invalid content triggers
stacktraces.
---
stem/descriptor/networkstatus.py | 47 ++++++++++++++---
test/unit/descriptor/networkstatus/document.py | 65 +++++++++++++++++++++--
2 files changed, 97 insertions(+), 15 deletions(-)
diff --git a/stem/descriptor/networkstatus.py b/stem/descriptor/networkstatus.py
index 6f1f7b1..e55c06f 100644
--- a/stem/descriptor/networkstatus.py
+++ b/stem/descriptor/networkstatus.py
@@ -308,25 +308,49 @@ class NetworkStatusDocument(stem.descriptor.Descriptor):
if validate and not self._validate_network_status_version():
raise ValueError("Invalid network-status-version: %s" % self.network_status_version)
+ vote = False
if self.vote_status == "vote": vote = True
elif self.vote_status == "consensus": vote = False
elif validate: raise ValueError("Unrecognized vote-status")
if vote:
read_keyword_line("consensus-methods", True)
- self.consensus_methods = [int(method) for method in self.consensus_methods.split(" ")]
- self.published = _strptime(_read_keyword_line("published", content, validate, True), validate, True)
+
+ if self.consensus_methods:
+ self.consensus_methods = [int(method) for method in self.consensus_methods.split(" ")]
+ else:
+ self.consensus_methods = []
+
+ line = _read_keyword_line("published", content, validate, True)
+
+ if line:
+ self.published = _strptime(line, validate, True)
else:
read_keyword_line("consensus-method", True)
if self.consensus_method != None:
self.consensus_method = int(self.consensus_method)
map(read_keyword_line, ["valid-after", "fresh-until", "valid-until"])
- self.valid_after = _strptime(self.valid_after, validate)
- self.fresh_until = _strptime(self.fresh_until, validate)
- self.valid_until = _strptime(self.valid_until, validate)
+
+ if self.valid_after:
+ self.valid_after = _strptime(self.valid_after, validate)
+ elif validate:
+ raise ValueError("Missing the 'valid-after' field")
+
+ if self.fresh_until:
+ self.fresh_until = _strptime(self.fresh_until, validate)
+ elif validate:
+ raise ValueError("Missing the 'fresh-until' field")
+
+ if self.valid_until:
+ self.valid_until = _strptime(self.valid_until, validate)
+ elif validate:
+ raise ValueError("Missing the 'valid-until' field")
+
voting_delay = _read_keyword_line("voting-delay", content, validate)
- self.vote_delay, self.dist_delay = [int(delay) for delay in voting_delay.split(" ")]
+
+ if voting_delay:
+ self.vote_delay, self.dist_delay = [int(delay) for delay in voting_delay.split(" ")]
client_versions = _read_keyword_line("client-versions", content, validate, True)
if client_versions:
@@ -334,7 +358,12 @@ class NetworkStatusDocument(stem.descriptor.Descriptor):
server_versions = _read_keyword_line("server-versions", content, validate, True)
if server_versions:
self.server_versions = [stem.version.Version(version_string) for version_string in server_versions.split(",")]
- self.known_flags = _read_keyword_line("known-flags", content, validate).split(" ")
+
+ flags_content = _read_keyword_line("known-flags", content, validate)
+
+ if flags_content:
+ self.known_flags = flags_content.split(" ")
+
read_keyword_line("params", True)
if self.params:
self.params = dict([(param.split("=")[0], int(param.split("=")[1])) for param in self.params.split(" ")])
@@ -346,7 +375,7 @@ class NetworkStatusDocument(stem.descriptor.Descriptor):
self.directory_authorities.append(DirectoryAuthority(dirauth_data, vote, validate))
# footer section
- if self.consensus_method >= 9 or vote and filter(lambda x: x >= 9, self.consensus_methods):
+ if self.consensus_method >= 9 or (vote and filter(lambda x: x >= 9, self.consensus_methods)):
if _peek_keyword(content) == "directory-footer":
content.readline()
elif validate:
@@ -364,7 +393,7 @@ class NetworkStatusDocument(stem.descriptor.Descriptor):
remainder = content.read()
if remainder:
- self.unrecognized_lines = content.read().split("\n")
+ self.unrecognized_lines = remainder.split("\n")
else:
self.unrecognized_lines = []
diff --git a/test/unit/descriptor/networkstatus/document.py b/test/unit/descriptor/networkstatus/document.py
index 027347b..8f770bb 100644
--- a/test/unit/descriptor/networkstatus/document.py
+++ b/test/unit/descriptor/networkstatus/document.py
@@ -10,6 +10,7 @@ from stem.descriptor.networkstatus import HEADER_STATUS_DOCUMENT_FIELDS, FOOTER_
NETWORK_STATUS_DOCUMENT_ATTR = {
"network-status-version": "3",
"vote-status": "consensus",
+ "consensus-methods": "9",
"consensus-method": "9",
"published": "2012-09-02 22:00:00",
"valid-after": "2012-09-02 22:00:00",
@@ -57,17 +58,20 @@ def get_network_status_document(attr = None, exclude = None, routers = None):
if not field in attr:
# Skip if it's not mandatory for this type of document. An exception is
- # made for the consensus' consensus-method field since it influences
- # validation, and is only missing for consensus-method lower than 2.
+ # made for the consensus' consensus-method and consensus-methods fields
+ # since it influences validation, and is only missing for
+ # consensus-method lower than 2.
if field == "consensus-method" and is_consensus:
pass
- elif not is_mandatory or not ((is_consensus and in_consensus) or (is_vote and in_vote)):
+ elif field == "consensus-methods" and is_vote:
+ pass
+ elif not is_mandatory or not ((is_consensus and in_consensus) or (is_vote and in_votes)):
continue
if field in attr:
- value = attr[keyword]
- del attr[keyword]
+ value = attr[field]
+ del attr[field]
elif field in NETWORK_STATUS_DOCUMENT_ATTR:
value = NETWORK_STATUS_DOCUMENT_ATTR[field]
@@ -82,7 +86,7 @@ def get_network_status_document(attr = None, exclude = None, routers = None):
return "\n".join(header_content + remainder + routers + footer_content)
class TestNetworkStatusDocument(unittest.TestCase):
- def test_document_minimal(self):
+ def test_minimal_consensus(self):
"""
Parses a minimal network status document.
"""
@@ -114,4 +118,53 @@ class TestNetworkStatusDocument(unittest.TestCase):
self.assertEqual(None, document.bandwidth_weights)
self.assertEqual([sig], document.directory_signatures)
self.assertEqual([], document.get_unrecognized_lines())
+
+ def test_minimal_vote(self):
+ """
+ Parses a minimal network status document.
+ """
+
+ document = NetworkStatusDocument(get_network_status_document({"vote-status": "vote"}))
+
+ expected_known_flags = [Flag.AUTHORITY, Flag.BADEXIT, Flag.EXIT,
+ Flag.FAST, Flag.GUARD, Flag.HSDIR, Flag.NAMED, Flag.RUNNING,
+ Flag.STABLE, Flag.UNNAMED, Flag.V2DIR, Flag.VALID]
+
+ sig = DirectorySignature("directory-signature " + NETWORK_STATUS_DOCUMENT_ATTR["directory-signature"])
+
+ self.assertEqual((), document.routers)
+ self.assertEqual("3", document.network_status_version)
+ self.assertEqual("vote", document.vote_status)
+ self.assertEqual(None, document.consensus_method)
+ self.assertEqual([9], document.consensus_methods)
+ self.assertEqual(datetime.datetime(2012, 9, 2, 22, 0, 0), document.published)
+ self.assertEqual(datetime.datetime(2012, 9, 2, 22, 0, 0), document.valid_after)
+ self.assertEqual(datetime.datetime(2012, 9, 2, 22, 0, 0), document.fresh_until)
+ self.assertEqual(datetime.datetime(2012, 9, 2, 22, 0, 0), document.valid_until)
+ self.assertEqual(300, document.vote_delay)
+ self.assertEqual(300, document.dist_delay)
+ self.assertEqual([], document.client_versions)
+ self.assertEqual([], document.server_versions)
+ self.assertEqual(expected_known_flags, document.known_flags)
+ self.assertEqual(None, document.params)
+ self.assertEqual([], document.directory_authorities)
+ self.assertEqual({}, document.bandwidth_weights)
+ self.assertEqual([sig], document.directory_signatures)
+ self.assertEqual([], document.get_unrecognized_lines())
+
+ def test_missing_fields(self):
+ """
+ Excludes mandatory fields from both a vote and consensus document.
+ """
+
+ for is_consensus in (True, False):
+ attr = {"vote-status": "consensus"} if is_consensus else {"vote-status": "vote"}
+ is_vote = not is_consensus
+
+ for entries in (HEADER_STATUS_DOCUMENT_FIELDS, FOOTER_STATUS_DOCUMENT_FIELDS):
+ for field, in_votes, in_consensus, is_mandatory in entries:
+ if is_mandatory and ((is_consensus and in_consensus) or (is_vote and in_votes)):
+ content = get_network_status_document(attr, exclude = (field,))
+ self.assertRaises(ValueError, NetworkStatusDocument, content)
+ NetworkStatusDocument(content, False) # constructs without validation
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits