[tor-commits] [stem/master] Config argument for the test runner

atagar at torproject.org atagar at torproject.org
Fri Nov 4 17:19:04 UTC 2011


commit 263dfd53cec592c10d9dbe40211ea56915ac7552
Author: Damian Johnson <atagar at torproject.org>
Date:   Sun Oct 30 00:51:47 2011 -0700

    Config argument for the test runner
    
    Integration tests can have custom behavior via a testrc, which is simple for
    now, and will grow as tests become more complex. Previously I was loading a
    static settings.cfg but that was stupid. The user's config file should neither
    be hardcoded nor under version control. This change also includes the config
    loading in the status output to stdout.
---
 run_tests.py      |   31 ++++++++++--------------
 stem/util/conf.py |    4 +-
 test/runner.py    |   68 ++++++++++++++++++++++++++++++++++++++++++----------
 test/settings.cfg |   17 -------------
 testrc.sample     |   17 +++++++++++++
 5 files changed, 87 insertions(+), 50 deletions(-)

diff --git a/run_tests.py b/run_tests.py
index ccdee42..2f2d6ae 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -15,10 +15,10 @@ import test.unit.version
 import test.integ.message
 import test.integ.system
 
-from stem.util import conf, enum, term
+from stem.util import enum, term
 
-OPT = "uit:h"
-OPT_EXPANDED = ["unit", "integ", "targets=", "help"]
+OPT = "uic:t:h"
+OPT_EXPANDED = ["unit", "integ", "config=", "targets=", "help"]
 DIVIDER = "=" * 70
 
 # (name, class) tuples for all of our unit and integration tests
