[tor-commits] [stem/master] Moving test target attributes to config

atagar at torproject.org atagar at torproject.org
Sat Jan 21 23:37:25 UTC 2012


commit 6c3fb340a83865f75399c60b4e9201810f60c8ee
Author: Damian Johnson <atagar at torproject.org>
Date:   Sat Jan 21 15:34:43 2012 -0800

    Moving test target attributes to config
    
    Moving the attributes for integration testing targets from a big dictionary in
    the source to a separate configuration file. Progress!
---
 run_tests.py      |  141 +++++++++++++++--------------------------------------
 stem/util/conf.py |   15 ++++--
 stem/util/enum.py |   26 +++++++++-
 test/settings.cfg |   58 ++++++++++++++++++++++
 4 files changed, 131 insertions(+), 109 deletions(-)

diff --git a/run_tests.py b/run_tests.py
index 770953d..fdd6901 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -32,7 +32,7 @@ import stem.util.enum
 import stem.util.log as log
 import stem.util.term as term
 
-OPT = "uic:t:l:h"
+OPT = "uic:l:t:h"
 OPT_EXPANDED = ["unit", "integ", "config=", "targets=", "log=", "tor=", "help"]
 DIVIDER = "=" * 70
 
@@ -64,99 +64,21 @@ INTEG_TESTS = (
 # Integration tests above the basic suite.
 TARGETS = stem.util.enum.Enum(*[(v, v) for v in ("ONLINE", "RELATIVE", "CONN_NONE", "CONN_OPEN", "CONN_PASSWORD", "CONN_COOKIE", "CONN_MULTIPLE", "CONN_SOCKET", "CONN_SCOOKIE", "CONN_PTRACE", "CONN_ALL")])
 
-# Attributes that integ targets can have are...
-# 
-# config
-#   Configuration option with which this is synced. If an option is set via
-#   both the config and '--target' argument then the argument takes precedence.
-# 
-# description
-#   The '--help' description of the target.
-# 
-# prereq
-#   Version that we need to run the target.
-# 
-# torrc
-#   Configuration options for the test instance. For each of these targets that
-#   we have we make an integration test run.
-# 
-# TODO: This is a very, very long block and it's only gonna get worse. Should
-# this be moved to a 'settings.cfg'? It might be problematic due to constants.
+CONFIG = {
+  "target.config": {},
+  "target.description": {},
+  "target.prereq": {},
+  "target.torrc": {},
+}
 
-TARGET_ATTR = {
-  TARGETS.ONLINE: {
-    "config": "test.integ.target.online",
-    "description": "Includes tests that require network activity.",
-  },
-  TARGETS.RELATIVE: {
-    "config": "test.integ.target.relative_data_dir",
-    "description": "Uses a relative path for tor's data directory.",
-  },
-  TARGETS.CONN_NONE: {
-    "config": "test.integ.target.connection.none",
-    "description": "Configuration without a way for controllers to connect.",
-    "torrc": (),
-  },
-  TARGETS.CONN_OPEN: {
-    "config": "test.integ.target.connection.open",
-    "description": "Configuration with an open control port (default).",
-    "torrc": (
-      test.runner.OPT_PORT,
-    ),
-  },
-  TARGETS.CONN_PASSWORD: {
-    "config": "test.integ.target.connection.password",
-    "description": "Configuration with password authentication.",
-    "torrc": (
-      test.runner.OPT_PORT,
-      test.runner.OPT_PASSWORD,
-    ),
-  },
-  TARGETS.CONN_COOKIE: {
-    "config": "test.integ.target.connection.cookie",
-    "description": "Configuration with an authentication cookie.",
-    "torrc": (
-      test.runner.OPT_PORT,
-      test.runner.OPT_COOKIE,
-    ),
-  },
-  TARGETS.CONN_MULTIPLE: {
-    "config": "test.integ.target.connection.multiple",
-    "description": "Configuration with both password and cookie authentication.",
-    "torrc": (
-      test.runner.OPT_PORT,
-      test.runner.OPT_PASSWORD,
-      test.runner.OPT_COOKIE,
-    ),
-  },
-  TARGETS.CONN_SOCKET: {
-    "config": "test.integ.target.connection.socket",
-    "description": "Configuration with a control socket.",
-    "torrc": (
-      test.runner.OPT_SOCKET,
-    ),
-  },
-  TARGETS.CONN_SCOOKIE: {
-    "config": "test.integ.target.connection.scookie",
-    "description": "Configuration with a control socket and authentication cookie.",
-    "torrc": (
-      test.runner.OPT_SOCKET,
-      test.runner.OPT_COOKIE,
-    ),
-  },
-  TARGETS.CONN_PTRACE: {
-    "config": "test.integ.target.connection.ptrace",
-    "description": "Configuration with an open control port and 'DisableDebuggerAttachment 0'",
-    "prereq": stem.version.Requirement.DISABLE_DEBUGGER_ATTACHMENT,
-    "torrc": (
-      test.runner.OPT_PORT,
-      test.runner.OPT_PTRACE,
-    ),
-  },
-  TARGETS.CONN_ALL: {
-    "config": "test.integ.target.connection.all",
-    "description": "Runs integration tests for all connection configurations.",
-  },
+# mapping between 'target.torrc' options and runner attributes
+# TODO: switch OPT_* to enums so this is unnecessary
+RUNNER_OPT_MAPPING = {
+  "PORT": test.runner.OPT_PORT,
+  "PASSWORD": test.runner.OPT_PASSWORD,
+  "COOKIE": test.runner.OPT_COOKIE,
+  "SOCKET": test.runner.OPT_SOCKET,
+  "PTRACE": test.runner.OPT_PTRACE,
 }
 
 DEFAULT_RUN_TARGET = TARGETS.CONN_OPEN
@@ -202,11 +124,26 @@ def print_logging(logging_buffer):
     print
 
 if __name__ == '__main__':
+  # loads the builtin testing configuration
+  stem_path = os.path.join(*os.path.split(__file__)[:-1])
+  stem_path = stem.util.system.expand_path(stem_path)
+  settings_path = os.path.join(stem_path, "test", "settings.cfg")
+  
+  test_config = stem.util.conf.get_config("test")
+  test_config.load(settings_path)
+  test_config.update(CONFIG)
+  
+  # parses target.torrc as csv values and convert to runner OPT_* values
+  for target in CONFIG["target.torrc"]:
+    CONFIG["target.torrc"][target] = []
+    
+    for opt in test_config.get_str_csv("target.torrc", [], sub_key = target):
+      CONFIG["target.torrc"][target].append(RUNNER_OPT_MAPPING[opt])
+  
   start_time = time.time()
   run_unit_tests = False
   run_integ_tests = False
   config_path = None
-  test_config = stem.util.conf.get_config("test")
   override_targets = []
   logging_runlevel = None
   tor_cmd = "tor"
@@ -259,7 +196,7 @@ if __name__ == '__main__':
       
       target_lines = []
       for target in TARGETS:
-        target_lines.append(description_format % (target, TARGET_ATTR[target]["description"]))
+        target_lines.append(description_format % (target, CONFIG["target.description"].get(target, "")))
       
       print HELP_MSG % "\n    ".join(target_lines)
       sys.exit()
@@ -294,7 +231,7 @@ if __name__ == '__main__':
   # override our configuration flags if both set a target.
   
   for target in override_targets:
-    target_config = TARGET_ATTR[target].get("config")
+    target_config = CONFIG["target.config"].get(target)
     if target_config: test_config.set(target_config, "true")
   
   error_tracker = test.output.ErrorTracker()
@@ -332,14 +269,14 @@ if __name__ == '__main__':
     # Queue up all the targets with torrc options we want to run against.
     
     integ_run_targets = []
-    all_run_targets = [t for t in TARGETS if "torrc" in TARGET_ATTR[t]]
+    all_run_targets = [t for t in TARGETS if CONFIG["target.torrc"].get(t)]
     
     if test_config.get("test.integ.target.connection.all", False):
       # test against everything with torrc options
       integ_run_targets = all_run_targets
     else:
       for target in all_run_targets:
-        target_config = TARGET_ATTR[target].get("config")
+        target_config = CONFIG["target.config"].get(target)
         
         if target_config and test_config.get(target_config, False):
           integ_run_targets.append(target)
@@ -354,21 +291,21 @@ if __name__ == '__main__':
     our_version, skip_targets = None, []
     
     for target in integ_run_targets:
-      target_prereq = TARGET_ATTR[target].get("prereq")
+      target_prereq = CONFIG["target.prereq"].get(target)
       
       if target_prereq:
         # lazy loaded to skip system call if we don't have any prereqs
         if not our_version:
           our_version = stem.version.get_system_tor_version(tor_cmd)
         
-        if our_version < target_prereq:
+        if our_version < stem.version.Requirement[target_prereq]:
           skip_targets.append(target)
     
     for target in integ_run_targets:
       if target in skip_targets: continue
       
       try:
-        integ_runner.start(tor_cmd, extra_torrc_opts = TARGET_ATTR[target].get("torrc", []))
+        integ_runner.start(tor_cmd, extra_torrc_opts = CONFIG["target.torrc"].get(target, []))
         
         print term.format("Running tests...", term.Color.BLUE, term.Attr.BOLD)
         print
@@ -392,7 +329,7 @@ if __name__ == '__main__':
       print
       
       for target in skip_targets:
-        print term.format("Unable to run target %s, this requires tor version %s" % (target, TARGET_ATTR[target]["prereq"]), term.Color.RED, term.Attr.BOLD)
+        print term.format("Unable to run target %s, this requires tor version %s" % (target, CONFIG["target.prereq"][target]), term.Color.RED, term.Attr.BOLD)
       
       print
     
diff --git a/stem/util/conf.py b/stem/util/conf.py
index 199a20c..132dbce 100644
--- a/stem/util/conf.py
+++ b/stem/util/conf.py
@@ -375,7 +375,7 @@ class Config():
     
     return val
   
-  def get_str_csv(self, key, default = None, count = None):
+  def get_str_csv(self, key, default = None, count = None, sub_key = None):
     """
     Fetches the given key as a comma separated value.
     
@@ -385,13 +385,18 @@ class Config():
                          the count
       count (int)      - if set then the default is returned when the number of
                          elements doesn't match this value
+      sub_key (str)    - handle the configuration entry as a dictionary and use
+                         this key within it
     
     Returns:
       list with the stripped values
     """
     
-    conf_value = self.get_value(key)
+    if sub_key: conf_value = self.get(key, {}).get(sub_key)
+    else: conf_value = self.get_value(key)
+    
     if conf_value == None: return default
+    elif not conf_value.strip(): return [] # empty string
     else:
       conf_comp = [entry.strip() for entry in conf_value.split(",")]
       
@@ -407,7 +412,7 @@ class Config():
       
       return conf_comp
   
-  def get_int_csv(self, key, default = None, count = None, min_value = None, max_value = None):
+  def get_int_csv(self, key, default = None, count = None, min_value = None, max_value = None, sub_key = None):
     """
     Fetches the given comma separated value, returning the default if the
     values aren't integers or don't follow the given constraints.
@@ -419,12 +424,14 @@ class Config():
       count (int)      - checks that the number of values matches this if set
       min_value (int)  - checks that all values are over this if set
       max_value (int)  - checks that all values are under this if set
+      sub_key (str)    - handle the configuration entry as a dictionary and use
+                         this key within it
     
     Returns:
       list with the stripped values
     """
     
-    conf_comp = self.get_str_csv(key, default, count)
+    conf_comp = self.get_str_csv(key, default, count, sub_key)
     if conf_comp == default: return default
     
     # validates the input, setting the error_msg if there's a problem
diff --git a/stem/util/enum.py b/stem/util/enum.py
index 6891663..c750352 100644
--- a/stem/util/enum.py
+++ b/stem/util/enum.py
@@ -80,7 +80,7 @@ class Enum:
     Provides the index of the given value in the collection.
     
     Arguments:
-      value - entry to be looked up
+      value (str) - entry to be looked up
     
     Returns:
       integer index of the given entry
@@ -96,7 +96,7 @@ class Enum:
     Provides the next enumeration after the given value.
     
     Arguments:
-      value - enumeration for which to get the next entry
+      value (str) - enumeration for which to get the next entry
     
     Returns:
       enum value following the given entry
@@ -116,7 +116,7 @@ class Enum:
     Provides the previous enumeration before the given value.
     
     Arguments:
-      value - enumeration for which to get the previous entry
+      value (str) - enumeration for which to get the previous entry
     
     Returns:
       enum value proceeding the given entry
@@ -131,6 +131,26 @@ class Enum:
     prev_index = (self._values.index(value) - 1) % len(self._values)
     return self._values[prev_index]
   
+  def __getitem__(self, item):
+    """
+    Provides the values for the given key.
+    
+    Arguments:
+      item (str) - key to be looked up
+    
+    Returns:
+      str with the value for the given key
+    
+    Raises:
+      ValueError if the key doesn't exist
+    """
+    
+    if item in self.__dict__:
+      return self.__dict__[item]
+    else:
+      keys = ", ".join(self.keys())
+      raise ValueError("'%s' isn't among our enumeration keys, which includes: %s" % (item, keys))
+  
   def __iter__(self):
     """
     Provides an ordered listing of the enums in this set.
diff --git a/test/settings.cfg b/test/settings.cfg
new file mode 100644
index 0000000..75977d6
--- /dev/null
+++ b/test/settings.cfg
@@ -0,0 +1,58 @@
+# Testing Configuration
+#
+# The following are globally accessable configuration attributes used by stem's
+# unit and integration tests. Top level configuraion categories are...
+#
+# target.* - Attributes of the integration testing targets. This helps
+#            determine what is ran when the user runs with '--target'.
+
+ ##################
+# CATEGORY: TARGET #
+ ##################
+
+# Configuration option with which the target is synced. If an option is set via
+# both the config and '--target' argument then the argument takes precedence.
+
+target.config ONLINE        => test.integ.target.online
+target.config RELATIVE      => test.integ.target.relative_data_dir
+target.config CONN_NONE     => test.integ.target.connection.none
+target.config CONN_OPEN     => test.integ.target.connection.open
+target.config CONN_PASSWORD => test.integ.target.connection.password
+target.config CONN_COOKIE   => test.integ.target.connection.cookie
+target.config CONN_MULTIPLE => test.integ.target.connection.multiple
+target.config CONN_SOCKET   => test.integ.target.connection.socket
+target.config CONN_SCOOKIE  => test.integ.target.connection.scookie
+target.config CONN_PTRACE   => test.integ.target.connection.ptrace
+target.config CONN_ALL      => test.integ.target.connection.all
+
+# The '--help' description of the target.
+
+target.description ONLINE        => Includes tests that require network activity.
+target.description RELATIVE      => Uses a relative path for tor's data directory.
+target.description CONN_NONE     => Configuration without a way for controllers to connect.
+target.description CONN_OPEN     => Configuration with an open control port (default).
+target.description CONN_PASSWORD => Configuration with password authentication.
+target.description CONN_COOKIE   => Configuration with an authentication cookie.
+target.description CONN_MULTIPLE => Configuration with both password and cookie authentication.
+target.description CONN_SOCKET   => Configuration with a control socket.
+target.description CONN_SCOOKIE  => Configuration with a control socket and authentication cookie.
+target.description CONN_PTRACE   => Configuration with an open control port and 'DisableDebuggerAttachment 0'
+target.description CONN_ALL      => Runs integration tests for all connection configurations.
+
+# Version that we need to run the target. These need to match an enumeration of
+# stem.version.Requirement.
+
+target.prereq CONN_PTRACE => DISABLE_DEBUGGER_ATTACHMENT
+
+# Torrc configuration options included with the target. Having this option set
+# means that each of these targets will have a dedicated integration test run.
+
+target.torrc CONN_NONE     => 
+target.torrc CONN_OPEN     => PORT
+target.torrc CONN_PASSWORD => PORT, PASSWORD
+target.torrc CONN_COOKIE   => PORT, COOKIE
+target.torrc CONN_MULTIPLE => PORT, PASSWORD, COOKIE
+target.torrc CONN_SOCKET   => SOCKET
+target.torrc CONN_SCOOKIE  => SOCKET, COOKIE
+target.torrc CONN_PTRACE   => PORT, PTRACE
+



More information about the tor-commits mailing list