|
16 | 16 | commit_and_close_db, |
17 | 17 | ExecutionResult, |
18 | 18 | datetime, |
| 19 | + SuiteStats, |
| 20 | + SuiteResults, |
| 21 | + TestMetrics, |
19 | 22 | ) |
20 | 23 | from robotframework_historic_parser.parserargs import parse_options |
21 | 24 |
|
@@ -208,6 +211,24 @@ def test_rfhistoric_parser_rf( |
208 | 211 |
|
209 | 212 | mock_print.assert_has_calls(expected_calls, any_order=True) |
210 | 213 |
|
| 214 | + @patch("robotframework_historic_parser.rfhistoricparser.process_junit_report") |
| 215 | + @patch("os.listdir", return_value=["output.xml"]) |
| 216 | + @patch("builtins.exit") |
| 217 | + def test_rfhistoric_parser_junit( |
| 218 | + self, mock_exit, mock_list, mock_process_junit_report |
| 219 | + ): |
| 220 | + |
| 221 | + opts = Mock() |
| 222 | + opts.report_type = "junit" |
| 223 | + opts.path = "/some/path" |
| 224 | + opts.output = "*.xml" |
| 225 | + |
| 226 | + with patch("builtins.print"): |
| 227 | + rfhistoric_parser(opts) |
| 228 | + |
| 229 | + mock_process_junit_report.assert_called_once_with(opts) |
| 230 | + mock_exit.assert_not_called() |
| 231 | + |
211 | 232 | @patch("robotframework_historic_parser.rfhistoricparser.process_allure_report") |
212 | 233 | @patch("os.listdir", return_value=["output.xml"]) |
213 | 234 | @patch("builtins.exit") |
@@ -334,12 +355,12 @@ def test_process_allure_report_json( |
334 | 355 | opts.executionname, |
335 | 356 | 10, |
336 | 357 | 1, |
337 | | - 5, |
| 358 | + 2, |
338 | 359 | "0", |
339 | 360 | 0, |
340 | 361 | 0, |
341 | 362 | 0, |
342 | | - 4, |
| 363 | + 7, |
343 | 364 | 0, |
344 | 365 | opts.projectname, |
345 | 366 | ) |
@@ -477,3 +498,190 @@ def test_remove_special_characters_with_numbers(self): |
477 | 498 | def test_remove_special_characters_with_spaces(self): |
478 | 499 | result = remove_special_characters("Remove these special characters!") |
479 | 500 | self.assertEqual(result, "Remove these special characters") |
| 501 | + |
| 502 | + |
| 503 | + |
| 504 | + def test_suite_stats_passed_suite(self): |
| 505 | + """Test SuiteStats with passed status""" |
| 506 | + suite_stats = SuiteStats() |
| 507 | + |
| 508 | + # Create a mock suite with PASS status |
| 509 | + mock_suite = Mock() |
| 510 | + mock_suite.tests = [Mock()] # Has tests |
| 511 | + mock_suite.status = "PASS" |
| 512 | + |
| 513 | + suite_stats.start_suite(mock_suite) |
| 514 | + |
| 515 | + self.assertEqual(suite_stats.total_suite, 1) |
| 516 | + self.assertEqual(suite_stats.passed_suite, 1) |
| 517 | + self.assertEqual(suite_stats.failed_suite, 0) |
| 518 | + self.assertEqual(suite_stats.skipped_suite, 0) |
| 519 | + |
| 520 | + def test_suite_stats_skipped_suite(self): |
| 521 | + """Test SuiteStats with skipped status""" |
| 522 | + suite_stats = SuiteStats() |
| 523 | + |
| 524 | + # Create a mock suite with SKIP status |
| 525 | + mock_suite = Mock() |
| 526 | + mock_suite.tests = [Mock()] # Has tests |
| 527 | + mock_suite.status = "SKIP" |
| 528 | + |
| 529 | + suite_stats.start_suite(mock_suite) |
| 530 | + |
| 531 | + self.assertEqual(suite_stats.total_suite, 1) |
| 532 | + self.assertEqual(suite_stats.passed_suite, 0) |
| 533 | + self.assertEqual(suite_stats.failed_suite, 0) |
| 534 | + self.assertEqual(suite_stats.skipped_suite, 1) |
| 535 | + |
| 536 | + def test_suite_stats_failed_suite(self): |
| 537 | + """Test SuiteStats with failed status""" |
| 538 | + suite_stats = SuiteStats() |
| 539 | + |
| 540 | + # Create a mock suite with FAIL status |
| 541 | + mock_suite = Mock() |
| 542 | + mock_suite.tests = [Mock()] # Has tests |
| 543 | + mock_suite.status = "FAIL" |
| 544 | + |
| 545 | + suite_stats.start_suite(mock_suite) |
| 546 | + |
| 547 | + self.assertEqual(suite_stats.total_suite, 1) |
| 548 | + self.assertEqual(suite_stats.passed_suite, 0) |
| 549 | + self.assertEqual(suite_stats.failed_suite, 1) |
| 550 | + self.assertEqual(suite_stats.skipped_suite, 0) |
| 551 | + |
| 552 | + def test_suite_stats_empty_suite(self): |
| 553 | + """Test SuiteStats with empty suite (no tests)""" |
| 554 | + suite_stats = SuiteStats() |
| 555 | + |
| 556 | + # Create a mock suite with no tests |
| 557 | + mock_suite = Mock() |
| 558 | + mock_suite.tests = [] # Empty test list |
| 559 | + mock_suite.status = "PASS" |
| 560 | + |
| 561 | + suite_stats.start_suite(mock_suite) |
| 562 | + |
| 563 | + # Should not count empty suites |
| 564 | + self.assertEqual(suite_stats.total_suite, 0) |
| 565 | + self.assertEqual(suite_stats.passed_suite, 0) |
| 566 | + self.assertEqual(suite_stats.failed_suite, 0) |
| 567 | + self.assertEqual(suite_stats.skipped_suite, 0) |
| 568 | + |
| 569 | + def test_suite_results_empty_suite(self): |
| 570 | + """Test SuiteResults with empty suite (no tests)""" |
| 571 | + mock_db = Mock() |
| 572 | + suite_results = SuiteResults(mock_db, "123", "False") |
| 573 | + |
| 574 | + # Create a mock suite with no tests |
| 575 | + mock_suite = Mock() |
| 576 | + mock_suite.tests = [] # Empty test list |
| 577 | + |
| 578 | + with patch("robotframework_historic_parser.rfhistoricparser.insert_into_suite_table") as mock_insert: |
| 579 | + suite_results.start_suite(mock_suite) |
| 580 | + # Should not insert empty suites |
| 581 | + mock_insert.assert_not_called() |
| 582 | + |
| 583 | + def test_suite_results_full_suite_name_true(self): |
| 584 | + """Test SuiteResults with full_suite_name=True""" |
| 585 | + mock_db = Mock() |
| 586 | + suite_results = SuiteResults(mock_db, "123", "True") |
| 587 | + |
| 588 | + # Create a mock suite with longname |
| 589 | + mock_suite = MagicMock() |
| 590 | + mock_suite.tests = [Mock()] # Has tests |
| 591 | + mock_suite.longname = "MyProject.MySuite.MySubSuite" |
| 592 | + mock_suite.status = "PASS" |
| 593 | + |
| 594 | + # Create a simple object to hold statistics |
| 595 | + class Stats: |
| 596 | + total = 10 |
| 597 | + passed = 5 |
| 598 | + failed = 3 |
| 599 | + skipped = 2 |
| 600 | + |
| 601 | + mock_suite.statistics = Stats() |
| 602 | + mock_suite.elapsedtime = 120000 # 2 minutes in milliseconds |
| 603 | + |
| 604 | + with patch("robotframework_historic_parser.rfhistoricparser.insert_into_suite_table") as mock_insert: |
| 605 | + suite_results.start_suite(mock_suite) |
| 606 | + # Verify insert was called with the full suite name |
| 607 | + self.assertTrue(mock_insert.called) |
| 608 | + # Check that longname was used (first call, argument index 2 is the suite name) |
| 609 | + call_args = mock_insert.call_args[0] |
| 610 | + self.assertEqual(call_args[2], "MyProject.MySuite.MySubSuite") |
| 611 | + |
| 612 | + def test_suite_results_visit_test_full_suite_name_true(self): |
| 613 | + """Test TestMetrics.visit_test with full_suite_name=True""" |
| 614 | + from robotframework_historic_parser.rfhistoricparser import TestMetrics |
| 615 | + |
| 616 | + mock_db = Mock() |
| 617 | + test_results = TestMetrics(mock_db, "123", "True") |
| 618 | + |
| 619 | + # Create a mock test with longname |
| 620 | + mock_test = Mock() |
| 621 | + mock_test.name = "MyTest" |
| 622 | + mock_test.longname = "MyProject.MySuite.MyTest" |
| 623 | + mock_test.status = "PASS" |
| 624 | + mock_test.elapsedtime = 60000 # 1 minute in milliseconds |
| 625 | + mock_test.message = "Test passed" |
| 626 | + mock_test.tags = ["tag1", "tag2"] |
| 627 | + |
| 628 | + with patch("robotframework_historic_parser.rfhistoricparser.insert_into_test_table"): |
| 629 | + test_results.visit_test(mock_test) |
| 630 | + |
| 631 | + @patch("mysql.connector.connect") |
| 632 | + def test_insert_into_execution_table_full_coverage(self, mock_connect): |
| 633 | + """Test insert_into_execution_table with all database operations""" |
| 634 | + mock_con = Mock() |
| 635 | + mock_ocon = Mock() |
| 636 | + mock_cursor = Mock() |
| 637 | + mock_root_cursor = Mock() |
| 638 | + |
| 639 | + mock_con.cursor.return_value = mock_cursor |
| 640 | + mock_ocon.cursor.return_value = mock_root_cursor |
| 641 | + |
| 642 | + # Mock the fetchone results |
| 643 | + mock_cursor.fetchone.side_effect = [ |
| 644 | + (1, 5, 10), # Execution_Id, Execution_Pass, Execution_Total |
| 645 | + (100,) # COUNT(*) |
| 646 | + ] |
| 647 | + |
| 648 | + result = insert_into_execution_table( |
| 649 | + mock_con, mock_ocon, "Test Execution", 10, 5, 3, "2.5", |
| 650 | + 20, 15, 3, 2, 0, "TestProject" |
| 651 | + ) |
| 652 | + |
| 653 | + # Verify the execution ID is returned |
| 654 | + self.assertEqual(result, "1") |
| 655 | + |
| 656 | + # Verify cursor operations |
| 657 | + mock_cursor.execute.assert_called() |
| 658 | + mock_con.commit.assert_called_once() |
| 659 | + mock_root_cursor.execute.assert_called_once() |
| 660 | + mock_ocon.commit.assert_called_once() |
| 661 | + |
| 662 | + @patch("mysql.connector.connect") |
| 663 | + def test_insert_into_execution_table_zero_total(self, mock_connect): |
| 664 | + """Test insert_into_execution_table handles division by zero""" |
| 665 | + mock_con = Mock() |
| 666 | + mock_ocon = Mock() |
| 667 | + mock_cursor = Mock() |
| 668 | + mock_root_cursor = Mock() |
| 669 | + |
| 670 | + mock_con.cursor.return_value = mock_cursor |
| 671 | + mock_ocon.cursor.return_value = mock_root_cursor |
| 672 | + |
| 673 | + # Mock with zero total to test division by zero handling |
| 674 | + mock_cursor.fetchone.side_effect = [ |
| 675 | + (1, 0, 0), # Execution_Id, Execution_Pass=0, Execution_Total=0 |
| 676 | + (100,) # COUNT(*) |
| 677 | + ] |
| 678 | + |
| 679 | + result = insert_into_execution_table( |
| 680 | + mock_con, mock_ocon, "Test Execution", 0, 0, 0, "0", |
| 681 | + 0, 0, 0, 0, 0, "TestProject" |
| 682 | + ) |
| 683 | + |
| 684 | + # Verify it doesn't crash and returns the execution ID |
| 685 | + self.assertEqual(result, "1") |
| 686 | + mock_con.commit.assert_called_once() |
| 687 | + mock_ocon.commit.assert_called_once() |
0 commit comments