[tor-commits] [ooni-probe/master] * Updated the Makefile which is used for checking if OONI tests are working.
isis at torproject.org
isis at torproject.org
Sun Nov 4 13:50:40 UTC 2012
commit 9b2132cea553a7e723efa1fb3afcce425c745ca3
Author: Isis Lovecruft <isis at torproject.org>
Date: Sun Nov 4 03:59:03 2012 +0000
* Updated the Makefile which is used for checking if OONI tests are working.
* Changes to nettest and runner to make them call parent classes correctly.
* Started working on adaptor class for nettest.TestCase.
---
bin/Makefile | 20 ++--
nettests/core/echo.py | 1 +
ooni/nettest.py | 272 +++++++++++++++++++++++++++++++++++++------------
ooni/runner.py | 240 +++++++++++++++++++++++++-------------------
4 files changed, 355 insertions(+), 178 deletions(-)
diff --git a/bin/Makefile b/bin/Makefile
index 307e81f..ee8976f 100644
--- a/bin/Makefile
+++ b/bin/Makefile
@@ -21,34 +21,34 @@ all: echot simplet captivet dnst httphostt squidt
all_debug: echod simpled captived dnsd httphostd squidd
simplet:
- ../bin/ooniprobe ../nettests/simpletest.py -a ../ooni/assets/hostnames.txt
+ ../bin/ooniprobe ../nettests/simpletest.py -a ../lists/short_hostname_list.txt
simpled:
- python -m pdb ../bin/ooniprobe ../nettests/simpletest.py -a ../ooni/assets/hostnames.txt
+ python -m pdb ../bin/ooniprobe ../nettests/simpletest.py -a ../lists/short_hostname_list.txt
echot:
- ../bin/ooniprobe ../nettests/core/echo.py -f ../ooni/assets/hostnames.txt
+ ../bin/ooniprobe ../nettests/core/echo.py -f ../lists/short_hostname_list.txt
echod:
- python -m pdb ../bin/ooniprobe ../nettests/core/echo.py -f ../ooni/assets/hostnames.txt
+ python -m pdb ../bin/ooniprobe -B ../nettests/core/echo.py -f ../lists/short_hostname_list.txt
captivet:
- ../bin/ooniprobe ../nettests/core/captiveportal.py -f ../ooni/assets/hostnames.txt
+ ../bin/ooniprobe ../nettests/core/captiveportal.py -f ../lists/short_hostname_list.txt
captived:
- python -m pdb ../bin/ooniprobe --spew ../nettests/core/captiveportal.py -f ../ooni/assets/hostnames.txt
+ python -m pdb ../bin/ooniprobe --spew ../nettests/core/captiveportal.py -f ../lists/short_hostname_list.txt
dnst:
- ../bin/ooniprobe ../nettests/core/dnstamper.py -f ../ooni/assets/hostnames.txt
+ ../bin/ooniprobe ../nettests/core/dnstamper.py -f ../lists/short_hostname_list.txt
dnsd:
- python -m pdb ../bin/ooniprobe --spew ../nettests/core/dnstamper.py -f ../ooni/assets/hostnames.txt
+ python -m pdb ../bin/ooniprobe --spew ../nettests/core/dnstamper.py -f ../lists/short_hostname_list.txt
squidt:
- ../bin/ooniprobe ../nettests/core/squid.py -f ../ooni/assets/hostnames.txt
+ ../bin/ooniprobe ../nettests/core/squid.py -f ../lists/short_hostname_list.txt
squidd:
- python -m pdb ../bin/ooniprobe --spew ../nettests/core/squid.py -f ../ooni/assets/hostnames.txt
+ python -m pdb ../bin/ooniprobe --spew ../nettests/core/squid.py -f ../lists/short_hostname_list.txt
#mvreports:
# for $report in `find ../ -name "report*"`; do mv $report test-results #; done
diff --git a/nettests/core/echo.py b/nettests/core/echo.py
new file mode 120000
index 0000000..d9926cd
--- /dev/null
+++ b/nettests/core/echo.py
@@ -0,0 +1 @@
+../../ooni/bridget/tests/echo.py
\ No newline at end of file
diff --git a/ooni/nettest.py b/ooni/nettest.py
index 541d65b..e01c4f1 100644
--- a/ooni/nettest.py
+++ b/ooni/nettest.py
@@ -65,7 +65,206 @@ class InputTestSuite(pyunit.TestSuite):
return result
-class TestCase(unittest.TestCase):
+class NetTestAdaptor(unittest.TestCase):
+ """
+ XXX fill me in
+ """
+ @staticmethod
+ def __copyattr__(obj, old, new=None, alt=None):
+ """
+ Assign me to a new attribute name to have a copy of the old
+ attribute, if it exists.
+
+ Example:
+ >>> self.sun = "black"
+ >>> self._watermelon = __copyattr__(self, "sun")
+ >>> self._clocknoise = __copyattr__(self, "sound")
+ >>> print self._watermelon
+ black
+ >>> print self._clocknoise
+
+ :param old:
+ A string representing the name of the old attribute
+ :param new:
+ (Optional) A string to set as the new attribute name.
+ :param alt:
+ (Optional) An alternate value to return if the old
+ attribute is not found.
+ :return:
+ If :param:`old` is found, I return it's value.
+
+ If :param:`old` is not found:
+ If :param:`alt` is given, I return :param:`alt`.
+ Else, I return None.
+
+ If :param:`new` is set, I do not return anything, and
+ instead I set the new attribute's name to :param:`name`
+ and it's value to the value which I would have otherwise
+ returned.
+ """
+ if not new:
+ if not alt:
+ if hasattr(obj, old):
+ return getattr(obj, old)
+ return
+ else:
+ if hasattr(obj, old):
+ return getattr(obj, old)
+ return alt
+ else:
+ if not alt:
+ if hasattr(obj, old):
+ _copy = getattr(obj, old)
+ else:
+ copy = None
+ setattr(obj, new, _copy)
+ else:
+ if hasattr(obj, old):
+ _copy = getattr(obj, old)
+ else:
+ _copy = alt
+ setattr(obj, new, _copy)
+
+ ## Using setattr in __init__ for now:
+ #def copyattr(self, *args, **kwargs):
+ # if len(args) >= 1:
+ # _copy = partial(__copyattr__, args[0])
+ # if len(args) == 2:
+ # return _copy(new=args[1])
+ # elif len(args) == 3:
+ # return _copy(new=args[1], alt=args[2])
+ # elif kwargs:
+ # return _copy(kwargs)
+ # else:
+ # return
+
+ def __init__(self, *args, **kwargs):
+ """
+ If you override me, you must call
+
+ ``super(NetTestCase, self).__init__(*args, **kwargs)``
+
+ at the beginning of your __init__ method. Keyword arguments passed to
+ the above statement become attributes of the adaptor, and can be used
+ to alter the logic of input handling and parent class instantiation.
+ Therefore, You probably do not need to pass me any keyword arguments
+ when calling me, i.e. using ``(*args, **kwargs)`` will work just fine.
+ """
+ log.debug("NetTestAdapter: created")
+ if kwargs:
+ if 'methodName' in kwargs:
+ log.debug("NetTestAdaptor: found 'methodName' in kwargs")
+ log.debug("NetTestAdaptor: calling unittest.TestCase.__init()")
+ super( NetTestAdaptor, self ).__init__(
+ methodName=kwargs['methodName'] )
+ else:
+ log.debug("NetTestAdaptor: calling unittest.TestCase.__init()")
+ super( NetTestAdaptor, self ).__init__( )
+
+ for key, value in kwargs.items(): ## Let subclasses define their
+ if key != 'methodName': ## instantiation without
+ if not hasattr(self, key): ## overriding parent classes
+ log.debug("NetTestAdaptor: calling setattr(self,%s,%s)"
+ % (key, value) )
+ setattr(self, key, value)
+
+ #setattr(self, "copyattr", __copy_attr__)
+
+ ## Internal attribute copies:
+ #self._input_parser = copyattr("inputParser", alt=__input_parser__)
+ #self._nettest_name = copyattr("name", alt="NetTestAdaptor"))
+
+ ## Set our inputs to the parsed and processed inputs:
+ #self.inputs = __get_inputs__()
+
+ ## xxx do we need:
+ if self.parsed_inputs:
+ self.inputs = self.parsed_inputs
+ else:
+ log.debug("Unable to find parsed inputs")
+
+ @staticmethod
+ def __input_parser__(one_input): return one_input
+
+ @classmethod
+ def setUpClass(cls):
+ """
+ Create a TestCase instance. This function is equivalent to '__init__'.
+ To add futher setup steps before a set of tests in a TestCase instance
+ run, create a function called 'setUp'.
+
+ Class attributes, such as `report`, `optParameters`, `name`, and
+ `author` should be overriden statically as class attributes in any
+ subclass of :class:`ooni.nettest.TestCase`, so that the calling
+ functions in ooni.runner can handle them correctly.
+ """
+ cls._raw_inputs = __copyattr__(cls, "inputs")
+ cls._input_file = __copyattr__(cls, "inputFile")
+ cls._input_parser = __copyattr__(cls, "inputParser", alt=__input_parser__)
+ cls._nettest_name = __copyattr__(cls, "name", alt="NetTestAdaptor")
+ cls.parsed_inputs = __get_inputs__(cls)
+ ## XXX we should handle options generation here
+
+ @classmethod
+ def __get_inputs__(cls):
+ """
+ I am called from the ooni.runner and you probably should not override
+ me. I gather the internal inputs from :class:`NetTestCase` attributes
+ and pass them through :meth:`NetTestCase.inputParser`. If you are
+ looking for a way to parse inputs from inputFile, see
+ :meth:`inputParser`. If :class:`NetTestCase` has an attribute
+ :attr:`inputFile`, I also handle opening that file, striping each line
+ of whitespace, and then sending the line to
+ :meth:`NetTestCase.inputParser`.
+
+ All inputs which I find, both statically set inputs and those returned
+ from processing an inputFile, I add to a list :ivar:`parsed`, after
+ parsing them. I return :ivar:`parsed`:
+
+ :ivar parsed:
+ A list of parsed inputs.
+ :return:
+ :ivar:`parsed`.
+ """
+ parsed = []
+
+ if cls._raw_inputs:
+ if isinstance(cls._raw_inputs, (list, tuple,)):
+ if len(cls._raw_inputs) > 0:
+ if len(cls._raw_inputs) == 1 and cls._raw_inputs[0] is None:
+ pass ## don't burn cycles on testing null inputs
+ else:
+ log.msg("Received direct inputs:\n%s" % cls._raw_inputs)
+ parsed.extend([cls._input_parser(x) for x in cls._raw_inputs])
+ elif isinstance(cls._raw_inputs, str):
+ separated = cls._raw_inputs.translate(None, ',') ## space delineates
+ inputlist = separated.split(' ')
+ parsed.extend([cls._input_parser(x) for x in inputlist])
+ else:
+ log.debug("inputs not string or list; type: %s"
+ % type(cls._raw_inputs))
+
+ if cls._input_file:
+ try:
+ log.debug("Opening input file")
+ fp = open(cls._input_file)
+ except:
+ log.debug("Couldn't open input file")
+ else:
+ log.debug("Running input file processor")
+ lines = [line.strip() for line in fp.readlines()]
+ fp.close()
+
+ ## add to what we've already parsed, if any:
+ log.debug("Parsing lines from input file")
+ parsed.extend([cls._input_parser(ln) for ln in lines])
+ else:
+ log.debug("%s specified that it doesn't need inputFile."
+ % cls._nettest_name)
+
+ return parsed
+
+class NetTestCase(NetTestAdaptor):
"""
This is the monad of the OONI nettest universe. When you write a nettest
you will subclass this object.
@@ -109,7 +308,7 @@ class TestCase(unittest.TestCase):
"""
name = "I Did Not Change The Name"
author = "Jane Doe <foo at example.com>"
- version = "0"
+ version = "0.0.0"
inputFile = None
inputs = [None]
@@ -118,28 +317,13 @@ class TestCase(unittest.TestCase):
report['errors'] = []
optParameters = None
+ optFlags = None
+ subCommands = None
requiresRoot = False
- def setUpClass(self, *args, **kwargs):
- """
- Create a TestCase instance. This function is equivalent to '__init__'.
- To add futher setup steps before a set of tests in a TestCase instance
- run, create a function called 'setUp'.
-
- Class attributes, such as `report`, `optParameters`, `name`, and
- `author` should be overriden statically as class attributes in any
- subclass of :class:`ooni.nettest.TestCase`, so that the calling
- functions in ooni.runner can handle them correctly.
- """
- if kwargs and 'methodName' in kwargs:
- return super( TestCase, self ).setUpClass(
- methodName=kwargs['methodName'] )
- return super( TestCase, self ).setUpClass( )
-
- #for key, value in kwargs.items():
- # setattr(self.__class__, key, value)
- #
- #self.inputs = self.getInputs()
+ @classmethod
+ def setUpClass(cls):
+ pass
def deferSetUp(self, ignored, result):
"""
@@ -157,53 +341,11 @@ class TestCase(unittest.TestCase):
log.debug("Not first run. Running test setup directly")
return unittest.TestCase.deferSetUp(self, ignored, result)
- @staticmethod
- def inputParser(inputs):
+ def inputParser(self, inputs):
"""Replace me with a custom function for parsing inputs."""
log.debug("Running custom input processor")
return inputs
- def _getInputs(self):
- """
- I am called from the ooni.runner and you probably should not override
- me. I gather the internal inputs from an instantiated test class and
- pass them to the rest of the runner.
-
- If you are looking for a way to parse inputs from inputFile, see
- :meth:`inputParser`.
- I open :attr:inputFile if there is one, and return inputs one by one
- after stripping them of whitespace and running them through the parser
- :meth:`inputParser`.
- """
-
- ## don't burn cycles on testing null inputs:
- if len(self.inputs) == 1 and self.inputs[0] == None:
- self.inputs = []
- processor = []
- else:
- log.msg("Received direct inputs:\n%s" % self.inputs)
- processor = [i for i in self.inputParser(self.inputs)]
-
- if self.inputFile:
- try:
- fp = open(self.inputFile)
- except Exception, e:
- log.err(e)
- fp.close()
- else:
- log.debug("Running input file processor")
- lines = [line.strip() for line in fp.readlines()]
- fp.close()
- parsed = [self.inputParser(ln) for ln in lines]
- both = itertools.chain(processor, parsed)
- elif self.inputFile is False:
- log.debug("%s specified that it doesn't need inputFile."
- % self.name)
- both = processor
- else:
- both = processor
- return both
-
def getOptions(self):
'''
diff --git a/ooni/runner.py b/ooni/runner.py
index ae086c1..cf3a7ee 100644
--- a/ooni/runner.py
+++ b/ooni/runner.py
@@ -22,7 +22,6 @@ from ooni.nettest import InputTestSuite, TestCase
from ooni.plugoo import tests as oonitests
from ooni.reporter import ReporterFactory
from ooni.utils import log, date
-from ooni.utils.assertions import isClass
from ooni.utils.legacy import LegacyOONITest
from ooni.utils.legacy import start_legacy_test, adapt_legacy_test
@@ -127,42 +126,48 @@ def processTestOptions(cls, config):
A configured and instantiated :class:`twisted.python.usage.Options`
class.
"""
- if cls.optParameters or cls.inputFile:
- if not cls.optParameters:
- cls.optParameters = []
+ #if cls.optParameters or cls.inputFile:
+ if not cls.optParameters:
+ cls.optParameters = []
- if cls.inputFile:
- cls.optParameters.append(cls.inputFile)
+ if cls.inputFile:
+ cls.optParameters.append(cls.inputFile)
- if not hasattr(cls, subCommands):
- cls.subCommands = []
+ log.debug("CLS IS %s" % cls)
+ log.debug("CLS OPTPARAM IS %s" % cls.optParameters)
- class Options(usage.Options):
- optParameters = cls.optParameters
- parseArgs = lambda a: cls.subCommands.append(a)
+ #if not hasattr(cls, subCommands):
+ # cls.subCommands = []
+
+ if not cls.subCommands:
+ cls.subCommands = []
+
+ class Options(usage.Options):
+ optParameters = cls.optParameters
+ parseArgs = lambda a: cls.subCommands.append(a)
- opts = Options()
- opts.parseOptions(config['subArgs'])
- cls.localOptions = opts
+ opts = Options()
+ opts.parseOptions(config['subArgs'])
+ cls.localOptions = opts
- if cls.inputFile:
- cls.inputFile = opts[cls.inputFile[0]]
- """
+ if cls.inputFile:
+ cls.inputFile = opts[cls.inputFile[0]]
+ """
+ try:
+ log.debug("%s: trying %s.localoptions.getOptions()..."
+ % (__name__, cls.name))
try:
- log.debug("%s: trying %s.localoptions.getOptions()..."
- % (__name__, cls.name))
- try:
- assert hasattr(cls, 'getOptions')
- except AssertionError, ae:
- options = opts.opt_help()
- raise Exception, "Cannot find %s.getOptions()" % cls.name
- else:
- options = cls.getOptions()
- except usage.UsageError:
+ assert hasattr(cls, 'getOptions')
+ except AssertionError, ae:
options = opts.opt_help()
+ raise Exception, "Cannot find %s.getOptions()" % cls.name
else:
- """
- return cls.localOptions
+ options = cls.getOptions()
+ except usage.UsageError:
+ options = opts.opt_help()
+ else:
+ """
+ return cls.localOptions
def loadTestsAndOptions(classes, config):
"""
@@ -178,90 +183,119 @@ def loadTestsAndOptions(classes, config):
DEPRECATED = LegacyOONITest
for klass in classes:
- if isinstance(klass, DEPRECATED) \
- and not issubclass(klass, TestCase):
- log.msg("Processing cases and options for legacy test %s"
- % ( klass.shortName if hasattr(klass, shortName)
- else 'oonitest' ))
- if hasattr(klass, description):
- log.msg("%s" % klass.description)
-
- subcmds = []
- if hasattr(klass, options): ## an unitiated Legacy test
- log.debug("%s.options found: %s " % (klass, klass.options))
- try:
- assert isclass(klass.options), \
- "%s is not class" % klass.qoptions
- except AssertionError, ae:
- log.debug(ae)
- else:
- ok = klass.options
- ok.parseArgs = lambda x: subcmds.append(x)
- try:
- opts = ok()
- opts.parseOptions(config['subArgs'])
- except Exception, e:
- log.debug(e)
- opts = {}
- finally:
- opts.append(opts)
-
- if hasattr(klass, local_options): ## we've been initialized already
- log.debug("%s.local_options found" % klass)
+ if isinstance(klass, DEPRECATED):
+ #not issubclass(klass, TestCase):
+ try:
+ cases, opts = processLegacyTest(klass, config)
+ if cases:
+ log.debug("Processing cases: %s" % str(cases))
+ return [], []
+ test_cases.append(cases)
+ except Exception, e:
+ log.err(e)
+ else:
try:
- assert klass.local_options is not None
opts = klass.local_options
+ option.append(opts)
except AttributeError, ae:
- opts = {}; log.debug(ae)
- finally:
- log.debug("type(opts) = %s" % type(opts))
- options.append(opts)
- try:
- cases = start_legacy_test(klass)
- #if cases: ## why are these empty lists here?
- # return [], [] ## were nettests having issues due
- except Exception, e: ## to legacy tests?
- cases = []; log.err(e)
- finally:
- log.debug(str(cases))
- test_cases.append(cases)
+ options.append([])
+ log.err(ae)
elif issubclass(klass, TestCase):
- log.debug("Processing cases and options for OONI %s test"
- % ( klass.name if hasattr( klass, 'name' ) \
- else TestCase.name ))
try:
- tests = reflect.prefixedMethodNames(klass, method_prefix)
+ cases, opts = processNetTest(klass, config, method_prefix)
except Exception, e:
- tests = []; log.debug(e)
+ log.err(e)
else:
- try:
- cases = makeTestCases(klass, tests, method_prefix)
- except Exception, e:
- cases = []; log.err(e)
- log.debug("loadTestsAndOptions(): test %s found cases=%s"
- % (tests, cases))
+ test_cases.append(cases)
+ options.append(opts)
- if hasattr(klass, 'optParameters') or hasattr(klass, 'inputFile'):
- try:
- opt = processTestOptions(klass, config)
- print opt, config
- except:
- opt = {}
- finally:
- instance = klass()
- try:
- inputs = instance._getInputs()
- except Exception, e:
- inputs = []; log.err(e)
- else:
- if opt and opt is not None:
- opt.update({'inputs':inputs})
- else: pass
- finally: options.append(opt)
- log.debug("loadTestsAndOptions(): type(opt)=%s" % type(opt))
return test_cases, options
+def processNetTest(klass, config, method_prefix):
+ try:
+ log.debug("Processing cases and options for OONI %s test"
+ % (klass.name if hasattr(klass, 'name') else 'Network Test'))
+
+ tests = reflect.prefixedMethodNames(klass, method_prefix)
+ if tests:
+ cases = makeTestCases(klass, tests, method_prefix)
+ log.debug("loadTestsAndOptions(): test %s found cases=%s"% (tests, cases))
+ try:
+ k = klass()
+ opts = processTestOptions(k, config)
+ except Exception, e:
+ opts = []
+ log.err(e)
+ else:
+ cases = []
+ except Exception, e:
+ log.err(e)
+
+ return cases, opts
+
+'''
+ if hasattr(klass, 'optParameters') or hasattr(klass, 'inputFile'):
+ try:
+ opts = processTestOptions(klass, config)
+ except:
+ opts = []
+ finally:
+ try:
+ k = klass()
+ inputs = k._getInputs()
+ except Exception, e:
+ inputs = []
+ log.err(e)
+ else:
+ if opts and len(inputs) != 0:
+ opts.append(['inputs', '', inputs, "cmdline inputs"])
+ log.debug("loadTestsAndOptions(): inputs=%s" % inputs)
+'''
+
+def processLegacyTest(klass, config):
+ log.msg("Processing cases and options for legacy test %s"
+ % ( klass.shortName if hasattr(klass, shortName) else 'oonitest' ))
+ if hasattr(klass, description):
+ log.msg("%s" % klass.description)
+
+ subcmds = []
+ if hasattr(klass, options): ## an unitiated Legacy test
+ log.debug("%s.options found: %s " % (klass, klass.options))
+ try:
+ assert isclass(klass.options), "legacytest.options is not class"
+ except AssertionError, ae:
+ log.debug(ae)
+ else:
+ ok = klass.options
+ ok.parseArgs = lambda x: subcmds.append(x)
+ try:
+ opts = ok()
+ opts.parseOptions(config['subArgs'])
+ except Exception, e:
+ log.err(e)
+ opts = {}
+
+ elif hasattr(klass, local_options): ## we've been initialized already
+ log.debug("%s.local_options found" % klass)
+ try:
+ assert klass.local_options is not None
+ opts = klass.local_options
+ except AttributeError, ae:
+ opts = {}; log.err(ae)
+
+ try:
+ cases = start_legacy_test(klass)
+ ## XXX we need to get these results into the reporter
+ if cases:
+ return [], []
+ except Exception, e:
+ cases = []; log.err(e)
+ finally:
+ log.debug(str(cases))
+
+ return cases, opts
+
class ORunner(object):
"""
This is a specialized runner used by the ooniprobe command line tool.
@@ -277,8 +311,8 @@ class ORunner(object):
try:
assert len(options) != 0, "Length of options is zero!"
except AssertionError, ae:
- self.inputs = []
log.err(ae)
+ self.inputs = []
else:
try:
first = options.pop(0)
More information about the tor-commits
mailing list