@@ -46,11 +46,12 @@ TARGET_ATTR = {
 HELP_MSG = """Usage runTests.py [OPTION]
 Runs tests for the stem library.
 
-  -u, --unit      runs unit tests
-  -i, --integ     runs integration tests
-  -t, --target    comma separated list of tor configurations to use for the
-                    integration tests (all are used by default)
-  -h, --help      presents this help
+  -u, --unit            runs unit tests
+  -i, --integ           runs integration tests
+  -c, --config PATH     path to a custom test configuration
+  -t, --target TARGET   comma separated list of tor configurations to use for
+                        the integration tests (all are used by default)
+  -h, --help            presents this help
 
   Integration targets:
     %s
@@ -60,6 +61,7 @@ if __name__ == '__main__':
   start_time = time.time()
   run_unit_tests = False
   run_integ_tests = False
+  config_path = None
   integ_targets = TARGETS.values()
   
   # parses user input, noting any issues
@@ -72,6 +74,7 @@ if __name__ == '__main__':
   for opt, arg in opts:
     if opt in ("-u", "--unit"): run_unit_tests = True
     elif opt in ("-i", "--integ"): run_integ_tests = True
+    elif opt in ("-c", "--config"): config_path = arg
     elif opt in ("-t", "--targets"):
       integ_targets = arg.split(",")
       
@@ -99,15 +102,6 @@ if __name__ == '__main__':
       print HELP_MSG % "\n    ".join(target_lines)
       sys.exit()
   
-  test_config = conf.get_config("test")
-  
-  try:
-    config_path = os.path.dirname(__file__) + "/test/settings.cfg"
-    test_config.load(config_path)
-  except IOError, exc:
-    print term.format("Unable to load testing configuration: %s" % exc, term.Color.RED, term.Attr.BOLD)
-    sys.exit(1)
-  
   if not run_unit_tests and not run_integ_tests:
     print "Nothing to run (for usage provide --help)\n"
     sys.exit()
@@ -129,7 +123,8 @@ if __name__ == '__main__':
     integ_runner = test.runner.get_runner()
     
     try:
-      integ_runner.start(user_config = test_config)
+      # TODO: note unused config options afterward
+      integ_runner.start(config_path = config_path)
       
       print term.format("Running tests...", term.Color.BLUE, term.Attr.BOLD)
       print
diff --git a/stem/util/conf.py b/stem/util/conf.py
index c7575ad..40cc956 100644
--- a/stem/util/conf.py
+++ b/stem/util/conf.py
@@ -359,7 +359,7 @@ class Config():
       if type(val) == type(conf_mappings[entry]):
         conf_mappings[entry] = val
   
-  def get_keys(self):
+  def keys(self):
     """
     Provides all keys in the currently loaded configuration.
     
@@ -369,7 +369,7 @@ class Config():
     
     return self._contents.keys()
   
-  def get_unused_keys(self):
+  def unused_keys(self):
     """
     Provides the configuration keys that have never been provided to a caller
     via the get, get_value, or update methods.
diff --git a/test/runner.py b/test/runner.py
index 5565e3e..6e2f998 100644
--- a/test/runner.py
+++ b/test/runner.py
@@ -25,7 +25,7 @@ import threading
 
 import stem.process
 
-from stem.util import term
+from stem.util import conf, term
 
 DEFAULT_CONFIG = {
   "test.integ.test_directory": "./test/data",
@@ -42,6 +42,10 @@ SocksPort 0
 ControlPort 1111
 """
 
+# We make some paths relative to stem's base directory (the one above us)
+# rather than the process' cwd. This doesn't end with a slash.
+STEM_BASE = "/".join(__file__.split("/")[:-2])
+
 # singleton Runner instance
 INTEG_RUNNER = None
 
@@ -68,15 +72,15 @@ class Runner:
     self._torrc_contents = ""
     self._tor_process = None
   
-  def start(self, quiet = False, user_config = None):
+  def start(self, quiet = False, config_path = None):
     """
     Makes temporary testing resources and starts tor, blocking until it
     completes.
     
     Arguments:
-      quiet (bool) - if False then this prints status information as we start
-                     up to stdout
-      user_config (stem.util.conf.Config) - custom test configuration
+      quiet (bool)      - if False then this prints status information as we
+                          start up to stdout
+      config_path (str) - path to a custom test configuration
     
     Raises:
       OSError if unable to run test preparations or start tor
@@ -88,8 +92,10 @@ class Runner:
     # it so we can start a fresh instance
     if self._tor_process: self.stop(quiet)
     
-    # apply any custom configuration attributes
-    if user_config: user_config.update(self._config)
+    _print_status("Setting up a test instance...\n", STATUS_ATTR, quiet)
+    
+    # load and apply any custom configurations
+    self._load_config(config_path, quiet)
     
     # if 'test_directory' is unset then we make a new data directory in /tmp
     # and clean it up when we're done
@@ -97,10 +103,9 @@ class Runner:
     config_test_dir = self._config["test.integ.test_directory"]
     
     if config_test_dir:
-      # makes paths relative of stem's base directory (the one above us)
+      # makes paths relative of stem's base directory
       if config_test_dir.startswith("./"):
-        stem_base = "/".join(__file__.split("/")[:-2])
-        config_test_dir = stem_base + config_test_dir[1:]
+        config_test_dir = STEM_BASE + config_test_dir[1:]
       
       self._test_dir = os.path.expanduser(config_test_dir)
     else:
@@ -259,6 +264,45 @@ class Runner:
     finally:
       self._runner_lock.release()
   
+  def _load_config(self, config_path, quiet):
+    """
+    Loads the given configuration file, printing the contents if successful and
+    the exception if not.
+    
+    Arguments:
+      config_path (str) - path to custom testing configuration options, skipped
+                          if None
+      quiet (bool)      - prints status information to stdout if False
+    """
+    
+    if not config_path:
+      _print_status("  loading test configuration... skipped\n", STATUS_ATTR, quiet)
+    else:
+      # loads custom testing configuration
+      test_config = conf.get_config("test")
+      config_path = os.path.abspath(config_path)
+      
+      try:
+        _print_status("  loading test configuration (%s)... " % config_path, STATUS_ATTR, quiet)
+        
+        test_config.load(config_path)
+        test_config.update(self._config)
+        
+        _print_status("done\n", STATUS_ATTR, quiet)
+        
+        for config_key in test_config.keys():
+          key_entry = "    %s => " % config_key
+          
+          # if there's multiple values then list them on separate lines
+          value_div = ",\n" + (" " * len(key_entry))
+          value_entry = value_div.join(test_config.get_value(config_key, multiple = True))
+          
+          _print_status(key_entry + value_entry + "\n", SUBSTATUS_ATTR, quiet)
+        
+        _print_status("\n", (), quiet)
+      except IOError, exc:
+        _print_status("failed (%s)\n" % exc, ERROR_ATTR, quiet)
+  
   def _run_setup(self, quiet):
     """
     Makes a temporary runtime resources of our integration test instance.
@@ -270,8 +314,6 @@ class Runner:
       OSError if unsuccessful
     """
     
-    _print_status("Setting up a test instance...\n", STATUS_ATTR, quiet)
-    
     # makes a temporary data directory if needed
     try:
       _print_status("  making test directory (%s)... " % self._test_dir, STATUS_ATTR, quiet)
@@ -302,7 +344,7 @@ class Runner:
       _print_status("\n", (), quiet)
     except Exception, exc:
       _print_status("failed (%s)\n\n" % exc, ERROR_ATTR, quiet)
-      raise exc
+      raise OSError(exc)
   
   def _start_tor(self, quiet):
     """
diff --git a/test/settings.cfg b/test/settings.cfg
deleted file mode 100644
index cff8d13..0000000
--- a/test/settings.cfg
+++ /dev/null
@@ -1,17 +0,0 @@
-# Integration Test Settings
-#
-# test.integ.test_directory
-#   Path used for our data directory and any temporary test resources. Relative
-#   paths are expanded in reference to the location of 'run_tests.py'.
-#   
-#   If set then the directory's contents are reused for future tests (so we
-#   have a faster startup and lower load on authorities). If set to an empty
-#   value then this makes a fresh data directory for each test run.
-#
-# test.integ.run.online
-#   Runs tests with network activity. If set then we'll wait for tor to fully
-#   bootstrap when starting, which won't happen without a network connection.
-
-test.integ.test_directory ./test/data
-test.integ.run.online false
-
diff --git a/testrc.sample b/testrc.sample
new file mode 100644
index 0000000..cff8d13
--- /dev/null
+++ b/testrc.sample
@@ -0,0 +1,17 @@
+# Integration Test Settings
+#
+# test.integ.test_directory
+#   Path used for our data directory and any temporary test resources. Relative
+#   paths are expanded in reference to the location of 'run_tests.py'.
+#   
+#   If set then the directory's contents are reused for future tests (so we
+#   have a faster startup and lower load on authorities). If set to an empty
+#   value then this makes a fresh data directory for each test run.
+#
+# test.integ.run.online
+#   Runs tests with network activity. If set then we'll wait for tor to fully
+#   bootstrap when starting, which won't happen without a network connection.
+
+test.integ.test_directory ./test/data
+test.integ.run.online false
+





More information about the tor-commits mailing list