Files
TinyTorch/modules/setup/tests/test_setup.py
Vijay Janapa Reddi c5084dad27 Restore comprehensive setup module with full educational content
- Restore complete setup module with system information and developer profiles
- Add NBDev educational patterns with #| export and #| hide directives
- Implement SystemInfo class for platform detection and compatibility checking
- Add DeveloperProfile class with ASCII art customization and personalization
- Include comprehensive educational content with step-by-step learning progression
- Expand test suite to 20 comprehensive tests covering all functionality:
  - Function execution and output validation
  - Arithmetic operations (basic, negative, floating-point)
  - System information collection and compatibility
  - Developer profile creation and customization
  - ASCII art file loading and fallback behavior
  - Error recovery and integration testing
- Update README with complete feature documentation and usage examples
- Convert to executed notebook showing rich output and system information
- Maintain file-based ASCII art system with tinytorch_flame.txt
- All tests passing with professional pytest structure
2025-07-10 19:30:01 -04:00

251 lines
9.0 KiB
Python

#!/usr/bin/env python3
"""
Tests for the setup module using pytest.
"""
import pytest
import sys
import os
from pathlib import Path
from io import StringIO
# Add the parent directory to the path so we can import setup_dev
sys.path.insert(0, str(Path(__file__).parent.parent))
from setup_dev import hello_tinytorch, add_numbers, SystemInfo, DeveloperProfile
class TestSetupFunctions:
"""Test setup module functions."""
def test_hello_tinytorch_executes(self):
"""Test that hello_tinytorch runs without error."""
# Should not raise any exceptions
hello_tinytorch()
def test_hello_tinytorch_prints_content(self, capsys):
"""Test that hello_tinytorch prints the expected content."""
hello_tinytorch()
captured = capsys.readouterr()
# Should print the branding text
assert "Tiny🔥Torch" in captured.out
assert "Build ML Systems from Scratch!" in captured.out
def test_add_numbers_basic(self):
"""Test basic addition functionality."""
assert add_numbers(2, 3) == 5
assert add_numbers(10, 15) == 25
assert add_numbers(0, 0) == 0
def test_add_numbers_negative(self):
"""Test addition with negative numbers."""
assert add_numbers(-5, 3) == -2
assert add_numbers(-10, -15) == -25
assert add_numbers(10, -5) == 5
def test_add_numbers_floats(self):
"""Test addition with floating point numbers."""
assert abs(add_numbers(2.5, 3.7) - 6.2) < 1e-9
assert abs(add_numbers(1.1, 2.2) - 3.3) < 1e-9
class TestSystemInfo:
"""Test SystemInfo class."""
def test_system_info_creation(self):
"""Test SystemInfo class instantiation."""
info = SystemInfo()
assert hasattr(info, 'python_version')
assert hasattr(info, 'platform')
assert hasattr(info, 'machine')
def test_system_info_properties(self):
"""Test SystemInfo properties."""
info = SystemInfo()
# Check python_version is a version tuple
assert hasattr(info.python_version, 'major')
assert hasattr(info.python_version, 'minor')
assert isinstance(info.python_version.major, int)
assert isinstance(info.python_version.minor, int)
# Check platform is a string
assert isinstance(info.platform, str)
assert len(info.platform) > 0
# Check machine is a string
assert isinstance(info.machine, str)
assert len(info.machine) > 0
def test_system_info_str(self):
"""Test SystemInfo string representation."""
info = SystemInfo()
str_repr = str(info)
assert isinstance(str_repr, str)
assert "Python" in str_repr
assert str(info.python_version.major) in str_repr
assert str(info.python_version.minor) in str_repr
assert info.platform in str_repr
assert info.machine in str_repr
def test_is_compatible(self):
"""Test SystemInfo compatibility check."""
info = SystemInfo()
compatible = info.is_compatible()
# Should return a boolean
assert isinstance(compatible, bool)
# Since we're running this test, Python should be >= 3.8
assert compatible is True
class TestDeveloperProfile:
"""Test DeveloperProfile class."""
def test_developer_profile_creation_defaults(self):
"""Test DeveloperProfile with default values."""
profile = DeveloperProfile()
# Check default values
assert profile.name == "Vijay Janapa Reddi"
assert profile.affiliation == "Harvard University"
assert profile.email == "vj@eecs.harvard.edu"
assert profile.github_username == "profvjreddi"
assert profile.ascii_art is not None # Should have default flame
def test_developer_profile_creation_custom(self):
"""Test DeveloperProfile with custom values."""
custom_art = """
Custom ASCII Art
****************
"""
profile = DeveloperProfile(
name="Test Student",
affiliation="Test University",
email="test@example.com",
github_username="teststudent",
ascii_art=custom_art
)
assert profile.name == "Test Student"
assert profile.affiliation == "Test University"
assert profile.email == "test@example.com"
assert profile.github_username == "teststudent"
assert profile.ascii_art == custom_art
def test_developer_profile_str(self):
"""Test DeveloperProfile string representation."""
profile = DeveloperProfile()
str_repr = str(profile)
assert isinstance(str_repr, str)
assert "👨‍💻" in str_repr
assert "Vijay Janapa Reddi" in str_repr
assert "Harvard University" in str_repr
assert "@profvjreddi" in str_repr
def test_developer_profile_signature(self):
"""Test DeveloperProfile signature method."""
profile = DeveloperProfile()
signature = profile.get_signature()
assert isinstance(signature, str)
assert "Built by" in signature
assert "Vijay Janapa Reddi" in signature
assert "@profvjreddi" in signature
def test_developer_profile_ascii_art(self):
"""Test DeveloperProfile ASCII art functionality."""
# Test default ASCII art
profile = DeveloperProfile()
ascii_art = profile.get_ascii_art()
assert isinstance(ascii_art, str)
assert "Tiny🔥Torch" in ascii_art
assert "Build ML Systems from Scratch!" in ascii_art
assert len(ascii_art) > 100 # Should be substantial ASCII art
# Test custom ASCII art
custom_art = "My Custom Art!"
custom_profile = DeveloperProfile(ascii_art=custom_art)
assert custom_profile.get_ascii_art() == custom_art
def test_developer_profile_full_profile(self):
"""Test DeveloperProfile full profile display."""
profile = DeveloperProfile()
full_profile = profile.get_full_profile()
assert isinstance(full_profile, str)
assert "Tiny🔥Torch" in full_profile
assert "Build ML Systems from Scratch!" in full_profile
assert "👨‍💻 Developer: Vijay Janapa Reddi" in full_profile
assert "🏛️ Affiliation: Harvard University" in full_profile
assert "📧 Email: vj@eecs.harvard.edu" in full_profile
assert "🐙 GitHub: @profvjreddi" in full_profile
assert "🔥 Ready to build ML systems from scratch!" in full_profile
class TestFileOperations:
"""Test file-related operations."""
def test_ascii_art_file_exists(self):
"""Test that the ASCII art file exists."""
art_file = Path(__file__).parent.parent / "tinytorch_flame.txt"
assert art_file.exists(), "ASCII art file should exist"
assert art_file.is_file(), "ASCII art should be a file"
def test_ascii_art_file_has_content(self):
"""Test that the ASCII art file has content."""
art_file = Path(__file__).parent.parent / "tinytorch_flame.txt"
content = art_file.read_text()
assert len(content) > 0, "ASCII art file should not be empty"
assert len(content.splitlines()) > 10, "ASCII art should have multiple lines"
def test_hello_tinytorch_handles_missing_file(self, monkeypatch, capsys):
"""Test that hello_tinytorch handles missing ASCII art file gracefully."""
# Mock Path.exists to return False
def mock_exists(self):
return False
monkeypatch.setattr(Path, "exists", mock_exists)
# Should still work without the file
hello_tinytorch()
captured = capsys.readouterr()
# Should still print the branding text
assert "🔥 TinyTorch 🔥" in captured.out
assert "Build ML Systems from Scratch!" in captured.out
class TestModuleIntegration:
"""Test integration between different parts of the setup module."""
def test_all_functions_work_together(self):
"""Test that all setup functions work without conflicts."""
# Test functions
hello_tinytorch() # Should not raise
sum_result = add_numbers(5, 10)
# Test classes
info = SystemInfo()
profile = DeveloperProfile()
# All should work without errors
assert sum_result == 15
assert str(info) # Should not be empty
assert str(profile) # Should not be empty
assert profile.get_signature() # Should not be empty
assert profile.get_ascii_art() # Should not be empty
def test_no_import_errors(self):
"""Test that imports work correctly."""
# If we got here, imports worked
assert callable(hello_tinytorch)
assert callable(add_numbers)
assert callable(SystemInfo)
assert callable(DeveloperProfile)