Skip to content

Commit c0f946c

Browse files
Add test
1 parent 84be8f6 commit c0f946c

File tree

1 file changed

+203
-0
lines changed

1 file changed

+203
-0
lines changed
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
"""
2+
Unit tests for DownloadManager utility functions.
3+
4+
Tests the network error detection and resume position helpers.
5+
"""
6+
7+
import unittest
8+
import os
9+
import sys
10+
11+
# Handle both CPython and MicroPython path handling
12+
try:
13+
# CPython has os.path
14+
from os.path import join, dirname
15+
except ImportError:
16+
# MicroPython doesn't have os.path, use string concatenation
17+
def join(*parts):
18+
return '/'.join(parts)
19+
def dirname(path):
20+
parts = path.split('/')
21+
return '/'.join(parts[:-1]) if len(parts) > 1 else '.'
22+
23+
# Add parent directory to path for imports
24+
sys.path.insert(0, join(dirname(__file__), '..', 'internal_filesystem', 'lib'))
25+
26+
# Import functions directly from the module file to avoid mpos.__init__ dependencies
27+
try:
28+
import importlib.util
29+
spec = importlib.util.spec_from_file_location(
30+
"download_manager",
31+
join(dirname(__file__), '..', 'internal_filesystem', 'lib', 'mpos', 'net', 'download_manager.py')
32+
)
33+
download_manager = importlib.util.module_from_spec(spec)
34+
spec.loader.exec_module(download_manager)
35+
except (ImportError, AttributeError):
36+
# MicroPython doesn't have importlib.util, import directly
37+
sys.path.insert(0, join(dirname(__file__), '..', 'internal_filesystem', 'lib', 'mpos', 'net'))
38+
import download_manager
39+
40+
is_network_error = download_manager.is_network_error
41+
get_resume_position = download_manager.get_resume_position
42+
43+
44+
class TestIsNetworkError(unittest.TestCase):
45+
"""Test network error detection utility."""
46+
47+
def test_detects_timeout_error_code(self):
48+
"""Should detect OSError with -110 (ETIMEDOUT) as network error."""
49+
error = OSError(-110, "Connection timed out")
50+
self.assertTrue(is_network_error(error))
51+
52+
def test_detects_connection_aborted_error_code(self):
53+
"""Should detect OSError with -113 (ECONNABORTED) as network error."""
54+
error = OSError(-113, "Connection aborted")
55+
self.assertTrue(is_network_error(error))
56+
57+
def test_detects_connection_reset_error_code(self):
58+
"""Should detect OSError with -104 (ECONNRESET) as network error."""
59+
error = OSError(-104, "Connection reset by peer")
60+
self.assertTrue(is_network_error(error))
61+
62+
def test_detects_host_unreachable_error_code(self):
63+
"""Should detect OSError with -118 (EHOSTUNREACH) as network error."""
64+
error = OSError(-118, "No route to host")
65+
self.assertTrue(is_network_error(error))
66+
67+
def test_detects_dns_error_code(self):
68+
"""Should detect OSError with -202 (DNS/connection error) as network error."""
69+
error = OSError(-202, "DNS lookup failed")
70+
self.assertTrue(is_network_error(error))
71+
72+
def test_detects_connection_reset_message(self):
73+
"""Should detect 'connection reset' in error message."""
74+
error = Exception("Connection reset by peer")
75+
self.assertTrue(is_network_error(error))
76+
77+
def test_detects_connection_aborted_message(self):
78+
"""Should detect 'connection aborted' in error message."""
79+
error = Exception("Connection aborted")
80+
self.assertTrue(is_network_error(error))
81+
82+
def test_detects_broken_pipe_message(self):
83+
"""Should detect 'broken pipe' in error message."""
84+
error = Exception("Broken pipe")
85+
self.assertTrue(is_network_error(error))
86+
87+
def test_detects_network_unreachable_message(self):
88+
"""Should detect 'network unreachable' in error message."""
89+
error = Exception("Network unreachable")
90+
self.assertTrue(is_network_error(error))
91+
92+
def test_detects_failed_to_download_chunk_message(self):
93+
"""Should detect 'failed to download chunk' message from download_manager."""
94+
error = OSError(-110, "Failed to download chunk after retries")
95+
self.assertTrue(is_network_error(error))
96+
97+
def test_rejects_value_error(self):
98+
"""Should not detect ValueError as network error."""
99+
error = ValueError("Invalid value")
100+
self.assertFalse(is_network_error(error))
101+
102+
def test_rejects_http_404_error(self):
103+
"""Should not detect HTTP 404 as network error."""
104+
error = RuntimeError("HTTP 404")
105+
self.assertFalse(is_network_error(error))
106+
107+
def test_rejects_file_not_found_error(self):
108+
"""Should not detect ENOENT (-2) as network error."""
109+
error = OSError(-2, "No such file or directory")
110+
self.assertFalse(is_network_error(error))
111+
112+
def test_rejects_permission_error(self):
113+
"""Should not detect permission errors as network error."""
114+
error = OSError(-13, "Permission denied")
115+
self.assertFalse(is_network_error(error))
116+
117+
def test_case_insensitive_detection(self):
118+
"""Should detect network errors regardless of case."""
119+
error1 = Exception("CONNECTION RESET")
120+
error2 = Exception("connection reset")
121+
error3 = Exception("Connection Reset")
122+
self.assertTrue(is_network_error(error1))
123+
self.assertTrue(is_network_error(error2))
124+
self.assertTrue(is_network_error(error3))
125+
126+
127+
class TestGetResumePosition(unittest.TestCase):
128+
"""Test resume position utility."""
129+
130+
def setUp(self):
131+
"""Create test directory."""
132+
self.test_dir = "tmp/test_download_manager"
133+
# Handle both CPython and MicroPython
134+
try:
135+
os.makedirs(self.test_dir, exist_ok=True)
136+
except (AttributeError, TypeError):
137+
# MicroPython doesn't have makedirs or exist_ok parameter
138+
try:
139+
os.mkdir(self.test_dir)
140+
except OSError:
141+
pass # Directory already exists
142+
143+
def tearDown(self):
144+
"""Clean up test files."""
145+
# Handle both CPython and MicroPython
146+
try:
147+
import shutil
148+
if os.path.exists(self.test_dir):
149+
shutil.rmtree(self.test_dir)
150+
except (ImportError, AttributeError):
151+
# MicroPython doesn't have shutil, manually remove files
152+
try:
153+
import os as os_module
154+
for f in os_module.listdir(self.test_dir):
155+
os_module.remove(join(self.test_dir, f))
156+
os_module.rmdir(self.test_dir)
157+
except (OSError, AttributeError):
158+
pass # Ignore errors during cleanup
159+
160+
def test_returns_zero_for_nonexistent_file(self):
161+
"""Should return 0 for files that don't exist."""
162+
nonexistent = join(self.test_dir, "nonexistent.bin")
163+
self.assertEqual(get_resume_position(nonexistent), 0)
164+
165+
def test_returns_file_size_for_existing_file(self):
166+
"""Should return file size for existing files."""
167+
test_file = join(self.test_dir, "test.bin")
168+
test_data = b"x" * 1024
169+
with open(test_file, "wb") as f:
170+
f.write(test_data)
171+
172+
self.assertEqual(get_resume_position(test_file), 1024)
173+
174+
def test_returns_zero_for_empty_file(self):
175+
"""Should return 0 for empty files."""
176+
test_file = join(self.test_dir, "empty.bin")
177+
with open(test_file, "wb") as f:
178+
pass # Create empty file
179+
180+
self.assertEqual(get_resume_position(test_file), 0)
181+
182+
def test_returns_correct_size_for_large_file(self):
183+
"""Should return correct size for larger files."""
184+
test_file = join(self.test_dir, "large.bin")
185+
test_data = b"x" * (1024 * 1024) # 1 MB (reduced from 10 MB to avoid memory issues)
186+
with open(test_file, "wb") as f:
187+
f.write(test_data)
188+
189+
self.assertEqual(get_resume_position(test_file), 1024 * 1024)
190+
191+
def test_returns_size_after_partial_write(self):
192+
"""Should return current size after partial write."""
193+
test_file = join(self.test_dir, "partial.bin")
194+
195+
# Write 1KB
196+
with open(test_file, "wb") as f:
197+
f.write(b"x" * 1024)
198+
self.assertEqual(get_resume_position(test_file), 1024)
199+
200+
# Append another 1KB
201+
with open(test_file, "ab") as f:
202+
f.write(b"y" * 1024)
203+
self.assertEqual(get_resume_position(test_file), 2048)

0 commit comments

Comments
 (0)