scalebox-sdk 0.1.11__py3-none-any.whl → 0.1.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. scalebox/__init__.py +1 -1
  2. scalebox/code_interpreter/code_interpreter_async.py +12 -12
  3. scalebox/code_interpreter/code_interpreter_sync.py +11 -11
  4. scalebox/generated/api_pb2_connect.py +3 -3
  5. scalebox/sandbox_sync/main.py +1 -1
  6. scalebox/test/aclient.py +72 -72
  7. scalebox/test/code_interpreter_centext.py +21 -21
  8. scalebox/test/code_interpreter_centext_sync.py +21 -21
  9. scalebox/test/code_interpreter_test.py +34 -34
  10. scalebox/test/code_interpreter_test_sync.py +34 -34
  11. scalebox/test/run_all_validation_tests.py +334 -334
  12. scalebox/test/test_basic.py +78 -78
  13. scalebox/test/test_code_interpreter_async_comprehensive.py +2653 -2653
  14. scalebox/test/test_code_interpreter_e2basync_comprehensive.py +2655 -2655
  15. scalebox/test/test_code_interpreter_e2bsync_comprehensive.py +3416 -3416
  16. scalebox/test/test_code_interpreter_execcode.py +3352 -0
  17. scalebox/test/test_code_interpreter_sync_comprehensive.py +3416 -3412
  18. scalebox/test/test_csx_desktop_examples.py +130 -0
  19. scalebox/test/test_e2b_first.py +11 -11
  20. scalebox/test/test_sandbox_async_comprehensive.py +736 -738
  21. scalebox/test/test_sandbox_stress_and_edge_cases.py +778 -778
  22. scalebox/test/test_sandbox_sync_comprehensive.py +779 -770
  23. scalebox/test/test_sandbox_usage_examples.py +987 -987
  24. scalebox/test/testacreate.py +24 -24
  25. scalebox/test/testagetinfo.py +18 -18
  26. scalebox/test/testcodeinterpreter_async.py +508 -508
  27. scalebox/test/testcodeinterpreter_sync.py +239 -239
  28. scalebox/test/testcomputeuse.py +245 -243
  29. scalebox/test/testnovnc.py +12 -12
  30. scalebox/test/testsandbox_async.py +202 -118
  31. scalebox/test/testsandbox_sync.py +71 -38
  32. scalebox/version.py +2 -2
  33. {scalebox_sdk-0.1.11.dist-info → scalebox_sdk-0.1.13.dist-info}/METADATA +1 -1
  34. {scalebox_sdk-0.1.11.dist-info → scalebox_sdk-0.1.13.dist-info}/RECORD +38 -36
  35. {scalebox_sdk-0.1.11.dist-info → scalebox_sdk-0.1.13.dist-info}/WHEEL +0 -0
  36. {scalebox_sdk-0.1.11.dist-info → scalebox_sdk-0.1.13.dist-info}/entry_points.txt +0 -0
  37. {scalebox_sdk-0.1.11.dist-info → scalebox_sdk-0.1.13.dist-info}/licenses/LICENSE +0 -0
  38. {scalebox_sdk-0.1.11.dist-info → scalebox_sdk-0.1.13.dist-info}/top_level.txt +0 -0
