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
| | # Copyright (C) 2016 all contributors <msgthr-public@80x24.org>
# License: GPL-2.0+ <https://www.gnu.org/licenses/gpl-2.0.txt>
require 'test/unit'
require 'msgthr'
class TestMsgthr < Test::Unit::TestCase
def test_msgthr
thr = Msgthr.new
parent_child = ''
# Note that C is added after B,
# hence it's message will be empty after adding B
expected_parent_child = '->B'
thr.add('a', %w(c b), 'abc')
thr.add('b', %w(c), 'B') do |parent, child|
parent_child = "#{parent.msg}->#{child.msg}"
end
assert_equal parent_child, expected_parent_child
thr.add('c', nil, 'c')
thr.add('D', nil, 'D')
thr.add('d', %w(missing), 'd')
rset = thr.thread!
rootset = thr.order! { |c| c.sort_by!(&:mid) }
assert_same rset, rootset
assert_equal %w(D c missing), rootset.map(&:mid)
assert_equal 'D', rootset[0].msg
assert_equal %w(b), rootset[1].children.map(&:mid)
out = ''.b
thr.walk_thread do |level, container, index|
msg = container.msg
summary = msg ? msg : "[missing: <#{container.mid}>]"
indent = ' ' * level
out << sprintf("#{indent} % 3d. %s\n", index, summary)
end
exp = <<EOF.b
0. D
1. c
0. B
0. abc
2. [missing: <missing>]
0. d
EOF
assert_equal exp, out
end
def test_order_in_thread
thr = Msgthr.new
thr.add(1, nil, 'a')
thr.add(2, [1], 'b')
thr.thread! do |ary|
ary.sort_by! do |cont|
cur = cont.topmost
cur ? cur : 0
end
end
out = ''
thr.walk_thread do |level, container, index|
msg = container.msg
out << "#{level} [#{index}] #{msg}\n"
end
exp = <<EOF.b
0 [0] a
1 [0] b
EOF
assert_equal exp, out
end
def test_out_of_order
thr = Msgthr.new
thr.thread!
assert_raise(Msgthr::StateError) { thr.add(1, nil, 'a') }
thr.clear # make things good again, following should not raise:
thr.add(1, nil, 'a')
thr.thread!
assert_raise(Msgthr::StateError) { thr.thread! }
out = []
thr.walk_thread do |level, container, index|
msg = container.msg
out << "#{level} [#{index}] #{msg}"
end
assert_equal [ '0 [0] a' ], out
assert_raise(Msgthr::StateError) { thr.thread! { raise "DO NOT CALL" } }
assert_raise(Msgthr::StateError) { thr.order! { |_| raise "DO NOT CALL" } }
# this is legal, even if non-sensical
thr.clear
thr.walk_thread { |level, container, index| raise "DO NOT CALL" }
end
def test_short_add_to_walk
thr = Msgthr.new
thr.add(1, nil, 'a')
thr.add(2, [1], 'b')
out = ''
thr.walk_thread do |level, container, index|
msg = container.msg
out << "#{level} [#{index}] #{msg}\n"
end
exp = <<EOF.b
0 [0] a
1 [0] b
EOF
assert_equal exp, out
end
def test_add_child_callback
thr = Msgthr.new
threads = {}
[1, 11, 12, 2, 21, 211].each{ |id| threads[id] = [id]}
my_add = lambda do |id, refs, msg|
thr.add(id, refs, msg) do |parent, child|
threads[child.mid] = threads[parent.mid]
end
end
# Create the following structure
# 1
# \
# | 1.1
# \
# 1.2
# 2
# \
# 2.1
# \
# 2.1.1
my_add.call(1, nil, '1')
my_add.call(11, [1], '1.1')
my_add.call(12, [1], '1.2')
my_add.call(2, nil, '2')
my_add.call(21, [2], '2.1')
my_add.call(211, [21], '2.1.1')
thr.thread!
thr.rootset.each do |cnt|
threads[cnt.mid][0] = cnt.msg
end
assert_equal threads[1], threads[11]
assert_equal threads[1], threads[12]
assert_equal threads[2], threads[21]
assert_equal threads[2], threads[211]
assert_equal threads[21], threads[211]
assert_equal threads[1][0], '1'
assert_equal threads[2][0], '2'
end
def test_no_lost_root
ids = [
[ 8, [] ],
[ 7, [8] ],
[ 6, [8, 7] ],
[ 3, [6, 7, 8] ],
[ 2, [6, 7, 8, 3] ],
[ 10, [8, 7, 6] ],
[ 9, [6, 3] ],
[ 5, [6, 7, 8, 3, 2] ],
[ 4, [2, 3] ],
[ 1, [2, 3, 4] ],
[ 'a', ],
[ 'b', ['a'] ],
]
[ [ :forward, ids, 2 ],
[ :backwards, ids.reverse, 3 ],
[ :shuffle, ids.shuffle, nil ],
].each do |desc,msgs,exp|
thr = Msgthr.new
msgs.each { |id| thr.add(id[0], id[1], id[0]) }
seen0 = nr = 0
thr.walk_thread do |level, _, _|
seen0 += 1 if level == 0
nr += 1
end
assert_equal nr, msgs.size, 'no lost messages'
assert_equal exp, seen0, "single root #{desc}" if exp
end
end
end
|