Skip to content

Commit 74df052

Browse files
committed
test: 新增异步ping模块错误处理及性能压力测试
新增异步ping针对无效目标和并发错误处理的测试用例;增加异步流性能、内存使用、背压、综合压力及性能基准测试,完善测试覆盖和健壮性验证;调整ping_multiple_async超时测试,增加慢响应超时场景测试。
1 parent 1a42ece commit 74df052

File tree

3 files changed

+357
-6
lines changed

3 files changed

+357
-6
lines changed

tests/test_basic.py

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
基本功能测试
33
"""
44

5+
import asyncio
56
import logging
67
import time
78

89
import pytest
9-
from ping_rs import PingResult, create_ping_stream, ping_multiple, ping_once
10+
from ping_rs import PingResult, create_ping_stream, ping_multiple, ping_once, ping_once_async
1011
from ping_rs.core_schema import TargetType
1112

1213
logger = logging.getLogger(__name__)
@@ -108,6 +109,84 @@ def test_ping_timeout(target: TargetType):
108109
logger.info(f"正常超时测试结果: {result}")
109110

110111

112+
# ============================================================================
113+
# 错误处理测试
114+
# ============================================================================
115+
116+
117+
@pytest.mark.asyncio
118+
async def test_error_handling_invalid_target():
119+
"""
120+
测试错误处理机制
121+
122+
验证:无效目标是否能正确处理,不会导致程序崩溃
123+
"""
124+
# 使用各种无效的目标
125+
invalid_targets = [
126+
"invalid.host.that.does.not.exist.example.com",
127+
"999.999.999.999", # 无效 IP
128+
"", # 空字符串
129+
]
130+
131+
for target in invalid_targets:
132+
try:
133+
result = await ping_once_async(target, timeout_ms=1000)
134+
135+
# 应该返回错误结果,而不是抛出异常
136+
logger.info(f"无效目标 '{target}' 结果: {result.type_name}")
137+
138+
# 验证是失败或退出状态
139+
assert result.is_exited() or result.is_timeout() or result.is_unknown()
140+
141+
except Exception as e:
142+
# 如果抛出异常,记录但不失败测试
143+
logger.warning(f"目标 '{target}' 抛出异常: {type(e).__name__}: {e}")
144+
145+
146+
@pytest.mark.asyncio
147+
async def test_concurrent_error_handling():
148+
"""
149+
测试并发场景下的错误处理
150+
151+
验证:多个任务同时失败时,错误处理是否正常
152+
"""
153+
# 混合有效和无效的目标
154+
targets = [
155+
"127.0.0.1", # 有效
156+
"invalid.host.example.com", # 无效
157+
"localhost", # 有效
158+
"999.999.999.999", # 无效
159+
"::1", # 有效(IPv6)
160+
]
161+
162+
tasks = [ping_once_async(target, timeout_ms=2000) for target in targets]
163+
164+
results = await asyncio.gather(*tasks, return_exceptions=True)
165+
166+
# 统计结果
167+
success_count = 0
168+
error_count = 0
169+
exception_count = 0
170+
171+
for i, result in enumerate(results):
172+
if isinstance(result, BaseException):
173+
exception_count += 1
174+
logger.warning(f"目标 {targets[i]} 抛出异常: {type(result).__name__}")
175+
elif result.is_success():
176+
success_count += 1
177+
else:
178+
error_count += 1
179+
180+
logger.info(f"并发错误处理: 成功={success_count}, 失败={error_count}, 异常={exception_count}")
181+
182+
# 验证至少有一些成功的
183+
assert success_count >= 2, "应该有至少 2 个成功的 ping"
184+
185+
# 如果有异常,说明错误处理不够完善
186+
if exception_count > 0:
187+
logger.warning(f"⚠️ 有 {exception_count} 个任务抛出异常,错误处理可能不够完善")
188+
189+
111190
if __name__ == "__main__":
112191
# 可以直接运行此文件进行测试
113192
_ = pytest.main(["-xvs", __file__])

tests/test_concurrent.py

Lines changed: 242 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
import asyncio
66
import logging
77
import time
8+
from collections.abc import Coroutine
89
from ipaddress import ip_address
10+
from typing import Any
911

1012
import pytest
11-
from ping_rs import ping_multiple_async
13+
from ping_rs import AsyncPingStream, PingResult, ping_multiple_async, ping_once_async
1214
from ping_rs.core_schema import TargetType
1315

1416
logger = logging.getLogger(__name__)
@@ -164,6 +166,245 @@ async def test_error_handling():
164166
assert failure_count == 1
165167

166168

169+
# ============================================================================
170+
# 性能和压力测试
171+
# ============================================================================
172+
173+
174+
@pytest.mark.asyncio
175+
async def test_async_stream_performance() -> None:
176+
"""
177+
测试异步流的性能
178+
179+
验证:异步流能否高效处理大量结果
180+
"""
181+
target = "127.0.0.1"
182+
max_count = 100
183+
184+
stream = AsyncPingStream(target, interval_ms=100, max_count=max_count)
185+
186+
start_time = time.time()
187+
results: list[PingResult] = []
188+
189+
async for result in stream:
190+
results.append(result)
191+
192+
elapsed = time.time() - start_time
193+
194+
# 验证结果
195+
assert len(results) == max_count
196+
197+
logger.info(f"异步流测试: {len(results)} 个结果, 耗时={elapsed:.2f}s")
198+
199+
# 性能断言
200+
if elapsed < 15: # 100 * 0.1s = 10s + 5s 缓冲
201+
logger.info("✅ 异步流性能良好")
202+
else:
203+
logger.warning(f"⚠️ 异步流性能较差 ({elapsed:.2f}s)")
204+
205+
206+
# ============================================================================
207+
# 内存和背压测试
208+
# ============================================================================
209+
210+
211+
@pytest.mark.asyncio
212+
async def test_memory_usage_unbounded_channel():
213+
"""
214+
测试无界通道的内存使用
215+
216+
验证:无界通道在背压场景下是否会导致内存溢出
217+
注意:这个测试只能间接验证,真正的内存测试需要监控工具
218+
"""
219+
target = "127.0.0.1"
220+
221+
# 创建多个长时间运行的流 (interval_ms 最小为 100ms)
222+
streams = [AsyncPingStream(target, interval_ms=100, max_count=100) for _ in range(10)]
223+
224+
# 慢速消费
225+
async def slow_consumer(stream: AsyncPingStream):
226+
results: list[PingResult] = []
227+
count = 0
228+
async for result in stream:
229+
results.append(result)
230+
count += 1
231+
if count % 100 == 0:
232+
await asyncio.sleep(0.1) # 模拟慢速消费
233+
return results
234+
235+
start_time = time.time()
236+
237+
# 并发运行所有流
238+
tasks = [slow_consumer(stream) for stream in streams]
239+
results = await asyncio.gather(*tasks)
240+
241+
elapsed = time.time() - start_time
242+
243+
# 验证结果
244+
total_results = sum(len(r) for r in results)
245+
logger.info(f"内存测试: {total_results} 个结果, 耗时={elapsed:.2f}s")
246+
247+
# 应该能正常完成 (10个流 × 100个结果 = 1000)
248+
assert total_results == 1000, f"结果数量不对: {total_results}"
249+
250+
logger.info("✅ 内存测试通过,无界通道能正常处理")
251+
252+
253+
@pytest.mark.asyncio
254+
async def test_backpressure_handling() -> None:
255+
"""
256+
测试背压处理
257+
258+
验证:生产速度 > 消费速度时,无界通道是否会导致内存问题
259+
预期:应该能正常处理,但可能会有内存增长
260+
"""
261+
target = "127.0.0.1"
262+
263+
# 创建一个快速生产的流 (interval_ms 最小为 100ms)
264+
stream = AsyncPingStream(target, interval_ms=100, max_count=100)
265+
266+
results: list[PingResult] = []
267+
start_time = time.time()
268+
269+
# 慢速消费
270+
async for result in stream:
271+
results.append(result)
272+
if len(results) % 50 == 0:
273+
await asyncio.sleep(0.5) # 每 50 个结果暂停一下
274+
275+
elapsed = time.time() - start_time
276+
277+
logger.info(f"背压测试: {len(results)} 个结果, 耗时={elapsed:.2f}s")
278+
279+
# 验证结果完整
280+
assert len(results) == 100
281+
282+
# 如果有背压,总时间应该受消费速度影响
283+
# 100 个结果,每 50 个暂停 0.5s,至少需要 1s (暂停时间) + 10s (ping时间)
284+
if elapsed >= 10.5:
285+
logger.info("✅ 背压机制可能正常工作")
286+
else:
287+
logger.warning(f"⚠️ 背压机制可能不完善 ({elapsed:.2f}s)")
288+
289+
290+
# ============================================================================
291+
# 综合压力测试
292+
# ============================================================================
293+
294+
295+
@pytest.mark.asyncio
296+
async def test_comprehensive_stress_test() -> None:
297+
"""
298+
综合压力测试
299+
300+
测试多种操作在高压力场景下的表现
301+
"""
302+
target = "127.0.0.1"
303+
304+
# 创建多种类型的任务
305+
tasks: list[Coroutine[Any, Any, PingResult] | Coroutine[Any, Any, list[PingResult]]] = [] # pyright: ignore[reportExplicitAny]
306+
307+
# 1. 多个 ping_once
308+
tasks.extend([ping_once_async(target, timeout_ms=5000) for _ in range(50)])
309+
310+
# 2. 多个 ping_multiple
311+
tasks.extend([ping_multiple_async(target, count=20, interval_ms=100) for _ in range(10)])
312+
313+
# 3. 异步流
314+
async def consume_stream() -> list[PingResult]:
315+
stream = AsyncPingStream(target, interval_ms=100, max_count=30)
316+
results: list[PingResult] = []
317+
async for result in stream:
318+
results.append(result)
319+
return results
320+
321+
tasks.extend([consume_stream() for _ in range(5)])
322+
323+
start_time = time.time()
324+
325+
# 执行所有任务
326+
results = await asyncio.gather(*tasks, return_exceptions=True)
327+
328+
elapsed = time.time() - start_time
329+
330+
# 统计结果
331+
success_count = sum(1 for r in results if not isinstance(r, Exception))
332+
error_count = sum(1 for r in results if isinstance(r, Exception))
333+
334+
logger.info(f"综合压力测试: 成功={success_count}, 失败={error_count}, 耗时={elapsed:.2f}s")
335+
336+
# 验证大部分任务成功
337+
assert success_count > 60, f"成功率过低: {success_count}/{len(tasks)}"
338+
339+
# 性能评估
340+
if elapsed < 30:
341+
logger.info("✅ 综合性能良好")
342+
else:
343+
logger.warning(f"⚠️ 综合性能较差 ({elapsed:.2f}s)")
344+
345+
346+
# ============================================================================
347+
# 性能基准测试
348+
# ============================================================================
349+
350+
351+
@pytest.mark.asyncio
352+
async def test_performance_baseline() -> None:
353+
"""
354+
性能基准测试
355+
356+
用于对比不同场景下的性能差异
357+
358+
注意: 使用 time.perf_counter() 而不是 time.time() 以获得更高精度
359+
"""
360+
target = "127.0.0.1"
361+
362+
# 测试 1: 单个 ping 延迟 (使用高精度计时器)
363+
start = time.perf_counter()
364+
_ = [await ping_once_async(target, timeout_ms=5000) for _ in range(100)]
365+
single_ping_latency = (time.perf_counter() - start) * 1000 / 100 # 转换为毫秒
366+
367+
# 测试 2: 100 个 ping 的总时间
368+
start = time.perf_counter()
369+
_ = await ping_multiple_async(target, count=100, interval_ms=100)
370+
multiple_ping_time = time.perf_counter() - start
371+
372+
# 测试 3: 并发性能
373+
start = time.perf_counter()
374+
_ = await asyncio.gather(*[ping_once_async(target, timeout_ms=5000) for _ in range(100)])
375+
concurrent_time = time.perf_counter() - start
376+
377+
# 输出基准数据
378+
logger.info("=" * 60)
379+
logger.info("性能基准测试结果:")
380+
logger.info(f" 单个 ping 延迟: {single_ping_latency:.2f}ms")
381+
logger.info(f" 100 个 ping 总时间: {multiple_ping_time:.2f}s")
382+
logger.info(f" 100 个并发 ping 时间: {concurrent_time:.2f}s")
383+
logger.info("=" * 60)
384+
385+
# 性能评估
386+
if single_ping_latency < 1.0:
387+
logger.info("✅ 单个 ping 延迟优秀 (<1ms)")
388+
elif single_ping_latency < 5.0:
389+
logger.info("⚠️ 单个 ping 延迟一般 (1-5ms)")
390+
else:
391+
logger.warning(f"❌ 单个 ping 延迟较差 ({single_ping_latency:.2f}ms)")
392+
393+
if multiple_ping_time < 15:
394+
logger.info("✅ 批量 ping 性能优秀 (<15s)")
395+
elif multiple_ping_time < 30:
396+
logger.info("⚠️ 批量 ping 性能一般 (15-30s)")
397+
else:
398+
logger.warning(f"❌ 批量 ping 性能较差 ({multiple_ping_time:.2f}s)")
399+
400+
if concurrent_time < 5:
401+
logger.info("✅ 并发性能优秀 (<5s)")
402+
elif concurrent_time < 15:
403+
logger.info("⚠️ 并发性能一般 (5-15s)")
404+
else:
405+
logger.warning(f"❌ 并发性能较差 ({concurrent_time:.2f}s)")
406+
407+
167408
if __name__ == "__main__":
168409
# 可以直接运行此文件进行测试
169410
_ = pytest.main(["-xvs", __file__])

0 commit comments

Comments
 (0)