activeldap 0.10.0 → 1.0.0
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.
- data/CHANGES +6 -30
- data/README +4 -2
- data/Rakefile +7 -0
- data/data/locale/en/LC_MESSAGES/active-ldap.mo +0 -0
- data/data/locale/ja/LC_MESSAGES/active-ldap.mo +0 -0
- data/examples/al-admin/po/en/al-admin.po +3 -3
- data/examples/al-admin/po/ja/al-admin.po +3 -3
- data/examples/al-admin/po/nl/al-admin.po +3 -3
- data/lib/active_ldap/adapter/base.rb +0 -2
- data/lib/active_ldap/base.rb +4 -2
- data/lib/active_ldap/get_text_support.rb +3 -11
- data/lib/active_ldap/operations.rb +1 -0
- data/lib/active_ldap/schema/syntaxes.rb +5 -3
- data/lib/active_ldap/validations.rb +25 -15
- data/lib/active_ldap.rb +1 -1
- data/po/en/active-ldap.po +1 -1
- data/po/ja/active-ldap.po +1 -1
- data/rails/plugin/active_ldap/init.rb +1 -1
- data/test/run-test.rb +2 -0
- data/test/test_base.rb +63 -1
- data/test/test_syntax.rb +3 -2
- data/test-unit-ext/NEWS.en +28 -0
- data/test-unit-ext/NEWS.ja +28 -0
- data/test-unit-ext/README.en +247 -0
- data/test-unit-ext/README.ja +246 -0
- data/test-unit-ext/Rakefile +111 -0
- data/{test → test-unit-ext/lib}/test-unit-ext/always-show-result.rb +0 -0
- data/test-unit-ext/lib/test-unit-ext/assertions.rb +40 -0
- data/test-unit-ext/lib/test-unit-ext/attributes.rb +129 -0
- data/{test → test-unit-ext/lib}/test-unit-ext/backtrace-filter.rb +0 -0
- data/test-unit-ext/lib/test-unit-ext/color.rb +59 -0
- data/test-unit-ext/lib/test-unit-ext/colorized-runner.rb +111 -0
- data/test-unit-ext/lib/test-unit-ext/diff.rb +516 -0
- data/{test → test-unit-ext/lib}/test-unit-ext/long-display-for-emacs.rb +0 -0
- data/test-unit-ext/lib/test-unit-ext/notification.rb +79 -0
- data/test-unit-ext/lib/test-unit-ext/omission.rb +96 -0
- data/test-unit-ext/lib/test-unit-ext/pending.rb +97 -0
- data/{test → test-unit-ext/lib}/test-unit-ext/priority.rb +25 -53
- data/test-unit-ext/lib/test-unit-ext/version.rb +3 -0
- data/test-unit-ext/lib/test-unit-ext/xml-report.rb +224 -0
- data/test-unit-ext/lib/test-unit-ext.rb +16 -0
- data/test-unit-ext/misc/rd2html.rb +42 -0
- data/test-unit-ext/test/run-test.rb +14 -0
- data/test-unit-ext/test/test_attributes.rb +139 -0
- data/test-unit-ext/test/test_color.rb +39 -0
- data/test-unit-ext/test/test_diff.rb +475 -0
- data/test-unit-ext/test/test_notification.rb +32 -0
- data/test-unit-ext/test/test_omission.rb +64 -0
- data/test-unit-ext/test/test_pending.rb +64 -0
- data/test-unit-ext/test/test_priority.rb +88 -0
- data/test-unit-ext/test/test_xml_report.rb +161 -0
- metadata +34 -7
- data/test/test-unit-ext.rb +0 -4
@@ -0,0 +1,516 @@
|
|
1
|
+
# port of Python's difflib.
|
2
|
+
|
3
|
+
module Test
|
4
|
+
module Diff
|
5
|
+
class SequenceMatcher
|
6
|
+
def initialize(from, to, &junk_predicate)
|
7
|
+
@from = from
|
8
|
+
@to = to
|
9
|
+
@junk_predicate = junk_predicate
|
10
|
+
update_to_indexes
|
11
|
+
end
|
12
|
+
|
13
|
+
def longest_match(from_start, from_end, to_start, to_end)
|
14
|
+
best_info = find_best_match_position(from_start, from_end,
|
15
|
+
to_start, to_end)
|
16
|
+
unless @junks.empty?
|
17
|
+
args = [from_start, from_end, to_start, to_end]
|
18
|
+
best_info = adjust_best_info_with_junk_predicate(false, best_info,
|
19
|
+
*args)
|
20
|
+
best_info = adjust_best_info_with_junk_predicate(true, best_info,
|
21
|
+
*args)
|
22
|
+
end
|
23
|
+
|
24
|
+
best_info
|
25
|
+
end
|
26
|
+
|
27
|
+
def blocks
|
28
|
+
@blocks ||= compute_blocks
|
29
|
+
end
|
30
|
+
|
31
|
+
def operations
|
32
|
+
@operations ||= compute_operations
|
33
|
+
end
|
34
|
+
|
35
|
+
def grouped_operations(context_size=nil)
|
36
|
+
context_size ||= 3
|
37
|
+
_operations = operations
|
38
|
+
_operations = [[:equal, 0, 0, 0, 0]] if _operations.empty?
|
39
|
+
expand_edge_equal_operations!(_operations, context_size)
|
40
|
+
|
41
|
+
group_window = context_size * 2
|
42
|
+
groups = []
|
43
|
+
group = []
|
44
|
+
_operations.each do |tag, from_start, from_end, to_start, to_end|
|
45
|
+
if tag == :equal and from_end - from_start > group_window
|
46
|
+
group << [tag,
|
47
|
+
from_start,
|
48
|
+
[from_end, from_start + context_size].min,
|
49
|
+
to_start,
|
50
|
+
[to_end, to_start + context_size].min]
|
51
|
+
groups << group
|
52
|
+
group = []
|
53
|
+
from_start = [from_start, from_end - context_size].max
|
54
|
+
to_start = [to_start, to_end - context_size].max
|
55
|
+
end
|
56
|
+
group << [tag, from_start, from_end, to_start, to_end]
|
57
|
+
end
|
58
|
+
groups << group unless group.empty?
|
59
|
+
groups
|
60
|
+
end
|
61
|
+
|
62
|
+
def ratio
|
63
|
+
@ratio ||= compute_ratio
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
def update_to_indexes
|
68
|
+
@to_indexes = {}
|
69
|
+
@junks = {}
|
70
|
+
each = @to.is_a?(String) ? :each_byte : :each
|
71
|
+
i = 0
|
72
|
+
@to.send(each) do |item|
|
73
|
+
@to_indexes[item] ||= []
|
74
|
+
@to_indexes[item] << i
|
75
|
+
i += 1
|
76
|
+
end
|
77
|
+
|
78
|
+
return if @junk_predicate.nil?
|
79
|
+
@to_indexes = @to_indexes.reject do |key, value|
|
80
|
+
junk = @junk_predicate.call(key)
|
81
|
+
@junks[key] = true if junk
|
82
|
+
junk
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def find_best_match_position(from_start, from_end, to_start, to_end)
|
87
|
+
best_from, best_to, best_size = from_start, to_start, 0
|
88
|
+
sizes = {}
|
89
|
+
from_start.upto(from_end) do |from_index|
|
90
|
+
_sizes = {}
|
91
|
+
(@to_indexes[@from[from_index]] || []).each do |to_index|
|
92
|
+
next if to_index < to_start
|
93
|
+
break if to_index > to_end
|
94
|
+
size = _sizes[to_index] = (sizes[to_index - 1] || 0) + 1
|
95
|
+
if size > best_size
|
96
|
+
best_from = from_index - size + 1
|
97
|
+
best_to = to_index - size + 1
|
98
|
+
best_size = size
|
99
|
+
end
|
100
|
+
end
|
101
|
+
sizes = _sizes
|
102
|
+
end
|
103
|
+
[best_from, best_to, best_size]
|
104
|
+
end
|
105
|
+
|
106
|
+
def adjust_best_info_with_junk_predicate(should_junk, best_info,
|
107
|
+
from_start, from_end,
|
108
|
+
to_start, to_end)
|
109
|
+
best_from, best_to, best_size = best_info
|
110
|
+
while best_from > from_start and best_to > to_start and
|
111
|
+
(should_junk ?
|
112
|
+
@junks.has_key?(@to[best_to - 1]) :
|
113
|
+
!@junks.has_key?(@to[best_to - 1])) and
|
114
|
+
@from[best_from - 1] == @to[best_to - 1]
|
115
|
+
best_from -= 1
|
116
|
+
best_to -= 1
|
117
|
+
best_size += 1
|
118
|
+
end
|
119
|
+
|
120
|
+
while best_from + best_size < from_end and
|
121
|
+
best_to + best_size < to_end and
|
122
|
+
(should_junk ?
|
123
|
+
@junks.has_key?(@to[best_to + best_size]) :
|
124
|
+
!@junks.has_key?(@to[best_to + best_size])) and
|
125
|
+
@from[best_from + best_size] == @to[best_to + best_size]
|
126
|
+
best_size += 1
|
127
|
+
end
|
128
|
+
|
129
|
+
[best_from, best_to, best_size]
|
130
|
+
end
|
131
|
+
|
132
|
+
def matches
|
133
|
+
@matches ||= compute_matches
|
134
|
+
end
|
135
|
+
|
136
|
+
def compute_matches
|
137
|
+
matches = []
|
138
|
+
queue = [[0, @from.size, 0, @to.size]]
|
139
|
+
until queue.empty?
|
140
|
+
from_start, from_end, to_start, to_end = queue.pop
|
141
|
+
match = longest_match(from_start, from_end - 1, to_start, to_end - 1)
|
142
|
+
match_from_index, match_to_index, size = match
|
143
|
+
unless size.zero?
|
144
|
+
if from_start < match_from_index and
|
145
|
+
to_start < match_to_index
|
146
|
+
queue.push([from_start, match_from_index,
|
147
|
+
to_start, match_to_index])
|
148
|
+
end
|
149
|
+
matches << match
|
150
|
+
if match_from_index + size < from_end and
|
151
|
+
match_to_index + size < to_end
|
152
|
+
queue.push([match_from_index + size, from_end,
|
153
|
+
match_to_index + size, to_end])
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
matches.sort_by do |(from_index, to_index, match_size)|
|
158
|
+
from_index
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def compute_blocks
|
163
|
+
blocks = []
|
164
|
+
current_from_index = current_to_index = current_size = 0
|
165
|
+
matches.each do |from_index, to_index, size|
|
166
|
+
if current_from_index + current_size == from_index and
|
167
|
+
current_to_index + current_size == to_index
|
168
|
+
current_size += size
|
169
|
+
else
|
170
|
+
unless current_size.zero?
|
171
|
+
blocks << [current_from_index, current_to_index, current_size]
|
172
|
+
end
|
173
|
+
current_from_index = from_index
|
174
|
+
current_to_index = to_index
|
175
|
+
current_size = size
|
176
|
+
end
|
177
|
+
end
|
178
|
+
unless current_size.zero?
|
179
|
+
blocks << [current_from_index, current_to_index, current_size]
|
180
|
+
end
|
181
|
+
|
182
|
+
blocks << [@from.size, @to.size, 0]
|
183
|
+
blocks
|
184
|
+
end
|
185
|
+
|
186
|
+
def compute_operations
|
187
|
+
from_index = to_index = 0
|
188
|
+
operations = []
|
189
|
+
blocks.each do |match_from_index, match_to_index, size|
|
190
|
+
tag = determine_tag(from_index, to_index,
|
191
|
+
match_from_index, match_to_index)
|
192
|
+
if tag != :equal
|
193
|
+
operations << [tag,
|
194
|
+
from_index, match_from_index,
|
195
|
+
to_index, match_to_index]
|
196
|
+
end
|
197
|
+
|
198
|
+
from_index, to_index = match_from_index + size, match_to_index + size
|
199
|
+
if size > 0
|
200
|
+
operations << [:equal,
|
201
|
+
match_from_index, from_index,
|
202
|
+
match_to_index, to_index]
|
203
|
+
end
|
204
|
+
end
|
205
|
+
operations
|
206
|
+
end
|
207
|
+
|
208
|
+
def compute_ratio
|
209
|
+
matches = blocks.inject(0) {|result, block| result + block[-1]}
|
210
|
+
length = @from.length + @to.length
|
211
|
+
if length.zero?
|
212
|
+
1.0
|
213
|
+
else
|
214
|
+
2.0 * matches / length
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def determine_tag(from_index, to_index,
|
219
|
+
match_from_index, match_to_index)
|
220
|
+
if from_index < match_from_index and to_index < match_to_index
|
221
|
+
:replace
|
222
|
+
elsif from_index < match_from_index
|
223
|
+
:delete
|
224
|
+
elsif to_index < match_to_index
|
225
|
+
:insert
|
226
|
+
else
|
227
|
+
:equal
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def expand_edge_equal_operations!(_operations, context_size)
|
232
|
+
tag, from_start, from_end, to_start, to_end = _operations[0]
|
233
|
+
if tag == :equal
|
234
|
+
_operations[0] = [tag,
|
235
|
+
[from_start, from_end - context_size].max,
|
236
|
+
from_end,
|
237
|
+
[to_start, to_end - context_size].max,
|
238
|
+
to_end]
|
239
|
+
end
|
240
|
+
|
241
|
+
tag, from_start, from_end, to_start, to_end = _operations[-1]
|
242
|
+
if tag == :equal
|
243
|
+
_operations[-1] = [tag,
|
244
|
+
from_start,
|
245
|
+
[from_end, from_start + context_size].min,
|
246
|
+
to_start,
|
247
|
+
[to_end, to_start + context_size].min]
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
class Differ
|
253
|
+
def initialize(from, to)
|
254
|
+
@from = from
|
255
|
+
@to = to
|
256
|
+
end
|
257
|
+
|
258
|
+
private
|
259
|
+
def tag(mark, contents)
|
260
|
+
contents.collect {|content| "#{mark}#{content}"}
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
class ReadableDiffer < Differ
|
265
|
+
def diff(options={})
|
266
|
+
result = []
|
267
|
+
matcher = SequenceMatcher.new(@from, @to)
|
268
|
+
matcher.operations.each do |args|
|
269
|
+
tag, from_start, from_end, to_start, to_end = args
|
270
|
+
case tag
|
271
|
+
when :replace
|
272
|
+
result.concat(diff_lines(from_start, from_end, to_start, to_end))
|
273
|
+
when :delete
|
274
|
+
result.concat(tag_deleted(@from[from_start...from_end]))
|
275
|
+
when :insert
|
276
|
+
result.concat(tag_inserted(@to[to_start...to_end]))
|
277
|
+
when :equal
|
278
|
+
result.concat(tag_equal(@from[from_start...from_end]))
|
279
|
+
else
|
280
|
+
raise "unknown tag: #{tag}"
|
281
|
+
end
|
282
|
+
end
|
283
|
+
result
|
284
|
+
end
|
285
|
+
|
286
|
+
private
|
287
|
+
def tag_deleted(contents)
|
288
|
+
tag("- ", contents)
|
289
|
+
end
|
290
|
+
|
291
|
+
def tag_inserted(contents)
|
292
|
+
tag("+ ", contents)
|
293
|
+
end
|
294
|
+
|
295
|
+
def tag_equal(contents)
|
296
|
+
tag(" ", contents)
|
297
|
+
end
|
298
|
+
|
299
|
+
def tag_difference(contents)
|
300
|
+
tag("? ", contents)
|
301
|
+
end
|
302
|
+
|
303
|
+
def find_diff_line_info(from_start, from_end, to_start, to_end)
|
304
|
+
best_ratio = 0.74
|
305
|
+
from_equal_index = to_equal_index = nil
|
306
|
+
from_best_index = to_best_index = nil
|
307
|
+
|
308
|
+
to_start.upto(to_end - 1) do |to_index|
|
309
|
+
from_start.upto(from_end - 1) do |from_index|
|
310
|
+
if @from[from_index] == @to[to_index]
|
311
|
+
from_equal_index ||= from_index
|
312
|
+
to_equal_index ||= to_index
|
313
|
+
next
|
314
|
+
end
|
315
|
+
|
316
|
+
matcher = SequenceMatcher.new(@from[from_index], @to[to_index],
|
317
|
+
&method(:space_character?))
|
318
|
+
if matcher.ratio > best_ratio
|
319
|
+
best_ratio = matcher.ratio
|
320
|
+
from_best_index = from_index
|
321
|
+
to_best_index = to_index
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
[best_ratio,
|
327
|
+
from_equal_index, to_equal_index,
|
328
|
+
from_best_index, to_best_index]
|
329
|
+
end
|
330
|
+
|
331
|
+
def diff_lines(from_start, from_end, to_start, to_end)
|
332
|
+
cut_off = 0.75
|
333
|
+
|
334
|
+
info = find_diff_line_info(from_start, from_end, to_start, to_end)
|
335
|
+
best_ratio, from_equal_index, to_equal_index, *info = info
|
336
|
+
from_best_index, to_best_index = info
|
337
|
+
|
338
|
+
if best_ratio < cut_off
|
339
|
+
if from_equal_index.nil?
|
340
|
+
tagged_from = tag_deleted(@from[from_start...from_end])
|
341
|
+
tagged_to = tag_inserted(@to[to_start...to_end])
|
342
|
+
if to_end - to_start < from_end - from_start
|
343
|
+
return tagged_to + tagged_from
|
344
|
+
else
|
345
|
+
return tagged_from + tagged_to
|
346
|
+
end
|
347
|
+
end
|
348
|
+
from_best_index = from_equal_index
|
349
|
+
to_best_index = to_equal_index
|
350
|
+
best_ratio = 1.0
|
351
|
+
end
|
352
|
+
|
353
|
+
_diff_lines(from_start, from_best_index, to_start, to_best_index) +
|
354
|
+
diff_line(@from[from_best_index], @to[to_best_index]) +
|
355
|
+
_diff_lines(from_best_index + 1, from_end, to_best_index + 1, to_end)
|
356
|
+
end
|
357
|
+
|
358
|
+
def _diff_lines(from_start, from_end, to_start, to_end)
|
359
|
+
if from_start < from_end
|
360
|
+
if to_start < to_end
|
361
|
+
diff_lines(from_start, from_end, to_start, to_end)
|
362
|
+
else
|
363
|
+
tag_deleted(@from[from_start...from_end])
|
364
|
+
end
|
365
|
+
else
|
366
|
+
tag_inserted(@to[to_start...to_end])
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
def diff_line(from_line, to_line)
|
371
|
+
from_tags = ""
|
372
|
+
to_tags = ""
|
373
|
+
matcher = SequenceMatcher.new(from_line, to_line,
|
374
|
+
&method(:space_character?))
|
375
|
+
matcher.operations.each do |tag, from_start, from_end, to_start, to_end|
|
376
|
+
from_length = from_end - from_start
|
377
|
+
to_length = to_end - to_start
|
378
|
+
case tag
|
379
|
+
when :replace
|
380
|
+
from_tags << "^" * from_length
|
381
|
+
to_tags << "^" * to_length
|
382
|
+
when :delete
|
383
|
+
from_tags << "-" * from_length
|
384
|
+
when :insert
|
385
|
+
to_tags << "+" * to_length
|
386
|
+
when :equal
|
387
|
+
from_tags << " " * from_length
|
388
|
+
to_tags << " " * to_length
|
389
|
+
else
|
390
|
+
raise "unknown tag: #{tag}"
|
391
|
+
end
|
392
|
+
end
|
393
|
+
format_diff_point(from_line, to_line, from_tags, to_tags)
|
394
|
+
end
|
395
|
+
|
396
|
+
def format_diff_point(from_line, to_line, from_tags, to_tags)
|
397
|
+
common = [n_leading_characters(from_line, ?\t),
|
398
|
+
n_leading_characters(to_line, ?\t)].min
|
399
|
+
common = [common, n_leading_characters(from_tags[0, common], " "[0])].min
|
400
|
+
from_tags = from_tags[common..-1].rstrip
|
401
|
+
to_tags = to_tags[common..-1].rstrip
|
402
|
+
|
403
|
+
result = tag_deleted([from_line])
|
404
|
+
unless from_tags.empty?
|
405
|
+
result.concat(tag_difference(["#{"\t" * common}#{from_tags}"]))
|
406
|
+
end
|
407
|
+
result.concat(tag_inserted([to_line]))
|
408
|
+
unless to_tags.empty?
|
409
|
+
result.concat(tag_difference(["#{"\t" * common}#{to_tags}"]))
|
410
|
+
end
|
411
|
+
result
|
412
|
+
end
|
413
|
+
|
414
|
+
def n_leading_characters(string, character)
|
415
|
+
n = 0
|
416
|
+
while string[n] == character
|
417
|
+
n += 1
|
418
|
+
end
|
419
|
+
n
|
420
|
+
end
|
421
|
+
|
422
|
+
def space_character?(character)
|
423
|
+
[" "[0], "\t"[0]].include?(character)
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
class UnifiedDiffer < Differ
|
428
|
+
def diff(options={})
|
429
|
+
groups = SequenceMatcher.new(@from, @to).grouped_operations
|
430
|
+
return [] if groups.empty?
|
431
|
+
return [] if same_content?(groups)
|
432
|
+
|
433
|
+
show_context = options[:show_context]
|
434
|
+
show_context = true if show_context.nil?
|
435
|
+
result = ["--- #{options[:from_label]}".rstrip,
|
436
|
+
"+++ #{options[:to_label]}".rstrip]
|
437
|
+
groups.each do |operations|
|
438
|
+
result << format_summary(operations, show_context)
|
439
|
+
operations.each do |args|
|
440
|
+
operation_tag, from_start, from_end, to_start, to_end = args
|
441
|
+
case operation_tag
|
442
|
+
when :replace
|
443
|
+
result.concat(tag("-", @from[from_start...from_end]))
|
444
|
+
result.concat(tag("+", @to[to_start...to_end]))
|
445
|
+
when :delete
|
446
|
+
result.concat(tag("-", @from[from_start...from_end]))
|
447
|
+
when :insert
|
448
|
+
result.concat(tag("+", @to[to_start...to_end]))
|
449
|
+
when :equal
|
450
|
+
result.concat(tag(" ", @from[from_start...from_end]))
|
451
|
+
end
|
452
|
+
end
|
453
|
+
end
|
454
|
+
result
|
455
|
+
end
|
456
|
+
|
457
|
+
private
|
458
|
+
def same_content?(groups)
|
459
|
+
return false if groups.size != 1
|
460
|
+
group = groups[0]
|
461
|
+
return false if group.size != 1
|
462
|
+
tag, from_start, from_end, to_start, to_end = group[0]
|
463
|
+
|
464
|
+
tag == :equal and [from_start, from_end] == [to_start, to_end]
|
465
|
+
end
|
466
|
+
|
467
|
+
def format_summary(operations, show_context)
|
468
|
+
_, first_from_start, _, first_to_start, _ = operations[0]
|
469
|
+
_, _, last_from_end, _, last_to_end = operations[-1]
|
470
|
+
summary = "@@ -%d,%d +%d,%d @@" % [first_from_start + 1,
|
471
|
+
last_from_end - first_from_start,
|
472
|
+
first_to_start + 1,
|
473
|
+
last_to_end - first_to_start,]
|
474
|
+
if show_context
|
475
|
+
interesting_line = find_interesting_line(first_from_start,
|
476
|
+
first_to_start,
|
477
|
+
:define_line?)
|
478
|
+
summary << " #{interesting_line}" if interesting_line
|
479
|
+
end
|
480
|
+
summary
|
481
|
+
end
|
482
|
+
|
483
|
+
def find_interesting_line(from_start, to_start, predicate)
|
484
|
+
from_index = from_start
|
485
|
+
to_index = to_start
|
486
|
+
while from_index >= 0 or to_index >= 0
|
487
|
+
[@from[from_index], @to[to_index]].each do |line|
|
488
|
+
return line if line and send(predicate, line)
|
489
|
+
end
|
490
|
+
|
491
|
+
from_index -= 1
|
492
|
+
to_index -= 1
|
493
|
+
end
|
494
|
+
nil
|
495
|
+
end
|
496
|
+
|
497
|
+
def define_line?(line)
|
498
|
+
/\A(?:[_a-zA-Z$]|\s*(?:class|module|def)\b)/ =~ line
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
module_function
|
503
|
+
def readable(from, to, options={})
|
504
|
+
diff(ReadableDiffer, from, to, options)
|
505
|
+
end
|
506
|
+
|
507
|
+
def unified(from, to, options={})
|
508
|
+
diff(UnifiedDiffer, from, to, options)
|
509
|
+
end
|
510
|
+
|
511
|
+
def diff(differ_class, from, to, options={})
|
512
|
+
differ = differ_class.new(from.split(/\r?\n/), to.split(/\r?\n/))
|
513
|
+
differ.diff(options).join("\n")
|
514
|
+
end
|
515
|
+
end
|
516
|
+
end
|
File without changes
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Test
|
2
|
+
module Unit
|
3
|
+
class TestResult
|
4
|
+
attr_reader :notifications
|
5
|
+
|
6
|
+
alias_method(:initialize_without_notifications, :initialize)
|
7
|
+
def initialize
|
8
|
+
initialize_without_notifications
|
9
|
+
@notifications = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_notification(notification)
|
13
|
+
@notifications << notification
|
14
|
+
notify_listeners(FAULT, notification)
|
15
|
+
notify_listeners(CHANGED, self)
|
16
|
+
end
|
17
|
+
|
18
|
+
def notification_count
|
19
|
+
@notifications.size
|
20
|
+
end
|
21
|
+
|
22
|
+
alias_method(:to_s_without_notifications, :to_s)
|
23
|
+
def to_s
|
24
|
+
to_s_without_notifications + ", #{notification_count} notifications"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Notification
|
29
|
+
include Util::BacktraceFilter
|
30
|
+
attr_reader :test_name, :location, :message
|
31
|
+
|
32
|
+
SINGLE_CHARACTER = 'N'
|
33
|
+
|
34
|
+
# Creates a new Notification with the given location and
|
35
|
+
# message.
|
36
|
+
def initialize(test_name, location, message)
|
37
|
+
@test_name = test_name
|
38
|
+
@location = location
|
39
|
+
@message = message
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns a single character representation of a notification.
|
43
|
+
def single_character_display
|
44
|
+
SINGLE_CHARACTER
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns a brief version of the error description.
|
48
|
+
def short_display
|
49
|
+
"#@test_name: #{@message.split("\n")[0]}"
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns a verbose version of the error description.
|
53
|
+
def long_display
|
54
|
+
location_display = filter_backtrace(location)[0]
|
55
|
+
location_display = location_display.sub(/\A(.+:\d+).*/, ' [\\1]')
|
56
|
+
"Notification:\n#{@test_name}#{location_display}:\n#{@message}"
|
57
|
+
end
|
58
|
+
|
59
|
+
# Overridden to return long_display.
|
60
|
+
def to_s
|
61
|
+
long_display
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class NotifiedError < StandardError
|
66
|
+
end
|
67
|
+
|
68
|
+
module AssertionsWithNotify
|
69
|
+
def notify(message)
|
70
|
+
notification = Notification.new(name, caller[0, 1], message)
|
71
|
+
@_result.add_notification(notification)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class TestCase
|
76
|
+
include AssertionsWithNotify
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Test
|
2
|
+
module Unit
|
3
|
+
class TestResult
|
4
|
+
attr_reader :omissions
|
5
|
+
|
6
|
+
alias_method(:initialize_without_omissions, :initialize)
|
7
|
+
def initialize
|
8
|
+
initialize_without_omissions
|
9
|
+
@omissions = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_omission(omission)
|
13
|
+
@omissions << omission
|
14
|
+
notify_listeners(FAULT, omission)
|
15
|
+
notify_listeners(CHANGED, self)
|
16
|
+
end
|
17
|
+
|
18
|
+
def omission_count
|
19
|
+
@omissions.size
|
20
|
+
end
|
21
|
+
|
22
|
+
alias_method(:to_s_without_omissions, :to_s)
|
23
|
+
def to_s
|
24
|
+
to_s_without_omissions + ", #{omission_count} omissions"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Omission
|
29
|
+
include Util::BacktraceFilter
|
30
|
+
attr_reader :test_name, :location, :message
|
31
|
+
|
32
|
+
SINGLE_CHARACTER = 'O'
|
33
|
+
|
34
|
+
# Creates a new Omission with the given location and
|
35
|
+
# message.
|
36
|
+
def initialize(test_name, location, message)
|
37
|
+
@test_name = test_name
|
38
|
+
@location = location
|
39
|
+
@message = message
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns a single character representation of a omission.
|
43
|
+
def single_character_display
|
44
|
+
SINGLE_CHARACTER
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns a brief version of the error description.
|
48
|
+
def short_display
|
49
|
+
"#@test_name: #{@message.split("\n")[0]}"
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns a verbose version of the error description.
|
53
|
+
def long_display
|
54
|
+
backtrace = filter_backtrace(location).join("\n")
|
55
|
+
"Omission:\n#{@test_name}\n#{@message}\n#{backtrace}"
|
56
|
+
end
|
57
|
+
|
58
|
+
# Overridden to return long_display.
|
59
|
+
def to_s
|
60
|
+
long_display
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class OmittedError < StandardError
|
65
|
+
end
|
66
|
+
|
67
|
+
module AssertionsWithOmit
|
68
|
+
def omit(message="Omitted", &block)
|
69
|
+
if block_given?
|
70
|
+
begin
|
71
|
+
yield
|
72
|
+
rescue Exception
|
73
|
+
raise OmittedError, message, $!.backtrace
|
74
|
+
end
|
75
|
+
flunk("Omission block should not be passed: #{message}")
|
76
|
+
else
|
77
|
+
raise OmittedError.new(message)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class TestCase
|
83
|
+
include AssertionsWithOmit
|
84
|
+
|
85
|
+
alias_method(:add_error_without_omission, :add_error)
|
86
|
+
def add_error(exception)
|
87
|
+
if exception.is_a?(OmittedError)
|
88
|
+
omission = Omission.new(name, exception.backtrace, exception.message)
|
89
|
+
@_result.add_omission(omission)
|
90
|
+
else
|
91
|
+
add_error_without_omission(exception)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|