[tor-commits] [stem/master] Unit tests for minimal vote and missing fields

atagar at torproject.org atagar at torproject.org
Sat Oct 13 18:35:45 UTC 2012


commit 725e2f9deb1a22b5cfca0242ea79d5c0548a40e3
Author: Damian Johnson <atagar at torproject.org>
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
 





More information about the tor-commits mailing list