From d05aa9bba4e363d3ddce96981e03f3c1a88bb3ab Mon Sep 17 00:00:00 2001 From: chalmer lowe Date: Mon, 6 Oct 2025 10:57:18 -0400 Subject: [PATCH 1/3] test: adds tests to confirm attribute extraction from classes --- .../tests/unit/test_generate_analyzer.py | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/scripts/microgenerator/tests/unit/test_generate_analyzer.py b/scripts/microgenerator/tests/unit/test_generate_analyzer.py index 67e9f563d..dec68c4e4 100644 --- a/scripts/microgenerator/tests/unit/test_generate_analyzer.py +++ b/scripts/microgenerator/tests/unit/test_generate_analyzer.py @@ -87,3 +87,185 @@ def test_import_extraction(self, code_snippet, expected_imports): expected = sorted(expected_imports) assert extracted == expected + + +# --- Tests CodeAnalyzer handling of Attributes --- + + +class TestCodeAnalyzerAttributes: + def assert_structures_equal(self, extracted, expected): + assert len(extracted) == len(expected) + for i in range(len(extracted)): + ext_class = extracted[i] + exp_class = expected[i] + assert ext_class["class_name"] == exp_class["class_name"] + assert ext_class["methods"] == exp_class["methods"] + # Sort attributes by name for order-independent comparison │ + assert sorted(ext_class["attributes"], key=lambda x: x["name"]) == sorted( + exp_class["attributes"], key=lambda x: x["name"] + ) + + @pytest.mark.parametrize( + "code_snippet, expected_structure", + [ + pytest.param( + """ +class MyClass: + CLASS_VAR = 123 +""", + [ + { + "class_name": "MyClass", + "methods": [], + "attributes": [{"name": "CLASS_VAR", "type": None}], + } + ], + id="class_var_assign", + ), + pytest.param( + """ +class MyClass: + class_var: int = 456 +""", + [ + { + "class_name": "MyClass", + "methods": [], + "attributes": [{"name": "class_var", "type": "int"}], + } + ], + id="class_var_annassign", + ), + pytest.param( + """ +class MyClass: + class_var: int +""", + [ + { + "class_name": "MyClass", + "methods": [], + "attributes": [{"name": "class_var", "type": "int"}], + } + ], + id="class_var_annassign_no_value", + ), + pytest.param( + """ +class MyClass: + def __init__(self): + self.instance_var = 789 +""", + [ + { + "class_name": "MyClass", + "methods": [ + { + "method_name": "__init__", + "args": [{"name": "self", "type": None}], + "return_type": None, + } + ], + "attributes": [{"name": "instance_var", "type": None}], + } + ], + id="instance_var_assign", + ), + pytest.param( + """ +class MyClass: + def __init__(self): + self.instance_var: str = 'hello' +""", + [ + { + "class_name": "MyClass", + "methods": [ + { + "method_name": "__init__", + "args": [{"name": "self", "type": None}], + "return_type": None, + } + ], + "attributes": [{"name": "instance_var", "type": "str"}], + } + ], + id="instance_var_annassign", + ), + pytest.param( + """ +class MyClass: + def __init__(self): + self.instance_var: str +""", + [ + { + "class_name": "MyClass", + "methods": [ + { + "method_name": "__init__", + "args": [{"name": "self", "type": None}], + "return_type": None, + } + ], + "attributes": [{"name": "instance_var", "type": "str"}], + } + ], + id="instance_var_annassign_no_value", + ), + pytest.param( + """ +class MyClass: + VAR_A = 1 + var_b: int = 2 + def __init__(self): + self.var_c = 3 + self.var_d: float = 4.0 +""", + [ + { + "class_name": "MyClass", + "methods": [ + { + "method_name": "__init__", + "args": [{"name": "self", "type": None}], + "return_type": None, + } + ], + "attributes": [ + {"name": "VAR_A", "type": None}, + {"name": "var_b", "type": "int"}, + {"name": "var_c", "type": None}, + {"name": "var_d", "type": "float"}, + ], + } + ], + id="mixed_attributes", + ), + pytest.param( + "a = 123 # Module level", + [], + id="module_level_assign", + ), + pytest.param( + "b: int = 456 # Module level", + [], + id="module_level_annassign", + ), + ], + ) + def test_attribute_extraction(self, code_snippet, expected_structure): + analyzer = CodeAnalyzer() + tree = ast.parse(code_snippet) + analyzer.visit(tree) + + extracted = analyzer.structure + # Normalize attributes for order-independent comparison + for item in extracted: + if "attributes" in item: + item["attributes"].sort(key=lambda x: x["name"]) + for item in expected_structure: + if "attributes" in item: + item["attributes"].sort(key=lambda x: x["name"]) + + assert extracted == expected_structure From e92c397409245ced745845aecec2b29242bbce3c Mon Sep 17 00:00:00 2001 From: Chalmer Lowe Date: Mon, 6 Oct 2025 11:55:57 -0400 Subject: [PATCH 2/3] Update scripts/microgenerator/tests/unit/test_generate_analyzer.py --- .../tests/unit/test_generate_analyzer.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/scripts/microgenerator/tests/unit/test_generate_analyzer.py b/scripts/microgenerator/tests/unit/test_generate_analyzer.py index dec68c4e4..f59ce87f7 100644 --- a/scripts/microgenerator/tests/unit/test_generate_analyzer.py +++ b/scripts/microgenerator/tests/unit/test_generate_analyzer.py @@ -93,17 +93,6 @@ def test_import_extraction(self, code_snippet, expected_imports): class TestCodeAnalyzerAttributes: - def assert_structures_equal(self, extracted, expected): - assert len(extracted) == len(expected) - for i in range(len(extracted)): - ext_class = extracted[i] - exp_class = expected[i] - assert ext_class["class_name"] == exp_class["class_name"] - assert ext_class["methods"] == exp_class["methods"] - # Sort attributes by name for order-independent comparison │ - assert sorted(ext_class["attributes"], key=lambda x: x["name"]) == sorted( - exp_class["attributes"], key=lambda x: x["name"] - ) @pytest.mark.parametrize( "code_snippet, expected_structure", From 4ef99b94ffc692c7fd534e26db946b688e523f7f Mon Sep 17 00:00:00 2001 From: chalmer lowe Date: Mon, 6 Oct 2025 12:14:47 -0400 Subject: [PATCH 3/3] adds docstring --- scripts/microgenerator/tests/unit/test_generate_analyzer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/microgenerator/tests/unit/test_generate_analyzer.py b/scripts/microgenerator/tests/unit/test_generate_analyzer.py index f59ce87f7..e17078a54 100644 --- a/scripts/microgenerator/tests/unit/test_generate_analyzer.py +++ b/scripts/microgenerator/tests/unit/test_generate_analyzer.py @@ -243,7 +243,8 @@ def __init__(self): ), ], ) - def test_attribute_extraction(self, code_snippet, expected_structure): + def test_attribute_extraction(self, code_snippet: str, expected_structure: list): + """Tests the extraction of class and instance attributes.""" analyzer = CodeAnalyzer() tree = ast.parse(code_snippet) analyzer.visit(tree)