@@ -0,0 +1,2683 @@
+from ooni import nettest%0Afrom ooni.utils import log%0Afrom twisted.internet import defer, protocol, reactor%0Afrom twisted.python import usage%0A%0Aimport os%0A%0A%0Adef which(program):%0A    def is_exe(fpath):%0A        return os.path.isfile(fpath) and os.access(fpath, os.X_OK)%0A%0A    fpath, fname = os.path.split(program)%0A    if fpath:%0A        if is_exe(program):%0A            return program%0A    else:%0A        for path in os.environ%5B%22PATH%22%5D.split(os.pathsep):%0A            path = path.strip('%22')%0A            exe_file = os.path.join(path, program)%0A            if is_exe(exe_file):%0A                return exe_file%0A    return None%0A%0A%0Aclass UsageOptions(usage.Options):%0A    optParameters = %5B%0A        %5B'interpreter', 'i', '', 'The interpreter to use'%5D,%0A        %5B'script', 's', '', 'The script to run'%5D%0A    %5D%0A%0A%0Aclass ScriptProcessProtocol(protocol.ProcessProtocol):%0A    def __init__(self, test_case):%0A        self.test_case = test_case%0A        self.deferred = defer.Deferred()%0A%0A    def connectionMade(self):%0A        log.debug(%22connectionMade%22)%0A        self.transport.closeStdin()%0A        self.test_case.report%5B'lua_output'%5D = %22%22%0A%0A    def outReceived(self, data):%0A        log.debug('outReceived: %25s' %25 data)%0A        self.test_case.report%5B'lua_output'%5D += data%0A%0A    def errReceived(self, data):%0A        log.err('Script error: %25s' %25 data)%0A        self.transport.signalProcess('KILL')%0A%0A    def processEnded(self, status):%0A        rc = status.value.exitCode%0A        log.debug('processEnded: %25s, %25s' %25 %5C%0A                  (rc, self.test_case.report%5B'lua_output'%5D))%0A        if rc == 0:%0A            self.deferred.callback(self)%0A        else:%0A            self.deferred.errback(rc)%0A%0A%0A# TODO: Maybe the script requires a back-end.%0Aclass Script(nettest.NetTestCase):%0A    name = %22Script test%22%0A    version = %220.1%22%0A    authors = %22Dominic Hamon%22%0A%0A    usageOptions = UsageOptions%0A    requiredOptions = %5B'interpreter', 'script'%5D%0A%0A    def test_run_script(self):%0A        %22%22%22%0A        We run the script specified in the usage options and take whatever%0A        is printed to stdout as the results of the test.%0A        %22%22%22%0A        processProtocol = ScriptProcessProtocol(self)%0A%0A        interpreter = self.localOptions%5B'interpreter'%5D%0A        if not which(interpreter):%0A            log.err('Unable to find %25s executable in PATH.' %25 interpreter)%0A            return%0A%0A        reactor.spawnProcess(processProtocol,%0A                             interpreter,%0A                             args=%5Binterpreter, self.localOptions%5B'script'%5D%5D,%0A                             env=%7B'HOME': os.environ%5B'HOME'%5D%7D,%0A                             usePTY=True)%0A%0A        if not reactor.running:%0A            reactor.run()%0A        return processProtocol.deferred%0A

Add Dominic Hamon's nettest for running tests written with other interpreters.