[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [ooni-probe/master] Store the state of the currently running tests, their progress and estimated time to completion
commit a3829e7a9700bfa748088c18e3afa44a086ed6e2
Author: Arturo Filastò <art@xxxxxxxxx>
Date: Mon Nov 26 03:38:47 2012 +0100
Store the state of the currently running tests, their progress and estimated time to completion
* Display every 5 seconds a summary of all the running tests and their progress
* Refactor input unit and nettest
---
ooni/config.py | 1 +
ooni/inputunit.py | 27 +++++++++++++-----
ooni/nettest.py | 22 ++++++++++-----
ooni/oonicli.py | 15 ++++++++++-
ooni/reporter.py | 2 +-
ooni/runner.py | 66 ++++++++++++++++++++++++++++++++--------------
tests/test_inputunit.py | 15 +++++++++-
7 files changed, 108 insertions(+), 40 deletions(-)
diff --git a/ooni/config.py b/ooni/config.py
index d86f4d7..69842b6 100644
--- a/ooni/config.py
+++ b/ooni/config.py
@@ -14,6 +14,7 @@ from ooni.utils import Storage
reports = Storage()
scapyFactory = None
stateDict = None
+state = Storage()
# XXX refactor this to use a database
resume_lock = defer.DeferredLock()
diff --git a/ooni/inputunit.py b/ooni/inputunit.py
index 65d578c..2ef89d8 100644
--- a/ooni/inputunit.py
+++ b/ooni/inputunit.py
@@ -9,7 +9,6 @@
# :authors: Arturo Filastò
# :license: see included LICENSE file
-
class InputUnitFactory(object):
"""
This is a factory that takes the size of input units to be generated a set
@@ -20,27 +19,39 @@ class InputUnitFactory(object):
all the elements in memory to be able to produce InputUnits.
"""
inputUnitSize = 10
+ length = None
def __init__(self, inputs=[]):
+ """
+ Args:
+ inputs (iterable): inputs *must* be an iterable.
+ """
self._inputs = iter(inputs)
- self._idx = 0
+ self.inputs = iter(inputs)
self._ended = False
def __iter__(self):
return self
+ def __len__(self):
+ """
+ Returns the number of input units in the input unit factory.
+ """
+ if not self.length:
+ self.length = sum(1 for _ in self._inputs)/self.inputUnitSize
+ return self.length
+
def next(self):
input_unit_elements = []
if self._ended:
raise StopIteration
- for i in xrange(self._idx, self._idx + self.inputUnitSize):
+ for i in xrange(self.inputUnitSize):
try:
- input_unit_elements.append(self._inputs.next())
+ input_unit_elements.append(self.inputs.next())
except StopIteration:
self._ended = True
break
- self._idx += self.inputUnitSize
if not input_unit_elements:
raise StopIteration
@@ -55,12 +66,12 @@ class InputUnit(object):
def __init__(self, inputs=[]):
self._inputs = iter(inputs)
- def __repr__(self):
+ def __str__(self):
return "<%s inputs=%s>" % (self.__class__, self._inputs)
def __add__(self, inputs):
- for input in inputs:
- self._inputs.append(input)
+ for i in inputs:
+ self._inputs.append(i)
def __iter__(self):
return self
diff --git a/ooni/nettest.py b/ooni/nettest.py
index e96fba1..e0393e7 100644
--- a/ooni/nettest.py
+++ b/ooni/nettest.py
@@ -22,6 +22,7 @@ from ooni.utils import log
class NoPostProcessor(Exception):
pass
+
class NetTestCase(object):
"""
This is the base of the OONI nettest universe. When you write a nettest
@@ -150,17 +151,22 @@ class NetTestCase(object):
if not self.localOptions[required_option]:
raise usage.UsageError("%s not specified!" % required_option)
- def _processOptions(self, options=None):
+ def _processOptions(self):
if self.inputFilename:
- self.inputs = self.inputProcessor(self.inputFilename)
-
- self._checkRequiredOptions()
+ inputProcessor = self.inputProcessor
+ inputFilename = self.inputFilename
+ class inputProcessorIterator(object):
+ """
+ Here we convert the input processor generator into an iterator
+ so that we can run it twice.
+ """
+ def __iter__(self):
+ return inputProcessor(inputFilename)
+ self.inputs = inputProcessorIterator()
- # XXX perhaps we may want to name and version to be inside of a
- # different method that is not called options.
return {'inputs': self.inputs,
- 'name': self.name,
- 'version': self.version}
+ 'name': self.name, 'version': self.version
+ }
def __repr__(self):
return "<%s inputs=%s>" % (self.__class__, self.inputs)
diff --git a/ooni/oonicli.py b/ooni/oonicli.py
index 3a8b3df..2b0991b 100644
--- a/ooni/oonicli.py
+++ b/ooni/oonicli.py
@@ -15,7 +15,7 @@ import random
import time
import yaml
-from twisted.internet import defer, reactor
+from twisted.internet import defer, reactor, task
from twisted.application import app
from twisted.python import usage, failure
from twisted.python.util import spewer
@@ -78,6 +78,15 @@ class Options(usage.Options):
except:
raise usage.UsageError("No test filename specified!")
+def updateStatusBar():
+ for test_filename in config.state.keys():
+ # The ETA is not updated so we we will not print it out for the
+ # moment.
+ eta = config.state[test_filename].eta()
+ progress = config.state[test_filename].progress()
+ progress_bar_frmt = "[%s] %s%%" % (test_filename, progress)
+ print progress_bar_frmt
+
def testsEnded(*arg, **kw):
"""
You can place here all the post shutdown tasks.
@@ -123,6 +132,10 @@ def run():
d2 = defer.DeferredList(deck_dl)
d2.addBoth(testsEnded)
+ # Print every 5 second the list of current tests running
+ l = task.LoopingCall(updateStatusBar)
+ l.start(5.0)
+
if config.start_reactor:
log.debug("Starting reactor")
reactor.run()
diff --git a/ooni/reporter.py b/ooni/reporter.py
index 3cc77d7..dac9aba 100644
--- a/ooni/reporter.py
+++ b/ooni/reporter.py
@@ -284,7 +284,7 @@ class OONIBReporter(OReporter):
bodyProducer = StringProducer(json.dumps(request))
try:
- response = yield self.agent.request("PUT", url,
+ response = yield self.agent.request("PUT", url,
bodyProducer=bodyProducer)
except:
# XXX we must trap this in the runner and make sure to report the data later.
diff --git a/ooni/runner.py b/ooni/runner.py
index e7a40fd..942e3b5 100644
--- a/ooni/runner.py
+++ b/ooni/runner.py
@@ -26,7 +26,7 @@ from ooni.nettest import NetTestCase, NoPostProcessor
from ooni import reporter, config
-from ooni.utils import log, checkForRoot, NotRootError
+from ooni.utils import log, checkForRoot, NotRootError, Storage
def processTest(obj):
"""
@@ -67,7 +67,7 @@ def processTest(obj):
try:
log.debug("processing options")
tmp_test_case_object = obj()
- tmp_test_case_object._processOptions(options)
+ tmp_test_case_object._checkRequiredOptions()
except usage.UsageError, e:
test_name = tmp_test_case_object.name
@@ -120,6 +120,9 @@ def makeTestCases(klass, tests, method_prefix):
cases.append((klass, method_prefix+test))
return cases
+class NoTestCasesFound(Exception):
+ pass
+
def loadTestsAndOptions(classes, cmd_line_options):
"""
Takes a list of test classes and returns their testcases and options.
@@ -134,7 +137,10 @@ def loadTestsAndOptions(classes, cmd_line_options):
test_cases = makeTestCases(klass, tests, method_prefix)
test_klass = klass()
- options = test_klass._processOptions(cmd_line_options)
+ options = test_klass._processOptions()
+
+ if not test_cases:
+ raise NoTestCasesFound
return test_cases, options
@@ -220,7 +226,8 @@ def runTestCasesWithInputUnit(test_cases, input_unit, oreporter):
dl = []
for test_input in input_unit:
log.debug("Running test with this input %s" % test_input)
- d = runTestCasesWithInput(test_cases, test_input, oreporter)
+ d = runTestCasesWithInput(test_cases,
+ test_input, oreporter)
dl.append(d)
return defer.DeferredList(dl)
@@ -322,28 +329,45 @@ def increaseInputUnitIdx(test_filename):
config.stateDict[test_filename] += 1
yield updateResumeFile(test_filename)
+def setupProgressMeters(test_filename, input_unit_factory,
+ test_case_number):
+ """
+ Sets up the meters required for keeping track of the current progress of
+ certain tests.
+ """
+ log.msg("Setting up progress meters")
+ if not config.state.test_filename:
+ config.state[test_filename] = Storage()
+
+ config.state[test_filename].per_item_average = 2.0
+
+ input_unit_idx = float(config.stateDict[test_filename])
+ input_unit_items = float(len(input_unit_factory) + 1)
+ test_case_number = float(test_case_number)
+ total_iterations = input_unit_items * test_case_number
+ current_iteration = input_unit_idx * test_case_number
+
+ def progress():
+ return (current_iteration / total_iterations) * 100.0
+
+ config.state[test_filename].progress = progress
+
+ def eta():
+ return (total_iterations - current_iteration) \
+ * config.state[test_filename].per_item_average
+ config.state[test_filename].eta = eta
+
+ config.state[test_filename].input_unit_idx = input_unit_idx
+ config.state[test_filename].input_unit_items = input_unit_items
+
+
@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))
- try:
- assert len(options) != 0, "Length of options is zero!"
- except AssertionError, ae:
- test_inputs = []
- log.err(ae)
- else:
- try:
- first = options.pop(0)
- except:
- first = options
- if 'inputs' in first:
- test_inputs = options['inputs']
- else:
- log.msg("Could not find inputs!")
- log.msg("options[0] = %s" % first)
- test_inputs = [None]
+ test_inputs = options['inputs']
if cmd_line_options['collector']:
log.msg("Using remote collector, please be patient while we create the report.")
@@ -379,6 +403,8 @@ def runTestCases(test_cases, options, cmd_line_options):
else:
config.stateDict[test_filename] = 0
+ setupProgressMeters(test_filename, input_unit_factory, len(test_cases))
+
try:
for input_unit in input_unit_factory:
log.debug("Running this input unit %s" % input_unit)
diff --git a/tests/test_inputunit.py b/tests/test_inputunit.py
index f591e23..1f9043c 100644
--- a/tests/test_inputunit.py
+++ b/tests/test_inputunit.py
@@ -1,10 +1,13 @@
import unittest
from ooni.inputunit import InputUnit, InputUnitFactory
+def dummyGenerator():
+ for x in range(100):
+ yield x
+
class TestInputUnit(unittest.TestCase):
def test_input_unit_factory(self):
- inputs = range(100)
- inputUnit = InputUnitFactory(inputs)
+ inputUnit = InputUnitFactory(range(100))
for i in inputUnit:
self.assertEqual(len(list(i)), inputUnit.inputUnitSize)
@@ -16,3 +19,11 @@ class TestInputUnit(unittest.TestCase):
idx += 1
self.assertEqual(idx, 100)
+
+ def test_input_unit_factory_length(self):
+ inputUnitFactory = InputUnitFactory(range(100))
+ l1 = len(inputUnitFactory)
+ l2 = sum(1 for _ in inputUnitFactory)
+ self.assertEqual(l1, 10)
+ self.assertEqual(l2, 10)
+
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits