|
25 | 25 | from ardupilot_methodic_configurator.annotate_params import (
|
26 | 26 | BASE_URL,
|
27 | 27 | PARAM_DEFINITION_XML_FILE,
|
| 28 | + Par, |
28 | 29 | arg_parser,
|
29 | 30 | create_doc_dict,
|
| 31 | + extract_parameter_name_and_validate, |
30 | 32 | format_columns,
|
31 | 33 | get_xml_data,
|
32 | 34 | get_xml_url,
|
@@ -568,6 +570,165 @@ def test_empty_parameter_file(self) -> None:
|
568 | 570 | # Check if the file is still empty
|
569 | 571 | assert updated_content == ""
|
570 | 572 |
|
| 573 | + def test_get_xml_url_valid_vehicles(self) -> None: |
| 574 | + """Test get_xml_url with all valid vehicle types.""" |
| 575 | + vehicle_types = ["ArduCopter", "ArduPlane", "Rover", "ArduSub", "AntennaTracker", "AP_Periph", "Blimp", "Heli", "SITL"] |
| 576 | + for vehicle in vehicle_types: |
| 577 | + url = get_xml_url(vehicle, "4.3") |
| 578 | + assert url.startswith(BASE_URL) |
| 579 | + assert "stable-4.3" in url |
| 580 | + assert url.endswith("/") |
| 581 | + |
| 582 | + def test_get_xml_url_invalid_vehicle(self) -> None: |
| 583 | + """Test get_xml_url with invalid vehicle type.""" |
| 584 | + with pytest.raises(ValueError, match="Vehicle type 'InvalidVehicle' is not supported."): |
| 585 | + get_xml_url("InvalidVehicle", "4.3") |
| 586 | + |
| 587 | + def test_split_into_lines_edge_cases(self) -> None: |
| 588 | + """Test split_into_lines with edge cases.""" |
| 589 | + # Test with various line lengths |
| 590 | + # Function will return largest possible chunks based on max length |
| 591 | + assert split_into_lines("a b c", 2) == ["a", "b", "c"] |
| 592 | + assert split_into_lines("", 10) == [] |
| 593 | + |
| 594 | + def test_format_columns_edge_cases(self) -> None: |
| 595 | + """Test format_columns with edge cases.""" |
| 596 | + # Empty dictionary |
| 597 | + assert format_columns({}) == [] |
| 598 | + |
| 599 | + # Single item |
| 600 | + assert format_columns({"Key": "Value"}) == ["Key: Value"] |
| 601 | + |
| 602 | + # Test with different max widths |
| 603 | + values = {"K1": "V1", "K2": "V2"} |
| 604 | + assert len(format_columns(values, max_width=20)[0]) <= 20 |
| 605 | + |
| 606 | + # Test with many columns |
| 607 | + many_values = {f"Key{i}": f"Value{i}" for i in range(20)} |
| 608 | + result = format_columns(many_values, max_width=200, max_columns=5) |
| 609 | + assert all(len(line) <= 200 for line in result) |
| 610 | + |
| 611 | + def test_create_doc_dict_edge_cases(self) -> None: |
| 612 | + """Test create_doc_dict with edge cases.""" |
| 613 | + # Test with empty XML |
| 614 | + empty_root = ET.Element("root") |
| 615 | + assert create_doc_dict(empty_root, "ArduCopter") == {} |
| 616 | + |
| 617 | + # Test with missing attributes |
| 618 | + param = ET.SubElement(empty_root, "param") |
| 619 | + assert create_doc_dict(empty_root, "ArduCopter") == {} |
| 620 | + |
| 621 | + # Test with minimal valid param |
| 622 | + param.set("name", "TEST_PARAM") |
| 623 | + param.set("humanName", "Test Parameter") |
| 624 | + param.set("documentation", "Test documentation") |
| 625 | + doc_dict = create_doc_dict(empty_root, "ArduCopter") |
| 626 | + assert "TEST_PARAM" in doc_dict |
| 627 | + assert doc_dict["TEST_PARAM"]["humanName"] == "Test Parameter" |
| 628 | + |
| 629 | + @patch("os.path.isfile") |
| 630 | + def test_update_parameter_documentation_sorting(self, mock_isfile) -> None: |
| 631 | + """Test parameter sorting in update_parameter_documentation.""" |
| 632 | + # Mock file existence check |
| 633 | + mock_isfile.return_value = True |
| 634 | + |
| 635 | + test_content = "PARAM_Z 100\nPARAM_A 200\nPARAM_M 300\n" |
| 636 | + |
| 637 | + # Create a real temporary file for testing |
| 638 | + with tempfile.NamedTemporaryFile(mode="w", delete=False) as temp_file: |
| 639 | + temp_file.write(test_content) |
| 640 | + temp_file_name = temp_file.name |
| 641 | + |
| 642 | + doc = { |
| 643 | + "PARAM_Z": {"humanName": "Z", "documentation": ["Z doc"], "fields": {}, "values": {}}, |
| 644 | + "PARAM_A": {"humanName": "A", "documentation": ["A doc"], "fields": {}, "values": {}}, |
| 645 | + "PARAM_M": {"humanName": "M", "documentation": ["M doc"], "fields": {}, "values": {}}, |
| 646 | + } |
| 647 | + |
| 648 | + try: |
| 649 | + # Test MissionPlanner sorting |
| 650 | + update_parameter_documentation(doc, temp_file_name, "missionplanner") |
| 651 | + |
| 652 | + with open(temp_file_name, encoding="utf-8") as f: |
| 653 | + content = f.read() |
| 654 | + |
| 655 | + # Verify content and order |
| 656 | + assert "PARAM_A" in content |
| 657 | + assert "PARAM_M" in content |
| 658 | + assert "PARAM_Z" in content |
| 659 | + assert content.index("PARAM_A") < content.index("PARAM_M") < content.index("PARAM_Z") |
| 660 | + |
| 661 | + # Test MAVProxy sorting |
| 662 | + # Reset file content |
| 663 | + with open(temp_file_name, "w", encoding="utf-8") as f: |
| 664 | + f.write(test_content) |
| 665 | + |
| 666 | + update_parameter_documentation(doc, temp_file_name, "mavproxy") |
| 667 | + |
| 668 | + with open(temp_file_name, encoding="utf-8") as f: |
| 669 | + content = f.read() |
| 670 | + |
| 671 | + # Verify content for MAVProxy format |
| 672 | + assert "PARAM_A" in content |
| 673 | + assert "PARAM_M" in content |
| 674 | + assert "PARAM_Z" in content |
| 675 | + |
| 676 | + finally: |
| 677 | + # Clean up |
| 678 | + os.unlink(temp_file_name) |
| 679 | + |
| 680 | + def test_extract_parameter_name_and_validate_invalid_cases(self) -> None: |
| 681 | + """Test parameter name validation with invalid cases.""" |
| 682 | + # Test invalid parameter name pattern |
| 683 | + with pytest.raises(SystemExit): |
| 684 | + extract_parameter_name_and_validate("invalid_param 100", "test.param", 1) |
| 685 | + |
| 686 | + # Test too long parameter name |
| 687 | + with pytest.raises(SystemExit): |
| 688 | + extract_parameter_name_and_validate("VERY_LONG_PARAMETER_NAME_THAT_EXCEEDS_LIMIT 100", "test.param", 1) |
| 689 | + |
| 690 | + # Test invalid separator |
| 691 | + with pytest.raises(SystemExit): |
| 692 | + extract_parameter_name_and_validate("PARAM:100", "test.param", 1) |
| 693 | + |
| 694 | + def test_par_class_methods(self) -> None: |
| 695 | + """Test Par class methods.""" |
| 696 | + # Test equality |
| 697 | + par1 = Par(100.0, "comment1") |
| 698 | + par2 = Par(100.0, "comment1") |
| 699 | + par3 = Par(200.0, "comment2") |
| 700 | + |
| 701 | + assert par1 == par2 |
| 702 | + assert par1 != par3 |
| 703 | + assert par1 != "not a Par object" |
| 704 | + |
| 705 | + # Test load_param_file with invalid values |
| 706 | + with tempfile.NamedTemporaryFile(mode="w", delete=False) as tf: |
| 707 | + tf.write("PARAM1 invalid_value\n") |
| 708 | + tf.flush() |
| 709 | + |
| 710 | + with pytest.raises(SystemExit): |
| 711 | + Par.load_param_file_into_dict(tf.name) |
| 712 | + |
| 713 | + def test_format_params_methods(self) -> None: |
| 714 | + """Test Par.format_params method.""" |
| 715 | + param_dict = {"PARAM1": Par(100.0, "comment1"), "PARAM2": Par(200.0), "PARAM3": 300.0} |
| 716 | + |
| 717 | + # Test MissionPlanner format |
| 718 | + mp_format = Par.format_params(param_dict, "missionplanner") |
| 719 | + assert any("PARAM1,100" in line for line in mp_format) |
| 720 | + assert any("# comment1" in line for line in mp_format) |
| 721 | + |
| 722 | + # Test MAVProxy format |
| 723 | + mavproxy_format = Par.format_params(param_dict, "mavproxy") |
| 724 | + # Use correct spacing format - 16 chars for name, 8 for value |
| 725 | + assert any("PARAM1 100.000000" in line for line in mavproxy_format) |
| 726 | + assert any("# comment1" in line for line in mavproxy_format) |
| 727 | + |
| 728 | + # Test invalid format |
| 729 | + with pytest.raises(SystemExit): |
| 730 | + Par.format_params(param_dict, "invalid_format") |
| 731 | + |
571 | 732 |
|
572 | 733 | class AnnotateParamsTest(unittest.TestCase):
|
573 | 734 | """Test annotate parameters."""
|
|
0 commit comments