@@ -1,778 +1,778 @@
1
- #!/usr/bin/env python3
2
- """
3
- Comprehensive stress testing and edge case validation for both sandbox modules.
4
-
5
- This test suite focuses on:
6
- - Stress testing under high load
7
- - Edge cases and boundary conditions
8
- - Error recovery and resilience
9
- - Resource management
10
- - Concurrent operations
11
- - Large data handling
12
- - Network failure simulation
13
- """
14
-
15
- import asyncio
16
- import concurrent.futures
17
- import datetime
18
- import logging
19
- import os
20
- import random
21
- import string
22
- import threading
23
- import time
24
- from typing import List, Optional
25
-
26
- from scalebox.exceptions import SandboxException
27
- from scalebox.sandbox.commands.command_handle import PtySize
28
- from scalebox.sandbox_async.main import AsyncSandbox
29
- from scalebox.sandbox_sync.main import Sandbox
30
-
31
- # 配置日志
32
- logging.basicConfig(
33
- level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
34
- )
35
- logger = logging.getLogger(__name__)
36
-
37
-
38
- class StressTestValidator:
39
- """Comprehensive stress testing and edge case validation."""
40
-
41
- def __init__(self):
42
- self.test_results = []
43
- self.failed_tests = []
44
-
45
- def log_test_result(
46
- self, test_name: str, success: bool, message: str = "", duration: float = 0
47
- ):
48
- """记录测试结果"""
49
- status = "✅ PASS" if success else "❌ FAIL"
50
- result = {
51
- "test": test_name,
52
- "success": success,
53
- "message": message,
54
- "duration": duration,
55
- }
56
- self.test_results.append(result)
57
-
58
- if not success:
59
- self.failed_tests.append(test_name)
60
-
61
- logger.info(f"{status} {test_name} ({duration:.3f}s) {message}")
62
-
63
- def run_test(self, test_func, test_name: str, is_async: bool = False):
64
- """运行单个测试并记录结果"""
65
- start_time = time.time()
66
- try:
67
- if is_async:
68
- asyncio.run(test_func())
69
- else:
70
- test_func()
71
- duration = time.time() - start_time
72
- self.log_test_result(test_name, True, duration=duration)
73
- except Exception as e:
74
- duration = time.time() - start_time
75
- self.log_test_result(test_name, False, str(e), duration=duration)
76
-
77
- # ======================== 大数据处理测试 ========================
78
-
79
- def test_large_file_operations_sync(self):
80
- """测试同步版本大文件操作"""
81
- sandbox = Sandbox(debug=True)
82
-
83
- try:
84
- # 生成大内容(10MB)
85
- large_content = "A" * (10 * 1024 * 1024)
86
-
87
- # 写入大文件
88
- start_time = time.time()
89
- result = sandbox.files.write("/tmp/large_file.txt", large_content)
90
- write_duration = time.time() - start_time
91
-
92
- logger.info(f"写入10MB文件耗时: {write_duration:.3f}s")
93
-
94
- # 读取大文件
95
- start_time = time.time()
96
- read_content = sandbox.files.read("/tmp/large_file.txt")
97
- read_duration = time.time() - start_time
98
-
99
- logger.info(f"读取10MB文件耗时: {read_duration:.3f}s")
100
-
101
- assert len(read_content) == len(large_content)
102
- assert read_content[:100] == large_content[:100] # 验证开头部分
103
-
104
- # 清理
105
- sandbox.files.remove("/tmp/large_file.txt")
106
-
107
- finally:
108
- try:
109
- sandbox.kill()
110
- except:
111
- pass
112
-
113
- async def test_large_file_operations_async(self):
114
- """测试异步版本大文件操作"""
115
- sandbox = await AsyncSandbox.create(debug=True)
116
-
117
- try:
118
- # 生成大内容(10MB)
119
- large_content = "B" * (10 * 1024 * 1024)
120
-
121
- # 写入大文件
122
- start_time = time.time()
123
- result = await sandbox.files.write(
124
- "/tmp/large_file_async.txt", large_content
125
- )
126
- write_duration = time.time() - start_time
127
-
128
- logger.info(f"异步写入10MB文件耗时: {write_duration:.3f}s")
129
-
130
- # 读取大文件
131
- start_time = time.time()
132
- read_content = await sandbox.files.read("/tmp/large_file_async.txt")
133
- read_duration = time.time() - start_time
134
-
135
- logger.info(f"异步读取10MB文件耗时: {read_duration:.3f}s")
136
-
137
- assert len(read_content) == len(large_content)
138
- assert read_content[:100] == large_content[:100]
139
-
140
- # 清理
141
- await sandbox.files.remove("/tmp/large_file_async.txt")
142
-
143
- finally:
144
- try:
145
- await sandbox.kill()
146
- except:
147
- pass
148
-
149
- # ======================== 并发压力测试 ========================
150
-
151
- def test_concurrent_sync_operations(self):
152
- """测试同步版本并发操作"""
153
- sandbox = Sandbox(debug=True)
154
-
155
- try:
156
-
157
- def worker(worker_id: int) -> dict:
158
- """工作线程函数"""
159
- results = []
160
-
161
- for i in range(10):
162
- # 文件操作
163
- filename = f"/tmp/worker_{worker_id}_file_{i}.txt"
164
- content = f"Worker {worker_id}, File {i}, Content: {random.randint(1, 1000)}"
165
-
166
- sandbox.files.write(filename, content)
167
- read_content = sandbox.files.read(filename)
168
- assert read_content == content
169
-
170
- # 命令执行
171
- result = sandbox.commands.run(
172
- f"echo 'Worker {worker_id}, Command {i}'"
173
- )
174
- assert result.exit_code == 0
175
-
176
- results.append({"file": filename, "command_output": result.stdout})
177
-
178
- # 清理文件
179
- sandbox.files.remove(filename)
180
-
181
- return {"worker_id": worker_id, "results": results}
182
-
183
- # 启动并发工作线程
184
- start_time = time.time()
185
-
186
- with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
187
- futures = [executor.submit(worker, i) for i in range(5)]
188
- results = [
189
- future.result()
190
- for future in concurrent.futures.as_completed(futures)
191
- ]
192
-
193
- duration = time.time() - start_time
194
-
195
- logger.info(f"5个并发工作线程完成,耗时: {duration:.3f}s")
196
- logger.info(f"总计处理: {len(results) * 10} 个文件操作和命令执行")
197
-
198
- assert len(results) == 5
199
-
200
- finally:
201
- try:
202
- sandbox.kill()
203
- except:
204
- pass
205
-
206
- async def test_concurrent_async_operations(self):
207
- """测试异步版本并发操作"""
208
- sandbox = await AsyncSandbox.create(debug=True)
209
-
210
- try:
211
-
212
- async def worker(worker_id: int) -> dict:
213
- """异步工作协程"""
214
- results = []
215
-
216
- for i in range(10):
217
- # 文件操作
218
- filename = f"/tmp/async_worker_{worker_id}_file_{i}.txt"
219
- content = f"Async Worker {worker_id}, File {i}, Content: {random.randint(1, 1000)}"
220
-
221
- await sandbox.files.write(filename, content)
222
- read_content = await sandbox.files.read(filename)
223
- assert read_content == content
224
-
225
- # 命令执行
226
- result = await sandbox.commands.run(
227
- f"echo 'Async Worker {worker_id}, Command {i}'"
228
- )
229
- assert result.exit_code == 0
230
-
231
- results.append({"file": filename, "command_output": result.stdout})
232
-
233
- # 清理文件
234
- await sandbox.files.remove(filename)
235
-
236
- return {"worker_id": worker_id, "results": results}
237
-
238
- # 启动并发协程
239
- start_time = time.time()
240
-
241
- tasks = [worker(i) for i in range(10)] # 异步可以支持更多并发
242
- results = await asyncio.gather(*tasks)
243
-
244
- duration = time.time() - start_time
245
-
246
- logger.info(f"10个并发协程完成,耗时: {duration:.3f}s")
247
- logger.info(f"总计处理: {len(results) * 10} 个异步文件操作和命令执行")
248
-
249
- assert len(results) == 10
250
-
251
- finally:
252
- try:
253
- await sandbox.kill()
254
- except:
255
- pass
256
-
257
- # ======================== 内存压力测试 ========================
258
-
259
- def test_memory_stress_sync(self):
260
- """测试同步版本内存压力"""
261
- sandbox = Sandbox(debug=True)
262
-
263
- try:
264
- # 创建大量小文件
265
- num_files = 1000
266
- file_paths = []
267
-
268
- start_time = time.time()
269
-
270
- for i in range(num_files):
271
- filename = f"/tmp/memory_test_{i}.txt"
272
- content = f"File {i}: " + "x" * 100 # 100字符内容
273
- sandbox.files.write(filename, content)
274
- file_paths.append(filename)
275
-
276
- create_duration = time.time() - start_time
277
- logger.info(f"创建 {num_files} 个文件耗时: {create_duration:.3f}s")
278
-
279
- # 读取所有文件
280
- start_time = time.time()
281
-
282
- for filename in file_paths:
283
- content = sandbox.files.read(filename)
284
- assert len(content) > 100
285
-
286
- read_duration = time.time() - start_time
287
- logger.info(f"读取 {num_files} 个文件耗时: {read_duration:.3f}s")
288
-
289
- # 清理所有文件
290
- start_time = time.time()
291
-
292
- for filename in file_paths:
293
- sandbox.files.remove(filename)
294
-
295
- cleanup_duration = time.time() - start_time
296
- logger.info(f"清理 {num_files} 个文件耗时: {cleanup_duration:.3f}s")
297
-
298
- finally:
299
- try:
300
- sandbox.kill()
301
- except:
302
- pass
303
-
304
- async def test_memory_stress_async(self):
305
- """测试异步版本内存压力"""
306
- sandbox = await AsyncSandbox.create(debug=True)
307
-
308
- try:
309
- # 创建大量小文件(异步批量操作)
310
- num_files = 1000
311
-
312
- start_time = time.time()
313
-
314
- # 准备文件数据
315
- files_data = [
316
- {
317
- "path": f"/tmp/async_memory_test_{i}.txt",
318
- "data": f"Async File {i}: " + "y" * 100,
319
- }
320
- for i in range(num_files)
321
- ]
322
-
323
- # 批量写入(分批处理避免内存过大)
324
- batch_size = 100
325
- for i in range(0, num_files, batch_size):
326
- batch = files_data[i : i + batch_size]
327
- await sandbox.files.write(batch)
328
-
329
- create_duration = time.time() - start_time
330
- logger.info(f"异步创建 {num_files} 个文件耗时: {create_duration:.3f}s")
331
-
332
- # 异步读取所有文件
333
- start_time = time.time()
334
-
335
- async def read_file(filename):
336
- return await sandbox.files.read(filename)
337
-
338
- # 分批并发读取
339
- batch_size = 50
340
- for i in range(0, num_files, batch_size):
341
- tasks = []
342
- for j in range(i, min(i + batch_size, num_files)):
343
- filename = f"/tmp/async_memory_test_{j}.txt"
344
- tasks.append(read_file(filename))
345
-
346
- results = await asyncio.gather(*tasks)
347
- for result in results:
348
- assert len(result) > 100
349
-
350
- read_duration = time.time() - start_time
351
- logger.info(f"异步读取 {num_files} 个文件耗时: {read_duration:.3f}s")
352
-
353
- # 异步清理所有文件
354
- start_time = time.time()
355
-
356
- for i in range(num_files):
357
- filename = f"/tmp/async_memory_test_{i}.txt"
358
- await sandbox.files.remove(filename)
359
-
360
- cleanup_duration = time.time() - start_time
361
- logger.info(f"异步清理 {num_files} 个文件耗时: {cleanup_duration:.3f}s")
362
-
363
- finally:
364
- try:
365
- await sandbox.kill()
366
- except:
367
- pass
368
-
369
- # ======================== 边界条件测试 ========================
370
-
371
- def test_edge_cases_sync(self):
372
- """测试同步版本边界条件"""
373
- sandbox = Sandbox(debug=True)
374
-
375
- try:
376
- # 空文件测试
377
- sandbox.files.write("/tmp/empty.txt", "")
378
- content = sandbox.files.read("/tmp/empty.txt")
379
- assert content == ""
380
-
381
- # 特殊字符文件名测试
382
- special_names = [
383
- "/tmp/file with spaces.txt",
384
- "/tmp/file_with_中文.txt",
385
- "/tmp/file-with-special-!@#$%.txt",
386
- ]
387
-
388
- for filename in special_names:
389
- try:
390
- sandbox.files.write(filename, f"Content for {filename}")
391
- content = sandbox.files.read(filename)
392
- assert f"Content for {filename}" in content
393
- sandbox.files.remove(filename)
394
- except Exception as e:
395
- logger.warning(f"特殊文件名 {filename} 测试失败: {e}")
396
-
397
- # 深层目录测试
398
- deep_path = (
399
- "/tmp/" + "/".join([f"dir{i}" for i in range(10)]) + "/deep_file.txt"
400
- )
401
- sandbox.files.write(deep_path, "Deep directory content")
402
- content = sandbox.files.read(deep_path)
403
- assert content == "Deep directory content"
404
-
405
- # 长命令测试
406
- long_command = "echo " + "A" * 1000
407
- result = sandbox.commands.run(long_command)
408
- assert result.exit_code == 0
409
- assert len(result.stdout) > 1000
410
-
411
- # 零退出码和非零退出码测试
412
- result_success = sandbox.commands.run("exit 0")
413
- assert result_success.exit_code == 0
414
-
415
- result_fail = sandbox.commands.run("exit 42")
416
- assert result_fail.exit_code == 42
417
-
418
- logger.info("所有边界条件测试通过")
419
-
420
- finally:
421
- try:
422
- sandbox.kill()
423
- except:
424
- pass
425
-
426
- async def test_edge_cases_async(self):
427
- """测试异步版本边界条件"""
428
- sandbox = await AsyncSandbox.create(debug=True)
429
-
430
- try:
431
- # 空文件测试
432
- await sandbox.files.write("/tmp/async_empty.txt", "")
433
- content = await sandbox.files.read("/tmp/async_empty.txt")
434
- assert content == ""
435
-
436
- # 二进制数据测试
437
- binary_data = bytes(range(256)) # 0-255的字节
438
- await sandbox.files.write("/tmp/binary_test.bin", binary_data)
439
- read_binary = await sandbox.files.read(
440
- "/tmp/binary_test.bin", format="bytes"
441
- )
442
- assert bytes(read_binary) == binary_data
443
-
444
- # 流式读取大文件测试
445
- large_content = "Stream test content\n" * 10000
446
- await sandbox.files.write("/tmp/stream_test.txt", large_content)
447
-
448
- stream = await sandbox.files.read("/tmp/stream_test.txt", format="stream")
449
- chunks = []
450
- async for chunk in stream:
451
- chunks.append(chunk)
452
-
453
- reconstructed = b"".join(chunks).decode("utf-8")
454
- assert reconstructed == large_content
455
-
456
- # 超时测试
457
- try:
458
- result = await sandbox.commands.run("sleep 1", timeout=0.5)
459
- # 如果执行到这里,说明超时没有生效
460
- logger.warning("超时测试可能没有生效")
461
- except Exception as e:
462
- logger.info(f"超时测试正确触发: {type(e).__name__}")
463
-
464
- logger.info("所有异步边界条件测试通过")
465
-
466
- finally:
467
- try:
468
- await sandbox.kill()
469
- except:
470
- pass
471
-
472
- # ======================== 错误恢复测试 ========================
473
-
474
- def test_error_recovery_sync(self):
475
- """测试同步版本错误恢复"""
476
- sandbox = Sandbox(debug=True)
477
-
478
- try:
479
- # 测试从文件操作错误中恢复
480
- error_count = 0
481
-
482
- # 尝试不存在的操作
483
- for i in range(5):
484
- try:
485
- sandbox.files.read(f"/nonexistent/file_{i}.txt")
486
- except Exception:
487
- error_count += 1
488
-
489
- # 错误后继续正常操作
490
- sandbox.files.write(f"/tmp/recovery_test_{i}.txt", f"Recovery test {i}")
491
- content = sandbox.files.read(f"/tmp/recovery_test_{i}.txt")
492
- assert content == f"Recovery test {i}"
493
- sandbox.files.remove(f"/tmp/recovery_test_{i}.txt")
494
-
495
- assert error_count == 5, "应该捕获5个错误"
496
-
497
- # 测试从命令错误中恢复
498
- error_count = 0
499
-
500
- for i in range(5):
501
- # 执行失败的命令
502
- result = sandbox.commands.run("ls /nonexistent_dir_12345")
503
- if result.exit_code != 0:
504
- error_count += 1
505
-
506
- # 错误后继续正常命令
507
- result = sandbox.commands.run(f"echo 'Command recovery test {i}'")
508
- assert result.exit_code == 0
509
- assert f"Command recovery test {i}" in result.stdout
510
-
511
- assert error_count == 5, "应该有5个失败的命令"
512
- logger.info("错误恢复测试通过")
513
-
514
- finally:
515
- try:
516
- sandbox.kill()
517
- except:
518
- pass
519
-
520
- async def test_error_recovery_async(self):
521
- """测试异步版本错误恢复"""
522
- sandbox = await AsyncSandbox.create(debug=True)
523
-
524
- try:
525
- # 测试异步错误恢复
526
- error_count = 0
527
-
528
- for i in range(5):
529
- try:
530
- await sandbox.files.read(f"/nonexistent/async_file_{i}.txt")
531
- except Exception:
532
- error_count += 1
533
-
534
- # 错误后继续正常操作
535
- await sandbox.files.write(
536
- f"/tmp/async_recovery_test_{i}.txt", f"Async recovery test {i}"
537
- )
538
- content = await sandbox.files.read(f"/tmp/async_recovery_test_{i}.txt")
539
- assert content == f"Async recovery test {i}"
540
- await sandbox.files.remove(f"/tmp/async_recovery_test_{i}.txt")
541
-
542
- assert error_count == 5
543
-
544
- # 并发错误恢复测试
545
- async def error_and_recovery(index):
546
- try:
547
- await sandbox.files.read(f"/nonexistent/concurrent_{index}.txt")
548
- except Exception:
549
- pass
550
-
551
- # 正常操作
552
- await sandbox.files.write(
553
- f"/tmp/concurrent_recovery_{index}.txt", f"Concurrent {index}"
554
- )
555
- content = await sandbox.files.read(
556
- f"/tmp/concurrent_recovery_{index}.txt"
557
- )
558
- assert content == f"Concurrent {index}"
559
- await sandbox.files.remove(f"/tmp/concurrent_recovery_{index}.txt")
560
- return True
561
-
562
- # 并发执行错误恢复
563
- tasks = [error_and_recovery(i) for i in range(10)]
564
- results = await asyncio.gather(*tasks)
565
-
566
- assert all(results), "所有并发错误恢复都应该成功"
567
- logger.info("异步错误恢复测试通过")
568
-
569
- finally:
570
- try:
571
- await sandbox.kill()
572
- except:
573
- pass
574
-
575
- # ======================== 资源管理测试 ========================
576
-
577
- def test_resource_management_sync(self):
578
- """测试同步版本资源管理"""
579
- sandboxes = []
580
-
581
- try:
582
- # 创建多个沙箱实例
583
- for i in range(3):
584
- sandbox = Sandbox(debug=True)
585
- sandboxes.append(sandbox)
586
-
587
- # 在每个沙箱中执行操作
588
- result = sandbox.commands.run(f"echo 'Sandbox {i} test'")
589
- assert result.exit_code == 0
590
-
591
- sandbox.files.write(f"/tmp/resource_test_{i}.txt", f"Resource test {i}")
592
-
593
- logger.info(f"创建了 {len(sandboxes)} 个沙箱实例")
594
-
595
- # 验证每个沙箱都是独立的
596
- for i, sandbox in enumerate(sandboxes):
597
- content = sandbox.files.read(f"/tmp/resource_test_{i}.txt")
598
- assert content == f"Resource test {i}"
599
-
600
- # 验证其他沙箱的文件不存在
601
- for j in range(len(sandboxes)):
602
- if i != j:
603
- exists = sandbox.files.exists(f"/tmp/resource_test_{j}.txt")
604
- # 注意:在debug模式下,可能共享文件系统,所以这个测试可能不适用
605
-
606
- finally:
607
- # 清理所有沙箱
608
- for i, sandbox in enumerate(sandboxes):
609
- try:
610
- sandbox.kill()
611
- logger.info(f"清理沙箱 {i}")
612
- except Exception as e:
613
- logger.error(f"清理沙箱 {i} 失败: {e}")
614
-
615
- async def test_resource_management_async(self):
616
- """测试异步版本资源管理"""
617
- sandboxes = []
618
-
619
- try:
620
- # 并发创建多个沙箱实例
621
- create_tasks = [AsyncSandbox.create(debug=True) for _ in range(3)]
622
- sandboxes = await asyncio.gather(*create_tasks)
623
-
624
- # 在每个沙箱中并发执行操作
625
- async def setup_sandbox(sandbox, index):
626
- result = await sandbox.commands.run(
627
- f"echo 'Async Sandbox {index} test'"
628
- )
629
- assert result.exit_code == 0
630
-
631
- await sandbox.files.write(
632
- f"/tmp/async_resource_test_{index}.txt",
633
- f"Async resource test {index}",
634
- )
635
- return sandbox
636
-
637
- setup_tasks = [
638
- setup_sandbox(sandbox, i) for i, sandbox in enumerate(sandboxes)
639
- ]
640
- await asyncio.gather(*setup_tasks)
641
-
642
- logger.info(f"创建了 {len(sandboxes)} 个异步沙箱实例")
643
-
644
- # 并发验证每个沙箱
645
- async def verify_sandbox(sandbox, index):
646
- content = await sandbox.files.read(
647
- f"/tmp/async_resource_test_{index}.txt"
648
- )
649
- assert content == f"Async resource test {index}"
650
- return True
651
-
652
- verify_tasks = [
653
- verify_sandbox(sandbox, i) for i, sandbox in enumerate(sandboxes)
654
- ]
655
- results = await asyncio.gather(*verify_tasks)
656
-
657
- assert all(results), "所有沙箱验证都应该成功"
658
-
659
- finally:
660
- # 并发清理所有沙箱
661
- cleanup_tasks = []
662
- for i, sandbox in enumerate(sandboxes):
663
-
664
- async def cleanup_sandbox(sb, idx):
665
- try:
666
- await sb.kill()
667
- logger.info(f"清理异步沙箱 {idx}")
668
- except Exception as e:
669
- logger.error(f"清理异步沙箱 {idx} 失败: {e}")
670
-
671
- cleanup_tasks.append(cleanup_sandbox(sandbox, i))
672
-
673
- if cleanup_tasks:
674
- await asyncio.gather(*cleanup_tasks, return_exceptions=True)
675
-
676
- # ======================== 主测试执行器 ========================
677
-
678
- def run_all_tests(self):
679
- """运行所有压力测试和边界条件测试"""
680
- logger.info("开始压力测试和边界条件测试...")
681
-
682
- # 大数据处理测试
683
- self.run_test(
684
- self.test_large_file_operations_sync, "Large File Operations Sync"
685
- )
686
- self.run_test(
687
- self.test_large_file_operations_async,
688
- "Large File Operations Async",
689
- is_async=True,
690
- )
691
-
692
- # 并发压力测试
693
- self.run_test(
694
- self.test_concurrent_sync_operations, "Concurrent Sync Operations"
695
- )
696
- self.run_test(
697
- self.test_concurrent_async_operations,
698
- "Concurrent Async Operations",
699
- is_async=True,
700
- )
701
-
702
- # 内存压力测试
703
- self.run_test(self.test_memory_stress_sync, "Memory Stress Sync")
704
- self.run_test(
705
- self.test_memory_stress_async, "Memory Stress Async", is_async=True
706
- )
707
-
708
- # 边界条件测试
709
- self.run_test(self.test_edge_cases_sync, "Edge Cases Sync")
710
- self.run_test(self.test_edge_cases_async, "Edge Cases Async", is_async=True)
711
-
712
- # 错误恢复测试
713
- self.run_test(self.test_error_recovery_sync, "Error Recovery Sync")
714
- self.run_test(
715
- self.test_error_recovery_async, "Error Recovery Async", is_async=True
716
- )
717
-
718
- # 资源管理测试
719
- self.run_test(self.test_resource_management_sync, "Resource Management Sync")
720
- self.run_test(
721
- self.test_resource_management_async,
722
- "Resource Management Async",
723
- is_async=True,
724
- )
725
-
726
- def print_summary(self):
727
- """打印测试摘要"""
728
- total_tests = len(self.test_results)
729
- passed_tests = sum(1 for r in self.test_results if r["success"])
730
- failed_tests = total_tests - passed_tests
731
-
732
- total_duration = sum(r["duration"] for r in self.test_results)
733
-
734
- print("\n" + "=" * 70)
735
- print("沙箱压力测试和边界条件测试报告")
736
- print("=" * 70)
737
- print(f"总测试数: {total_tests}")
738
- print(f"通过数: {passed_tests}")
739
- print(f"失败数: {failed_tests}")
740
- print(f"总耗时: {total_duration:.3f}秒")
741
- print(f"成功率: {(passed_tests/total_tests*100):.1f}%")
742
-
743
- if self.failed_tests:
744
- print(f"\n失败的测试:")
745
- for test in self.failed_tests:
746
- print(f" ❌ {test}")
747
-
748
- # 性能统计
749
- sync_tests = [
750
- r for r in self.test_results if "Sync" in r["test"] and r["success"]
751
- ]
752
- async_tests = [
753
- r for r in self.test_results if "Async" in r["test"] and r["success"]
754
- ]
755
-
756
- if sync_tests:
757
- avg_sync_time = sum(r["duration"] for r in sync_tests) / len(sync_tests)
758
- print(f"\n同步版本平均测试时间: {avg_sync_time:.3f}秒")
759
-
760
- if async_tests:
761
- avg_async_time = sum(r["duration"] for r in async_tests) / len(async_tests)
762
- print(f"异步版本平均测试时间: {avg_async_time:.3f}秒")
763
-
764
- print("=" * 70)
765
-
766
-
767
- def main():
768
- """主函数"""
769
- validator = StressTestValidator()
770
-
771
- try:
772
- validator.run_all_tests()
773
- finally:
774
- validator.print_summary()
775
-
776
-
777
- if __name__ == "__main__":
778
- main()
1
+ #!/usr/bin/env python3
2
+ """
3
+ Comprehensive stress testing and edge case validation for both sandbox modules.
4
+
5
+ This test suite focuses on:
6
+ - Stress testing under high load
7
+ - Edge cases and boundary conditions
8
+ - Error recovery and resilience
9
+ - Resource management
10
+ - Concurrent operations
11
+ - Large data handling
12
+ - Network failure simulation
13
+ """
14
+
15
+ import asyncio
16
+ import concurrent.futures
17
+ import datetime
18
+ import logging
19
+ import os
20
+ import random
21
+ import string
22
+ import threading
23
+ import time
24
+ from typing import List, Optional
25
+
26
+ from scalebox.exceptions import SandboxException
27
+ from scalebox.sandbox.commands.command_handle import PtySize
28
+ from scalebox.sandbox_async.main import AsyncSandbox
29
+ from scalebox.sandbox_sync.main import Sandbox
30
+
31
+ # 配置日志
32
+ logging.basicConfig(
33
+ level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
34
+ )
35
+ logger = logging.getLogger(__name__)
36
+
37
+
38
+ class StressTestValidator:
39
+ """Comprehensive stress testing and edge case validation."""
40
+
41
+ def __init__(self):
42
+ self.test_results = []
43
+ self.failed_tests = []
44
+
45
+ def log_test_result(
46
+ self, test_name: str, success: bool, message: str = "", duration: float = 0
47
+ ):
48
+ """记录测试结果"""
49
+ status = "✅ PASS" if success else "❌ FAIL"
50
+ result = {
51
+ "test": test_name,
52
+ "success": success,
53
+ "message": message,
54
+ "duration": duration,
55
+ }
56
+ self.test_results.append(result)
57
+
58
+ if not success:
59
+ self.failed_tests.append(test_name)
60
+
61
+ logger.info(f"{status} {test_name} ({duration:.3f}s) {message}")
62
+
63
+ def run_test(self, test_func, test_name: str, is_async: bool = False):
64
+ """运行单个测试并记录结果"""
65
+ start_time = time.time()
66
+ try:
67
+ if is_async:
68
+ asyncio.run(test_func())
69
+ else:
70
+ test_func()
71
+ duration = time.time() - start_time
72
+ self.log_test_result(test_name, True, duration=duration)
73
+ except Exception as e:
74
+ duration = time.time() - start_time
75
+ self.log_test_result(test_name, False, str(e), duration=duration)
76
+
77
+ # ======================== 大数据处理测试 ========================
78
+
79
+ def test_large_file_operations_sync(self):
80
+ """测试同步版本大文件操作"""
81
+ sandbox = Sandbox(debug=True)
82
+
83
+ try:
84
+ # 生成大内容(10MB)
85
+ large_content = "A" * (10 * 1024 * 1024)
86
+
87
+ # 写入大文件
88
+ start_time = time.time()
89
+ result = sandbox.files.write("/tmp/large_file.txt", large_content)
90
+ write_duration = time.time() - start_time
91
+
92
+ logger.info(f"写入10MB文件耗时: {write_duration:.3f}s")
93
+
94
+ # 读取大文件
95
+ start_time = time.time()
96
+ read_content = sandbox.files.read("/tmp/large_file.txt")
97
+ read_duration = time.time() - start_time
98
+
99
+ logger.info(f"读取10MB文件耗时: {read_duration:.3f}s")
100
+
101
+ assert len(read_content) == len(large_content)
102
+ assert read_content[:100] == large_content[:100] # 验证开头部分
103
+
104
+ # 清理
105
+ sandbox.files.remove("/tmp/large_file.txt")
106
+
107
+ finally:
108
+ try:
109
+ sandbox.kill()
110
+ except:
111
+ pass
112
+
113
+ async def test_large_file_operations_async(self):
114
+ """测试异步版本大文件操作"""
115
+ sandbox = await AsyncSandbox.create(debug=True)
116
+
117
+ try:
118
+ # 生成大内容(10MB)
119
+ large_content = "B" * (10 * 1024 * 1024)
120
+
121
+ # 写入大文件
122
+ start_time = time.time()
123
+ result = await sandbox.files.write(
124
+ "/tmp/large_file_async.txt", large_content
125
+ )
126
+ write_duration = time.time() - start_time
127
+
128
+ logger.info(f"异步写入10MB文件耗时: {write_duration:.3f}s")
129
+
130
+ # 读取大文件
131
+ start_time = time.time()
132
+ read_content = await sandbox.files.read("/tmp/large_file_async.txt")
133
+ read_duration = time.time() - start_time
134
+
135
+ logger.info(f"异步读取10MB文件耗时: {read_duration:.3f}s")
136
+
137
+ assert len(read_content) == len(large_content)
138
+ assert read_content[:100] == large_content[:100]
139
+
140
+ # 清理
141
+ await sandbox.files.remove("/tmp/large_file_async.txt")
142
+
143
+ finally:
144
+ try:
145
+ await sandbox.kill()
146
+ except:
147
+ pass
148
+
149
+ # ======================== 并发压力测试 ========================
150
+
151
+ def test_concurrent_sync_operations(self):
152
+ """测试同步版本并发操作"""
153
+ sandbox = Sandbox(debug=True)
154
+
155
+ try:
156
+
157
+ def worker(worker_id: int) -> dict:
158
+ """工作线程函数"""
159
+ results = []
160
+
161
+ for i in range(10):
162
+ # 文件操作
163
+ filename = f"/tmp/worker_{worker_id}_file_{i}.txt"
164
+ content = f"Worker {worker_id}, File {i}, Content: {random.randint(1, 1000)}"
165
+
166
+ sandbox.files.write(filename, content)
167
+ read_content = sandbox.files.read(filename)
168
+ assert read_content == content
169
+
170
+ # 命令执行
171
+ result = sandbox.commands.run(
172
+ f"echo 'Worker {worker_id}, Command {i}'"
173
+ )
174
+ assert result.exit_code == 0
175
+
176
+ results.append({"file": filename, "command_output": result.stdout})
177
+
178
+ # 清理文件
179
+ sandbox.files.remove(filename)
180
+
181
+ return {"worker_id": worker_id, "results": results}
182
+
183
+ # 启动并发工作线程
184
+ start_time = time.time()
185
+
186
+ with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
187
+ futures = [executor.submit(worker, i) for i in range(5)]
188
+ results = [
189
+ future.result()
190
+ for future in concurrent.futures.as_completed(futures)
191
+ ]
192
+
193
+ duration = time.time() - start_time
194
+
195
+ logger.info(f"5个并发工作线程完成,耗时: {duration:.3f}s")
196
+ logger.info(f"总计处理: {len(results) * 10} 个文件操作和命令执行")
197
+
198
+ assert len(results) == 5
199
+
200
+ finally:
201
+ try:
202
+ sandbox.kill()
203
+ except:
204
+ pass
205
+
206
+ async def test_concurrent_async_operations(self):
207
+ """测试异步版本并发操作"""
208
+ sandbox = await AsyncSandbox.create(debug=True)
209
+
210
+ try:
211
+
212
+ async def worker(worker_id: int) -> dict:
213
+ """异步工作协程"""
214
+ results = []
215
+
216
+ for i in range(10):
217
+ # 文件操作
218
+ filename = f"/tmp/async_worker_{worker_id}_file_{i}.txt"
219
+ content = f"Async Worker {worker_id}, File {i}, Content: {random.randint(1, 1000)}"
220
+
221
+ await sandbox.files.write(filename, content)
222
+ read_content = await sandbox.files.read(filename)
223
+ assert read_content == content
224
+
225
+ # 命令执行
226
+ result = await sandbox.commands.run(
227
+ f"echo 'Async Worker {worker_id}, Command {i}'"
228
+ )
229
+ assert result.exit_code == 0
230
+
231
+ results.append({"file": filename, "command_output": result.stdout})
232
+
233
+ # 清理文件
234
+ await sandbox.files.remove(filename)
235
+
236
+ return {"worker_id": worker_id, "results": results}
237
+
238
+ # 启动并发协程
239
+ start_time = time.time()
240
+
241
+ tasks = [worker(i) for i in range(10)] # 异步可以支持更多并发
242
+ results = await asyncio.gather(*tasks)
243
+
244
+ duration = time.time() - start_time
245
+
246
+ logger.info(f"10个并发协程完成,耗时: {duration:.3f}s")
247
+ logger.info(f"总计处理: {len(results) * 10} 个异步文件操作和命令执行")
248
+
249
+ assert len(results) == 10
250
+
251
+ finally:
252
+ try:
253
+ await sandbox.kill()
254
+ except:
255
+ pass
256
+
257
+ # ======================== 内存压力测试 ========================
258
+
259
+ def test_memory_stress_sync(self):
260
+ """测试同步版本内存压力"""
261
+ sandbox = Sandbox(debug=True)
262
+
263
+ try:
264
+ # 创建大量小文件
265
+ num_files = 1000
266
+ file_paths = []
267
+
268
+ start_time = time.time()
269
+
270
+ for i in range(num_files):
271
+ filename = f"/tmp/memory_test_{i}.txt"
272
+ content = f"File {i}: " + "x" * 100 # 100字符内容
273
+ sandbox.files.write(filename, content)
274
+ file_paths.append(filename)
275
+
276
+ create_duration = time.time() - start_time
277
+ logger.info(f"创建 {num_files} 个文件耗时: {create_duration:.3f}s")
278
+
279
+ # 读取所有文件
280
+ start_time = time.time()
281
+
282
+ for filename in file_paths:
283
+ content = sandbox.files.read(filename)
284
+ assert len(content) > 100
285
+
286
+ read_duration = time.time() - start_time
287
+ logger.info(f"读取 {num_files} 个文件耗时: {read_duration:.3f}s")
288
+
289
+ # 清理所有文件
290
+ start_time = time.time()
291
+
292
+ for filename in file_paths:
293
+ sandbox.files.remove(filename)
294
+
295
+ cleanup_duration = time.time() - start_time
296
+ logger.info(f"清理 {num_files} 个文件耗时: {cleanup_duration:.3f}s")
297
+
298
+ finally:
299
+ try:
300
+ sandbox.kill()
301
+ except:
302
+ pass
303
+
304
+ async def test_memory_stress_async(self):
305
+ """测试异步版本内存压力"""
306
+ sandbox = await AsyncSandbox.create(debug=True)
307
+
308
+ try:
309
+ # 创建大量小文件(异步批量操作)
310
+ num_files = 1000
311
+
312
+ start_time = time.time()
313
+
314
+ # 准备文件数据
315
+ files_data = [
316
+ {
317
+ "path": f"/tmp/async_memory_test_{i}.txt",
318
+ "data": f"Async File {i}: " + "y" * 100,
319
+ }
320
+ for i in range(num_files)
321
+ ]
322
+
323
+ # 批量写入(分批处理避免内存过大)
324
+ batch_size = 100
325
+ for i in range(0, num_files, batch_size):
326
+ batch = files_data[i : i + batch_size]
327
+ await sandbox.files.write(batch)
328
+
329
+ create_duration = time.time() - start_time
330
+ logger.info(f"异步创建 {num_files} 个文件耗时: {create_duration:.3f}s")
331
+
332
+ # 异步读取所有文件
333
+ start_time = time.time()
334
+
335
+ async def read_file(filename):
336
+ return await sandbox.files.read(filename)
337
+
338
+ # 分批并发读取
339
+ batch_size = 50
340
+ for i in range(0, num_files, batch_size):
341
+ tasks = []
342
+ for j in range(i, min(i + batch_size, num_files)):
343
+ filename = f"/tmp/async_memory_test_{j}.txt"
344
+ tasks.append(read_file(filename))
345
+
346
+ results = await asyncio.gather(*tasks)
347
+ for result in results:
348
+ assert len(result) > 100
349
+
350
+ read_duration = time.time() - start_time
351
+ logger.info(f"异步读取 {num_files} 个文件耗时: {read_duration:.3f}s")
352
+
353
+ # 异步清理所有文件
354
+ start_time = time.time()
355
+
356
+ for i in range(num_files):
357
+ filename = f"/tmp/async_memory_test_{i}.txt"
358
+ await sandbox.files.remove(filename)
359
+
360
+ cleanup_duration = time.time() - start_time
361
+ logger.info(f"异步清理 {num_files} 个文件耗时: {cleanup_duration:.3f}s")
362
+
363
+ finally:
364
+ try:
365
+ await sandbox.kill()
366
+ except:
367
+ pass
368
+
369
+ # ======================== 边界条件测试 ========================
370
+
371
+ def test_edge_cases_sync(self):
372
+ """测试同步版本边界条件"""
373
+ sandbox = Sandbox(debug=True)
374
+
375
+ try:
376
+ # 空文件测试
377
+ sandbox.files.write("/tmp/empty.txt", "")
378
+ content = sandbox.files.read("/tmp/empty.txt")
379
+ assert content == ""
380
+
381
+ # 特殊字符文件名测试
382
+ special_names = [
383
+ "/tmp/file with spaces.txt",
384
+ "/tmp/file_with_中文.txt",
385
+ "/tmp/file-with-special-!@#$%.txt",
386
+ ]
387
+
388
+ for filename in special_names:
389
+ try:
390
+ sandbox.files.write(filename, f"Content for {filename}")
391
+ content = sandbox.files.read(filename)
392
+ assert f"Content for {filename}" in content
393
+ sandbox.files.remove(filename)
394
+ except Exception as e:
395
+ logger.warning(f"特殊文件名 {filename} 测试失败: {e}")
396
+
397
+ # 深层目录测试
398
+ deep_path = (
399
+ "/tmp/" + "/".join([f"dir{i}" for i in range(10)]) + "/deep_file.txt"
400
+ )
401
+ sandbox.files.write(deep_path, "Deep directory content")
402
+ content = sandbox.files.read(deep_path)
403
+ assert content == "Deep directory content"
404
+
405
+ # 长命令测试
406
+ long_command = "echo " + "A" * 1000
407
+ result = sandbox.commands.run(long_command)
408
+ assert result.exit_code == 0
409
+ assert len(result.stdout) > 1000
410
+
411
+ # 零退出码和非零退出码测试
412
+ result_success = sandbox.commands.run("exit 0")
413
+ assert result_success.exit_code == 0
414
+
415
+ result_fail = sandbox.commands.run("exit 42")
416
+ assert result_fail.exit_code == 42
417
+
418
+ logger.info("所有边界条件测试通过")
419
+
420
+ finally:
421
+ try:
422
+ sandbox.kill()
423
+ except:
424
+ pass
425
+
426
+ async def test_edge_cases_async(self):
427
+ """测试异步版本边界条件"""
428
+ sandbox = await AsyncSandbox.create(debug=True)
429
+
430
+ try:
431
+ # 空文件测试
432
+ await sandbox.files.write("/tmp/async_empty.txt", "")
433
+ content = await sandbox.files.read("/tmp/async_empty.txt")
434
+ assert content == ""
435
+
436
+ # 二进制数据测试
437
+ binary_data = bytes(range(256)) # 0-255的字节
438
+ await sandbox.files.write("/tmp/binary_test.bin", binary_data)
439
+ read_binary = await sandbox.files.read(
440
+ "/tmp/binary_test.bin", format="bytes"
441
+ )
442
+ assert bytes(read_binary) == binary_data
443
+
444
+ # 流式读取大文件测试
445
+ large_content = "Stream test content\n" * 10000
446
+ await sandbox.files.write("/tmp/stream_test.txt", large_content)
447
+
448
+ stream = await sandbox.files.read("/tmp/stream_test.txt", format="stream")
449
+ chunks = []
450
+ async for chunk in stream:
451
+ chunks.append(chunk)
452
+
453
+ reconstructed = b"".join(chunks).decode("utf-8")
454
+ assert reconstructed == large_content
455
+
456
+ # 超时测试
457
+ try:
458
+ result = await sandbox.commands.run("sleep 1", timeout=0.5)
459
+ # 如果执行到这里,说明超时没有生效
460
+ logger.warning("超时测试可能没有生效")
461
+ except Exception as e:
462
+ logger.info(f"超时测试正确触发: {type(e).__name__}")
463
+
464
+ logger.info("所有异步边界条件测试通过")
465
+
466
+ finally:
467
+ try:
468
+ await sandbox.kill()
469
+ except:
470
+ pass
471
+
472
+ # ======================== 错误恢复测试 ========================
473
+
474
+ def test_error_recovery_sync(self):
475
+ """测试同步版本错误恢复"""
476
+ sandbox = Sandbox(debug=True)
477
+
478
+ try:
479
+ # 测试从文件操作错误中恢复
480
+ error_count = 0
481
+
482
+ # 尝试不存在的操作
483
+ for i in range(5):
484
+ try:
485
+ sandbox.files.read(f"/nonexistent/file_{i}.txt")
486
+ except Exception:
487
+ error_count += 1
488
+
489
+ # 错误后继续正常操作
490
+ sandbox.files.write(f"/tmp/recovery_test_{i}.txt", f"Recovery test {i}")
491
+ content = sandbox.files.read(f"/tmp/recovery_test_{i}.txt")
492
+ assert content == f"Recovery test {i}"
493
+ sandbox.files.remove(f"/tmp/recovery_test_{i}.txt")
494
+
495
+ assert error_count == 5, "应该捕获5个错误"
496
+
497
+ # 测试从命令错误中恢复
498
+ error_count = 0
499
+
500
+ for i in range(5):
501
+ # 执行失败的命令
502
+ result = sandbox.commands.run("ls /nonexistent_dir_12345")
503
+ if result.exit_code != 0:
504
+ error_count += 1
505
+
506
+ # 错误后继续正常命令
507
+ result = sandbox.commands.run(f"echo 'Command recovery test {i}'")
508
+ assert result.exit_code == 0
509
+ assert f"Command recovery test {i}" in result.stdout
510
+
511
+ assert error_count == 5, "应该有5个失败的命令"
512
+ logger.info("错误恢复测试通过")
513
+
514
+ finally:
515
+ try:
516
+ sandbox.kill()
517
+ except:
518
+ pass
519
+
520
+ async def test_error_recovery_async(self):
521
+ """测试异步版本错误恢复"""
522
+ sandbox = await AsyncSandbox.create(debug=True)
523
+
524
+ try:
525
+ # 测试异步错误恢复
526
+ error_count = 0
527
+
528
+ for i in range(5):
529
+ try:
530
+ await sandbox.files.read(f"/nonexistent/async_file_{i}.txt")
531
+ except Exception:
532
+ error_count += 1
533
+
534
+ # 错误后继续正常操作
535
+ await sandbox.files.write(
536
+ f"/tmp/async_recovery_test_{i}.txt", f"Async recovery test {i}"
537
+ )
538
+ content = await sandbox.files.read(f"/tmp/async_recovery_test_{i}.txt")
539
+ assert content == f"Async recovery test {i}"
540
+ await sandbox.files.remove(f"/tmp/async_recovery_test_{i}.txt")
541
+
542
+ assert error_count == 5
543
+
544
+ # 并发错误恢复测试
545
+ async def error_and_recovery(index):
546
+ try:
547
+ await sandbox.files.read(f"/nonexistent/concurrent_{index}.txt")
548
+ except Exception:
549
+ pass
550
+
551
+ # 正常操作
552
+ await sandbox.files.write(
553
+ f"/tmp/concurrent_recovery_{index}.txt", f"Concurrent {index}"
554
+ )
555
+ content = await sandbox.files.read(
556
+ f"/tmp/concurrent_recovery_{index}.txt"
557
+ )
558
+ assert content == f"Concurrent {index}"
559
+ await sandbox.files.remove(f"/tmp/concurrent_recovery_{index}.txt")
560
+ return True
561
+
562
+ # 并发执行错误恢复
563
+ tasks = [error_and_recovery(i) for i in range(10)]
564
+ results = await asyncio.gather(*tasks)
565
+
566
+ assert all(results), "所有并发错误恢复都应该成功"
567
+ logger.info("异步错误恢复测试通过")
568
+
569
+ finally:
570
+ try:
571
+ await sandbox.kill()
572
+ except:
573
+ pass
574
+
575
+ # ======================== 资源管理测试 ========================
576
+
577
+ def test_resource_management_sync(self):
578
+ """测试同步版本资源管理"""
579
+ sandboxes = []
580
+
581
+ try:
582
+ # 创建多个沙箱实例
583
+ for i in range(3):
584
+ sandbox = Sandbox(debug=True)
585
+ sandboxes.append(sandbox)
586
+
587
+ # 在每个沙箱中执行操作
588
+ result = sandbox.commands.run(f"echo 'Sandbox {i} test'")
589
+ assert result.exit_code == 0
590
+
591
+ sandbox.files.write(f"/tmp/resource_test_{i}.txt", f"Resource test {i}")
592
+
593
+ logger.info(f"创建了 {len(sandboxes)} 个沙箱实例")
594
+
595
+ # 验证每个沙箱都是独立的
596
+ for i, sandbox in enumerate(sandboxes):
597
+ content = sandbox.files.read(f"/tmp/resource_test_{i}.txt")
598
+ assert content == f"Resource test {i}"
599
+
600
+ # 验证其他沙箱的文件不存在
601
+ for j in range(len(sandboxes)):
602
+ if i != j:
603
+ exists = sandbox.files.exists(f"/tmp/resource_test_{j}.txt")
604
+ # 注意:在debug模式下,可能共享文件系统,所以这个测试可能不适用
605
+
606
+ finally:
607
+ # 清理所有沙箱
608
+ for i, sandbox in enumerate(sandboxes):
609
+ try:
610
+ sandbox.kill()
611
+ logger.info(f"清理沙箱 {i}")
612
+ except Exception as e:
613
+ logger.error(f"清理沙箱 {i} 失败: {e}")
614
+
615
+ async def test_resource_management_async(self):
616
+ """测试异步版本资源管理"""
617
+ sandboxes = []
618
+
619
+ try:
620
+ # 并发创建多个沙箱实例
621
+ create_tasks = [AsyncSandbox.create(debug=True) for _ in range(3)]
622
+ sandboxes = await asyncio.gather(*create_tasks)
623
+
624
+ # 在每个沙箱中并发执行操作
625
+ async def setup_sandbox(sandbox, index):
626
+ result = await sandbox.commands.run(
627
+ f"echo 'Async Sandbox {index} test'"
628
+ )
629
+ assert result.exit_code == 0
630
+
631
+ await sandbox.files.write(
632
+ f"/tmp/async_resource_test_{index}.txt",
633
+ f"Async resource test {index}",
634
+ )
635
+ return sandbox
636
+
637
+ setup_tasks = [
638
+ setup_sandbox(sandbox, i) for i, sandbox in enumerate(sandboxes)
639
+ ]
640
+ await asyncio.gather(*setup_tasks)
641
+
642
+ logger.info(f"创建了 {len(sandboxes)} 个异步沙箱实例")
643
+
644
+ # 并发验证每个沙箱
645
+ async def verify_sandbox(sandbox, index):
646
+ content = await sandbox.files.read(
647
+ f"/tmp/async_resource_test_{index}.txt"
648
+ )
649
+ assert content == f"Async resource test {index}"
650
+ return True
651
+
652
+ verify_tasks = [
653
+ verify_sandbox(sandbox, i) for i, sandbox in enumerate(sandboxes)
654
+ ]
655
+ results = await asyncio.gather(*verify_tasks)
656
+
657
+ assert all(results), "所有沙箱验证都应该成功"
658
+
659
+ finally:
660
+ # 并发清理所有沙箱
661
+ cleanup_tasks = []
662
+ for i, sandbox in enumerate(sandboxes):
663
+
664
+ async def cleanup_sandbox(sb, idx):
665
+ try:
666
+ await sb.kill()
667
+ logger.info(f"清理异步沙箱 {idx}")
668
+ except Exception as e:
669
+ logger.error(f"清理异步沙箱 {idx} 失败: {e}")
670
+
671
+ cleanup_tasks.append(cleanup_sandbox(sandbox, i))
672
+
673
+ if cleanup_tasks:
674
+ await asyncio.gather(*cleanup_tasks, return_exceptions=True)
675
+
676
+ # ======================== 主测试执行器 ========================
677
+
678
+ def run_all_tests(self):
679
+ """运行所有压力测试和边界条件测试"""
680
+ logger.info("开始压力测试和边界条件测试...")
681
+
682
+ # 大数据处理测试
683
+ self.run_test(
684
+ self.test_large_file_operations_sync, "Large File Operations Sync"
685
+ )
686
+ self.run_test(
687
+ self.test_large_file_operations_async,
688
+ "Large File Operations Async",
689
+ is_async=True,
690
+ )
691
+
692
+ # 并发压力测试
693
+ self.run_test(
694
+ self.test_concurrent_sync_operations, "Concurrent Sync Operations"
695
+ )
696
+ self.run_test(
697
+ self.test_concurrent_async_operations,
698
+ "Concurrent Async Operations",
699
+ is_async=True,
700
+ )
701
+
702
+ # 内存压力测试
703
+ self.run_test(self.test_memory_stress_sync, "Memory Stress Sync")
704
+ self.run_test(
705
+ self.test_memory_stress_async, "Memory Stress Async", is_async=True
706
+ )
707
+
708
+ # 边界条件测试
709
+ self.run_test(self.test_edge_cases_sync, "Edge Cases Sync")
710
+ self.run_test(self.test_edge_cases_async, "Edge Cases Async", is_async=True)
711
+
712
+ # 错误恢复测试
713
+ self.run_test(self.test_error_recovery_sync, "Error Recovery Sync")
714
+ self.run_test(
715
+ self.test_error_recovery_async, "Error Recovery Async", is_async=True
716
+ )
717
+
718
+ # 资源管理测试
719
+ self.run_test(self.test_resource_management_sync, "Resource Management Sync")
720
+ self.run_test(
721
+ self.test_resource_management_async,
722
+ "Resource Management Async",
723
+ is_async=True,
724
+ )
725
+
726
+ def print_summary(self):
727
+ """打印测试摘要"""
728
+ total_tests = len(self.test_results)
729
+ passed_tests = sum(1 for r in self.test_results if r["success"])
730
+ failed_tests = total_tests - passed_tests
731
+
732
+ total_duration = sum(r["duration"] for r in self.test_results)
733
+
734
+ print("\n" + "=" * 70)
735
+ print("沙箱压力测试和边界条件测试报告")
736
+ print("=" * 70)
737
+ print(f"总测试数: {total_tests}")
738
+ print(f"通过数: {passed_tests}")
739
+ print(f"失败数: {failed_tests}")
740
+ print(f"总耗时: {total_duration:.3f}秒")
741
+ print(f"成功率: {(passed_tests/total_tests*100):.1f}%")
742
+
743
+ if self.failed_tests:
744
+ print(f"\n失败的测试:")
745
+ for test in self.failed_tests:
746
+ print(f" ❌ {test}")
747
+
748
+ # 性能统计
749
+ sync_tests = [
750
+ r for r in self.test_results if "Sync" in r["test"] and r["success"]
751
+ ]
752
+ async_tests = [
753
+ r for r in self.test_results if "Async" in r["test"] and r["success"]
754
+ ]
755
+
756
+ if sync_tests:
757
+ avg_sync_time = sum(r["duration"] for r in sync_tests) / len(sync_tests)
758
+ print(f"\n同步版本平均测试时间: {avg_sync_time:.3f}秒")
759
+
760
+ if async_tests:
761
+ avg_async_time = sum(r["duration"] for r in async_tests) / len(async_tests)
762
+ print(f"异步版本平均测试时间: {avg_async_time:.3f}秒")
763
+
764
+ print("=" * 70)
765
+
766
+
767
+ def main():
768
+ """主函数"""
769
+ validator = StressTestValidator()
770
+
771
+ try:
772
+ validator.run_all_tests()
773
+ finally:
774
+ validator.print_summary()
775
+
776
+
777
+ if __name__ == "__main__":
778
+ main()