[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [ooni-probe/master] * Refactored nettest.TestCase to get it to recognize test suboptions.



commit 1928f5b0b898c1cdfe817e9432b92543c4763298
Author: Isis Lovecruft <isis@xxxxxxxxxxxxxx>
Date:   Wed Oct 31 17:10:12 2012 +0000

    * Refactored nettest.TestCase to get it to recognize test suboptions.
---
 ooni/nettest.py |  194 +++++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 146 insertions(+), 48 deletions(-)

diff --git a/ooni/nettest.py b/ooni/nettest.py
index d35bd4d..3c87b7f 100644
--- a/ooni/nettest.py
+++ b/ooni/nettest.py
@@ -1,9 +1,21 @@
+# -*- coding: utf-8 -*-
+
 import itertools
 import os
 
-from twisted.trial import unittest, itrial
-from twisted.internet import defer, utils
-from ooni.utils import log
+from inspect                   import classify_class_attrs
+from pprint                    import pprint
+
+from twisted.internet          import defer, utils
+from twisted.python            import usage
+from twisted.trial             import unittest, itrial
+from zope.interface.exceptions import BrokenImplementation
+
+from ooni.inputunit            import InputUnitProcessor
+from ooni.utils                import log
+from ooni.utils.assertions     import isClass, isNotClass
+from ooni.utils.assertions     import isOldStyleClass, isNewStyleClass
+
 
 pyunit = __import__('unittest')
 
@@ -33,19 +45,19 @@ class TestCase(unittest.TestCase):
     you will subclass this object.
 
     * inputs: can be set to a static set of inputs. All the tests (the methods
-      starting with the "test_" prefix) will be run once per input.  At every run
-      the _input_ attribute of the TestCase instance will be set to the value of
-      the current iteration over inputs.  Any python iterable object can be set
-      to inputs.
+      starting with the "test_" prefix) will be run once per input.  At every
+      run the _input_ attribute of the TestCase instance will be set to the
+      value of the current iteration over inputs.  Any python iterable object
+      can be set to inputs.
 
-    * inputFile: attribute should be set to an array containing the command line
-      argument that should be used as the input file. Such array looks like
-      this:
+    * inputFile: attribute should be set to an array containing the command
+      line argument that should be used as the input file. Such array looks
+      like this:
 
           ``["commandlinearg", "c", "The description"]``
 
-      The second value of such arrray is the shorthand for the command line arg.
-      The user will then be able to specify inputs to the test via:
+      The second value of such arrray is the shorthand for the command line
+      arg.  The user will then be able to specify inputs to the test via:
 
           ``ooniprobe mytest.py --commandlinearg path/to/file.txt``
 
@@ -55,8 +67,8 @@ class TestCase(unittest.TestCase):
 
 
     * inputProcessor: should be set to a function that takes as argument an
-      open file descriptor and it will yield the input to be passed to the test
-      instance.
+      open file descriptor and it will yield the input to be passed to the
+      test instance.
 
     * name: should be set to the name of the test.
 
@@ -67,32 +79,47 @@ class TestCase(unittest.TestCase):
 
     * version: is the version string of the test.
     """
-    name = "I Did Not Change The Name"
-    author = "John Doe <foo@xxxxxxxxxxx>"
-    version = "0"
+    name    = "I Did Not Change The Name"
+    author  = "John Doe <foo@xxxxxxxxxxx>"
+    version = "0.0.0"
 
-    inputs = [None]
     inputFile = None
+    inputs    = [None]
 
     report = {}
     report['errors'] = []
 
     optParameters = None
+    optFlags      = None
+    subCommands   = None
 
-    def _raaun(self, methodName, result):
-        from twisted.internet import reactor
-        method = getattr(self, methodName)
-        log.debug("Running %s" % methodName)
-        d = defer.maybeDeferred(
-                utils.runWithWarningsSuppressed, self._getSuppress(), method)
-        d.addBoth(lambda x : call.active() and call.cancel() or x)
-        return d
+    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.
+        """
+        methodName = 'runTest'
+        if kwargs:
+            if 'methodName' in kwargs:
+                methodName = kwargs['methodName']
+
+        super(TestCase, self).__init__(methodName=methodName)
 
