1+ import unittest
2+ import subprocess
3+ import os
4+
5+ class TestCollatzConjecture (unittest .TestCase ):
6+ def setUp (self ):
7+ # Resolve the absolute path to the script
8+ self .script_path = os .path .join (
9+ os .path .dirname (__file__ ), ".." ,
10+ "math" , "Collatz-Conjecture" , "Collatz-Conjecture.py"
11+ )
12+ self .script_path = os .path .abspath (self .script_path )
13+
14+ def run_script (self , inputs ):
15+ """Feeding simulated user inputs to the script."""
16+ input_data = "\n " .join (inputs + ["n" ]) + "\n "
17+ env = os .environ .copy ()
18+ env ["PYTHONIOENCODING" ] = "utf-8"
19+
20+ result = subprocess .run (
21+ ["python" , self .script_path ],
22+ input = input_data ,
23+ text = True ,
24+ capture_output = True ,
25+ encoding = 'utf-8' ,
26+ env = env
27+ )
28+ return result .stdout
29+
30+ def test_standard_flow (self ):
31+ """Test a simple number sequence (4 -> 2 -> 1)."""
32+ output = self .run_script (["4" , "n" ])
33+ self .assertIn ("Starting with: 4" , output )
34+ self .assertIn ("Total steps: 2" , output )
35+ self .assertIn ("✅ SEQUENCE COMPLETE!" , output )
36+
37+ def test_detailed_explanation (self ):
38+ """Test the 'y' prompt for step-by-step details."""
39+ output = self .run_script (["3" , "y" , "n" ])
40+ self .assertIn ("Detailed Steps:" , output )
41+ self .assertIn ("3 is odd" , output )
42+ self .assertIn ("= 10" , output )
43+
44+ def test_mathematical_power_of_two (self ):
45+ """Test a power of two (16), which should go straight to 1."""
46+ # 16 -> 8 -> 4 -> 2 -> 1 (4 steps)
47+ output = self .run_script (["16" , "n" , "n" ])
48+ self .assertIn ("Total steps: 4" , output )
49+ self .assertIn ("Highest number reached: 16" , output )
50+
51+
52+ def test_input_case_insensitivity (self ):
53+ """Ensure 'Y' and 'YES' work just as well as 'y'."""
54+ output = self .run_script (["3" , "YES" , "Y" , "2" , "N" , "N" ])
55+ self .assertIn ("Detailed Steps:" , output ) # Proves YES worked
56+ self .assertIn ("Starting with: 2" , output ) # Proves Y worked for 'again'
57+
58+ def test_recovery_from_multiple_bad_inputs (self ):
59+ """Test if the script can handle 3 errors in a row and still work."""
60+ output = self .run_script (["abc" , "-10" , "0" , "5" , "n" , "n" ])
61+ self .assertIn ("❌ Please enter a valid number!" , output )
62+ self .assertIn ("❌ Please enter a positive integer!" , output )
63+ self .assertIn ("Starting with: 5" , output ) # Proves it eventually succeeded
64+
65+ def test_complex_sequence_27 (self ):
66+ """Test number 27, known for its long path (111 steps)."""
67+ output = self .run_script (["27" , "n" , "n" ])
68+ self .assertIn ("Total steps: 111" , output )
69+ self .assertIn ("Highest number reached: 9232" , output )
70+
71+ def test_cache_logic_via_loop (self ):
72+ """Run the script twice to verify the loop and goodbye message work."""
73+ # 4 (no details, yes again), then 2 (no details, no again)
74+ output = self .run_script (["4" , "n" , "y" , "2" , "n" ])
75+ self .assertIn ("Starting with: 4" , output )
76+ self .assertIn ("Starting with: 2" , output )
77+ self .assertIn ("Goodbye!" , output )
78+
79+ def test_invalid_string_input (self ):
80+ """Ensure script handles non-integers without crashing."""
81+ output = self .run_script (["abc" , "1" , "n" ])
82+ self .assertIn ("❌ Please enter a valid number!" , output )
83+
84+ def test_zero_input_validation (self ):
85+ """Ensure script rejects zero or negative integers."""
86+ output = self .run_script (["0" , "1" , "n" ])
87+ self .assertIn ("❌ Please enter a positive integer!" , output )
88+
89+ def test_large_input_guardrail (self ):
90+ """Test the MAX_INPUT (10^12) constraint."""
91+ large_val = str (10 ** 12 + 1 )
92+ output = self .run_script ([large_val , "1" , "n" ])
93+ self .assertIn ("⚠️ Input too large!" , output )
94+
95+ if __name__ == '__main__' :
96+ unittest .main ()
0 commit comments