[tor-commits] [stem/master] Unit tests for extrainfo descriptors

atagar at torproject.org atagar at torproject.org
Mon May 14 00:14:27 UTC 2012


commit 77a498ae31d0c95d1cdaa88752965fcea870990d
Author: Damian Johnson <atagar at torproject.org>
Date:   Mon May 7 09:23:09 2012 -0700

    Unit tests for extrainfo descriptors
    
    Few basic unit tests for extrainfo descriptors. These are done in a similar
    fashion to the server descriptors. I'm highly tempted to refactor out some
    common bits, but at present that would probably hurt code readability more than
    it would help. This'll change if I keep using the same type of helpers for
    descriptor unit tests.
---
 run_tests.py                                 |    2 +
 stem/descriptor/extrainfo_descriptor.py      |    8 ++
 test/unit/descriptor/__init__.py             |    2 +-
 test/unit/descriptor/extrainfo_descriptor.py |  131 ++++++++++++++++++++++++++
 4 files changed, 142 insertions(+), 1 deletions(-)

diff --git a/run_tests.py b/run_tests.py
index 818bfb8..bf2c2bc 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -21,6 +21,7 @@ import test.unit.socket.control_line
 import test.unit.socket.control_message
 import test.unit.descriptor.reader
 import test.unit.descriptor.server_descriptor
+import test.unit.descriptor.extrainfo_descriptor
 import test.unit.util.conf
 import test.unit.util.connection
 import test.unit.util.enum
