commit 263dfd53cec592c10d9dbe40211ea56915ac7552 Author: Damian Johnson atagar@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 +