[tor-commits] [ooni-probe/master] * Refactored ooni/runner.py again, so that it actually runs old OONITests, new
isis at torproject.org
isis at torproject.org
Sat Oct 20 14:29:33 UTC 2012
commit 1bbbad8b1553a7e9f2ca7b5bcb76ec47acce22f6
Author: Isis Lovecruft <isis at torproject.org>
Date: Sat Oct 20 13:30:30 2012 +0000
* Refactored ooni/runner.py again, so that it actually runs old OONITests, new
tests still work too. I tested all of them (both nettests and legacy tests)
and then diffed the results from before and after these changes. Some tests
*are* broken, e.g. dnstamper doesn't seem to find its inputs correctly, but
none of the tests are any more broken than they were before this commit --
the difference is that old tests will run.
* There was a rather nasty problem with old tests not finding their
subOptions(), which was due to Twisted's usage.Options class only being able
to handle one level of subOptions. When the old tests expected
ooni/ooniprobe.py to have the global_options and themselves to have
local_options, what was actually happening was that ooni/oonicli.py took the
first level, and then the test took the second, and thus was missing any
global_options.
* There was also a bug where, before, ooni/runner.py would do things like:
self.legacy_test.local_options = self.subOptions
when self.legacy_test is an already instantiated subclass of
ooni.plugoo.tests.OONITest. Because 'local_options' in this case is
obviously an attribute of the class, we have to do setattr(). Because of
this series of bugs, the legacy tests were missing a whole bunch of class
attributes which they required to run, read test inputs from files, and
really do anything at all.
* The function ooni.utils.legacy.start_legacy_test() now handles creating
a defer.gatherResults(defer.DeferredList), where the defer.DeferredList
is all of the calls to ooni.plugoo.tests.OONITest.startTest(args) with
all of the iterations of args.
* Added and updated a bunch of documentation.
* TODO some more documentation.
* TODO there might be more bugs. I sure as hell hope not. :(
* TODO go to bed now. I am getting grumpy.
---
ooni/runner.py | 41 +++++++++--
ooni/utils/legacy.py | 184 ++++++++++++++++++++++++++++++--------------------
2 files changed, 145 insertions(+), 80 deletions(-)
diff --git a/ooni/runner.py b/ooni/runner.py
index 55ad401..59c0c45 100644
--- a/ooni/runner.py
+++ b/ooni/runner.py
@@ -1,3 +1,16 @@
+#-*- coding: utf-8 -*-
+#
+# runner.py
+# ---------
+# Handles running ooni.nettests as well as ooni.plugoo.tests.OONITests.
+#
+# :authors: Isis Lovecruft, Arturo Filasto
+# :license: see included LICENSE file
+# :copyright: (c) 2012 Isis Lovecruft, Arturo Filasto, The Tor Project, Inc.
+# :version: 0.1.0-pre-alpha
+#
+
+
import os
import sys
import types
@@ -134,16 +147,24 @@ def loadTestsAndOptions(classes, config):
_old_klass_type = LegacyOONITest
for klass in classes:
-
- try:
- assert not isinstance(klass, _old_klass_type), "Legacy test detected"
- except:
- assert isinstance(klass, _old_klass_type)
+ if isinstance(klass, _old_klass_type):
try:
- start_legacy_test(klass)
+ cases = start_legacy_test(klass)
+ #cases.callback()
+ if cases:
+ print cases
+ return [], []
+ testCases.append(cases)
except Exception, e:
log.err(e)
- else:
+ else:
+ try:
+ opts = klass.local_options
+ options.append(opts)
+ except AttributeError, ae:
+ options.append([])
+ log.err(ae)
+ elif not isinstance(klass, _old_klass_type):
tests = reflect.prefixedMethodNames(klass, methodPrefix)
if tests:
cases = makeTestCases(klass, tests, methodPrefix)
@@ -155,6 +176,11 @@ def loadTestsAndOptions(classes, config):
except AttributeError, ae:
options.append([])
log.err(ae)
+ else:
+ try:
+ raise RuntimeError, "Class is some strange type!"
+ except RuntimeError, re:
+ log.err(re)
return testCases, options
@@ -220,4 +246,3 @@ class ORunner(object):
self.reporterFactory.options = self.options
for inputUnit in InputUnitFactory(self.inputs):
self.runWithInputUnit(inputUnit)
-
diff --git a/ooni/utils/legacy.py b/ooni/utils/legacy.py
index 0575670..aa415a9 100755
--- a/ooni/utils/legacy.py
+++ b/ooni/utils/legacy.py
@@ -15,7 +15,9 @@ import inspect
import os
import yaml
-from twisted.internet import defer
+from twisted.internet import defer, reactor
+from twisted.python import log as tplog
+from twisted.python.usage import Options as tpOptions
from ooni import nettest
from ooni.plugoo.tests import OONITest
@@ -70,14 +72,13 @@ class LegacyOONITest(nettest.TestCase):
from ooni.plugoo.tests import OONITest
__bases__ = (OONITest, )
-
def __getattr__(self, name):
"""
- Override of builtin getattr for :class:`ooni.runner.LegacyTest` so that
- method calls to a LegacyTest instance or its parent class OONITest do
- not return unhandled errors, but rather report that the method is unknown.
+ Override of builtin getattr for :class:`ooni.runner.LegacyTest` so
+ that method calls to a LegacyTest instance or its parent class
+ OONITest do not return unhandled errors, but rather report that the
+ method is unknown.
"""
-
def __unknown_method__(*a):
log.msg("Call to unknown method %s.%s" % (self.originalTest, name))
if a:
@@ -127,7 +128,6 @@ class LegacyOONITest(nettest.TestCase):
An instance of :meth:`inspect.attrgetter` which searches for
methods within a test class named 'options'.
"""
-
original_test = self.originalTest
original_class = original_test.__class__
class_string = str(original_class)
@@ -145,7 +145,7 @@ class LegacyOONITest(nettest.TestCase):
except KeyError, keyerr:
log.debug(keyerr)
- options = {}
+ options = tpOptions
try:
options = options_finder(getattr(original_class, test_class))
except AttributeError:
@@ -153,10 +153,12 @@ class LegacyOONITest(nettest.TestCase):
except Exception, e:
log.err(e)
finally:
- return sub_options
+ return options()
def __init__(self, obj, config):
"""
+ xxx fill me in
+
:param obj:
An uninstantiated old test, which should be a subclass of
:class:`ooni.plugoo.tests.OONITest`.
@@ -169,56 +171,72 @@ class LegacyOONITest(nettest.TestCase):
:ivar was_named:
:attr subOptions:
"""
-
super(LegacyOONITest, self).__init__()
self.originalTest = obj
-
- self.subArgs = (None, )
- if 'subArgs' in config:
- self.subArgs = config['subArgs']
-
- self.name = 'LegacyOONITest'
- self.was_named = False
+ self.start_time = date.now()
+ self.name = 'LegacyOONITest'
+ self.was_named = False
try:
- self.name = self.originalTest.shortName
+ self.name = self.originalTest.shortName
self.was_named = True
except AttributeError:
if self.originalTest.name and self.originalTest.name != 'oonitest':
- self.name = self.originalTest.name
+ self.name = self.originalTest.name
self.was_named = True
+ if 'subArgs' in config:
+ self.subArgs = config['subArgs']
+ else:
+ self.subArgs = (None, )
+ log.msg("No suboptions to test %s found; continuing..."% self.name)
+
try:
self.subOptions = self.originalTest.options()
except AttributeError:
if self.was_named is False:
self.subOptions = self.find_missing_options()
else:
- self.subOptions = {}
+ self.subOptions = None
log.msg("That test appears to have a name, but no options!")
- self.legacy_test = self.originalTest(None, None, None, None)
- self.legacy_test.name = self.name
- print "self.legacy_test.name: %s" % self.legacy_test.name
- print "self.name: %s" % self.name
- self.legacy_test.start_time = date.now()
-
if self.subOptions is not None:
- self.subOptions.parseOptions(self.subArgs)
- self.legacy_test.local_options = self.subOptions
-
- self.reporter = []
- self.legacy_test.report = LegacyReporter(report_target=self.reporter)
+ if len(self.subArgs) > 0:
+ self.subOptions.parseOptions(self.subArgs)
+ self.local_options = self.subOptions
+ else:
+ print self.subOptions
if 'reportfile' in config:
self.reporter_file = config['reportfile']
else:
- now = date.now()
- time = date.rfc3339(now, utc=True, use_system_timezone=False)
- filename = str(self.name) + "-" + str(time) + ".yaml"
+ filename = str(self.name) + "-" + str(date.timestamp()) + ".yaml"
self.reporter_file = os.path.join(os.getcwd(), filename)
+ self.reporter = []
+ self.report = LegacyReporter(report_target=self.reporter)
+
+ self.legacy_test = self.originalTest(None, self.local_options,
+ None, self.report)
+ setattr(self.legacy_test, 'name', self.name)
+ setattr(self.legacy_test, 'start_time', self.start_time)
+
+ self.inputs = {}
+ for keys, values in self.legacy_test.assets.items():
+ self.inputs[keys] = values
+ setattr(self.legacy_test, 'inputs', self.inputs)
- self.legacy_test.initialize()
- self.legacy_test.assets = self.legacy_test.load_assets()
+ @defer.inlineCallbacks
+ def run_with_args(self, args):
+ """
+ Handler for calling :meth:`ooni.plugoo.tests.OONITest.startTest` with
+ each :param:`args` that, in the old framework, would have been
+ generated one line at a time by
+ :class:`ooni.plugoo.assets.Asset`. This function is wrapped with
+ :meth:`twisted.internet.defer.inlineCallbacks` so that the result of
+ each call to :meth:`ooni.plugoo.tests.OONITest.experiment` is returned
+ immediately as :ivar:`returned`.
+ """
+ result = yield self.legacy_test.startTest(args)
+ defer.returnValue(result)
def adapt_legacy_test(obj, config):
"""
@@ -241,6 +259,10 @@ def adapt_legacy_test(obj, config):
def report_legacy_test_to_file(legacy_test, file=None):
"""
+ xxx this function current does not get used, and could easily be handled
+ by ooni.runner.loadTestsAndOptions, or some other function in
+ ooni.runner.
+
xxx fill me in
"""
reporter_file = legacy_test.reporter_file
@@ -260,6 +282,21 @@ def report_legacy_test_to_file(legacy_test, file=None):
log.msg("Finished reporting.")
def log_legacy_test_results(result, legacy_test, args):
+ """
+ Callback function for deferreds in :func:`start_legacy_test` which
+ handles updating the legacy_test's :class:`legacy_test.report`.
+
+ :param result:
+ The possible result of a deferred which has been returned from
+ :meth:`ooni.plugoo.test.OONITest.experiment` and
+ :meth:`ooni.plugoo.test.OONITest.control`.
+ :param legacy_test:
+ The :class:`LegacyOONITest` which we're processing.
+ :param args:
+ The current inputs which we're giving to legacy_test.startTest().
+ :return:
+ The :param:`legacy_test`.
+ """
if result:
legacy_test.report({args: result})
log.debug("Legacy test %s with args:\n%s\nreturned result:\n%s"
@@ -268,50 +305,53 @@ def log_legacy_test_results(result, legacy_test, args):
legacy_test.report({args: None})
log.debug("No results return for %s with args:\n%s"
% (legacy_test.name, args))
-
- at defer.inlineCallbacks
-def run_legacy_test_with_args(legacy_test, args):
- """
- Handler for calling :meth:`ooni.plugoo.tests.OONITest.startTest` with each
- :param:`args` that, in the old framework, would have been generated one
- line at a time by :class:`ooni.plugoo.assets.Asset`. This function is
- wrapped with :meth:`twisted.internet.defer.inlineCallbacks` so that the
- result of each call to :meth:`ooni.plugoo.tests.OONITest.experiment` is
- returned immediately as :ivar:`returned`.
- """
-
- result = yield legacy_test.startTest(args)
- defer.returnValue(result)
+ return legacy_test
def start_legacy_test(legacy_test):
"""
- xxx fill me in
-
- need a list of startTest(args) which return deferreds
+ This is the main function which should be used to call a legacy test, it
+ handles parsing the deprecated :class:`ooni.plugoo.assets.Asset` items as
+ inputs, and calls back to a custom, backwards-compatible Reporter.
+
+ For each input to the legacy_test, this function creates a
+ :class:`twisted.internet.defer.Deferred` which has already received its
+ :meth:`callback`. The end result is a
+ :class:`twisted.internet.defer.gatherResults` of all the outcomes of
+ :param:`legacy_test` for each of the inputs.
+
+ :param legacy_test:
+ A :class:`LegacyOONITest` to process.
+ :ivar results:
+ A list of :class:`twisted.internet.defer.Deferred`s which gets
+ processed as a :class:`twisted.internet.defer.DeferredList`.
+ :ivar current_input:
+ The input we are current working on, i.e. what would have been 'args'
+ (as in, 'experiment(args)') in the old design.
+ :return:
+ A :class:`twisted.internet.defer.gatherResults`.
"""
-
results = []
+ current_input = {}
- if len(legacy_test.assets.items()) != 0:
- for keys, values in legacy_test.assets.items():
+ if len(legacy_test.inputs) > 0:
+ for keys, values in legacy_test.inputs:
for value in values:
- args[keys] = value
- log.debug("Running %s with args: %s" % (legacy_test.name,
- args))
- d = run_legacy_test_with_args(args)
- d.addCallback(log_legacy_test_results, legacy_test, args)
- d.addErrback(log.err)
- d.addCallback(report_legacy_test_to_file, legacy_test)
- d.addErrback(log.err)
+ current_input[keys] = value
+ log.debug("Running %s with args: %s"
+ % (legacy_test.name, current_input))
+ d = legacy_test.run_with_args(current_input)
+ d.addCallback(log_legacy_test_results, legacy_test,
+ current_input)
+ d.addErrback(tplog.err)
results.append(d)
else:
- args['zero_input_test'] = True
- log.debug("Running %s with args: %s" % (legacy_test.name, args))
- d = run_legacy_test_with_args(args)
- d.addCallback(log_legacy_test_results, legacy_test, args)
- d.addErrback(log.err)
- d.addCallback(report_legacy_test_to_file, legacy_test)
- d.addErrback(log.err)
+ current_input['zero_input_test'] = True
+ log.debug("Running %s with current input: %s"
+ % (legacy_test.name, current_input))
+ d = legacy_test.run_with_args(current_input)
+ d.addCallback(log_legacy_test_results, legacy_test, current_input)
+ d.addErrback(tplog.err)
results.append(d)
- defer.DeferredList(results)
+ dlist = defer.gatherResults(results)
+ return dlist
More information about the tor-commits
mailing list