commit f7a6a5229c859aa2cc66f307c0b2363ca00346db Author: aagbsn aagbsn@extc.org Date: Sat Jan 12 20:35:19 2013 +0000
add processTest to NetTest class
ripped out of runner.py and needs testing --- ooni/nettest.py | 98 ++++++++++++++++++++++++++++++++++++++++-- ooni/nettesttask.py | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 212 insertions(+), 4 deletions(-)
diff --git a/ooni/nettest.py b/ooni/nettest.py index ecf7765..511c3da 100644 --- a/ooni/nettest.py +++ b/ooni/nettest.py @@ -30,6 +30,12 @@ class NetTest(object):
self.report = report
+ def start(self): + """ + Start tests and generate measurements. + """ + raise NotImplementedError + def loadNetTest(self, net_test_object): """ Creates all the necessary test_cases (a list of tuples containing the @@ -52,11 +58,16 @@ class NetTest(object): """ try: if os.path.isfile(net_test_object): - return self._loadNetTestFile(net_test_object) + test_cases = self._loadNetTestFile(net_test_object) except TypeError: if isinstance(net_test_object, StringIO) or \ isinstance(net_test_object, str): - return self._loadNetTestString(net_test_object) + test_cases = self._loadNetTestString(net_test_object) + + if not test_cases: + raise NoTestCasesFound + + return test_cases
def _loadNetTestString(self, net_test_string): """ @@ -110,8 +121,79 @@ class NetTest(object): measurement.netTest = self yield measurement
-class NoPostProcessor(Exception): - pass + def processTestCasesOptions(self): + self.options #XXX is this cmd_line_options? + + # get set of unique classes + test_classes = set([]) + for test_class, test_method in self.test_cases: + test_classes.add(test_class) + + #XXX where should the options bound to a test_class get stashed? + for test_class in test_classes: + options = self._processOptions() + + #XXX: is options passed to init the same as cmd_line_options??? + def _processTest(self, nettest_test_case, cmd_line_options): + """ + Process the parameters and :class:`twisted.python.usage.Options` of a + :class:`ooni.nettest.Nettest`. + + :param obj: + An uninstantiated old test, which should be a subclass of + :class:`ooni.plugoo.tests.OONITest`. + + :param cmd_line_options: + A configured and instantiated :class:`twisted.python.usage.Options` + class. + + """ + obj = nettest_test_case + if not hasattr(obj.usageOptions, 'optParameters'): + obj.usageOptions.optParameters = [] + + if obj.inputFile: + obj.usageOptions.optParameters.append(obj.inputFile) + + if obj.baseParameters: + for parameter in obj.baseParameters: + obj.usageOptions.optParameters.append(parameter) + + if obj.baseFlags: + if not hasattr(obj.usageOptions, 'optFlags'): + obj.usageOptions.optFlags = [] + for flag in obj.baseFlags: + obj.usageOptions.optFlags.append(flag) + + options = obj.usageOptions() + + options.parseOptions(cmd_line_options['subargs']) + obj.localOptions = options + + if obj.inputFile: + obj.inputFilename = options[obj.inputFile[0]] + + try: + log.debug("processing options") + tmp_test_case_object = obj() + tmp_test_case_object._checkRequiredOptions() + + except usage.UsageError, e: + test_name = tmp_test_case_object.name + log.err("There was an error in running %s!" % test_name) + log.err("%s" % e) + options.opt_help() + raise usage.UsageError("Error in parsing command line args for %s" % test_name) + + # who checks for root? + if obj.requiresRoot: + try: + checkForRoot() + except NotRootError: + log.err("%s requires root to run" % obj.name) + sys.exit(1) + + return obj
class NetTestCase(object): """ @@ -261,3 +343,11 @@ class NetTestCase(object): def __repr__(self): return "<%s inputs=%s>" % (self.__class__, self.inputs)
+class FailureToLoadNetTest(Exception): + pass +class NoPostProcessor(Exception): + pass +class InvalidOption(Exception): + pass +class MissingRequiredOption(Exception): + pass diff --git a/ooni/nettesttask.py b/ooni/nettesttask.py new file mode 100644 index 0000000..48c5108 --- /dev/null +++ b/ooni/nettesttask.py @@ -0,0 +1,118 @@ +from twisted.internet.task import CooperativeTask +from twisted.internet import defer +from ooni.reporter import OONIBReporter, YAMLReporter, OONIBReportError +from ooni.utils import log +import time +from twisted.internet.task import cooperate + +class NetTestTask(CooperativeTask): + """ + The object produced by a NetTestTaskFactory. + + A NetTestTask wraps a test_ callable with its options and input unit. + + """ + def __init__(self, test_case, test_input, oonib_reporter=None, yaml_reporter=None): + test_class, test_method = test_case + #log.debug("Running %s with %s..." % (test_method, test_input)) + self.oonib_reporter = oonib_reporter + self.oonib_reporter = yaml_reporter + self.test_instance = test_class() + self.test_instance.input = test_input + self.test_instance.report = {} + self.test_instance._start_time = time.time() + self.test_instance._setUp() + self.test_instance.setUp() + self.test = getattr(self.test_instance, test_method) + + # XXX: override CoordinatedTask methods + def start(self): #??? + d = defer.maybeDeferred(self.test) + d.addCallback(self.test_done) + d.addErrback(self.test_error) + return d + + def write_report(self): + if not self.oonib_reporter: + return self.yaml_reporter.testDone(self.test_instance, str(self.test)) + d1 = self.oonib_reporter.testDone(self.test_instance, str(self.test)) + d2 = self.yaml_reporter.testDone(self.test_instance, str(self.test)) + dl = defer.DeferredList([d1, d2]) + @dl.addErrback + def reportingFailed(failure): + log.err("Error in reporting %s" % self.test) + log.exception(failure) + return dl + + def test_done(self, result): + log.msg("Finished running %s" % self.test) + log.debug("Deferred callback result: %s" % result) + return self.write_report() + + def test_error(self, failure): + log.err("Error in running %s" % self.test) + log.exception(failure) + return self.write_report() + + #XXX: does not implement tests_done! + +class NetTestTaskFactory(object): + def __init__(self, test_cases, input_unit_list): + self.input_unit_list = input_unit_list + self.inputs = self.generate_inputs() + self.test_cases = test_cases + + def __iter__(self): + return self + + def next(self): + return self.inputs.next() + # XXX: raise exception or fire callback when inputs are exhausted + + def generate_inputs(self): + for input_unit in self.input_unit_list: + for test_case in self.test_cases: + yield NetTestTask(test_case, input_unit) + +@defer.inlineCallbacks +def runTestCases(test_cases, options, cmd_line_options): + + log.debug("Running %s" % test_cases) + log.debug("Options %s" % options) + log.debug("cmd_line_options %s" % dict(cmd_line_options)) + + test_inputs = options['inputs'] + + oonib_reporter = OONIBReporter(cmd_line_options) + yaml_reporter = YAMLReporter(cmd_line_options) + + if cmd_line_options['collector']: + log.msg("Using remote collector, please be patient while we create the report.") + try: + yield oonib_reporter.createReport(options) + except OONIBReportError: + log.err("Error in creating new report") + log.msg("We will only create reports to a file") + oonib_reporter = None + else: + oonib_reporter = None + + yield yaml_reporter.createReport(options) + log.msg("Reporting to file %s" % yaml_reporter._stream.name) + + nettest_task_factory = NetTestTaskFactory(test_cases, test_inputs) + + #XXX: resume is not supported! + try: + #XXX: override the default cooperator, set up own scheduler + #XXX: add callback when tasks are all exhausted + for nettest_task in nettest_task_factory.generate_inputs(): + nettest_task.yaml_reporter = yaml_reporter + nettest_task.oonib_reporter = oonib_reporter + log.debug("Running %s with input unit %s" % (nettest_task, + nettest_task.test_instance.input)) + # feed the cooperator + nettest_task.start() + + except Exception: + log.exception("Problem in running test")
tor-commits@lists.torproject.org