mirror of
https://github.com/harvard-edge/cs249r_book.git
synced 2026-05-01 10:09:18 -05:00
feat: integrate TinyTorch into MLSysBook repository
TinyTorch educational deep learning framework now lives at tinytorch/
Structure:
- tinytorch/src/ - Source modules (single source of truth)
- tinytorch/tito/ - CLI tool
- tinytorch/tests/ - Test suite
- tinytorch/site/ - Jupyter Book website
- tinytorch/milestones/ - Historical ML implementations
- tinytorch/datasets/ - Educational datasets (tinydigits, tinytalks)
- tinytorch/assignments/ - NBGrader assignments
- tinytorch/instructor/ - Teaching materials
Workflows (with tinytorch- prefix):
- tinytorch-ci.yml - CI/CD pipeline
- tinytorch-publish-dev.yml - Dev site deployment
- tinytorch-publish-live.yml - Live site deployment
- tinytorch-build-pdf.yml - PDF generation
- tinytorch-release-check.yml - Release validation
Repository Variables added:
- TINYTORCH_ROOT = tinytorch
- TINYTORCH_SRC = tinytorch/src
- TINYTORCH_SITE = tinytorch/site
- TINYTORCH_TESTS = tinytorch/tests
All workflows use \${{ vars.TINYTORCH_* }} for path configuration.
Note: tinytorch/site/_static/favicon.svg kept as SVG (valid for favicons)
This commit is contained in:
203
tinytorch/tests/cli/test_cli_execution.py
Normal file
203
tinytorch/tests/cli/test_cli_execution.py
Normal file
@@ -0,0 +1,203 @@
|
||||
"""
|
||||
CLI Execution Tests - Smoke tests for each command
|
||||
|
||||
This test suite ensures:
|
||||
1. Each command can be executed without crashing (help mode)
|
||||
2. Commands with subcommands show their subcommand help
|
||||
3. Error messages are helpful when commands fail
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add tito to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
||||
|
||||
from tito.main import TinyTorchCLI
|
||||
|
||||
|
||||
class TestCommandExecution:
|
||||
"""Test that all commands can be executed (smoke tests)."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test fixtures."""
|
||||
self.cli = TinyTorchCLI()
|
||||
self.project_root = Path(__file__).parent.parent.parent
|
||||
|
||||
def test_bare_tito_command(self):
|
||||
"""Test bare 'tito' command shows welcome screen."""
|
||||
result = subprocess.run(
|
||||
[sys.executable, '-m', 'tito.main'],
|
||||
cwd=self.project_root,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
|
||||
# Should exit successfully
|
||||
assert result.returncode == 0, f"Bare tito command failed: {result.stderr}"
|
||||
|
||||
# Should show welcome message
|
||||
assert "Welcome to Tiny" in result.stdout or "TORCH" in result.stdout
|
||||
assert "Command Groups:" in result.stdout or "Quick Start:" in result.stdout
|
||||
|
||||
def test_tito_help(self):
|
||||
"""Test 'tito -h' shows help."""
|
||||
result = subprocess.run(
|
||||
[sys.executable, '-m', 'tito.main', '-h'],
|
||||
cwd=self.project_root,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
|
||||
assert result.returncode == 0
|
||||
# Custom help displays logo and commands
|
||||
assert "TinyTorch" in result.stdout or "TORCH" in result.stdout
|
||||
assert "Quick Start" in result.stdout or "module" in result.stdout
|
||||
|
||||
def test_tito_version(self):
|
||||
"""Test 'tito --version' shows version."""
|
||||
result = subprocess.run(
|
||||
[sys.executable, '-m', 'tito.main', '--version'],
|
||||
cwd=self.project_root,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
|
||||
assert result.returncode == 0
|
||||
assert "Tiny" in result.stdout or "CLI" in result.stdout
|
||||
|
||||
@pytest.mark.parametrize("command", [
|
||||
'setup', 'system', 'module', 'src', 'package', 'nbgrader',
|
||||
'milestones', 'benchmark', 'community', 'export', 'test',
|
||||
'grade', 'logo'
|
||||
])
|
||||
def test_command_help_works(self, command):
|
||||
"""Test that each command's help can be displayed."""
|
||||
result = subprocess.run(
|
||||
[sys.executable, '-m', 'tito.main', command, '-h'],
|
||||
cwd=self.project_root,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
|
||||
# Help should always succeed
|
||||
assert result.returncode == 0, (
|
||||
f"Command '{command} -h' failed with exit code {result.returncode}\n"
|
||||
f"stderr: {result.stderr}"
|
||||
)
|
||||
|
||||
# Should show usage
|
||||
assert "usage:" in result.stdout.lower(), (
|
||||
f"Command '{command} -h' didn't show usage"
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize("command,subcommand", [
|
||||
('system', 'info'),
|
||||
('system', 'doctor'),
|
||||
('module', 'status'),
|
||||
('module', 'list'),
|
||||
('community', 'join'),
|
||||
('milestones', 'status'),
|
||||
])
|
||||
def test_subcommand_help_works(self, command, subcommand):
|
||||
"""Test that subcommands can show help."""
|
||||
result = subprocess.run(
|
||||
[sys.executable, '-m', 'tito.main', command, subcommand, '-h'],
|
||||
cwd=self.project_root,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
|
||||
# Subcommand help should work
|
||||
# Note: Some commands might return non-zero if not fully implemented
|
||||
# but should at least not crash
|
||||
assert result.returncode in [0, 1, 2], (
|
||||
f"Command '{command} {subcommand} -h' crashed with exit code {result.returncode}"
|
||||
)
|
||||
|
||||
|
||||
class TestCommandGrouping:
|
||||
"""Test that commands are properly grouped and discoverable."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test fixtures."""
|
||||
self.project_root = Path(__file__).parent.parent.parent
|
||||
|
||||
def test_student_facing_commands_discoverable(self):
|
||||
"""Test that main student-facing commands are easily discoverable."""
|
||||
result = subprocess.run(
|
||||
[sys.executable, '-m', 'tito.main'],
|
||||
cwd=self.project_root,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
|
||||
# Key student commands should be visible
|
||||
student_commands = ['setup', 'module', 'milestones']
|
||||
|
||||
for cmd in student_commands:
|
||||
assert cmd in result.stdout, (
|
||||
f"Student command '{cmd}' not visible in welcome screen"
|
||||
)
|
||||
|
||||
def test_developer_commands_documented(self):
|
||||
"""Test that developer commands are documented in help."""
|
||||
result = subprocess.run(
|
||||
[sys.executable, '-m', 'tito.main', '-h'],
|
||||
cwd=self.project_root,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
|
||||
# Developer commands should be in help
|
||||
dev_commands = ['src', 'package', 'nbgrader']
|
||||
|
||||
for cmd in dev_commands:
|
||||
assert cmd in result.stdout, (
|
||||
f"Developer command '{cmd}' not in help text"
|
||||
)
|
||||
|
||||
|
||||
class TestErrorMessages:
|
||||
"""Test that error messages are helpful."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test fixtures."""
|
||||
self.project_root = Path(__file__).parent.parent.parent
|
||||
|
||||
def test_invalid_command_shows_help(self):
|
||||
"""Test that invalid commands show helpful error."""
|
||||
result = subprocess.run(
|
||||
[sys.executable, '-m', 'tito.main', 'nonexistent'],
|
||||
cwd=self.project_root,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
|
||||
# Should fail
|
||||
assert result.returncode != 0
|
||||
|
||||
# Should mention the invalid command
|
||||
combined_output = result.stdout + result.stderr
|
||||
assert 'nonexistent' in combined_output or 'invalid choice' in combined_output.lower()
|
||||
|
||||
def test_missing_subcommand_shows_help(self):
|
||||
"""Test that missing subcommands show help."""
|
||||
# Try module command without subcommand
|
||||
result = subprocess.run(
|
||||
[sys.executable, '-m', 'tito.main', 'module'],
|
||||
cwd=self.project_root,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
|
||||
# Should show help or error
|
||||
# Some commands might have default behavior, others require subcommand
|
||||
combined_output = result.stdout + result.stderr
|
||||
assert len(combined_output) > 0, "No output from command without subcommand"
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__, '-v'])
|
||||
Reference in New Issue
Block a user