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

File 10952-tol-bin.2.patch, 5.2 KB (added by robertwb, 21 months 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: