Wiki

Ticket #186: testify-refactor.patch

File testify-refactor.patch, 17.2 KB (added by hopscc, 14 years ago)
  • Source/TestifyRunner.cobra

     
    2929    var _statusCount as int 
    3030    var _resultsWriter as IndentedWriter? 
    3131 
     32    const bar = '----------------------------------------------------------------------------------------------------' 
    3233 
    3334    ## Init 
    3435 
     
    317318        return result1 
    318319 
    319320    def _testifyFile(baseName as String) as int 
     321        """Main processing method for Testify""" 
     322         
    320323        Node.setCompiler(nil) 
    321324        verbose = not _firstAttempt 
    322325        compilerVerbosity = if(.verbosity, .verbosity, if(verbose, 1, 0)) 
     
    328331        assert File.exists(baseName) 
    329332 
    330333        source = File.readAllText(baseName) 
    331         bar = '----------------------------------------------------------------------------------------------------' 
    332334 
    333335        print 
    334336        print 
     
    336338        print 'RUN [baseName]' 
    337339        print '    [Utils.combinePaths(Environment.currentDirectory, baseName)]' 
    338340        print '    Test #[_testifyCount+1]' 
    339         print bar 
    340         print bar 
     341        print .bar 
     342        print .bar 
    341343        if verbose 
    342344            Utils.printSource(source) 
    343             print bar 
     345            print .bar 
    344346        lines = List<of String>(source.split(c'\n'))  # CC: axe list wrapper 
    345         firstLine = lines[0] 
    346         firstLineInsensitive = firstLine.trim.replace(' ', '') 
    347347 
    348348        fileNames = [baseName] 
    349349        options = .testifyOptions 
    350         willRunExe = _willRunExes 
     350        options['willRunExe'] = _willRunExes # internal, specific to testify 
    351351 
     352        firstLine = '' 
     353        rc = _processFirstlineDirectives(baseName, fileNames, inout lines, inout options, out firstLine) 
     354        if rc == 0, return 0 
     355        firstLineInsensitive = firstLine.trim.replace(' ', '') 
     356         
     357        # Check for inline warning and error messages that are expected. 
     358        # (Starting in 2007-12 this is now the preferred way to specify these messages-- 
     359        #  with the actual line of code that generates them. 
     360        #  The old method of specifying at the top will still be needed for errors 
     361        #  and warnings that have no associated line number.) 
     362        expectingError = false 
     363        inLineMessages = _getInlineMessages(lines, out expectingError) 
     364        if inLineMessages.count > 0 #  hasInlineMessages 
     365            return _testifyInlineMessages(inLineMessages, expectingError,  
     366                                            compilerVerbosity, [baseName], options, verbose ) 
     367 
     368        if firstLineInsensitive.startsWith('#.error.') 
     369            # deprecated: put errors on the lines where they occur. the "hasInlineMessages" code above will detect them. 
     370            # note that errors that are only detected by the backend C# compiler are not detectable by testify 
     371            # CC: support split with a String extension method 
     372            # error = firstLine.split('.error.',1)[1].trim.toLower 
     373            index = firstLine.indexOf('.error.') 
     374            error = firstLine.substring(index+7).trim.toLower 
     375            return _testifyHeadError(error, compilerVerbosity, fileNames, options, verbose)  
     376 
     377        if firstLineInsensitive.startsWith('#.warning.') 
     378            # deprecated: put warnings on the lines where they occur. the "hasInlineMessages" code above will detect them. 
     379            index = firstLine.indexOf('.warning.') 
     380            warning = firstLine.substring(index+9).trim.toLower 
     381            return _testifyHeadWarning(warning, compilerVerbosity, fileNames, options, verbose)  
     382 
     383        return _testifyStd(compilerVerbosity, fileNames, options, verbose) 
     384 
     385 
     386    def _processFirstlineDirectives(baseName as String, fileNames as List<of String>, lines as inout List<of String>,  
     387                                    options as inout OptionValues, firstLine as out String) as int 
     388        """ 
     389        Check first few lines for Testify directives (start with '#.')  and process or setup 
     390        for later handling. 
     391        """  
     392        firstLine = lines[0] 
     393        firstLineInsensitive = firstLine.trim.replace(' ', '') 
    352394        while firstLineInsensitive.startsWith('#.') 
    353395 
    354396            if firstLineInsensitive.startsWith('#.multi.') 
     
    392434                continue 
    393435 
    394436            if firstLineInsensitive.startsWith('#.compile-only.')  # also meaning don't run the .exe 
    395                 willRunExe = false 
     437                options['willRunExe'] = false 
    396438                lines = lines[1:] 
    397439                firstLine = lines[0] 
    398440                firstLineInsensitive = firstLine.trim.replace(' ', '') 
     
    425467                break 
    426468             
    427469            throw Exception('Bad first line: [lines[0]]') 
     470 
     471        return 1 # continue processing in caller 
    428472         
    429         # Check for inline warning and error messages that are expected. 
    430         # (Starting in 2007-12 this is now the preferred way to specify these messages-- 
    431         #  with the actual line of code that generates them. 
    432         #  The old method of specifying at the top will still be needed for errors 
    433         #  and warnings that have no associated line number.) 
    434         hasInlineMessages = false 
     473    def _getInlineMessages(lines as List<of String>, expectingError as out bool) as Dictionary<of int, String> 
     474        """Walk lines and accumulate inline warnings and error messages.""" 
    435475        inLineMessages = Dictionary<of int, String>() 
    436476        lineNum = 1 
    437477        expectingError = false 
    438478        for line in lines 
    439479            if lineNum > 1 and ('.warning.' in line or '.error.' in line) 
    440                 hasInlineMessages = true 
    441480                if '.warning.' in line 
    442481                    message = line[line.indexOf('.warning.') + '.warning.'.length:] 
    443482                    messageType = 'w' 
     
    449488                    throw FallThroughException(line) 
    450489                inLineMessages[lineNum] = messageType + message 
    451490            lineNum += 1 
    452         if hasInlineMessages 
    453             try 
    454                 c = Compiler(compilerVerbosity, _cachedTestifyModules, commandLineArgParser=_cl.argParser) 
    455                 c.testifyFilesNamed([baseName], options, _resultsWriter to !, verbose) 
    456             catch StopCompilation 
    457                 pass 
    458             catch exc as Exception 
    459                 print 'Internal exception: [exc]' 
    460                 .failed 
    461                 return 0 
    462             for msg in c.messages 
    463                 if not msg.hasSourceSite or msg.lineNum == 0 
    464                     print 'Not expecting messages without any line number information:' 
    465                     print msg 
    466                     bad = true 
    467                     continue 
    468                 if not inLineMessages.containsKey(msg.lineNum) 
    469                     print 'Encountered unexpected message:' 
    470                     print msg 
    471                     bad = true 
    472                     continue 
    473                 expected = inLineMessages[msg.lineNum] 
    474                 branch expected[0] 
    475                     on c'w' 
    476                         if msg.isError 
    477                             print 'Expecting warning on line [msg.lineNum], but got error instead.' 
    478                             bad = true 
    479                     on c'e' 
    480                         if not msg.isError 
    481                             print 'Expecting error on line [msg.lineNum], but got warning instead.' 
    482                             bad = true 
    483                     else 
    484                         throw FallThroughException(expected) 
    485                 if bad 
    486                     continue 
    487                 expected = expected[1:].trim 
    488                 if msg.message.trim.toLower.indexOf(expected.toLower) == -1 
    489                     print 'Expecting message :', expected 
    490                     print 'But got           :', msg.message.trim 
    491                     print 'At line           :', msg.lineNum 
    492                     bad = true 
    493                     continue 
    494                 # we made it! same type of message and text 
    495                 print 'Message for line [msg.lineNum] was expected.' 
    496                 inLineMessages.remove(msg.lineNum) 
    497             # check for expected messages that never occurred 
    498             for key in inLineMessages.keys 
     491        return inLineMessages 
     492         
     493    def _testifyInlineMessages(inLineMessages as Dictionary<of int, String>,  
     494                                expectingError as bool,  
     495                                compilerVerbosity as int,  
     496                                fileNames as IList<of String>, 
     497                                options as OptionValues, verbose as bool) as int  
     498        """Testify on files that have inline checks for compiler errors and warnings"""                      
     499        try 
     500            c = Compiler(compilerVerbosity, _cachedTestifyModules, commandLineArgParser=_cl.argParser) 
     501            c.testifyFilesNamed(fileNames, options, _resultsWriter to !, verbose) 
     502        catch StopCompilation 
     503            pass 
     504        catch exc as Exception 
     505            print 'Internal exception: [exc]' 
     506            .failed 
     507            return 0 
     508        for msg in c.messages 
     509            if not msg.hasSourceSite or msg.lineNum == 0 
     510                print 'Not expecting messages without any line number information:' 
     511                print msg 
    499512                bad = true 
    500                 print 'Expecting message on line [key]:', inLineMessages[key][1:].trim 
    501             if bad 
    502                 .failed 
    503                 return 0 
    504             else if expectingError 
    505                 return 1 
    506             else if willRunExe 
    507                 # a test case with nothing but warnings is still executed 
    508                 return _testifyRun(c, bar) 
    509             else 
    510                 return 1 
     513                continue 
     514            if not inLineMessages.containsKey(msg.lineNum) 
     515                print 'Encountered unexpected message:' 
     516                print msg 
     517                bad = true 
     518                continue 
     519            expected = inLineMessages[msg.lineNum] 
     520            branch expected[0] 
     521                on c'w' 
     522                    if msg.isError 
     523                        print 'Expecting warning on line [msg.lineNum], but got error instead.' 
     524                        bad = true 
     525                on c'e' 
     526                    if not msg.isError 
     527                        print 'Expecting error on line [msg.lineNum], but got warning instead.' 
     528                        bad = true 
     529                else 
     530                    throw FallThroughException(expected) 
     531            if bad, continue 
     532            expected = expected[1:].trim 
     533            if msg.message.trim.toLower.indexOf(expected.toLower) == -1 
     534                print 'Expecting message :', expected 
     535                print 'But got           :', msg.message.trim 
     536                print 'At line           :', msg.lineNum 
     537                bad = true 
     538                continue 
     539            # we made it! same type of message and text 
     540            print 'Message for line [msg.lineNum] was expected.' 
     541            inLineMessages.remove(msg.lineNum) 
    511542 
    512         if firstLineInsensitive.startsWith('#.error.') 
    513             # deprecated: put errors on the lines where they occur. the "hasInlineMessages" code above will detect them. 
    514             # note that errors that are only detected by the backend C# compiler are not detectable by testify 
    515             # CC: support split with a String extension method 
    516             # error = firstLine.split('.error.',1)[1].trim.toLower 
    517             index = firstLine.indexOf('.error.') 
    518             error = firstLine.substring(index+7).trim.toLower 
    519             try 
    520                 c = Compiler(compilerVerbosity, _cachedTestifyModules, commandLineArgParser=_cl.argParser) 
    521                 c.testifyFilesNamed(fileNames, options, _resultsWriter to !, verbose) 
    522                 print 'Expecting error(s): [error]' 
    523                 print 'No error at all.' 
    524                 if c.errors.count > 0 
    525                     print 'warning: error count > 0 but StopCompilation was not thrown' 
    526                 .failed 
    527                 return 0 
    528             catch StopCompilation 
    529                 assert c.errors.count 
    530                 expectedErrors = error.split(c'&') 
    531                 for i = 0 .. expectedErrors.length 
    532                     expectedError = expectedErrors[i].trim 
    533                     print 'Expecting error substring [i+1] of [expectedErrors.length]: **[expectedError]**' 
    534                     if i >= c.errors.count 
    535                         print 'Ran out of real errors.' 
    536                         .failed 
    537                         return 0 
    538                     actualError = c.errors[i] 
    539                     if actualError.message.toLower.indexOf(expectedError)==-1 
    540                         print 'Actual error is: **[actualError.message]**' 
    541                         .failed 
    542                         return 0 
    543                     else 
    544                         print 'Matches: "[actualError.message]"' 
    545                 if c.errors.count > expectedErrors.length 
    546                     print 'There are more actual errors than expected errors:' 
    547                     for i = expectedErrors.length .. c.errors.count 
    548                         print 'Another actual error: [c.errors[i].message]' 
     543        # check for expected messages that never occurred 
     544        for key in inLineMessages.keys 
     545            bad = true 
     546            print 'Expecting message on line [key]:', inLineMessages[key][1:].trim 
     547        if bad 
     548            .failed 
     549            return 0 
     550        else if expectingError 
     551            return 1 
     552        else if options.boolValue('willRunExe') # a test case with nothing but warnings is still executed 
     553            return _testifyRun(c) 
     554        return 1 
     555 
     556    def _testifyHeadError(error as String, compilerVerbosity as int, fileNames as IList<of String>, 
     557                            options as OptionValues, verbose as bool) as int  
     558        try 
     559            c = Compiler(compilerVerbosity, _cachedTestifyModules, commandLineArgParser=_cl.argParser) 
     560            c.testifyFilesNamed(fileNames, options, _resultsWriter to !, verbose) 
     561            print 'Expecting error(s): [error]' 
     562            print 'No error at all.' 
     563            if c.errors.count > 0 
     564                print 'warning: error count > 0 but StopCompilation was not thrown' 
     565            .failed 
     566            return 0 
     567        catch StopCompilation 
     568            assert c.errors.count 
     569            expectedErrors = error.split(c'&') 
     570            for i in 0 : expectedErrors.length 
     571                expectedError = expectedErrors[i].trim 
     572                print 'Expecting error substring [i+1] of [expectedErrors.length]: **[expectedError]**' 
     573                if i >= c.errors.count 
     574                    print 'Ran out of real errors.' 
    549575                    .failed 
    550576                    return 0 
    551             catch exc as Exception 
    552                 print 'Internal exception: [exc]' 
     577                actualError = c.errors[i] 
     578                if actualError.message.toLower.indexOf(expectedError) == -1 
     579                    print 'Actual error is: **[actualError.message]**' 
     580                    .failed 
     581                    return 0 
     582                print 'Matches: "[actualError.message]"' 
     583            if c.errors.count > expectedErrors.length 
     584                print 'There are more actual errors than expected errors:' 
     585                for i in expectedErrors.length : c.errors.count 
     586                    print 'Another actual error: [c.errors[i].message]' 
    553587                .failed 
    554588                return 0 
    555             return 1 
     589        catch exc as Exception 
     590            print 'Internal exception: [exc]' 
     591            .failed 
     592            return 0 
     593        return 1 
    556594 
    557         if firstLineInsensitive.startsWith('#.warning.') 
    558             # deprecated: put warnings on the lines where they occur. the "hasInlineMessages" code above will detect them. 
    559             index = firstLine.indexOf('.warning.') 
    560             warning = firstLine.substring(index+9).trim.toLower 
    561             # TODO: the following code both checks for warnings to be thrown as well as going through a list of warnings. Seems like it should just need to do one or the other. 
    562             try 
    563                 c = Compiler(compilerVerbosity, _cachedTestifyModules, commandLineArgParser=_cl.argParser) 
    564                 c.testifyFilesNamed(fileNames, options, _resultsWriter to !, verbose) 
    565             catch StopCompilation 
    566                 print 'Expecting warning substring: "[warning]"' 
    567                 print 'But got errors.' 
     595    def _testifyHeadWarning(warning as String, compilerVerbosity as int, fileNames as IList<of String>, 
     596                            options as OptionValues, verbose as bool) as int  
     597        # TODO: the following code both checks for warnings to be thrown as well as going through a list of warnings. Seems like it should just need to do one or the other. 
     598        try 
     599            c = Compiler(compilerVerbosity, _cachedTestifyModules, commandLineArgParser=_cl.argParser) 
     600            c.testifyFilesNamed(fileNames, options, _resultsWriter to !, verbose) 
     601        catch StopCompilation 
     602            print 'Expecting warning substring: "[warning]"' 
     603            print 'But got errors.' 
     604            .failed 
     605            return 0 
     606        catch exc as Exception 
     607            print 'Internal exception: [exc]' 
     608            .failed 
     609            return 0 
     610        expectedWarnings = warning.split(c'&') 
     611        for i in 0 : expectedWarnings.length 
     612            expectedWarning = expectedWarnings[i].trim 
     613            print 'Expecting warning substring [i+1] of [expectedWarnings.length]: **[expectedWarning]**' 
     614            if i >= c.warnings.count 
     615                print 'Ran out of real warnings.' 
    568616                .failed 
    569617                return 0 
    570             catch exc as Exception 
    571                 print 'Internal exception: [exc]' 
     618            actualWarning = c.warnings[i] 
     619            if actualWarning.message.toLower.indexOf(expectedWarning)==-1 
     620                print 'Actual warning is: **[actualWarning.message]**' 
    572621                .failed 
    573622                return 0 
    574             expectedWarnings = warning.split(c'&') 
    575             for i = 0 .. expectedWarnings.length 
    576                 expectedWarning = expectedWarnings[i].trim 
    577                 print 'Expecting warning substring [i+1] of [expectedWarnings.length]: **[expectedWarning]**' 
    578                 if i >= c.warnings.count 
    579                     print 'Ran out of real warnings.' 
    580                     .failed 
    581                     return 0 
    582                 actualWarning = c.warnings[i] 
    583                 if actualWarning.message.toLower.indexOf(expectedWarning)==-1 
    584                     print 'Actual warning is: **[actualWarning.message]**' 
    585                     .failed 
    586                     return 0 
    587                 else 
    588                     print 'Matches: "[actualWarning.message]"' 
    589             if c.warnings.count > expectedWarnings.length 
    590                 print 'There are more actual warnings than expected warnings:' 
    591                 for i = expectedWarnings.length .. c.warnings.count 
    592                     print 'Another actual warning: [c.warnings[i].message]' 
    593                 .failed 
    594                 return 0 
    595             return 1 
    596  
     623            else 
     624                print 'Matches: "[actualWarning.message]"' 
     625        if c.warnings.count > expectedWarnings.length 
     626            print 'There are more actual warnings than expected warnings:' 
     627            for i in expectedWarnings.length : c.warnings.count 
     628                print 'Another actual warning: [c.warnings[i].message]' 
     629            .failed 
     630            return 0 
     631        return 1 
     632             
     633    def _testifyStd(compilerVerbosity as int, fileNames as IList<of String>, 
     634                            options as OptionValues, verbose as bool) as int         
    597635        c = Compiler(compilerVerbosity, _cachedTestifyModules, commandLineArgParser=_cl.argParser) 
    598  
    599636        try 
    600637            c.testifyFilesNamed(fileNames, options, _resultsWriter to !, verbose) 
    601638        catch StopCompilation 
     
    606643            .failed 
    607644            return 0 
    608645 
    609         if willRunExe and options.boolValue('compile')  # maybe changed by compiler directive 
    610             willRunExe = false 
     646        if options.boolValue('willRunExe') and options.boolValue('compile')  # maybe changed by compiler directive 
     647            options['willRunExe'] = false 
     648             
    611649        if c.messages.count 
    612650            # can't be errors or StopCompilation would have been caught above 
    613651            print 'Unexpected warnings in test.' 
    614652            .failed 
    615653            return 0 
    616654 
    617         if willRunExe 
    618             return _testifyRun(c, bar) 
    619         else 
    620             return 1 
     655        if options.boolValue('willRunExe') 
     656            return _testifyRun(c) 
     657        return 1 
    621658 
    622     def _testifyRun(c as Compiler, bar as String) as int 
     659    def _testifyRun(c as Compiler) as int 
    623660        if not c.fullExeFileName.endsWith('.exe') 
    624661            if File.exists(c.fullExeFileName) 
    625662                print 'Produced file "[c.fullExeFileName]" as expected.' 
     
    648685                .failed 
    649686                return 0 
    650687 
    651         print bar 
     688        print .bar 
    652689 
    653690        _cachedTestifyModules = for mod in c.modules where mod inherits AssemblyModule get mod 
    654691