Skip to content

Commit fe53817

Browse files
committed
Add unittest for turtledemo command line interface
- Create comprehensive test suite for turtledemo CLI functionality - Test directory structure, file existence, and module imports - Validate __main__.py structure and syntax - Check individual demo files and configuration - Use proper unittest.TestCase with standard assertions - All tests pass with both pytest and unittest runners
1 parent e835630 commit fe53817

File tree

1 file changed

+128
-184
lines changed

1 file changed

+128
-184
lines changed

Lib/test/test_turtledemo.py

Lines changed: 128 additions & 184 deletions
Original file line numberDiff line numberDiff line change
@@ -1,194 +1,138 @@
1-
"""
2-
Test command line interface for turtledemo module.
3-
4-
This test suite validates the CLI functionality of the turtledemo module,
5-
which provides a GUI-based demo viewer for turtle graphics examples.
6-
"""
1+
import unittest
72
import sys
83
import os
94
import importlib.util
105

116

12-
class SimpleTest:
13-
"""Simple test framework to avoid compatibility issues."""
14-
def __init__(self):
15-
self.passed = 0
16-
self.failed = 0
17-
18-
def assert_true(self, condition, msg=""):
19-
if condition:
20-
self.passed += 1
21-
print(f"✓ {msg}")
22-
else:
23-
self.failed += 1
24-
print(f"✗ {msg}")
25-
26-
def assert_equal(self, a, b, msg=""):
27-
if a == b:
28-
self.passed += 1
29-
print(f"✓ {msg}")
30-
else:
31-
self.failed += 1
32-
print(f"✗ {msg}: {a} != {b}")
33-
34-
def assert_in(self, item, container, msg=""):
35-
if item in container:
36-
self.passed += 1
37-
print(f"✓ {msg}")
38-
else:
39-
self.failed += 1
40-
print(f"✗ {msg}: {item} not in {container}")
41-
42-
def assert_is_instance(self, obj, expected_type, msg=""):
43-
if isinstance(obj, expected_type):
44-
self.passed += 1
45-
print(f"✓ {msg}")
46-
else:
47-
self.failed += 1
48-
print(f"✗ {msg}: {type(obj)} != {expected_type}")
49-
50-
def assert_has_attr(self, obj, attr, msg=""):
51-
if hasattr(obj, attr):
52-
self.passed += 1
53-
print(f"✓ {msg}")
54-
else:
55-
self.failed += 1
56-
print(f"✗ {msg}: {attr} not found")
57-
58-
def summary(self):
59-
total = self.passed + self.failed
60-
print(f"\nTest Summary: {self.passed}/{total} passed, {self.failed} failed")
61-
return self.failed == 0
62-
63-
64-
def test_turtledemo_cli():
7+
class TurtledemoCommandLineTest(unittest.TestCase):
658
"""Test command line interface for turtledemo module."""
66-
test = SimpleTest()
67-
68-
print("Testing turtledemo command line interface...")
69-
70-
# Test 1: Check turtledemo directory structure
71-
demo_dir = os.path.join(os.path.dirname(__file__), '..', 'turtledemo')
72-
test.assert_true(os.path.exists(demo_dir), "turtledemo directory exists")
73-
test.assert_true(os.path.isdir(demo_dir), "turtledemo is a directory")
74-
75-
# Test 2: Check __main__.py exists
76-
main_file = os.path.join(demo_dir, '__main__.py')
77-
test.assert_true(os.path.exists(main_file), "__main__.py exists")
78-
79-
# Test 3: Check demo files exist
80-
if os.path.exists(demo_dir):
81-
demo_files = [f for f in os.listdir(demo_dir)
82-
if f.endswith('.py') and not f.startswith('_')]
83-
test.assert_true(len(demo_files) > 0, f"found {len(demo_files)} demo files")
84-
85-
# Check for known demo files
86-
expected_demos = ['bytedesign.py', 'chaos.py', 'clock.py', 'colormixer.py', 'forest.py']
87-
for demo in expected_demos:
88-
test.assert_in(demo, demo_files, f"demo file {demo} exists")
89-
90-
# Test 4: Test module import (may fail due to dependencies)
91-
try:
92-
spec = importlib.util.spec_from_file_location("turtledemo",
93-
os.path.join(demo_dir, '__init__.py'))
94-
if spec and spec.loader:
95-
turtledemo = importlib.util.module_from_spec(spec)
96-
spec.loader.exec_module(turtledemo)
97-
test.assert_true(True, "turtledemo module imported successfully")
98-
else:
99-
test.assert_true(False, "could not create spec for turtledemo")
100-
except Exception as e:
101-
test.assert_true(False, f"turtledemo import failed: {e}")
102-
103-
# Test 5: Test __main__ module structure
104-
try:
105-
main_file = os.path.join(demo_dir, '__main__.py')
106-
with open(main_file, 'r') as f:
107-
content = f.read()
108-
109-
# Check for key functions and classes
110-
test.assert_in('def main():', content, "main function defined")
111-
test.assert_in('class DemoWindow', content, "DemoWindow class defined")
112-
test.assert_in('def getExampleEntries():', content, "getExampleEntries function defined")
113-
test.assert_in('if __name__ == \'__main__\':', content, "__main__ guard present")
114-
115-
# Check for imports
116-
test.assert_in('import sys', content, "sys import present")
117-
test.assert_in('import os', content, "os import present")
118-
test.assert_in('from tkinter import', content, "tkinter import present")
119-
120-
except Exception as e:
121-
test.assert_true(False, f"failed to read __main__.py: {e}")
122-
123-
# Test 6: Test individual demo files structure
124-
demo_files_to_check = ['bytedesign.py', 'chaos.py', 'clock.py']
125-
for demo_file in demo_files_to_check:
126-
demo_path = os.path.join(demo_dir, demo_file)
127-
if os.path.exists(demo_path):
128-
try:
129-
with open(demo_path, 'r') as f:
130-
content = f.read()
131-
test.assert_in('def main():', content, f"{demo_file} has main function")
132-
has_main_guard = ('if __name__ == \'__main__\':' in content or
133-
'if __name__ == "__main__":' in content)
134-
test.assert_true(has_main_guard, f"{demo_file} has __main__ guard")
135-
except Exception as e:
136-
test.assert_true(False, f"failed to read {demo_file}: {e}")
137-
138-
# Test 7: Check configuration files
139-
config_file = os.path.join(demo_dir, 'turtle.cfg')
140-
test.assert_true(os.path.exists(config_file), "turtle.cfg exists")
141-
142-
# Test 8: Test command line execution simulation
143-
try:
144-
# Simulate what happens when running: python -m turtledemo
145-
main_file = os.path.join(demo_dir, '__main__.py')
146-
147-
# Read the file to check it's syntactically valid Python
148-
with open(main_file, 'r') as f:
149-
content = f.read()
150-
151-
# Try to compile it
152-
compile(content, main_file, 'exec')
153-
test.assert_true(True, "__main__.py is valid Python code")
154-
155-
except SyntaxError as e:
156-
test.assert_true(False, f"__main__.py has syntax error: {e}")
157-
except Exception as e:
158-
test.assert_true(False, f"failed to validate __main__.py: {e}")
159-
160-
# Test 9: Check for documentation strings
161-
try:
162-
main_file = os.path.join(demo_dir, '__main__.py')
163-
with open(main_file, 'r') as f:
164-
content = f.read()
165-
166-
# Check for module docstring
167-
test.assert_true(content.startswith('"""') or content.startswith("'''"),
168-
"__main__.py has module docstring")
169-
170-
# Check for function docstrings
171-
test.assert_in('"""', content, "contains docstrings")
172-
173-
except Exception as e:
174-
test.assert_true(False, f"failed to check docstrings: {e}")
175-
176-
# Test 10: Test CLI entry point
177-
try:
178-
# The CLI entry point should be the main() function in __main__.py
179-
main_file = os.path.join(demo_dir, '__main__.py')
180-
with open(main_file, 'r') as f:
181-
content = f.read()
182-
183-
# Check that main() is called when run as script
184-
test.assert_in('main()', content, "main() is called in __main__")
185-
186-
except Exception as e:
187-
test.assert_true(False, f"failed to check CLI entry point: {e}")
1889

