forked from SamarpanCoder2002/Singly-Link-List-Visualizer
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_stack_and_huffman.py
More file actions
663 lines (490 loc) · 20.3 KB
/
test_stack_and_huffman.py
File metadata and controls
663 lines (490 loc) · 20.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
#!/usr/bin/env python3
"""
栈 (Stack) 测试程序
"""
import unittest
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from DS_visual.stack.stack_model import StackModel
class TestStackBasics(unittest.TestCase):
"""栈基本功能测试"""
def setUp(self):
"""每个测试前的初始化"""
self.stack = StackModel(capacity=5)
def test_initial_state(self):
"""测试初始状态"""
self.assertTrue(self.stack.is_empty())
self.assertFalse(self.stack.is_full())
self.assertEqual(self.stack.top, -1)
self.assertEqual(len(self.stack), 0)
def test_custom_capacity(self):
"""测试自定义容量"""
stack = StackModel(capacity=10)
self.assertEqual(stack.capacity, 10)
stack = StackModel(capacity=1)
self.assertEqual(stack.capacity, 1)
def test_push_single_element(self):
"""测试压入单个元素"""
success = self.stack.push(10)
self.assertTrue(success)
self.assertFalse(self.stack.is_empty())
self.assertEqual(self.stack.top, 0)
self.assertEqual(len(self.stack), 1)
def test_push_multiple_elements(self):
"""测试压入多个元素"""
values = [1, 2, 3, 4, 5]
for val in values:
success = self.stack.push(val)
self.assertTrue(success)
self.assertEqual(len(self.stack), 5)
self.assertEqual(self.stack.top, 4)
self.assertTrue(self.stack.is_full())
def test_push_to_full_stack(self):
"""测试向满栈压入元素"""
# 填满栈
for i in range(5):
self.stack.push(i)
# 尝试压入第6个元素
success = self.stack.push(99)
self.assertFalse(success)
self.assertTrue(self.stack.is_full())
self.assertEqual(len(self.stack), 5)
def test_pop_single_element(self):
"""测试弹出单个元素"""
self.stack.push(42)
value = self.stack.pop()
self.assertEqual(value, 42)
self.assertTrue(self.stack.is_empty())
self.assertEqual(self.stack.top, -1)
def test_pop_multiple_elements(self):
"""测试弹出多个元素(LIFO顺序)"""
values = [1, 2, 3, 4, 5]
for val in values:
self.stack.push(val)
popped = []
while not self.stack.is_empty():
popped.append(self.stack.pop())
# 应该是逆序弹出
self.assertEqual(popped, [5, 4, 3, 2, 1])
def test_pop_from_empty_stack(self):
"""测试从空栈弹出"""
value = self.stack.pop()
self.assertIsNone(value)
self.assertTrue(self.stack.is_empty())
def test_peek(self):
"""测试查看栈顶元素"""
self.stack.push(10)
self.stack.push(20)
self.stack.push(30)
top = self.stack.peek()
self.assertEqual(top, 30)
self.assertEqual(len(self.stack), 3) # peek不改变栈
def test_peek_empty_stack(self):
"""测试查看空栈栈顶"""
top = self.stack.peek()
self.assertIsNone(top)
def test_clear(self):
"""测试清空栈"""
for i in range(5):
self.stack.push(i)
self.stack.clear()
self.assertTrue(self.stack.is_empty())
self.assertEqual(self.stack.top, -1)
self.assertEqual(len(self.stack), 0)
def test_len(self):
"""测试长度函数"""
self.assertEqual(len(self.stack), 0)
self.stack.push(1)
self.assertEqual(len(self.stack), 1)
self.stack.push(2)
self.stack.push(3)
self.assertEqual(len(self.stack), 3)
self.stack.pop()
self.assertEqual(len(self.stack), 2)
class TestStackOperations(unittest.TestCase):
"""栈操作序列测试"""
def setUp(self):
"""每个测试前的初始化"""
self.stack = StackModel(capacity=10)
def test_push_pop_sequence(self):
"""测试压入弹出序列"""
# 压入3个
self.stack.push(1)
self.stack.push(2)
self.stack.push(3)
# 弹出2个
self.assertEqual(self.stack.pop(), 3)
self.assertEqual(self.stack.pop(), 2)
# 再压入2个
self.stack.push(4)
self.stack.push(5)
# 验证顺序
self.assertEqual(self.stack.pop(), 5)
self.assertEqual(self.stack.pop(), 4)
self.assertEqual(self.stack.pop(), 1)
def test_alternating_push_pop(self):
"""测试交替压入弹出"""
for i in range(10):
self.stack.push(i)
if i % 2 == 1:
self.stack.pop()
# 应该剩下5个元素: 0, 2, 4, 6, 8
self.assertEqual(len(self.stack), 5)
def test_fill_empty_fill(self):
"""测试填满-清空-再填满"""
# 第一次填满
for i in range(10):
self.stack.push(i)
self.assertTrue(self.stack.is_full())
# 清空
self.stack.clear()
self.assertTrue(self.stack.is_empty())
# 再次填满
for i in range(10, 20):
self.stack.push(i)
self.assertTrue(self.stack.is_full())
self.assertEqual(self.stack.peek(), 19)
def test_mixed_type_values(self):
"""测试混合类型值"""
self.stack.push(1)
self.stack.push("hello")
self.stack.push(3.14)
self.stack.push([1, 2, 3])
self.stack.push({"key": "value"})
self.assertEqual(self.stack.pop(), {"key": "value"})
self.assertEqual(self.stack.pop(), [1, 2, 3])
self.assertEqual(self.stack.pop(), 3.14)
self.assertEqual(self.stack.pop(), "hello")
self.assertEqual(self.stack.pop(), 1)
class TestStackEdgeCases(unittest.TestCase):
"""栈边界情况测试"""
def setUp(self):
"""每个测试前的初始化"""
self.stack = StackModel(capacity=5)
def test_single_capacity_stack(self):
"""测试容量为1的栈"""
stack = StackModel(capacity=1)
self.assertTrue(stack.push(42))
self.assertTrue(stack.is_full())
self.assertFalse(stack.push(99))
self.assertEqual(stack.pop(), 42)
self.assertTrue(stack.is_empty())
def test_large_capacity_stack(self):
"""测试大容量栈"""
stack = StackModel(capacity=1000)
for i in range(1000):
success = stack.push(i)
self.assertTrue(success)
self.assertTrue(stack.is_full())
self.assertEqual(len(stack), 1000)
def test_repeated_clears(self):
"""测试重复清空"""
self.stack.push(1)
self.stack.clear()
self.assertTrue(self.stack.is_empty())
self.stack.clear() # 再次清空
self.assertTrue(self.stack.is_empty())
# 清空后仍可正常使用
self.stack.push(2)
self.assertEqual(self.stack.pop(), 2)
def test_empty_operations(self):
"""测试空栈上的各种操作"""
self.assertIsNone(self.stack.pop())
self.assertIsNone(self.stack.peek())
self.assertTrue(self.stack.is_empty())
self.assertEqual(len(self.stack), 0)
def test_full_stack_operations(self):
"""测试满栈上的操作"""
# 填满栈
for i in range(5):
self.stack.push(i)
# 验证满栈状态
self.assertTrue(self.stack.is_full())
self.assertFalse(self.stack.push(99))
# peek应该仍然工作
self.assertEqual(self.stack.peek(), 4)
# pop后应该可以再push
self.stack.pop()
self.assertFalse(self.stack.is_full())
self.assertTrue(self.stack.push(99))
class TestStackProperties(unittest.TestCase):
"""栈性质测试"""
def test_lifo_property(self):
"""测试后进先出性质"""
stack = StackModel(capacity=10)
values = [1, 2, 3, 4, 5]
for val in values:
stack.push(val)
result = []
while not stack.is_empty():
result.append(stack.pop())
# 应该是逆序
self.assertEqual(result, [5, 4, 3, 2, 1])
def test_top_pointer_consistency(self):
"""测试栈顶指针一致性"""
stack = StackModel(capacity=10)
# 初始top为-1
self.assertEqual(stack.top, -1)
# 每次push,top递增
for i in range(5):
stack.push(i)
self.assertEqual(stack.top, i)
# 每次pop,top递减
for i in range(4, -1, -1):
self.assertEqual(stack.top, i)
stack.pop()
# 最后应该回到-1
self.assertEqual(stack.top, -1)
def test_repr(self):
"""测试字符串表示"""
stack = StackModel(capacity=5)
stack.push(1)
stack.push(2)
stack.push(3)
repr_str = repr(stack)
self.assertIn("Stack", repr_str)
self.assertIn("top=2", repr_str)
self.assertIn("[1, 2, 3]", repr_str)
def run_stack_tests():
"""运行所有栈测试"""
loader = unittest.TestLoader()
suite = unittest.TestSuite()
suite.addTests(loader.loadTestsFromTestCase(TestStackBasics))
suite.addTests(loader.loadTestsFromTestCase(TestStackOperations))
suite.addTests(loader.loadTestsFromTestCase(TestStackEdgeCases))
suite.addTests(loader.loadTestsFromTestCase(TestStackProperties))
runner = unittest.TextTestRunner(verbosity=2)
result = runner.run(suite)
print("\n" + "="*60)
print("栈测试结果摘要:")
print(f"运行测试数: {result.testsRun}")
print(f"成功: {result.testsRun - len(result.failures) - len(result.errors)}")
print(f"失败: {len(result.failures)}")
print(f"错误: {len(result.errors)}")
print("="*60)
return result.wasSuccessful()
# ============================================================
# Huffman树测试
# ============================================================
from DS_visual.binary_tree.huffman_tree.huffman_model import HuffmanModel, HuffmanNode
class TestHuffmanNode(unittest.TestCase):
"""Huffman节点测试"""
def test_node_creation(self):
"""测试节点创建"""
node = HuffmanNode(weight=5.0)
self.assertEqual(node.weight, 5.0)
self.assertIsNone(node.left)
self.assertIsNone(node.right)
self.assertEqual(node.label, "")
def test_node_with_children(self):
"""测试带子节点的节点"""
left = HuffmanNode(weight=2.0, label="A")
right = HuffmanNode(weight=3.0, label="B")
parent = HuffmanNode(weight=5.0, left=left, right=right, label="5.0")
self.assertEqual(parent.weight, 5.0)
self.assertEqual(parent.left, left)
self.assertEqual(parent.right, right)
self.assertEqual(parent.label, "5.0")
def test_node_repr(self):
"""测试节点字符串表示"""
node = HuffmanNode(weight=10.5)
self.assertIn("10.5", repr(node))
class TestHuffmanModel(unittest.TestCase):
"""Huffman树模型测试"""
def setUp(self):
"""每个测试前的初始化"""
self.model = HuffmanModel()
def test_initial_state(self):
"""测试初始状态"""
self.assertIsNone(self.model.root)
self.assertEqual(len(self.model.steps), 0)
def test_build_empty_weights(self):
"""测试空权值列表"""
root, steps, before, after = self.model.build_with_steps([])
self.assertIsNone(root)
self.assertEqual(len(steps), 0)
self.assertEqual(len(before), 0)
self.assertEqual(len(after), 0)
def test_build_single_weight(self):
"""测试单个权值"""
weights = [5.0]
root, steps, before, after = self.model.build_with_steps(weights)
self.assertIsNotNone(root)
self.assertEqual(root.weight, 5.0)
self.assertEqual(len(steps), 0) # 单节点无需合并
def test_build_two_weights(self):
"""测试两个权值"""
weights = [3.0, 5.0]
root, steps, before, after = self.model.build_with_steps(weights)
self.assertIsNotNone(root)
self.assertEqual(root.weight, 8.0)
self.assertEqual(len(steps), 1)
# 验证步骤
left, right, parent = steps[0]
self.assertEqual(left.weight, 3.0)
self.assertEqual(right.weight, 5.0)
self.assertEqual(parent.weight, 8.0)
def test_build_multiple_weights(self):
"""测试多个权值"""
weights = [5, 9, 12, 13, 16, 45]
root, steps, before, after = self.model.build_with_steps(weights)
# 根节点权值应该是所有权值之和
self.assertEqual(root.weight, sum(weights))
# 步骤数应该是n-1
self.assertEqual(len(steps), len(weights) - 1)
# 快照数量应该等于步骤数
self.assertEqual(len(before), len(steps))
self.assertEqual(len(after), len(steps))
def test_build_process_correctness(self):
"""测试构建过程正确性"""
weights = [1, 2, 3, 4]
root, steps, before, after = self.model.build_with_steps(weights)
# 验证每一步都是合并最小的两个节点
for i, (left, right, parent) in enumerate(steps):
self.assertEqual(parent.weight, left.weight + right.weight)
self.assertEqual(parent.left, left)
self.assertEqual(parent.right, right)
# 验证before快照是排序的
self.assertEqual(before[i], sorted(before[i]))
# 验证after快照是排序的
self.assertEqual(after[i], sorted(after[i]))
class TestHuffmanBuilding(unittest.TestCase):
"""Huffman树构建测试"""
def test_small_tree(self):
"""测试小型Huffman树"""
model = HuffmanModel()
weights = [1, 2, 3]
root, steps, before, after = model.build_with_steps(weights)
self.assertIsNotNone(root)
self.assertEqual(len(steps), 2)
# 第一步:合并1和2
self.assertEqual(steps[0][0].weight, 1)
self.assertEqual(steps[0][1].weight, 2)
self.assertEqual(steps[0][2].weight, 3)
# 第二步:合并3和3(前面的结果)
self.assertEqual(steps[1][2].weight, 6)
def test_classic_example(self):
"""测试经典教材示例"""
model = HuffmanModel()
weights = [5, 9, 12, 13, 16, 45]
root, steps, before, after = model.build_with_steps(weights)
# 验证根节点
self.assertEqual(root.weight, 100)
# 验证步骤数
self.assertEqual(len(steps), 5)
# 第一步应该合并最小的两个:5和9
first_left = min(steps[0][0].weight, steps[0][1].weight)
first_right = max(steps[0][0].weight, steps[0][1].weight)
self.assertEqual(first_left, 5)
self.assertEqual(first_right, 9)
def test_equal_weights(self):
"""测试相同权值"""
model = HuffmanModel()
weights = [1, 1, 1, 1]
root, steps, before, after = model.build_with_steps(weights)
self.assertEqual(root.weight, 4)
self.assertEqual(len(steps), 3)
def test_increasing_weights(self):
"""测试递增权值"""
model = HuffmanModel()
weights = [1, 2, 3, 4, 5]
root, steps, before, after = model.build_with_steps(weights)
self.assertEqual(root.weight, sum(weights))
self.assertEqual(len(steps), len(weights) - 1)
class TestHuffmanSnapshots(unittest.TestCase):
"""Huffman快照测试"""
def test_snapshot_consistency(self):
"""测试快照一致性"""
model = HuffmanModel()
weights = [3, 5, 7, 9]
root, steps, before, after = model.build_with_steps(weights)
# before和after数量应该相等
self.assertEqual(len(before), len(after))
# 每个before应该比对应的after多一个元素(合并前 vs 合并后)
for i in range(len(before)):
self.assertEqual(len(before[i]), len(after[i]) + 1)
def test_snapshot_order(self):
"""测试快照排序"""
model = HuffmanModel()
weights = [10, 5, 15, 20, 8]
root, steps, before, after = model.build_with_steps(weights)
# 所有before快照应该是排序的
for snapshot in before:
self.assertEqual(snapshot, sorted(snapshot))
# 所有after快照应该是排序的
for snapshot in after:
self.assertEqual(snapshot, sorted(snapshot))
def test_snapshot_weight_sum(self):
"""测试快照权值总和"""
model = HuffmanModel()
weights = [1, 2, 3, 4, 5]
total = sum(weights)
root, steps, before, after = model.build_with_steps(weights)
# 每个快照的权值总和应该保持不变
for snapshot in before:
self.assertAlmostEqual(sum(snapshot), total)
for snapshot in after:
self.assertAlmostEqual(sum(snapshot), total)
class TestHuffmanEdgeCases(unittest.TestCase):
"""Huffman边界情况测试"""
def test_very_small_weights(self):
"""测试非常小的权值"""
model = HuffmanModel()
weights = [0.1, 0.2, 0.3]
root, steps, before, after = model.build_with_steps(weights)
self.assertIsNotNone(root)
self.assertAlmostEqual(root.weight, 0.6)
def test_large_weights(self):
"""测试大权值"""
model = HuffmanModel()
weights = [1000, 2000, 3000, 4000]
root, steps, before, after = model.build_with_steps(weights)
self.assertEqual(root.weight, 10000)
def test_many_nodes(self):
"""测试大量节点"""
model = HuffmanModel()
weights = list(range(1, 21)) # 20个节点
root, steps, before, after = model.build_with_steps(weights)
self.assertEqual(root.weight, sum(weights))
self.assertEqual(len(steps), 19)
def test_float_weights(self):
"""测试浮点权值"""
model = HuffmanModel()
weights = [1.5, 2.7, 3.2, 4.8]
root, steps, before, after = model.build_with_steps(weights)
self.assertAlmostEqual(root.weight, sum(weights), places=6)
def run_huffman_tests():
"""运行所有Huffman树测试"""
loader = unittest.TestLoader()
suite = unittest.TestSuite()
suite.addTests(loader.loadTestsFromTestCase(TestHuffmanNode))
suite.addTests(loader.loadTestsFromTestCase(TestHuffmanModel))
suite.addTests(loader.loadTestsFromTestCase(TestHuffmanBuilding))
suite.addTests(loader.loadTestsFromTestCase(TestHuffmanSnapshots))
suite.addTests(loader.loadTestsFromTestCase(TestHuffmanEdgeCases))
runner = unittest.TextTestRunner(verbosity=2)
result = runner.run(suite)
print("\n" + "="*60)
print("Huffman树测试结果摘要:")
print(f"运行测试数: {result.testsRun}")
print(f"成功: {result.testsRun - len(result.failures) - len(result.errors)}")
print(f"失败: {len(result.failures)}")
print(f"错误: {len(result.errors)}")
print("="*60)
return result.wasSuccessful()
if __name__ == '__main__':
print("="*60)
print("运行栈测试...")
print("="*60)
stack_success = run_stack_tests()
print("\n" + "="*60)
print("运行Huffman树测试...")
print("="*60)
huffman_success = run_huffman_tests()
print("\n" + "="*60)
print("总体结果:")
print(f"栈测试: {'✅ 通过' if stack_success else '❌ 失败'}")
print(f"Huffman树测试: {'✅ 通过' if huffman_success else '❌ 失败'}")
print("="*60)
sys.exit(0 if (stack_success and huffman_success) else 1)