|
| 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 | +""" |
| 7 | +import sys |
| 8 | +import os |
| 9 | +import importlib.util |
| 10 | + |
| 11 | + |
| 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(): |
| 65 | + """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}") |
| 188 | + |
| 189 | + return test.summary() |
| 190 | + |
| 191 | + |
| 192 | +if __name__ == '__main__': |
| 193 | + success = test_turtledemo_cli() |
| 194 | + sys.exit(0 if success else 1) |
0 commit comments