189-
return test.summary()
10+
def setUp(self):
11+
"""Set up test fixtures."""
12+
self.demo_dir = os.path.join(os.path.dirname(__file__), '..', 'turtledemo')
13+
self.main_file = os.path.join(self.demo_dir, '__main__.py')
14+
15+
def test_turtledemo_directory_exists(self):
16+
"""Test that turtledemo directory exists."""
17+
self.assertTrue(os.path.exists(self.demo_dir), "turtledemo directory exists")
18+
self.assertTrue(os.path.isdir(self.demo_dir), "turtledemo is a directory")
19+
20+
def test_main_file_exists(self):
21+
"""Test that __main__.py exists."""
22+
self.assertTrue(os.path.exists(self.main_file), "__main__.py exists")
23+
24+
def test_demo_files_exist(self):
25+
"""Test that demo files exist."""
26+
if os.path.exists(self.demo_dir):
27+
demo_files = [f for f in os.listdir(self.demo_dir)
28+
if f.endswith('.py') and not f.startswith('_')]
29+
self.assertGreater(len(demo_files), 0, f"found {len(demo_files)} demo files")
30+
31+
# Check for known demo files
32+
expected_demos = ['bytedesign.py', 'chaos.py', 'clock.py', 'colormixer.py', 'forest.py']
33+
for demo in expected_demos:
34+
self.assertIn(demo, demo_files, f"demo file {demo} exists")
35+
36+
def test_module_import(self):
37+
"""Test that turtledemo module can be imported."""
38+
try:
39+
spec = importlib.util.spec_from_file_location("turtledemo",
40+
os.path.join(self.demo_dir, '__init__.py'))
41+
if spec and spec.loader:
42+
turtledemo = importlib.util.module_from_spec(spec)
43+
spec.loader.exec_module(turtledemo)
44+
self.assertTrue(True, "turtledemo module imported successfully")
45+
else:
46+
self.fail("could not create spec for turtledemo")
47+
except Exception as e:
48+
self.fail(f"turtledemo import failed: {e}")
49+
50+
def test_main_module_structure(self):
51+
"""Test __main__ module structure."""
52+
try:
53+
with open(self.main_file, 'r') as f:
54+
content = f.read()
55+
56+
# Check for key functions and classes
57+
self.assertIn('def main():', content, "main function defined")
58+
self.assertIn('class DemoWindow', content, "DemoWindow class defined")
59+
self.assertIn('def getExampleEntries():', content, "getExampleEntries function defined")
60+
self.assertIn('if __name__ == \'__main__\':', content, "__main__ guard present")
61+
62+
# Check for imports
63+
self.assertIn('import sys', content, "sys import present")
64+
self.assertIn('import os', content, "os import present")
65+
self.assertIn('from tkinter import', content, "tkinter import present")
66+
67+
except Exception as e:
68+
self.fail(f"failed to read __main__.py: {e}")
69+
70+
def test_individual_demo_files_structure(self):
71+
"""Test individual demo files structure."""
72+
demo_files_to_check = ['bytedesign.py', 'chaos.py', 'clock.py']
73+
for demo_file in demo_files_to_check:
74+
with self.subTest(demo_file=demo_file):
75+
demo_path = os.path.join(self.demo_dir, demo_file)
76+
if os.path.exists(demo_path):
77+
try:
78+
with open(demo_path, 'r') as f:
79+
content = f.read()
80+
self.assertIn('def main():', content, f"{demo_file} has main function")
81+
has_main_guard = ('if __name__ == \'__main__\':' in content or
82+
'if __name__ == "__main__":' in content)
83+
self.assertTrue(has_main_guard, f"{demo_file} has __main__ guard")
84+
except Exception as e:
85+
self.fail(f"failed to read {demo_file}: {e}")
86+
87+
def test_configuration_files(self):
88+
"""Test configuration files exist."""
89+
config_file = os.path.join(self.demo_dir, 'turtle.cfg')
90+
self.assertTrue(os.path.exists(config_file), "turtle.cfg exists")
91+
92+
def test_main_file_syntax(self):
93+
"""Test that __main__.py is valid Python code."""
94+
try:
95+
with open(self.main_file, 'r') as f:
96+
content = f.read()
97+
98+
# Try to compile it
99+
compile(content, self.main_file, 'exec')
100+
# If we get here, compilation succeeded
101+
self.assertTrue(True, "__main__.py is valid Python code")
102+
103+
except SyntaxError as e:
104+
self.fail(f"__main__.py has syntax error: {e}")
105+
except Exception as e:
106+
self.fail(f"failed to validate __main__.py: {e}")
107+
108+
def test_documentation_strings(self):
109+
"""Test for documentation strings."""
110+
try:
111+
with open(self.main_file, 'r') as f:
112+
content = f.read()
113+
114+
# Check for module docstring
115+
has_docstring = (content.startswith('"""') or content.startswith("'''"))
116+
self.assertTrue(has_docstring, "__main__.py has module docstring")
117+
118+
# Check for function docstrings
119+
self.assertIn('"""', content, "contains docstrings")
120+
121+
except Exception as e:
122+
self.fail(f"failed to check docstrings: {e}")
123+
124+
def test_cli_entry_point(self):
125+
"""Test CLI entry point."""
126+
try:
127+
with open(self.main_file, 'r') as f:
128+
content = f.read()
129+
130+
# Check that main() is called when run as script
131+
self.assertIn('main()', content, "main() is called in __main__")
132+
133+
except Exception as e:
134+
self.fail(f"failed to check CLI entry point: {e}")
190135

191136

192137
if __name__ == '__main__':
193-
success = test_turtledemo_cli()
194-
sys.exit(0 if success else 1)
138+
unittest.main()

0 commit comments

Comments
 (0)