import pytest import inspect from unittest.mock import MagicMock, patch from textwrap import dedent from slic.utils.shortcut import Shortcut, as_shortcut, shortcuts, Registry @pytest.fixture(autouse=True) def reset_shortcuts(): # Reset shortcuts before each test shortcuts._instance = None yield shortcuts._instance = None # Tests for Shortcut class class TestShortcutInitialization: def test_init_with_custom_name(self): # Verify initialization with custom name func = lambda x: x * 2 sc = Shortcut(func, name="Doubler") assert sc.func(5) == 10 assert sc.name == "Doubler" def test_init_without_name(self): # Test auto-naming from __name__ def addition_fct(a, b): return a + b sc = Shortcut(addition_fct) assert sc.name == "addition fct" assert sc.func(2, 3) == 5 def test_run_method(capsys): # Test direct execution while verifying print output execution_results = [] def sample(): execution_results.append("executed") return "ok" sc = Shortcut(sample, name="TestRun") task = sc.run(hold=False) # Testing immediate execution # Verify the print output captured = capsys.readouterr() assert captured.out == 'Running "TestRun"\n' # Verify the function executed assert execution_results == ["executed"] # Verify Task properties assert task.func == sample assert task.func() == "ok" assert task.status == "done" def test_repr_output(): # Test exact __repr__ format def my_func(): pass sc = Shortcut(my_func, name="Test Repr") assert repr(sc) == f"Shortcut \"Test Repr\"" def test_source_with_regular_function(): # Test source with normal function def example(): a = 1 b = 2 return a + b sc = Shortcut(example) src = sc.source assert "def example():" in src assert "a = 1" in src assert "b = 2" in src assert "return a + b" in src @patch('inspect.getsource') def test_source_error_handling(mock_getsource): # Test exception handling when inspect fails mock_getsource.side_effect = OSError("No source") sc = Shortcut(lambda x: x) with pytest.raises(OSError): _ = sc.source # Tests for as_shortcut decorator def test_as_shortcut_basic(): # Test basic decorator without parameters @as_shortcut def basic_func(): return 42 assert isinstance(basic_func, Shortcut) assert basic_func.name == "basic func" assert basic_func.func() == 42 def test_as_shortcut_with_name(): # Test decorator with name parameter @as_shortcut(name="CustomName") def decorated(): pass assert decorated.name == "CustomName" def test_as_shortcut_factory_pattern(): # Test factory pattern usage decorator = as_shortcut(name="FromFactory") @decorator def sample(): pass assert sample.name == "FromFactory" # Tests for shortcuts singleton class TestShortcutsSingleton: def setup_method(self): # Reset singleton before each test shortcuts._instance = None def test_singleton_behavior(self): # Verify singleton pattern inst1 = shortcuts inst2 = shortcuts assert inst1 is inst2 def test_registration(self): # Test automatic registration @as_shortcut(name="FuncA") def func_a(): pass @as_shortcut(name="FuncB") def func_b(): pass assert len(shortcuts._get()) == 2 assert "FuncA" in shortcuts._get() assert shortcuts["FuncA"].func == func_a.func def test_getitem_access(self): # Test dictionary-style access @as_shortcut(name="TestAccess") def access_test(): return 123 assert shortcuts["TestAccess"].func() == 123 def test_missing_key(self): # Test access with invalid key with pytest.raises(KeyError): _ = shortcuts["InvalidKey"] def test_repr_output(self): # Test registry display @as_shortcut(name="DisplayTest") def to_display(): pass repr_str = repr(shortcuts) assert "DisplayTest" in repr_str assert "Shortcut" in repr_str # Full integration tests class TestFullIntegration: def setup_method(self): shortcuts._instance = None def test_workflow(self, capsys): # Test complete workflow from creation to execution @as_shortcut(name="FullTest") def integration_test(a, b): print(f"Calculating {a} + {b}") return a + b sc = shortcuts["FullTest"] assert sc.name == "FullTest" task = sc.run(hold=True) out, _ = capsys.readouterr() assert "Running \"FullTest\"" in out assert task.func(3, 4) == 7 source = sc.source assert "def integration_test(a, b):" in source assert "print(f\"Calculating {a} + {b}\")" in source def test_multiple_shortcuts(self): # Test multiple shortcuts coexistence @as_shortcut(name="First") def first(): return 1 @as_shortcut(name="Second") def second(): return 2 assert shortcuts["First"].func() == 1 assert shortcuts["Second"].func() == 2 assert len(shortcuts._get()) == 2 def test_registry_inheritance(): # Verify Shortcut inherits from Registry assert issubclass(Shortcut, Registry)