+        #for key, value in kwargs.items():
+        #    setattr(self.__class__, key, value)
+        #
+        #self.inputs = self.getInputs()
 
     def deferSetUp(self, ignored, result):
         """
-        If we have the reporterFactory set we need to write the header. If such
-        method is not present we will only run the test skipping header
+        If we have the reporterFactory set we need to write the header. If
+        such method is not present we will only run the test skipping header
         writing.
         """
         if result.reporterFactory.firstrun:
@@ -103,31 +130,102 @@ class TestCase(unittest.TestCase):
         else:
             return unittest.TestCase.deferSetUp(self, ignored, result)
 
-    def inputProcessor(self, fp):
-        for x in fp.readlines():
-            yield x.strip()
+    def _raaun(self, methodName, result):
+        from twisted.internet import reactor
+        method = getattr(self, methodName)
+        log.debug("Running %s" % methodName)
+        d = defer.maybeDeferred(
+                utils.runWithWarningsSuppressed, self._getSuppress(), method)
+        d.addBoth(lambda x : call.active() and call.cancel() or x)
+        return d
+
+    @staticmethod
+    def inputParser(inputs):
+        """Replace me with a custom function for parsing inputs."""
+        return inputs
+
+    def __input_file_processor__(self, fp):
+        """
+        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`.
+        """
+        for line in fp.readlines():
+            yield self.inputParser(line.strip())
         fp.close()
 
-    def getOptions(self):
+    def __get_inputs__(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`.
+        """
+        processor = InputUnitProcessor(self.inputs,
+                                       input_filter=None,
+                                       catch_err=False)
+        processed = processor.process()
+
+        log.msg("Received direct inputs:\n%s" % inputs)
+        log.debug("Our InputUnitProcessor is %s" % processor)
+
+        #while processed is not StopIteration:
+        #    self.inputs = processed
+        #    yield self.inputs
+        #else:
+        #    if self.inputFile:
+
         if self.inputFile:
             try:
-                assert isinstance(self.inputFile, str)
-            except AssertionError, ae:
-                log.err(ae)
+                fp = open(self.inputFile) ## xxx fixme:
+            except Exception, e:          ## bad news to leave file
+                log.err(e)                ## descriptors open
+            else:
+                from_file = self.__input_file_processor__(fp)
+                self.inputs = itertools.chain(processor, from_file)
+        elif self.inputFile is False:
+            log.debug("%s specified that it doesn't need inputFile."
+                      % self.__class__.__name__)
+            self.inputs = processed
+        else:
+            raise BrokenImplementation
+
+        return self.inputs
+
+    def getOptions(self):
+        '''
+        for attr in attributes:
+            if not attr.name is 'optParameters' or attr.name is 'optFlags':
+                continue
+            elif attr.name is 'optParameters':
+                cls._optParameters = attr.object
+            elif attr.name is 'optFlags':
+                log.debug("Applying %s.%s = %s to data descriptor..."
+                          % (cls.__name__, "_"+attr.name, attr.object))
+                cls._optParameters = attr.object
             else:
-                if os.path.isfile(self.inputFile):
-                    print self.inputFile
-                    fp = open(self.inputFile)
-                    self.inputs = self.inputProcessor(fp)
-        elif not self.inputs[0]:
-            pass
-        elif self.inputFile:
-            raise usage.UsageError("No input file specified!")
-        # XXX perhaps we may want to name and version to be inside of a
-        # different object that is not called options.
-        return {'inputs': self.inputs,
-                'name': self.name,
-                'version': self.version}
+                log.debug("How did we get here? attr.name = %s" % attr.name)
+        '''
+        if self.localOptions:
+            if self.inputs[0] is not None or self.inputFile is not None:
+                self.__get_inputs__()
+            return self.localOptions
+        else:
+            raise Exception, "could not find cls.localOptions! 234"
+
+        # if options:
+        #     return options
+        # else:
+        #     ## is this safe to do? it might turn Hofstaeder...
+        #     return self.__dict__
+        ####################
+        ## original return
+        ####################
+        #return {'inputs': self.inputs,
+        #        'name': self.name,
+        #        'version': self.version}
 
     def __repr__(self):
         return "<%s inputs=%s>" % (self.__class__, self.inputs)



_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits