|
21 | 21 | ESC_CONNECTION_DICT, |
22 | 22 | ESC_SERIAL_SAME_PORT_PROTOCOLS, |
23 | 23 | FC_CONNECTION_TYPE_PATHS, |
| 24 | + FRAME_CLASS_DICT, |
24 | 25 | GNSS_RECEIVER_CONNECTION, |
25 | 26 | I2C_PORTS, |
26 | 27 | OTHER_PORTS, |
|
33 | 34 | SERIAL_PROTOCOLS_DICT, |
34 | 35 | SPI_PORTS, |
35 | 36 | get_connection_type_tuples_with_labels, |
| 37 | + get_frame_class_sub_dict, |
36 | 38 | ) |
37 | 39 |
|
38 | 40 |
|
@@ -556,3 +558,83 @@ def test_esc_serial_same_port_protocols_contents(self) -> None: |
556 | 558 | assert protocol in vehicle_protocols, ( |
557 | 559 | f"Protocol '{protocol}' from ESC_SERIAL_SAME_PORT_PROTOCOLS not found in ESC_CONNECTION_DICT['{vtype}']" |
558 | 560 | ) |
| 561 | + |
| 562 | + |
| 563 | +class TestFrameClassDict: |
| 564 | + """Tests for FRAME_CLASS_DICT and get_frame_class_sub_dict.""" |
| 565 | + |
| 566 | + def test_frame_class_dict_structure(self) -> None: |
| 567 | + """FRAME_CLASS_DICT is keyed by vehicle type with int->str sub-dicts.""" |
| 568 | + assert isinstance(FRAME_CLASS_DICT, dict) |
| 569 | + assert len(FRAME_CLASS_DICT) > 0 |
| 570 | + |
| 571 | + for vtype, sub_dict in FRAME_CLASS_DICT.items(): |
| 572 | + assert isinstance(vtype, str), f"Vehicle type key '{vtype}' is not a string" |
| 573 | + assert isinstance(sub_dict, dict), f"Sub-dict for '{vtype}' is not a dict" |
| 574 | + for key, value in sub_dict.items(): |
| 575 | + assert isinstance(key, int), f"Frame class key '{key}' in '{vtype}' is not an int" |
| 576 | + assert isinstance(value, str), f"Frame class value '{value}' in '{vtype}' is not a string" |
| 577 | + assert value.strip(), f"Frame class name for key {key} in '{vtype}' is empty" |
| 578 | + |
| 579 | + def test_frame_class_dict_contains_required_vehicle_types(self) -> None: |
| 580 | + """FRAME_CLASS_DICT contains entries for all expected vehicle types.""" |
| 581 | + for required in ("ArduCopter", "Heli", "Rover", "ArduPlane"): |
| 582 | + assert required in FRAME_CLASS_DICT, f"Missing vehicle type '{required}' in FRAME_CLASS_DICT" |
| 583 | + |
| 584 | + def test_arducopter_frame_class_values(self) -> None: |
| 585 | + """ArduCopter sub-dict contains the standard multirotor frame classes.""" |
| 586 | + sub = FRAME_CLASS_DICT["ArduCopter"] |
| 587 | + assert sub[1] == "Quad" |
| 588 | + assert sub[2] == "Hexa" |
| 589 | + assert sub[3] == "Octa" |
| 590 | + assert sub[6] == "Heli" |
| 591 | + assert sub[11] == "Heli_Dual" |
| 592 | + assert sub[13] == "HeliQuad" |
| 593 | + |
| 594 | + def test_heli_frame_class_values(self) -> None: |
| 595 | + """Heli sub-dict contains only helicopter-relevant frame classes.""" |
| 596 | + sub = FRAME_CLASS_DICT["Heli"] |
| 597 | + assert sub[6] == "Heli" |
| 598 | + assert sub[11] == "Heli_Dual" |
| 599 | + assert sub[13] == "HeliQuad" |
| 600 | + # Non-heli classes must not appear |
| 601 | + assert 1 not in sub, "Quad should not be in Heli FRAME_CLASS_DICT" |
| 602 | + assert 2 not in sub, "Hexa should not be in Heli FRAME_CLASS_DICT" |
| 603 | + |
| 604 | + def test_rover_frame_class_values(self) -> None: |
| 605 | + """Rover sub-dict uses Rover-specific frame class values.""" |
| 606 | + sub = FRAME_CLASS_DICT["Rover"] |
| 607 | + assert sub[1] == "Rover" |
| 608 | + assert sub[2] == "Boat" |
| 609 | + assert sub[3] == "BalanceBot" |
| 610 | + # Multirotor classes must not appear in Rover |
| 611 | + assert 4 not in sub, "OctaQuad should not be in Rover FRAME_CLASS_DICT" |
| 612 | + |
| 613 | + def test_arduplane_frame_class_is_empty(self) -> None: |
| 614 | + """ArduPlane has no FRAME_CLASS parameter, so its sub-dict must be empty.""" |
| 615 | + assert FRAME_CLASS_DICT["ArduPlane"] == {} |
| 616 | + |
| 617 | + def test_get_frame_class_sub_dict_known_vehicle_types(self) -> None: |
| 618 | + """get_frame_class_sub_dict returns the correct sub-dict for known vehicle types.""" |
| 619 | + assert get_frame_class_sub_dict("ArduCopter") is FRAME_CLASS_DICT["ArduCopter"] |
| 620 | + assert get_frame_class_sub_dict("Heli") is FRAME_CLASS_DICT["Heli"] |
| 621 | + assert get_frame_class_sub_dict("Rover") is FRAME_CLASS_DICT["Rover"] |
| 622 | + assert get_frame_class_sub_dict("ArduPlane") is FRAME_CLASS_DICT["ArduPlane"] |
| 623 | + |
| 624 | + def test_get_frame_class_sub_dict_unknown_type_falls_back_to_arducopter(self) -> None: |
| 625 | + """get_frame_class_sub_dict falls back to ArduCopter for unknown vehicle types.""" |
| 626 | + result = get_frame_class_sub_dict("UnknownVehicle") |
| 627 | + assert result is FRAME_CLASS_DICT["ArduCopter"] |
| 628 | + |
| 629 | + def test_get_frame_class_sub_dict_empty_string_falls_back_to_arducopter(self) -> None: |
| 630 | + """get_frame_class_sub_dict falls back to ArduCopter when fw_type is empty.""" |
| 631 | + result = get_frame_class_sub_dict("") |
| 632 | + assert result is FRAME_CLASS_DICT["ArduCopter"] |
| 633 | + |
| 634 | + def test_frame_class_values_are_unique_per_vehicle_type(self) -> None: |
| 635 | + """Within each vehicle type, frame class names must be unique.""" |
| 636 | + for vtype, sub_dict in FRAME_CLASS_DICT.items(): |
| 637 | + if not sub_dict: |
| 638 | + continue |
| 639 | + names = list(sub_dict.values()) |
| 640 | + assert len(names) == len(set(names)), f"Duplicate frame class names found in '{vtype}'" |
0 commit comments