1# Copyright 2017 The Abseil Authors. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15import datetime 16import io 17import os 18import re 19import subprocess 20import sys 21import tempfile 22import threading 23import time 24import unittest 25from unittest import mock 26from xml.etree import ElementTree 27from xml.parsers import expat 28 29from absl import logging 30from absl.testing import _bazelize_command 31from absl.testing import absltest 32from absl.testing import parameterized 33from absl.testing import xml_reporter 34 35 36class StringIOWriteLn(io.StringIO): 37 38 def writeln(self, line): 39 self.write(line + '\n') 40 41 42class MockTest(absltest.TestCase): 43 failureException = AssertionError 44 45 def __init__(self, name): 46 super(MockTest, self).__init__() 47 self.name = name 48 49 def id(self): 50 return self.name 51 52 def runTest(self): 53 return 54 55 def shortDescription(self): 56 return "This is this test's description." 57 58 59# str(exception_type) is different between Python 2 and 3. 60def xml_escaped_exception_type(exception_type): 61 return xml_reporter._escape_xml_attr(str(exception_type)) 62 63 64OUTPUT_STRING = '\n'.join([ 65 r'<\?xml version="1.0"\?>', 66 ('<testsuites name="" tests="%(tests)d" failures="%(failures)d"' 67 ' errors="%(errors)d" time="%(run_time).3f" timestamp="%(start_time)s">'), 68 ('<testsuite name="%(suite_name)s" tests="%(tests)d"' 69 ' failures="%(failures)d" errors="%(errors)d" time="%(run_time).3f"' 70 ' timestamp="%(start_time)s">'), 71 (' <testcase name="%(test_name)s" status="%(status)s" result="%(result)s"' 72 ' time="%(run_time).3f" classname="%(classname)s"' 73 ' timestamp="%(start_time)s">%(message)s'), 74 ' </testcase>', '</testsuite>', 75 '</testsuites>', 76]) 77 78FAILURE_MESSAGE = r""" 79 <failure message="e" type="{}"><!\[CDATA\[Traceback \(most recent call last\): 80 File ".*xml_reporter_test\.py", line \d+, in get_sample_failure 81 self.fail\(\'e\'\) 82AssertionError: e 83\]\]></failure>""".format(xml_escaped_exception_type(AssertionError)) 84 85ERROR_MESSAGE = r""" 86 <error message="invalid literal for int\(\) with base 10: (')?a(')?" type="{}"><!\[CDATA\[Traceback \(most recent call last\): 87 File ".*xml_reporter_test\.py", line \d+, in get_sample_error 88 int\('a'\) 89ValueError: invalid literal for int\(\) with base 10: '?a'? 90\]\]></error>""".format(xml_escaped_exception_type(ValueError)) 91 92UNICODE_MESSAGE = r""" 93 <%s message="{0}" type="{1}"><!\[CDATA\[Traceback \(most recent call last\): 94 File ".*xml_reporter_test\.py", line \d+, in get_unicode_sample_failure 95 raise AssertionError\(u'\\xe9'\) 96AssertionError: {0} 97\]\]></%s>""".format( 98 r'\xe9', 99 xml_escaped_exception_type(AssertionError)) 100 101NEWLINE_MESSAGE = r""" 102 <%s message="{0}" type="{1}"><!\[CDATA\[Traceback \(most recent call last\): 103 File ".*xml_reporter_test\.py", line \d+, in get_newline_message_sample_failure 104 raise AssertionError\(\'{2}'\) 105AssertionError: {3} 106\]\]></%s>""".format( 107 'new
line', 108 xml_escaped_exception_type(AssertionError), 109 r'new\\nline', 110 'new\nline') 111 112UNEXPECTED_SUCCESS_MESSAGE = '\n'.join([ 113 '', 114 (r' <error message="" type=""><!\[CDATA\[Test case ' 115 r'__main__.MockTest.unexpectedly_passing_test should have failed, ' 116 r'but passed.\]\]></error>'), 117]) 118 119UNICODE_ERROR_MESSAGE = UNICODE_MESSAGE % ('error', 'error') 120NEWLINE_ERROR_MESSAGE = NEWLINE_MESSAGE % ('error', 'error') 121 122 123class TextAndXMLTestResultTest(absltest.TestCase): 124 125 def setUp(self): 126 super().setUp() 127 self.stream = StringIOWriteLn() 128 self.xml_stream = io.StringIO() 129 130 def _make_result(self, times): 131 timer = mock.Mock() 132 timer.side_effect = times 133 return xml_reporter._TextAndXMLTestResult(self.xml_stream, self.stream, 134 'foo', 0, timer) 135 136 def _assert_match(self, regex, output): 137 fail_msg = 'Expected regex:\n{}\nTo match:\n{}'.format(regex, output) 138 self.assertRegex(output, regex, fail_msg) 139 140 def _assert_valid_xml(self, xml_output): 141 try: 142 expat.ParserCreate().Parse(xml_output) 143 except expat.ExpatError as e: 144 raise AssertionError('Bad XML output: {}\n{}'.format(e, xml_output)) 145 146 def _simulate_error_test(self, test, result): 147 result.startTest(test) 148 result.addError(test, self.get_sample_error()) 149 result.stopTest(test) 150 151 def _simulate_failing_test(self, test, result): 152 result.startTest(test) 153 result.addFailure(test, self.get_sample_failure()) 154 result.stopTest(test) 155 156 def _simulate_passing_test(self, test, result): 157 result.startTest(test) 158 result.addSuccess(test) 159 result.stopTest(test) 160 161 def _iso_timestamp(self, timestamp): 162 return datetime.datetime.utcfromtimestamp(timestamp).isoformat() + '+00:00' 163 164 def test_with_passing_test(self): 165 start_time = 0 166 end_time = 2 167 result = self._make_result((start_time, start_time, end_time, end_time)) 168 169 test = MockTest('__main__.MockTest.passing_test') 170 result.startTestRun() 171 result.startTest(test) 172 result.addSuccess(test) 173 result.stopTest(test) 174 result.stopTestRun() 175 result.printErrors() 176 177 run_time = end_time - start_time 178 expected_re = OUTPUT_STRING % { 179 'suite_name': 'MockTest', 180 'tests': 1, 181 'failures': 0, 182 'errors': 0, 183 'run_time': run_time, 184 'start_time': re.escape(self._iso_timestamp(start_time),), 185 'test_name': 'passing_test', 186 'classname': '__main__.MockTest', 187 'status': 'run', 188 'result': 'completed', 189 'attributes': '', 190 'message': '' 191 } 192 self._assert_match(expected_re, self.xml_stream.getvalue()) 193 194 def test_with_passing_subtest(self): 195 start_time = 0 196 end_time = 2 197 result = self._make_result((start_time, start_time, end_time, end_time)) 198 199 test = MockTest('__main__.MockTest.passing_test') 200 subtest = unittest.case._SubTest(test, 'msg', None) 201 result.startTestRun() 202 result.startTest(test) 203 result.addSubTest(test, subtest, None) 204 result.stopTestRun() 205 result.printErrors() 206 207 run_time = end_time - start_time 208 expected_re = OUTPUT_STRING % { 209 'suite_name': 'MockTest', 210 'tests': 1, 211 'failures': 0, 212 'errors': 0, 213 'run_time': run_time, 214 'start_time': re.escape(self._iso_timestamp(start_time),), 215 'test_name': r'passing_test \[msg\]', 216 'classname': '__main__.MockTest', 217 'status': 'run', 218 'result': 'completed', 219 'attributes': '', 220 'message': '' 221 } 222 self._assert_match(expected_re, self.xml_stream.getvalue()) 223 224 def test_with_passing_subtest_with_dots_in_parameter_name(self): 225 start_time = 0 226 end_time = 2 227 result = self._make_result((start_time, start_time, end_time, end_time)) 228 229 test = MockTest('__main__.MockTest.passing_test') 230 subtest = unittest.case._SubTest(test, 'msg', {'case': 'a.b.c'}) 231 result.startTestRun() 232 result.startTest(test) 233 result.addSubTest(test, subtest, None) 234 result.stopTestRun() 235 result.printErrors() 236 237 run_time = end_time - start_time 238 expected_re = OUTPUT_STRING % { 239 'suite_name': 240 'MockTest', 241 'tests': 242 1, 243 'failures': 244 0, 245 'errors': 246 0, 247 'run_time': 248 run_time, 249 'start_time': 250 re.escape(self._iso_timestamp(start_time),), 251 'test_name': 252 r'passing_test \[msg\] \(case='a.b.c'\)', 253 'classname': 254 '__main__.MockTest', 255 'status': 256 'run', 257 'result': 258 'completed', 259 'attributes': 260 '', 261 'message': 262 '' 263 } 264 self._assert_match(expected_re, self.xml_stream.getvalue()) 265 266 def get_sample_error(self): 267 try: 268 int('a') 269 except ValueError: 270 error_values = sys.exc_info() 271 return error_values 272 273 def get_sample_failure(self): 274 try: 275 self.fail('e') 276 except AssertionError: 277 error_values = sys.exc_info() 278 return error_values 279 280 def get_newline_message_sample_failure(self): 281 try: 282 raise AssertionError('new\nline') 283 except AssertionError: 284 error_values = sys.exc_info() 285 return error_values 286 287 def get_unicode_sample_failure(self): 288 try: 289 raise AssertionError(u'\xe9') 290 except AssertionError: 291 error_values = sys.exc_info() 292 return error_values 293 294 def get_terminal_escape_sample_failure(self): 295 try: 296 raise AssertionError('\x1b') 297 except AssertionError: 298 error_values = sys.exc_info() 299 return error_values 300 301 def test_with_failing_test(self): 302 start_time = 10 303 end_time = 20 304 result = self._make_result((start_time, start_time, end_time, end_time)) 305 306 test = MockTest('__main__.MockTest.failing_test') 307 result.startTestRun() 308 result.startTest(test) 309 result.addFailure(test, self.get_sample_failure()) 310 result.stopTest(test) 311 result.stopTestRun() 312 result.printErrors() 313 314 run_time = end_time - start_time 315 expected_re = OUTPUT_STRING % { 316 'suite_name': 'MockTest', 317 'tests': 1, 318 'failures': 1, 319 'errors': 0, 320 'run_time': run_time, 321 'start_time': re.escape(self._iso_timestamp(start_time),), 322 'test_name': 'failing_test', 323 'classname': '__main__.MockTest', 324 'status': 'run', 325 'result': 'completed', 326 'attributes': '', 327 'message': FAILURE_MESSAGE 328 } 329 self._assert_match(expected_re, self.xml_stream.getvalue()) 330 331 def test_with_failing_subtest(self): 332 start_time = 10 333 end_time = 20 334 result = self._make_result((start_time, start_time, end_time, end_time)) 335 336 test = MockTest('__main__.MockTest.failing_test') 337 subtest = unittest.case._SubTest(test, 'msg', None) 338 result.startTestRun() 339 result.startTest(test) 340 result.addSubTest(test, subtest, self.get_sample_failure()) 341 result.stopTestRun() 342 result.printErrors() 343 344 run_time = end_time - start_time 345 expected_re = OUTPUT_STRING % { 346 'suite_name': 'MockTest', 347 'tests': 1, 348 'failures': 1, 349 'errors': 0, 350 'run_time': run_time, 351 'start_time': re.escape(self._iso_timestamp(start_time),), 352 'test_name': r'failing_test \[msg\]', 353 'classname': '__main__.MockTest', 354 'status': 'run', 355 'result': 'completed', 356 'attributes': '', 357 'message': FAILURE_MESSAGE 358 } 359 self._assert_match(expected_re, self.xml_stream.getvalue()) 360 361 def test_with_error_test(self): 362 start_time = 100 363 end_time = 200 364 result = self._make_result((start_time, start_time, end_time, end_time)) 365 366 test = MockTest('__main__.MockTest.failing_test') 367 result.startTestRun() 368 result.startTest(test) 369 result.addError(test, self.get_sample_error()) 370 result.stopTest(test) 371 result.stopTestRun() 372 result.printErrors() 373 xml = self.xml_stream.getvalue() 374 375 self._assert_valid_xml(xml) 376 377 run_time = end_time - start_time 378 expected_re = OUTPUT_STRING % { 379 'suite_name': 'MockTest', 380 'tests': 1, 381 'failures': 0, 382 'errors': 1, 383 'run_time': run_time, 384 'start_time': re.escape(self._iso_timestamp(start_time),), 385 'test_name': 'failing_test', 386 'classname': '__main__.MockTest', 387 'status': 'run', 388 'result': 'completed', 389 'attributes': '', 390 'message': ERROR_MESSAGE 391 } 392 self._assert_match(expected_re, xml) 393 394 def test_with_error_subtest(self): 395 start_time = 10 396 end_time = 20 397 result = self._make_result((start_time, start_time, end_time, end_time)) 398 399 test = MockTest('__main__.MockTest.error_test') 400 subtest = unittest.case._SubTest(test, 'msg', None) 401 result.startTestRun() 402 result.startTest(test) 403 result.addSubTest(test, subtest, self.get_sample_error()) 404 result.stopTestRun() 405 result.printErrors() 406 407 run_time = end_time - start_time 408 expected_re = OUTPUT_STRING % { 409 'suite_name': 'MockTest', 410 'tests': 1, 411 'failures': 0, 412 'errors': 1, 413 'run_time': run_time, 414 'start_time': re.escape(self._iso_timestamp(start_time),), 415 'test_name': r'error_test \[msg\]', 416 'classname': '__main__.MockTest', 417 'status': 'run', 418 'result': 'completed', 419 'attributes': '', 420 'message': ERROR_MESSAGE 421 } 422 self._assert_match(expected_re, self.xml_stream.getvalue()) 423 424 def test_with_fail_and_error_test(self): 425 """Tests a failure and subsequent error within a single result.""" 426 start_time = 123 427 end_time = 456 428 result = self._make_result((start_time, start_time, end_time, end_time)) 429 430 test = MockTest('__main__.MockTest.failing_test') 431 result.startTestRun() 432 result.startTest(test) 433 result.addFailure(test, self.get_sample_failure()) 434 # This could happen in tearDown 435 result.addError(test, self.get_sample_error()) 436 result.stopTest(test) 437 result.stopTestRun() 438 result.printErrors() 439 xml = self.xml_stream.getvalue() 440 441 self._assert_valid_xml(xml) 442 443 run_time = end_time - start_time 444 expected_re = OUTPUT_STRING % { 445 'suite_name': 'MockTest', 446 'tests': 1, 447 'failures': 1, # Only the failure is tallied (because it was first). 448 'errors': 0, 449 'run_time': run_time, 450 'start_time': re.escape(self._iso_timestamp(start_time),), 451 'test_name': 'failing_test', 452 'classname': '__main__.MockTest', 453 'status': 'run', 454 'result': 'completed', 455 'attributes': '', 456 # Messages from failure and error should be concatenated in order. 457 'message': FAILURE_MESSAGE + ERROR_MESSAGE 458 } 459 self._assert_match(expected_re, xml) 460 461 def test_with_error_and_fail_test(self): 462 """Tests an error and subsequent failure within a single result.""" 463 start_time = 123 464 end_time = 456 465 result = self._make_result((start_time, start_time, end_time, end_time)) 466 467 test = MockTest('__main__.MockTest.failing_test') 468 result.startTestRun() 469 result.startTest(test) 470 result.addError(test, self.get_sample_error()) 471 result.addFailure(test, self.get_sample_failure()) 472 result.stopTest(test) 473 result.stopTestRun() 474 result.printErrors() 475 xml = self.xml_stream.getvalue() 476 477 self._assert_valid_xml(xml) 478 479 run_time = end_time - start_time 480 expected_re = OUTPUT_STRING % { 481 'suite_name': 'MockTest', 482 'tests': 1, 483 'failures': 0, 484 'errors': 1, # Only the error is tallied (because it was first). 485 'run_time': run_time, 486 'start_time': re.escape(self._iso_timestamp(start_time),), 487 'test_name': 'failing_test', 488 'classname': '__main__.MockTest', 489 'status': 'run', 490 'result': 'completed', 491 'attributes': '', 492 # Messages from error and failure should be concatenated in order. 493 'message': ERROR_MESSAGE + FAILURE_MESSAGE 494 } 495 self._assert_match(expected_re, xml) 496 497 def test_with_newline_error_test(self): 498 start_time = 100 499 end_time = 200 500 result = self._make_result((start_time, start_time, end_time, end_time)) 501 502 test = MockTest('__main__.MockTest.failing_test') 503 result.startTestRun() 504 result.startTest(test) 505 result.addError(test, self.get_newline_message_sample_failure()) 506 result.stopTest(test) 507 result.stopTestRun() 508 result.printErrors() 509 xml = self.xml_stream.getvalue() 510 511 self._assert_valid_xml(xml) 512 513 run_time = end_time - start_time 514 expected_re = OUTPUT_STRING % { 515 'suite_name': 'MockTest', 516 'tests': 1, 517 'failures': 0, 518 'errors': 1, 519 'run_time': run_time, 520 'start_time': re.escape(self._iso_timestamp(start_time),), 521 'test_name': 'failing_test', 522 'classname': '__main__.MockTest', 523 'status': 'run', 524 'result': 'completed', 525 'attributes': '', 526 'message': NEWLINE_ERROR_MESSAGE 527 } + '\n' 528 self._assert_match(expected_re, xml) 529 530 def test_with_unicode_error_test(self): 531 start_time = 100 532 end_time = 200 533 result = self._make_result((start_time, start_time, end_time, end_time)) 534 535 test = MockTest('__main__.MockTest.failing_test') 536 result.startTestRun() 537 result.startTest(test) 538 result.addError(test, self.get_unicode_sample_failure()) 539 result.stopTest(test) 540 result.stopTestRun() 541 result.printErrors() 542 xml = self.xml_stream.getvalue() 543 544 self._assert_valid_xml(xml) 545 546 run_time = end_time - start_time 547 expected_re = OUTPUT_STRING % { 548 'suite_name': 'MockTest', 549 'tests': 1, 550 'failures': 0, 551 'errors': 1, 552 'run_time': run_time, 553 'start_time': re.escape(self._iso_timestamp(start_time),), 554 'test_name': 'failing_test', 555 'classname': '__main__.MockTest', 556 'status': 'run', 557 'result': 'completed', 558 'attributes': '', 559 'message': UNICODE_ERROR_MESSAGE 560 } 561 self._assert_match(expected_re, xml) 562 563 def test_with_terminal_escape_error(self): 564 start_time = 100 565 end_time = 200 566 result = self._make_result((start_time, start_time, end_time, end_time)) 567 568 test = MockTest('__main__.MockTest.failing_test') 569 result.startTestRun() 570 result.startTest(test) 571 result.addError(test, self.get_terminal_escape_sample_failure()) 572 result.stopTest(test) 573 result.stopTestRun() 574 result.printErrors() 575 576 self._assert_valid_xml(self.xml_stream.getvalue()) 577 578 def test_with_expected_failure_test(self): 579 start_time = 100 580 end_time = 200 581 result = self._make_result((start_time, start_time, end_time, end_time)) 582 error_values = '' 583 584 try: 585 raise RuntimeError('Test expectedFailure') 586 except RuntimeError: 587 error_values = sys.exc_info() 588 589 test = MockTest('__main__.MockTest.expected_failing_test') 590 result.startTestRun() 591 result.startTest(test) 592 result.addExpectedFailure(test, error_values) 593 result.stopTest(test) 594 result.stopTestRun() 595 result.printErrors() 596 597 run_time = end_time - start_time 598 expected_re = OUTPUT_STRING % { 599 'suite_name': 'MockTest', 600 'tests': 1, 601 'failures': 0, 602 'errors': 0, 603 'run_time': run_time, 604 'start_time': re.escape(self._iso_timestamp(start_time),), 605 'test_name': 'expected_failing_test', 606 'classname': '__main__.MockTest', 607 'status': 'run', 608 'result': 'completed', 609 'attributes': '', 610 'message': '' 611 } 612 self._assert_match(re.compile(expected_re, re.DOTALL), 613 self.xml_stream.getvalue()) 614 615 def test_with_unexpected_success_error_test(self): 616 start_time = 100 617 end_time = 200 618 result = self._make_result((start_time, start_time, end_time, end_time)) 619 620 test = MockTest('__main__.MockTest.unexpectedly_passing_test') 621 result.startTestRun() 622 result.startTest(test) 623 result.addUnexpectedSuccess(test) 624 result.stopTest(test) 625 result.stopTestRun() 626 result.printErrors() 627 628 run_time = end_time - start_time 629 expected_re = OUTPUT_STRING % { 630 'suite_name': 'MockTest', 631 'tests': 1, 632 'failures': 0, 633 'errors': 1, 634 'run_time': run_time, 635 'start_time': re.escape(self._iso_timestamp(start_time),), 636 'test_name': 'unexpectedly_passing_test', 637 'classname': '__main__.MockTest', 638 'status': 'run', 639 'result': 'completed', 640 'attributes': '', 641 'message': UNEXPECTED_SUCCESS_MESSAGE 642 } 643 self._assert_match(expected_re, self.xml_stream.getvalue()) 644 645 def test_with_skipped_test(self): 646 start_time = 100 647 end_time = 100 648 result = self._make_result((start_time, start_time, end_time, end_time)) 649 650 test = MockTest('__main__.MockTest.skipped_test_with_reason') 651 result.startTestRun() 652 result.startTest(test) 653 result.addSkip(test, 'b"r') 654 result.stopTest(test) 655 result.stopTestRun() 656 result.printErrors() 657 658 run_time = end_time - start_time 659 expected_re = OUTPUT_STRING % { 660 'suite_name': 'MockTest', 661 'tests': 1, 662 'failures': 0, 663 'errors': 0, 664 'run_time': run_time, 665 'start_time': re.escape(self._iso_timestamp(start_time),), 666 'test_name': 'skipped_test_with_reason', 667 'classname': '__main__.MockTest', 668 'status': 'notrun', 669 'result': 'suppressed', 670 'message': '' 671 } 672 self._assert_match(expected_re, self.xml_stream.getvalue()) 673 674 def test_suite_time(self): 675 start_time1 = 100 676 end_time1 = 200 677 start_time2 = 400 678 end_time2 = 700 679 name = '__main__.MockTest.failing_test' 680 result = self._make_result((start_time1, start_time1, end_time1, 681 start_time2, end_time2, end_time2)) 682 683 test = MockTest('%s1' % name) 684 result.startTestRun() 685 result.startTest(test) 686 result.addSuccess(test) 687 result.stopTest(test) 688 689 test = MockTest('%s2' % name) 690 result.startTest(test) 691 result.addSuccess(test) 692 result.stopTest(test) 693 result.stopTestRun() 694 result.printErrors() 695 696 run_time = max(end_time1, end_time2) - min(start_time1, start_time2) 697 timestamp = self._iso_timestamp(start_time1) 698 expected_prefix = """<?xml version="1.0"?> 699<testsuites name="" tests="2" failures="0" errors="0" time="%.3f" timestamp="%s"> 700<testsuite name="MockTest" tests="2" failures="0" errors="0" time="%.3f" timestamp="%s"> 701""" % (run_time, timestamp, run_time, timestamp) 702 xml_output = self.xml_stream.getvalue() 703 self.assertTrue( 704 xml_output.startswith(expected_prefix), 705 '%s not found in %s' % (expected_prefix, xml_output)) 706 707 def test_with_no_suite_name(self): 708 start_time = 1000 709 end_time = 1200 710 result = self._make_result((start_time, start_time, end_time, end_time)) 711 712 test = MockTest('__main__.MockTest.bad_name') 713 result.startTestRun() 714 result.startTest(test) 715 result.addSuccess(test) 716 result.stopTest(test) 717 result.stopTestRun() 718 result.printErrors() 719 720 run_time = end_time - start_time 721 expected_re = OUTPUT_STRING % { 722 'suite_name': 'MockTest', 723 'tests': 1, 724 'failures': 0, 725 'errors': 0, 726 'run_time': run_time, 727 'start_time': re.escape(self._iso_timestamp(start_time),), 728 'test_name': 'bad_name', 729 'classname': '__main__.MockTest', 730 'status': 'run', 731 'result': 'completed', 732 'attributes': '', 733 'message': '' 734 } 735 self._assert_match(expected_re, self.xml_stream.getvalue()) 736 737 def test_unnamed_parameterized_testcase(self): 738 """Test unnamed parameterized test cases. 739 740 Unnamed parameterized test cases might have non-alphanumeric characters in 741 their test method names. This test ensures xml_reporter handles them 742 correctly. 743 """ 744 745 class ParameterizedTest(parameterized.TestCase): 746 747 @parameterized.parameters(('a (b.c)',)) 748 def test_prefix(self, case): 749 self.assertTrue(case.startswith('a')) 750 751 start_time = 1000 752 end_time = 1200 753 result = self._make_result((start_time, start_time, end_time, end_time)) 754 test = ParameterizedTest(methodName='test_prefix0') 755 result.startTestRun() 756 result.startTest(test) 757 result.addSuccess(test) 758 result.stopTest(test) 759 result.stopTestRun() 760 result.printErrors() 761 762 run_time = end_time - start_time 763 classname = xml_reporter._escape_xml_attr( 764 unittest.util.strclass(test.__class__)) 765 expected_re = OUTPUT_STRING % { 766 'suite_name': 'ParameterizedTest', 767 'tests': 1, 768 'failures': 0, 769 'errors': 0, 770 'run_time': run_time, 771 'start_time': re.escape(self._iso_timestamp(start_time),), 772 'test_name': re.escape('test_prefix0 ('a (b.c)')'), 773 'classname': classname, 774 'status': 'run', 775 'result': 'completed', 776 'attributes': '', 777 'message': '' 778 } 779 self._assert_match(expected_re, self.xml_stream.getvalue()) 780 781 def teststop_test_without_pending_test(self): 782 end_time = 1200 783 result = self._make_result((end_time,)) 784 785 test = MockTest('__main__.MockTest.bad_name') 786 result.stopTest(test) 787 result.stopTestRun() 788 # Just verify that this doesn't crash 789 790 def test_text_and_xmltest_runner(self): 791 runner = xml_reporter.TextAndXMLTestRunner(self.xml_stream, self.stream, 792 'foo', 1) 793 result1 = runner._makeResult() 794 result2 = xml_reporter._TextAndXMLTestResult(None, None, None, 0, None) 795 self.failUnless(type(result1) is type(result2)) 796 797 def test_timing_with_time_stub(self): 798 """Make sure that timing is correct even if time.time is stubbed out.""" 799 try: 800 saved_time = time.time 801 time.time = lambda: -1 802 reporter = xml_reporter._TextAndXMLTestResult(self.xml_stream, 803 self.stream, 804 'foo', 0) 805 test = MockTest('bar') 806 reporter.startTest(test) 807 self.failIf(reporter.start_time == -1) 808 finally: 809 time.time = saved_time 810 811 def test_concurrent_add_and_delete_pending_test_case_result(self): 812 """Make sure adding/deleting pending test case results are thread safe.""" 813 result = xml_reporter._TextAndXMLTestResult(None, self.stream, None, 0, 814 None) 815 def add_and_delete_pending_test_case_result(test_name): 816 test = MockTest(test_name) 817 result.addSuccess(test) 818 result.delete_pending_test_case_result(test) 819 820 for i in range(50): 821 add_and_delete_pending_test_case_result('add_and_delete_test%s' % i) 822 self.assertEqual(result.pending_test_case_results, {}) 823 824 def test_concurrent_test_runs(self): 825 """Make sure concurrent test runs do not race each other.""" 826 num_passing_tests = 20 827 num_failing_tests = 20 828 num_error_tests = 20 829 total_num_tests = num_passing_tests + num_failing_tests + num_error_tests 830 831 times = [0] + [i for i in range(2 * total_num_tests) 832 ] + [2 * total_num_tests - 1] 833 result = self._make_result(times) 834 threads = [] 835 names = [] 836 result.startTestRun() 837 for i in range(num_passing_tests): 838 name = 'passing_concurrent_test_%s' % i 839 names.append(name) 840 test_name = '__main__.MockTest.%s' % name 841 # xml_reporter uses id(test) as the test identifier. 842 # In a real testing scenario, all the test instances are created before 843 # running them. So all ids will be unique. 844 # We must do the same here: create test instance beforehand. 845 test = MockTest(test_name) 846 threads.append(threading.Thread( 847 target=self._simulate_passing_test, args=(test, result))) 848 for i in range(num_failing_tests): 849 name = 'failing_concurrent_test_%s' % i 850 names.append(name) 851 test_name = '__main__.MockTest.%s' % name 852 test = MockTest(test_name) 853 threads.append(threading.Thread( 854 target=self._simulate_failing_test, args=(test, result))) 855 for i in range(num_error_tests): 856 name = 'error_concurrent_test_%s' % i 857 names.append(name) 858 test_name = '__main__.MockTest.%s' % name 859 test = MockTest(test_name) 860 threads.append(threading.Thread( 861 target=self._simulate_error_test, args=(test, result))) 862 for t in threads: 863 t.start() 864 for t in threads: 865 t.join() 866 867 result.stopTestRun() 868 result.printErrors() 869 tests_not_in_xml = [] 870 for tn in names: 871 if tn not in self.xml_stream.getvalue(): 872 tests_not_in_xml.append(tn) 873 msg = ('Expected xml_stream to contain all test %s results, but %s tests ' 874 'are missing. List of missing tests: %s' % ( 875 total_num_tests, len(tests_not_in_xml), tests_not_in_xml)) 876 self.assertEqual([], tests_not_in_xml, msg) 877 878 def test_add_failure_during_stop_test(self): 879 """Tests an addFailure() call from within a stopTest() call stack.""" 880 result = self._make_result((0, 2)) 881 test = MockTest('__main__.MockTest.failing_test') 882 result.startTestRun() 883 result.startTest(test) 884 885 # Replace parent stopTest method from unittest.TextTestResult with 886 # a version that calls self.addFailure(). 887 with mock.patch.object( 888 unittest.TextTestResult, 889 'stopTest', 890 side_effect=lambda t: result.addFailure(t, self.get_sample_failure())): 891 # Run stopTest in a separate thread since we are looking to verify that 892 # it does not deadlock, and would otherwise prevent the test from 893 # completing. 894 stop_test_thread = threading.Thread(target=result.stopTest, args=(test,)) 895 stop_test_thread.daemon = True 896 stop_test_thread.start() 897 898 stop_test_thread.join(10.0) 899 self.assertFalse(stop_test_thread.is_alive(), 900 'result.stopTest(test) call failed to complete') 901 902 903class XMLTest(absltest.TestCase): 904 905 def test_escape_xml(self): 906 self.assertEqual(xml_reporter._escape_xml_attr('"Hi" <\'>\t\r\n'), 907 '"Hi" <'>	
') 908 909 910class XmlReporterFixtureTest(absltest.TestCase): 911 912 def _get_helper(self): 913 binary_name = 'absl/testing/tests/xml_reporter_helper_test' 914 return _bazelize_command.get_executable_path(binary_name) 915 916 def _run_test_and_get_xml(self, flag): 917 """Runs xml_reporter_helper_test and returns an Element instance. 918 919 Runs xml_reporter_helper_test in a new process so that it can 920 exercise the entire test infrastructure, and easily test issues in 921 the test fixture. 922 923 Args: 924 flag: flag to pass to xml_reporter_helper_test 925 926 Returns: 927 The Element instance of the XML output. 928 """ 929 930 xml_fhandle, xml_fname = tempfile.mkstemp() 931 os.close(xml_fhandle) 932 933 try: 934 binary = self._get_helper() 935 args = [binary, flag, '--xml_output_file=%s' % xml_fname] 936 ret = subprocess.call(args) 937 self.assertEqual(ret, 0) 938 939 xml = ElementTree.parse(xml_fname).getroot() 940 finally: 941 os.remove(xml_fname) 942 943 return xml 944 945 def _run_test(self, flag, num_errors, num_failures, suites): 946 xml_fhandle, xml_fname = tempfile.mkstemp() 947 os.close(xml_fhandle) 948 949 try: 950 binary = self._get_helper() 951 args = [binary, flag, '--xml_output_file=%s' % xml_fname] 952 ret = subprocess.call(args) 953 self.assertNotEqual(ret, 0) 954 955 xml = ElementTree.parse(xml_fname).getroot() 956 logging.info('xml output is:\n%s', ElementTree.tostring(xml)) 957 finally: 958 os.remove(xml_fname) 959 960 self.assertEqual(int(xml.attrib['errors']), num_errors) 961 self.assertEqual(int(xml.attrib['failures']), num_failures) 962 self.assertLen(xml, len(suites)) 963 actual_suites = sorted( 964 xml.findall('testsuite'), key=lambda x: x.attrib['name']) 965 suites = sorted(suites, key=lambda x: x['name']) 966 for actual_suite, expected_suite in zip(actual_suites, suites): 967 self.assertEqual(actual_suite.attrib['name'], expected_suite['name']) 968 self.assertLen(actual_suite, len(expected_suite['cases'])) 969 actual_cases = sorted(actual_suite.findall('testcase'), 970 key=lambda x: x.attrib['name']) 971 expected_cases = sorted(expected_suite['cases'], key=lambda x: x['name']) 972 for actual_case, expected_case in zip(actual_cases, expected_cases): 973 self.assertEqual(actual_case.attrib['name'], expected_case['name']) 974 self.assertEqual(actual_case.attrib['classname'], 975 expected_case['classname']) 976 if 'error' in expected_case: 977 actual_error = actual_case.find('error') 978 self.assertEqual(actual_error.attrib['message'], 979 expected_case['error']) 980 if 'failure' in expected_case: 981 actual_failure = actual_case.find('failure') 982 self.assertEqual(actual_failure.attrib['message'], 983 expected_case['failure']) 984 985 return xml 986 987 def test_set_up_module_error(self): 988 self._run_test( 989 flag='--set_up_module_error', 990 num_errors=1, 991 num_failures=0, 992 suites=[{'name': '__main__', 993 'cases': [{'name': 'setUpModule', 994 'classname': '__main__', 995 'error': 'setUpModule Errored!'}]}]) 996 997 def test_tear_down_module_error(self): 998 self._run_test( 999 flag='--tear_down_module_error', 1000 num_errors=1, 1001 num_failures=0, 1002 suites=[{'name': 'FailableTest', 1003 'cases': [{'name': 'test', 1004 'classname': '__main__.FailableTest'}]}, 1005 {'name': '__main__', 1006 'cases': [{'name': 'tearDownModule', 1007 'classname': '__main__', 1008 'error': 'tearDownModule Errored!'}]}]) 1009 1010 def test_set_up_class_error(self): 1011 self._run_test( 1012 flag='--set_up_class_error', 1013 num_errors=1, 1014 num_failures=0, 1015 suites=[{'name': 'FailableTest', 1016 'cases': [{'name': 'setUpClass', 1017 'classname': '__main__.FailableTest', 1018 'error': 'setUpClass Errored!'}]}]) 1019 1020 def test_tear_down_class_error(self): 1021 self._run_test( 1022 flag='--tear_down_class_error', 1023 num_errors=1, 1024 num_failures=0, 1025 suites=[{'name': 'FailableTest', 1026 'cases': [{'name': 'test', 1027 'classname': '__main__.FailableTest'}, 1028 {'name': 'tearDownClass', 1029 'classname': '__main__.FailableTest', 1030 'error': 'tearDownClass Errored!'}]}]) 1031 1032 def test_set_up_error(self): 1033 self._run_test( 1034 flag='--set_up_error', 1035 num_errors=1, 1036 num_failures=0, 1037 suites=[{'name': 'FailableTest', 1038 'cases': [{'name': 'test', 1039 'classname': '__main__.FailableTest', 1040 'error': 'setUp Errored!'}]}]) 1041 1042 def test_tear_down_error(self): 1043 self._run_test( 1044 flag='--tear_down_error', 1045 num_errors=1, 1046 num_failures=0, 1047 suites=[{'name': 'FailableTest', 1048 'cases': [{'name': 'test', 1049 'classname': '__main__.FailableTest', 1050 'error': 'tearDown Errored!'}]}]) 1051 1052 def test_test_error(self): 1053 self._run_test( 1054 flag='--test_error', 1055 num_errors=1, 1056 num_failures=0, 1057 suites=[{'name': 'FailableTest', 1058 'cases': [{'name': 'test', 1059 'classname': '__main__.FailableTest', 1060 'error': 'test Errored!'}]}]) 1061 1062 def test_set_up_failure(self): 1063 self._run_test( 1064 flag='--set_up_fail', 1065 num_errors=0, 1066 num_failures=1, 1067 suites=[{'name': 'FailableTest', 1068 'cases': [{'name': 'test', 1069 'classname': '__main__.FailableTest', 1070 'failure': 'setUp Failed!'}]}]) 1071 1072 def test_tear_down_failure(self): 1073 self._run_test( 1074 flag='--tear_down_fail', 1075 num_errors=0, 1076 num_failures=1, 1077 suites=[{'name': 'FailableTest', 1078 'cases': [{'name': 'test', 1079 'classname': '__main__.FailableTest', 1080 'failure': 'tearDown Failed!'}]}]) 1081 1082 def test_test_fail(self): 1083 self._run_test( 1084 flag='--test_fail', 1085 num_errors=0, 1086 num_failures=1, 1087 suites=[{'name': 'FailableTest', 1088 'cases': [{'name': 'test', 1089 'classname': '__main__.FailableTest', 1090 'failure': 'test Failed!'}]}]) 1091 1092 def test_test_randomization_seed_logging(self): 1093 # We expect the resulting XML to start as follows: 1094 # <testsuites ...> 1095 # <properties> 1096 # <property name="test_randomize_ordering_seed" value="17" /> 1097 # ... 1098 # 1099 # which we validate here. 1100 out = self._run_test_and_get_xml('--test_randomize_ordering_seed=17') 1101 expected_attrib = {'name': 'test_randomize_ordering_seed', 'value': '17'} 1102 property_attributes = [ 1103 prop.attrib for prop in out.findall('./properties/property')] 1104 self.assertIn(expected_attrib, property_attributes) 1105 1106 1107if __name__ == '__main__': 1108 absltest.main() 1109