|
5 | 5 | import asyncio |
6 | 6 | import logging |
7 | 7 | import time |
| 8 | +from collections.abc import Coroutine |
8 | 9 | from ipaddress import ip_address |
| 10 | +from typing import Any |
9 | 11 |
|
10 | 12 | import pytest |
11 | | -from ping_rs import ping_multiple_async |
| 13 | +from ping_rs import AsyncPingStream, PingResult, ping_multiple_async, ping_once_async |
12 | 14 | from ping_rs.core_schema import TargetType |
13 | 15 |
|
14 | 16 | logger = logging.getLogger(__name__) |
@@ -164,6 +166,245 @@ async def test_error_handling(): |
164 | 166 | assert failure_count == 1 |
165 | 167 |
|
166 | 168 |
|
| 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 | + |
167 | 408 | if __name__ == "__main__": |
168 | 409 | # 可以直接运行此文件进行测试 |
169 | 410 | _ = pytest.main(["-xvs", __file__]) |
0 commit comments