[tor-commits] [stem/master] Server descriptor test for metrics content

atagar at torproject.org atagar at torproject.org
Mon Mar 26 00:10:01 UTC 2012


commit d72c21de710ba78e16b6d0fa29949fb43a2e5eb6
Author: Damian Johnson <atagar at torproject.org>
Date:   Sat Mar 24 18:15:27 2012 -0700

    Server descriptor test for metrics content
    
    Test for parsing a single server descriptor and checking its content. This
    includes a few fixes and improvements for the ServerDescriptorV2 class, the
    most intersting of which is that declaring instance variables prior to init
    makes a single reference. Hence all server descriptors were using the same exit
    policy. Oops. :)
---
 run_tests.py                               |    2 +
 stem/descriptor/server_descriptor.py       |  111 +++++++++++++++++-----------
 test/integ/descriptor/__init__.py          |    2 +-
 test/integ/descriptor/server_descriptor.py |   83 +++++++++++++++++++++
 4 files changed, 155 insertions(+), 43 deletions(-)

diff --git a/run_tests.py b/run_tests.py
index e0574d3..3cb259d 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -31,6 +31,7 @@ import test.integ.control.base_controller
 import test.integ.socket.control_message
 import test.integ.socket.control_socket
 import test.integ.descriptor.reader
+import test.integ.descriptor.server_descriptor
 import test.integ.util.conf
 import test.integ.util.system
 import test.integ.version