@@ -94,6 +95,7 @@ UNIT_TESTS = (
   test.unit.util.tor_tools.TestTorTools,
   test.unit.descriptor.reader.TestDescriptorReader,
   test.unit.descriptor.server_descriptor.TestServerDescriptor,
+  test.unit.descriptor.extrainfo_descriptor.TestExtraInfoDescriptor,
   test.unit.version.TestVersion,
   test.unit.socket.control_message.TestControlMessage,
   test.unit.socket.control_line.TestControlLine,
diff --git a/stem/descriptor/extrainfo_descriptor.py b/stem/descriptor/extrainfo_descriptor.py
index fceaf60..e13787f 100644
--- a/stem/descriptor/extrainfo_descriptor.py
+++ b/stem/descriptor/extrainfo_descriptor.py
@@ -18,6 +18,7 @@ Extra-info descriptors are available from a few sources...
 
 parse_file - Iterates over the extra-info descriptors in a file.
 ExtraInfoDescriptor - Tor extra-info descriptor.
+  +- get_unrecognized_lines - lines with unrecognized content
 """
 
 import datetime
@@ -159,6 +160,8 @@ class ExtraInfoDescriptor(stem.descriptor.Descriptor):
     self.write_history_interval = None
     self.write_history_values = []
     
+    self._unrecognized_lines = []
+    
     entries, first_keyword, last_keyword, _ = \
       stem.descriptor._get_descriptor_components(raw_contents, validate, ())
     
@@ -178,6 +181,9 @@ class ExtraInfoDescriptor(stem.descriptor.Descriptor):
     
     self._parse(entries, validate)
   
+  def get_unrecognized_lines(self):
+    return list(self._unrecognized_lines)
+  
   def _parse(self, entries, validate):
     """
     Parses a series of 'keyword => (value, pgp block)' mappings and applies
@@ -227,4 +233,6 @@ class ExtraInfoDescriptor(stem.descriptor.Descriptor):
           raise ValueError("Router signature line must be followed by a signature block: %s" % line)
         
         self.signature = block_contents
+      else:
+        self._unrecognized_lines.append(line)
 
diff --git a/test/unit/descriptor/__init__.py b/test/unit/descriptor/__init__.py
index ae61cda..cf4fceb 100644
--- a/test/unit/descriptor/__init__.py
+++ b/test/unit/descriptor/__init__.py
@@ -2,5 +2,5 @@
 Unit tests for stem.descriptor.
 """
 
-__all__ = ["reader", "server_descriptor"]
+__all__ = ["reader", "extrainfo_descriptor", "server_descriptor"]
 
diff --git a/test/unit/descriptor/extrainfo_descriptor.py b/test/unit/descriptor/extrainfo_descriptor.py
new file mode 100644
index 0000000..15ffe68
--- /dev/null
+++ b/test/unit/descriptor/extrainfo_descriptor.py
@@ -0,0 +1,131 @@
+"""
+Unit tests for stem.descriptor.extrainfo_descriptor.
+"""
+
+import unittest
+from stem.descriptor.extrainfo_descriptor import ExtraInfoDescriptor
+
+CRYPTO_BLOB = """
+K5FSywk7qvw/boA4DQcqkls6Ize5vcBYfhQ8JnOeRQC9+uDxbnpm3qaYN9jZ8myj
+k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
+7LZqklu+gVvhMKREpchVqlAwXkWR44VENm24Hs+mT3M=
+"""
+
+EXTRAINFO_DESCRIPTOR_ATTR = (
+  ("extra-info", "ninja B2289C3EAB83ECD6EB916A2F481A02E6B76A0A48"),
+  ("published", "2012-05-05 17:03:50"),
+  ("router-signature", "\n-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----" % CRYPTO_BLOB),
+)
+
+def _make_descriptor(attr = None, exclude = None):
+  """
+  Constructs a minimal extrainfo descriptor with the given attributes.
+  
+  Arguments:
+    attr (dict)     - keyword/value mappings to be included in the descriptor
+    exclude (list)  - mandatory keywords to exclude from the descriptor
+  
+  Returns:
+    str with customized descriptor content
+  """
+  
+  descriptor_lines = []
+  if attr is None: attr = {}
+  if exclude is None: exclude = []
+  attr = dict(attr) # shallow copy since we're destructive
+  
+  for keyword, value in EXTRAINFO_DESCRIPTOR_ATTR:
+    if keyword in exclude: continue
+    elif keyword in attr:
+      value = attr[keyword]
+      del attr[keyword]
+    
+    # if this is the last entry then we should dump in any unused attributes
+    if keyword == "router-signature":
+      for attr_keyword, attr_value in attr.items():
+        descriptor_lines.append("%s %s" % (attr_keyword, attr_value))
+    
+    descriptor_lines.append("%s %s" % (keyword, value))
+  
+  return "\n".join(descriptor_lines)
+
+class TestExtraInfoDescriptor(unittest.TestCase):
+  def test_minimal_extrainfo_descriptor(self):
+    """
+    Basic sanity check that we can parse an extrainfo descriptor with minimal
+    attributes.
+    """
+    
+    desc_text = _make_descriptor()
+    desc = ExtraInfoDescriptor(desc_text)
+    
+    self.assertEquals("ninja", desc.nickname)
+    self.assertEquals("B2289C3EAB83ECD6EB916A2F481A02E6B76A0A48", desc.fingerprint)
+    self.assertTrue(CRYPTO_BLOB in desc.signature)
+  
+  def test_unrecognized_line(self):
+    """
+    Includes unrecognized content in the descriptor.
+    """
+    
+    desc_text = _make_descriptor({"pepperjack": "is oh so tasty!"})
+    desc = ExtraInfoDescriptor(desc_text)
+    self.assertEquals(["pepperjack is oh so tasty!"], desc.get_unrecognized_lines())
+  
+  def test_proceeding_line(self):
+    """
+    Includes a line prior to the 'extra-info' entry.
+    """
+    
+    desc_text = "exit-streams-opened port=80\n" + _make_descriptor()
+    self._expect_invalid_attr(desc_text)
+  
+  def test_trailing_line(self):
+    """
+    Includes a line after the 'router-signature' entry.
+    """
+    
+    desc_text = _make_descriptor() + "\nexit-streams-opened port=80"
+    self._expect_invalid_attr(desc_text)
+  
+  def test_extrainfo_line_missing_fields(self):
+    """
+    Checks that validation catches when the extra-info line is missing fields
+    and that without validation both the nickname and fingerprint are left as
+    None.
+    """
+    
+    test_entry = (
+      "ninja",
+      "ninja ",
+      "B2289C3EAB83ECD6EB916A2F481A02E6B76A0A48",
+      " B2289C3EAB83ECD6EB916A2F481A02E6B76A0A48",
+    )
+    
+    for entry in test_entry:
+      desc_text = _make_descriptor({"extra-info": entry})
+      desc = self._expect_invalid_attr(desc_text, "nickname")
+      self.assertEquals(None, desc.nickname)
+      self.assertEquals(None, desc.fingerprint)
+  
+  def _expect_invalid_attr(self, desc_text, attr = None, expected_value = None):
+    """
+    Asserts that construction will fail due to desc_text having a malformed
+    attribute. If an attr is provided then we check that it matches an expected
+    value when we're constructed without validation.
+    """
+    
+    self.assertRaises(ValueError, ExtraInfoDescriptor, desc_text)
+    desc = ExtraInfoDescriptor(desc_text, validate = False)
+    
+    if attr:
+      # check that the invalid attribute matches the expected value when
+      # constructed without validation
+      
+      self.assertEquals(expected_value, getattr(desc, attr))
+    else:
+      # check a default attribute
+      self.assertEquals("ninja", desc.nickname)
+    
+    return desc
+





More information about the tor-commits mailing list