Skip to content

Commit e0d5532

Browse files
committed
refactors test_generate_parser to use analyze_classes
1 parent b77a7cf commit e0d5532

File tree

1 file changed

+158
-0
lines changed

1 file changed

+158
-0
lines changed
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright 2025 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
17+
import pytest
18+
from unittest import mock
19+
import textwrap as tw
20+
21+
from scripts.microgenerator import generate
22+
23+
24+
# --- Tests for parse_code() ---
25+
def test_parse_code_empty():
26+
analyzed_classes, imports, types = generate.parse_code("")
27+
assert analyzed_classes == []
28+
assert imports == set()
29+
assert types == set()
30+
31+
32+
def test_parse_code_simple_class():
33+
code = tw.dedent(
34+
"""
35+
class MyClass:
36+
pass
37+
"""
38+
)
39+
analyzed_classes, _, _ = generate.parse_code(code)
40+
assert len(analyzed_classes) == 1
41+
assert analyzed_classes[0]["class_name"] == "MyClass"
42+
43+
44+
def test_parse_code_simple_function():
45+
code = tw.dedent(
46+
"""
47+
def my_function():
48+
pass
49+
"""
50+
)
51+
52+
# In the microgenerator, the focus is parsing major classes (and their
53+
# associated methods in the GAPIC generated code, not parsing top-level
54+
# functions. Thus we do not expect it to capture this top-level function.
55+
analyzed_classes, _, _ = generate.parse_code(code)
56+
assert len(analyzed_classes) == 0
57+
58+
59+
def test_parse_code_invalid_syntax():
60+
with pytest.raises(SyntaxError):
61+
# incorrect indentation and missing trailing colon on func definition.
62+
code = tw.dedent(
63+
"""
64+
class MyClass:
65+
pass
66+
def func()
67+
pass
68+
"""
69+
)
70+
generate.parse_code(code)
71+
72+
73+
def test_parse_code_with_imports_and_types():
74+
code = tw.dedent(
75+
"""
76+
import os
77+
import sys as system
78+
from typing import List, Optional, Dict
79+
from . import my_module
80+
81+
class MyClass:
82+
attr: Dict[str, int]
83+
def method(self, x: List[str]) -> Optional[int]:
84+
return None
85+
def method2(self, y: 'MyClass') -> None:
86+
pass
87+
"""
88+
)
89+
analyzed_classes, imports, types = generate.parse_code(code)
90+
91+
expected_imports = {
92+
"import os",
93+
"import sys as system",
94+
"from typing import List, Optional, Dict",
95+
"from . import my_module",
96+
}
97+
assert imports == expected_imports
98+
99+
expected_types = {
100+
"Dict",
101+
"str",
102+
"int",
103+
"List",
104+
"Optional",
105+
"MyClass",
106+
"None",
107+
}
108+
assert types == expected_types
109+
assert len(analyzed_classes) == 1
110+
111+
112+
# --- Tests for parse_file() ---
113+
# parse_file() wraps parse_code() and simply reads in content from a file
114+
# as a string using the built in open() function and passes the string intact
115+
# to parse_code().
116+
@mock.patch("builtins.open", new_callable=mock.mock_open)
117+
def test_parse_file_reads_and_parses(mock_file):
118+
read_data = tw.dedent(
119+
"""
120+
class TestClass:
121+
pass
122+
"""
123+
)
124+
mock_file.return_value.read.return_value = read_data
125+
analyzed_classes, _, _ = generate.parse_file("dummy/path/file.py")
126+
mock_file.assert_called_once_with("dummy/path/file.py", "r", encoding="utf-8")
127+
assert len(analyzed_classes) == 1
128+
assert analyzed_classes[0]["class_name"] == "TestClass"
129+
130+
131+
@mock.patch("builtins.open", side_effect=FileNotFoundError)
132+
def test_parse_file_not_found(mock_file):
133+
with pytest.raises(FileNotFoundError):
134+
generate.parse_file("nonexistent.py")
135+
mock_file.assert_called_once_with("nonexistent.py", "r", encoding="utf-8")
136+
137+
138+
@mock.patch("builtins.open", new_callable=mock.mock_open)
139+
def test_parse_file_syntax_error(mock_file):
140+
mock_file.return_value.read.return_value = "a = ("
141+
with pytest.raises(SyntaxError):
142+
generate.parse_file("syntax_error.py")
143+
mock_file.assert_called_once_with("syntax_error.py", "r", encoding="utf-8")
144+
145+
146+
@mock.patch(
147+
"scripts.microgenerator.generate.parse_code", return_value=([], set(), set())
148+
)
149+
@mock.patch("builtins.open", new_callable=mock.mock_open)
150+
def test_parse_file_calls_parse_code(mock_file, mock_parse_code):
151+
"""This test simply confirms that parse_code() gets called internally.
152+
153+
Other parse_code tests ensure that it works as expected.
154+
"""
155+
read_data = "some code"
156+
mock_file.return_value.read.return_value = read_data
157+
generate.parse_file("some_file.py")
158+
mock_parse_code.assert_called_once_with(read_data)

0 commit comments

Comments
 (0)