0001"""
0002More-or-less like doctest, except with Logo
0003"""
0004
0005import os
0006import doctest
0007import sys
0008import traceback
0009from cStringIO import StringIO
0010import reader
0011import interpreter
0012from pylogo import builtins
0013from pylogo import oobuiltins
0014
0015def testfile(filename, globs=None, name=None,
0016             verbose=None, optionflags=0,
0017             report=True, master=None,
0018             interp=None,
0019             verbose_summary=False):
0020    if globs is None:
0021        globs = {}
0022    if interp is None:
0023        interp = interpreter.RootFrame()
0024        interp.import_module(builtins)
0025        interp.import_module(oobuiltins)
0026    interp.vars.update(globs)
0027    if name is None:
0028        name = os.path.basename(filename)
0029    runner = LogoRunner(interp, verbose=verbose,
0030                        optionflags=optionflags)
0031    s = open(filename).read()
0032    parser = doctest.DocTestParser()
0033    test = parser.get_doctest(s, globs, name,
0034                              filename, 0)
0035    runner.run(test)
0036    if report:
0037        runner.summarize(verbose or verbose_summary)
0038    if master is None:
0039        master = runner
0040    else:
0041        master.merge(runner)
0042    return runner.failures, runner.tries
0043
0044class LogoRunner(doctest.DocTestRunner):
0045
0046    def __init__(self, interpreter, *args, **kw):
0047        doctest.DocTestRunner.__init__(self, *args, **kw)
0048        self.interpreter = interpreter
0049
0050    def _DocTestRunner__run(self, test, compileflags, out):
0051        failures, tries = self._run(test, compileflags, out)
0052        self._DocTestRunner__record_outcome(test, failures, tries)
0053        return failures, tries
0054
0055    #/////////////////////////////////////////////////////////////////
0056    # DocTest Running
0057    #/////////////////////////////////////////////////////////////////
0058
0059    def _DocTestRunner__run(self, test, compileflags, out):
0060        """
0061        Run the examples in `test`.  Write the outcome of each example
0062        with one of the `DocTestRunner.report_*` methods, using the
0063        writer function `out`.  `compileflags` is the set of compiler
0064        flags that should be used to execute examples.  Return a tuple
0065        `(f, t)`, where `t` is the number of examples tried, and `f`
0066        is the number of examples that failed.  The examples are run
0067        in the namespace `test.globs`.
0068        """
0069        # Keep track of the number of failures and tries.
0070        failures = tries = 0
0071
0072        # Save the option flags (since option directives can be used
0073        # to modify them).
0074        original_optionflags = self.optionflags
0075
0076        SUCCESS, FAILURE, BOOM = range(3) # `outcome` state
0077
0078        check = self._checker.check_output
0079
0080        # Process each example.
0081        for examplenum, example in enumerate(test.examples):
0082
0083            # If REPORT_ONLY_FIRST_FAILURE is set, then supress
0084            # reporting after the first failure.
0085            quiet = (self.optionflags & doctest.REPORT_ONLY_FIRST_FAILURE and
0086                     failures > 0)
0087
0088            # Merge in the example's options.
0089            self.optionflags = original_optionflags
0090            if example.options:
0091                for (optionflag, val) in example.options.items():
0092                    if val:
0093                        self.optionflags |= optionflag
0094                    else:
0095                        self.optionflags &= ~optionflag
0096
0097            # Record that we started this example.
0098            tries += 1
0099            if not quiet:
0100                self.report_start(out, test, example)
0101
0102            # Use a special filename for compile(), so we can retrieve
0103            # the source code during interactive debugging (see
0104            # __patched_linecache_getlines).
0105            filename = '<doctest %s[%d]>' % (test.name, examplenum)
0106
0107            # Run the example in the given context (globs), and record
0108            # any exception that gets raised.  (But don't intercept
0109            # keyboard interrupts.)
0110            try:
0111                # Don't blink!  This is where the user's code gets run.
0112                self.run_example(example.source, filename, compileflags, test.globs)
0113                self.debugger.set_continue() # ==== Example Finished ====
0114                exception = None
0115            except KeyboardInterrupt:
0116                raise
0117            except:
0118                exception = sys.exc_info()
0119                self.debugger.set_continue() # ==== Example Finished ====
0120
0121            got = self._fakeout.getvalue()  # the actual output
0122            self._fakeout.truncate(0)
0123            outcome = FAILURE   # guilty until proved innocent or insane
0124
0125            # If the example executed without raising any exceptions,
0126            # verify its output.
0127            if exception is None:
0128                if check(example.want, got, self.optionflags):
0129                    outcome = SUCCESS
0130
0131            # The example raised an exception:  check if it was expected.
0132            else:
0133                exc_info = sys.exc_info()
0134                exc_msg = traceback.format_exception_only(*exc_info[:2])[-1]
0135                if not quiet:
0136                    got += doctest._exception_traceback(exc_info)
0137
0138                # If `example.exc_msg` is None, then we weren't expecting
0139                # an exception.
0140                if example.exc_msg is None:
0141                    outcome = BOOM
0142
0143                # We expected an exception:  see whether it matches.
0144                elif check(example.exc_msg, exc_msg, self.optionflags):
0145                    outcome = SUCCESS
0146
0147                # Another chance if they didn't care about the detail.
0148                elif self.optionflags & doctest.IGNORE_EXCEPTION_DETAIL:
0149                    m1 = re.match(r'[^:]*:', example.exc_msg)
0150                    m2 = re.match(r'[^:]*:', exc_msg)
0151                    if m1 and m2 and check(m1.group(0), m2.group(0),
0152                                           self.optionflags):
0153                        outcome = SUCCESS
0154
0155            # Report the outcome.
0156            if outcome is SUCCESS:
0157                if not quiet:
0158                    self.report_success(out, test, example, got)
0159            elif outcome is FAILURE:
0160                if not quiet:
0161                    self.report_failure(out, test, example, got)
0162                failures += 1
0163            elif outcome is BOOM:
0164                if not quiet:
0165                    self.report_unexpected_exception(out, test, example,
0166                                                     exc_info)
0167                failures += 1
0168            else:
0169                assert False, ("unknown outcome", outcome)
0170
0171        # Restore the option flags (in case they were modified)
0172        self.optionflags = original_optionflags
0173
0174        # Record and return the number of failures and tries.
0175        self._DocTestRunner__record_outcome(test, failures, tries)
0176        return failures, tries
0177
0178    prompts = {
0179        None: '',
0180        'to': '',
0181        '[': '',
0182        '(': '',
0183        'func': '',
0184        }
0185
0186    def run_example(self, source, filename,
0187                    compileflags, globs):
0188        input = StringIO(source)
0189        input = reader.TrackingStream(input, name=filename)
0190        tokenizer = reader.FileTokenizer(
0191            input, prompt=self.prompts)
0192        interp = self.interpreter
0193        interp.push_tokenizer(tokenizer)
0194        try:
0195            v = interp.expr_top()
0196            if v is not None:
0197                print builtins.logo_repr(v)
0198        finally:
0199            interp.pop_tokenizer()
0200
0201if __name__ == '__main__':
0202    filenames = sys.argv[1:]
0203    for filename in filenames:
0204        if filename.startswith('-'):
0205            continue
0206        print "testing", filename
0207        testfile(filename)