| 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(' ', '') |
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.""" |
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 |
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) |
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.' |
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.' |
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 |