@@ -98,6 +99,7 @@ INTEG_TESTS = (
   test.integ.util.conf.TestConf,
   test.integ.util.system.TestSystem,
   test.integ.descriptor.reader.TestDescriptorReader,
+  test.integ.descriptor.server_descriptor.TestServerDescriptor,
   test.integ.version.TestVersion,
   test.integ.socket.control_socket.TestControlSocket,
   test.integ.socket.control_message.TestControlMessage,
diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py
index 70db1fe..7715a96 100644
--- a/stem/descriptor/server_descriptor.py
+++ b/stem/descriptor/server_descriptor.py
@@ -192,45 +192,33 @@ class ServerDescriptorV2(stem.descriptor.Descriptor):
   
   Attributes:
     nickname (str)           - relay's nickname (*)
+    fingerprint (str)        - fourty hex digits that make up the relay's fingerprint
     address (str)            - IPv4 address of the relay (*)
     or_port (int)            - port used for relaying (*)
     socks_port (int)         - deprecated attribute, always zero (*)
     dir_port (int)           - deprecated port used for descriptor mirroring (*)
-    average_bandwidth (int)  - rate of traffic relay is willing to relay in bytes/s (*)
-    burst_bandwidth (int)    - rate of traffic relay is willing to burst to in bytes/s (*)
-    observed_bandwidth (int) - estimated capacity of the relay based on usage in bytes/s (*)
     platform (str)           - operating system and tor version
     tor_version (stem.version.Version) - version of tor
-    exit_policy (stem.exit_policy.ExitPolicy) - relay's stated exit policy
+    operating_system (str)   - relay's operating system
+    uptime (int)             - relay's uptime when published in seconds
     published (datetime.datetime) - time in GMT when the descriptor was generated (*)
-    fingerprint (str)        - fourty hex digits that make up the relay's fingerprint
+    contact (str)            - relay's contact information
     hibernating (bool)       - flag to indicate if the relay was hibernating when published (*)
-    uptime (int)             - relay's uptime when published in seconds
+    exit_policy (stem.exit_policy.ExitPolicy) - relay's stated exit policy
+    family (list)            - nicknames or fingerprints of relays it has a declared family with (*)
+    average_bandwidth (int)  - rate of traffic relay is willing to relay in bytes/s (*)
+    burst_bandwidth (int)    - rate of traffic relay is willing to burst to in bytes/s (*)
+    observed_bandwidth (int) - estimated capacity of the relay based on usage in bytes/s (*)
     onion_key (str)          - key used to encrypt EXTEND cells (*)
     onion_key_type (str)     - block type of the onion_key, probably "RSA PUBLIC KEY" (*)
     signing_key (str)        - relay's long-term identity key (*)
     signing_key_type (str)   - block type of the signing_key, probably "RSA PUBLIC KEY" (*)
-    router_sig (str)         - signature for this descriptor (*)
-    router_sig_type (str)    - block type of the router_sig, probably "SIGNATURE" (*)
-    contact (str)            - relay's contact information
-    family (list)            - nicknames or fingerprints of relays it has a declared family with (*)
+    signature (str)          - signature for this descriptor (*)
+    signature_type (str)     - block type of the signature, probably "SIGNATURE" (*)
     
     (*) required fields, others are left as None if undefined
   """
   
-  nickname = address = or_port = socks_port = dir_port = None
-  average_bandwidth = burst_bandwidth = observed_bandwidth = None
-  platform = tor_version = published = fingerprint = uptime = None
-  onion_key = onion_key_type = signing_key = signing_key_type = None
-  router_sig = router_sig_type = contact = None
-  hibernating = False
-  family = unrecognized_lines = []
-  
-  # TODO: Until we have a proper ExitPolicy class this is just a list of the
-  # exit policy strings...
-  
-  exit_policy = []
-  
   def __init__(self, contents, validate = True, annotations = None):
     """
     Version 2 server descriptor constructor, created from an individual relay's
@@ -253,14 +241,49 @@ class ServerDescriptorV2(stem.descriptor.Descriptor):
     
     stem.descriptor.Descriptor.__init__(self, contents)
     
-    self._annotation_lines = annotations
-    self._annotation_dict = {}
+    self.nickname = None
+    self.fingerprint = None
+    self.address = None
+    self.or_port = None
+    self.socks_port = None
+    self.dir_port = None
+    self.platform = None
+    self.tor_version = None
+    self.operating_system = None
+    self.uptime = None
+    self.published = None
+    self.contact = None
+    self.hibernating = False
+    self.family = []
+    self.average_bandwidth = None
+    self.burst_bandwidth = None
+    self.observed_bandwidth = None
+    self.onion_key = None
+    self.onion_key_type = None
+    self.signing_key = None
+    self.signing_key_type = None
+    self.signature = None
+    self.signature_type = None
+    
+    # TODO: Until we have a proper ExitPolicy class this is just a list of the
+    # exit policy strings...
     
-    for line in annotations:
-      if " " in line:
-        key, value = line.split(" ", 1)
-        self._annotation_dict[key] = value
-      else: self._annotation_dict[line] = None
+    self.exit_policy = []
+    
+    self._unrecognized_lines = []
+    
+    if annotations:
+      self._annotation_lines = annotations
+      self._annotation_dict = {}
+      
+      for line in annotations:
+        if " " in line:
+          key, value = line.split(" ", 1)
+          self._annotation_dict[key] = value
+        else: self._annotation_dict[line] = None
+    else:
+      self._annotation_lines = []
+      self._annotation_dict = {}
     
     # A descriptor contains a series of 'keyword lines' which are simply a
     # keyword followed by an optional value. Lines can also be followed by a
@@ -271,7 +294,6 @@ class ServerDescriptorV2(stem.descriptor.Descriptor):
     # does not matter so breaking it into key / value pairs.
     
     entries = {}
-    
     remaining_contents = contents.split("\n")
     while remaining_contents:
       line = remaining_contents.pop(0)
@@ -343,12 +365,14 @@ class ServerDescriptorV2(stem.descriptor.Descriptor):
             raise ValueError("Router line's SocksPort should be zero: %s" % router_comp[3])
           elif not stem.util.connection.is_valid_port(router_comp[4], allow_zero = True):
             raise ValueError("Router line's DirPort is invalid: %s" % router_comp[4])
+        elif not (router_comp[2].isdigit() and router_comp[3].isdigit() and router_comp[4].isdigit()):
+          continue
         
         self.nickname   = router_comp[0]
         self.address    = router_comp[1]
-        self.or_port    = router_comp[2]
-        self.socks_port = router_comp[3]
-        self.dir_port   = router_comp[4]
+        self.or_port    = int(router_comp[2])
+        self.socks_port = int(router_comp[3])
+        self.dir_port   = int(router_comp[4])
       elif keyword == "bandwidth":
         # "bandwidth" bandwidth-avg bandwidth-burst bandwidth-observed
         bandwidth_comp = value.split()
@@ -379,13 +403,16 @@ class ServerDescriptorV2(stem.descriptor.Descriptor):
         # version followed by the os like the following...
         # platform Tor 0.2.2.35 (git-73ff13ab3cc9570d) on Linux x86_64
         #
-        # There's no guerentee that we'll be able to pick out the version.
+        # There's no guerentee that we'll be able to pick these out the
+        # version, but might as well try to save our caller the effot.
         
-        platform_comp = self.platform.split()
+        platform_match = re.match("^Tor (\S*).* on (.*)$", self.platform)
         
-        if platform_comp[0] == "Tor" and len(platform_comp) >= 2:
+        if platform_match:
+          version_str, self.operating_system = platform_match.groups()
+          
           try:
-            self.tor_version = stem.version.Version(platform_comp[1])
+            self.tor_version = stem.version.Version(version_str)
           except ValueError: pass
       elif keyword == "published":
         # "published" YYYY-MM-DD HH:MM:SS
@@ -439,17 +466,17 @@ class ServerDescriptorV2(stem.descriptor.Descriptor):
         if validate and (not block_type or not block_contents):
           raise ValueError("Router signature line must be followed by a signature block: %s" % line)
         
-        self.router_sig_type = block_type
-        self.router_sig = block_contents
+        self.signature_type = block_type
+        self.signature = block_contents
       elif keyword == "contact":
         self.contact = value
       elif keyword == "family":
         self.family = value.split(" ")
       else:
-        self.unrecognized_lines.append(line)
+        self._unrecognized_lines.append(line)
   
   def get_unrecognized_lines(self):
-    return list(unrecognized_lines)
+    return list(self._unrecognized_lines)
   
   def get_annotations(self):
     """
diff --git a/test/integ/descriptor/__init__.py b/test/integ/descriptor/__init__.py
index e1095ca..b143c2a 100644
--- a/test/integ/descriptor/__init__.py
+++ b/test/integ/descriptor/__init__.py
@@ -2,5 +2,5 @@
 Integration tests for stem.descriptor.* contents.
 """
 
-__all__ = ["reader"]
+__all__ = ["reader", "server_descriptor"]
 
diff --git a/test/integ/descriptor/server_descriptor.py b/test/integ/descriptor/server_descriptor.py
new file mode 100644
index 0000000..34b77a4
--- /dev/null
+++ b/test/integ/descriptor/server_descriptor.py
@@ -0,0 +1,83 @@
+"""
+Integration tests for stem.descriptor.server_descriptor.
+"""
+
+import os
+import datetime
+import unittest
+
+import stem.version
+import stem.descriptor.server_descriptor
+
+my_dir = os.path.dirname(__file__)
+DESCRIPTOR_TEST_DATA = os.path.join(my_dir, "data")
+
+class TestServerDescriptor(unittest.TestCase):
+  def test_metrics_descriptor(self):
+    """
+    Parses and checks our results against a server descriptor from metrics.
+    """
+    
+    descriptor_path = os.path.join(DESCRIPTOR_TEST_DATA, "example_descriptor")
+    
+    descriptor_file = open(descriptor_path)
+    descriptor_contents = descriptor_file.read()
+    descriptor_file.close()
+    
+    expected_published = datetime.datetime(2012, 3, 1, 17, 15, 27)
+    
+    expected_family = [
+      "$0CE3CFB1E9CC47B63EA8869813BF6FAB7D4540C1",
+      "$1FD187E8F69A9B74C9202DC16A25B9E7744AB9F6",
+      "$74FB5EFA6A46DE4060431D515DC9A790E6AD9A7C",
+      "$77001D8DA9BF445B0F81AA427A675F570D222E6A",
+      "$B6D83EC2D9E18B0A7A33428F8CFA9C536769E209",
+      "$D2F37F46182C23AB747787FD657E680B34EAF892",
+      "$E0BD57A11F00041A9789577C53A1B784473669E4",
+      "$E5E3E9A472EAF7BE9682B86E92305DB4C71048EF",
+    ]
+    
+    expected_onion_key = """-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAJv5IIWQ+WDWYUdyA/0L8qbIkEVH/cwryZWoIaPAzINfrw1WfNZGtBmg
+skFtXhOHHqTRN4GPPrZsAIUOQGzQtGb66IQgT4tO/pj+P6QmSCCdTfhvGfgTCsC+
+WPi4Fl2qryzTb3QO5r5x7T8OsG2IBUET1bLQzmtbC560SYR49IvVAgMBAAE=
+-----END RSA PUBLIC KEY-----"""
+    
+    expected_signing_key = """-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAKwvOXyztVKnuYvpTKt+nS3XIKeO8dVungi8qGoeS+6gkR6lDtGfBTjd
+uE9UIkdAl9zi8/1Ic2wsUNHE9jiS0VgeupITGZY8YOyMJJ/xtV1cqgiWhq1dUYaq
+51TOtUogtAPgXPh4J+V8HbFFIcCzIh3qCO/xXo+DSHhv7SSif1VpAgMBAAE=
+-----END RSA PUBLIC KEY-----"""
+    
+    expected_signature = """-----BEGIN SIGNATURE-----
+dskLSPz8beUW7bzwDjR6EVNGpyoZde83Ejvau+5F2c6cGnlu91fiZN3suE88iE6e
+758b9ldq5eh5mapb8vuuV3uO+0Xsud7IEOqfxdkmk0GKnUX8ouru7DSIUzUL0zqq
+Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
+-----END SIGNATURE-----"""
+    
+    desc = stem.descriptor.server_descriptor.ServerDescriptorV2(descriptor_contents)
+    self.assertEquals("caerSidi", desc.nickname)
+    self.assertEquals("A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB", desc.fingerprint)
+    self.assertEquals("71.35.133.197", desc.address)
+    self.assertEquals(9001, desc.or_port)
+    self.assertEquals(0, desc.socks_port)
+    self.assertEquals(0, desc.dir_port)
+    self.assertEquals("Tor 0.2.1.30 on Linux x86_64", desc.platform)
+    self.assertEquals(stem.version.Version("0.2.1.30"), desc.tor_version)
+    self.assertEquals("Linux x86_64", desc.operating_system)
+    self.assertEquals(588217, desc.uptime)
+    self.assertEquals(expected_published, desc.published)
+    self.assertEquals("www.atagar.com/contact", desc.contact)
+    self.assertEquals(False, desc.hibernating)
+    self.assertEquals(expected_family, desc.family)
+    self.assertEquals(153600, desc.average_bandwidth)
+    self.assertEquals(256000, desc.burst_bandwidth)
+    self.assertEquals(104590, desc.observed_bandwidth)
+    self.assertEquals(["reject *:*"], desc.exit_policy)
+    self.assertEquals(expected_onion_key, desc.onion_key)
+    self.assertEquals("RSA PUBLIC KEY", desc.onion_key_type)
+    self.assertEquals(expected_signing_key, desc.signing_key)
+    self.assertEquals("RSA PUBLIC KEY", desc.signing_key_type)
+    self.assertEquals(expected_signature, desc.signature)
+    self.assertEquals("SIGNATURE", desc.signature_type)
+





More information about the tor-commits mailing list