Ticket #10952: 10952-tol-bin.2.patch

File 10952-tol-bin.2.patch, 5.2 KB (added by robertwb, 9 years ago)
  • sage-doctest

    # HG changeset patch
    # User Robert Bradshaw <robertwb@math.washington.edu>
    # Date 1300429764 25200
    # Node ID c1e730e089904d01bd592cc6029e6eb0ca78a5a2
    # Parent  26abf552ceaa2a69e7cf50ad4826a64406284cf2
    #10952 - better handling of numerial noise in doctests
    
    diff -r 26abf552ceaa -r c1e730e08990 sage-doctest
    a b  
    216216    sys.exit(runner.failures)
    217217""" % dict
    218218
    219 NONE=0; LONG_TIME=1; RANDOM=2; OPTIONAL=3; NOT_IMPLEMENTED=4; NOT_TESTED=5
     219NONE=0; LONG_TIME=1; RANDOM=2; OPTIONAL=3; NOT_IMPLEMENTED=4; NOT_TESTED=5; TOLERANCE=6
     220tolerance_pattern = re.compile(r'\b((?:abs(?:olute)?)|(?:rel(?:ative)?))? *?tol(?:erance)?\b( +[0-9.e+-]+)?')
    220221
    221222def comment_modifier(s):
    222223    sind = s.find('#')
     
    235236        v.append(NOT_TESTED)
    236237    if 'random' in L:
    237238        v.append(RANDOM)
     239    m = tolerance_pattern.search(L)
     240    if m:
     241        v.append(TOLERANCE)
     242        rel_or_abs, epsilon = m.groups()
     243        if rel_or_abs is not None:
     244            rel_or_abs = rel_or_abs[:3]
     245        if epsilon is None:
     246            epsilon = 1e-15
     247        else:
     248            epsilon = float(epsilon.strip())
     249        v.append((rel_or_abs, epsilon))
    238250    return v, L
    239251
     252def close_tolerance(comment_tags):
     253    rel_or_abs, epsilon = comment_tags[comment_tags.index(TOLERANCE) + 1]
     254    return "... ''', res, %r%s)" % (epsilon, '' if rel_or_abs is None else ", '%s'" % rel_or_abs)
     255
    240256def preparse_line_with_prompt(L):
    241257    i = L.find(':')
    242258    if i == -1:
     
    255271    # t: Deal with code whose output should be ignored.
    256272    t = []
    257273
    258     # following two: used only for parsing only_optional; list of comments
    259     comment_modifiers = [] 
     274    # used for adapting the output based on comments
     275    has_tolerance = False
     276    comment_modifiers = []
    260277    last_prompt_comment = ''
    261278   
    262279    for L in s.splitlines():
     280        if not L.strip():
     281            if has_tolerance:
     282                t.append(close_tolerance(c))
     283                has_tolerance = False
     284       
    263285        begin = L.lstrip()[:5]
    264286        comment = ''
    265287        if begin == 'sage:':
     288            if has_tolerance:
     289                t.append(close_tolerance(c))
    266290            c, comment = comment_modifier(L)
    267291            last_prompt_comment = comment
    268292            line = ''
     
    276300                L = '\n'   # not tested
    277301            if OPTIONAL in c and not (only_optional or optional):
    278302                L = '\n'
     303            if TOLERANCE in c:
     304                t.append(">>> res = Exception")
     305                L = "sage: res = %s" % L.lstrip()[5:]
     306                has_tolerance = True
     307            else:
     308                has_tolerance = False
    279309            line = preparse_line_with_prompt(L)
    280310            if RANDOM in c:
    281311                # count spaces at the beginning of line to fix alignment later
     
    285315                # append a line saying 'ignore' followed by ellipsis (...)
    286316                # and an empty line, to ignore the output given in the test
    287317                line += '\n' + ' '*i + 'ignore ...\n'
     318            if has_tolerance:
     319                line += "\n>>> check_with_tolerance('''"
    288320            t.append(line)
    289321           
    290322        elif begin.startswith('...'):
     
    294326           
    295327        else:
    296328            comment = last_prompt_comment
     329            if has_tolerance:
     330                L = "... " + L
    297331            t.append(L)
    298332       
    299333        comment_modifiers.append(comment)
    300334
     335    if has_tolerance:
     336        t.append(close_tolerance(c))
     337
    301338    # The very last line -- which is typically """ -- must never be marked as optional,
    302339    # or it might not get included, which would be a syntax error.
    303340    comment_modifiers[-1] = ''
     
    419456    s = "# -*- coding: utf-8 -*-\n"
    420457    s += "from sage.all_cmdline import *; \n"
    421458    s += "import sage.plot.plot; sage.plot.plot.DOCTEST_MODE=True\n"  # turn off image popup
    422     s += """
     459    s += r"""
    423460def warning_function(f):
    424461    import warnings
    425462
     
    433470def change_warning_output(file):
    434471    import warnings
    435472    warnings.showwarning = warning_function(file)
     473
     474import re
     475float_pattern = re.compile('[+-]?((\d*\.?\d+)|(\d+\.?))([eE][+-]?\d+)?')
     476
     477def check_with_tolerance(expected, actual, epsilon, rel_or_abs=None):
     478    if actual is Exception:
     479        return # error computing actual
     480    else:
     481        actual = str(actual)
     482    expected = re.sub('\n +', '\n', expected)
     483    actual = re.sub('\n +', '\n', actual)
     484    assert float_pattern.sub('#', expected.strip()) == float_pattern.sub('#', actual.strip()), \
     485        "Expected '" + expected + "' got '" + actual + "'"
     486    for expected_value, actual_value in zip(float_pattern.finditer(expected), float_pattern.finditer(actual)):
     487        expected_value = float(expected_value.group())
     488        actual_value = float(actual_value.group())
     489        if rel_or_abs == 'abs' or expected_value == 0:
     490            assert abs(expected_value - actual_value) < epsilon, "Out of tolerance %s vs %s" % (expected_value, actual_value)
     491        else:
     492            assert abs((expected_value - actual_value) / expected_value) < epsilon, "Out of tolerance %s vs %s" % (expected_value, actual_value)
    436493"""
    437494
    438495    if not library_code: