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.
Files changed (53) hide show
  1. data/CHANGES +6 -30
  2. data/README +4 -2
  3. data/Rakefile +7 -0
  4. data/data/locale/en/LC_MESSAGES/active-ldap.mo +0 -0
  5. data/data/locale/ja/LC_MESSAGES/active-ldap.mo +0 -0
  6. data/examples/al-admin/po/en/al-admin.po +3 -3
  7. data/examples/al-admin/po/ja/al-admin.po +3 -3
  8. data/examples/al-admin/po/nl/al-admin.po +3 -3
  9. data/lib/active_ldap/adapter/base.rb +0 -2
  10. data/lib/active_ldap/base.rb +4 -2
  11. data/lib/active_ldap/get_text_support.rb +3 -11
  12. data/lib/active_ldap/operations.rb +1 -0
  13. data/lib/active_ldap/schema/syntaxes.rb +5 -3
  14. data/lib/active_ldap/validations.rb +25 -15
  15. data/lib/active_ldap.rb +1 -1
  16. data/po/en/active-ldap.po +1 -1
  17. data/po/ja/active-ldap.po +1 -1
  18. data/rails/plugin/active_ldap/init.rb +1 -1
  19. data/test/run-test.rb +2 -0
  20. data/test/test_base.rb +63 -1
  21. data/test/test_syntax.rb +3 -2
  22. data/test-unit-ext/NEWS.en +28 -0
  23. data/test-unit-ext/NEWS.ja +28 -0
  24. data/test-unit-ext/README.en +247 -0
  25. data/test-unit-ext/README.ja +246 -0
  26. data/test-unit-ext/Rakefile +111 -0
  27. data/{test → test-unit-ext/lib}/test-unit-ext/always-show-result.rb +0 -0
  28. data/test-unit-ext/lib/test-unit-ext/assertions.rb +40 -0
  29. data/test-unit-ext/lib/test-unit-ext/attributes.rb +129 -0
  30. data/{test → test-unit-ext/lib}/test-unit-ext/backtrace-filter.rb +0 -0
  31. data/test-unit-ext/lib/test-unit-ext/color.rb +59 -0
  32. data/test-unit-ext/lib/test-unit-ext/colorized-runner.rb +111 -0
  33. data/test-unit-ext/lib/test-unit-ext/diff.rb +516 -0
  34. data/{test → test-unit-ext/lib}/test-unit-ext/long-display-for-emacs.rb +0 -0
  35. data/test-unit-ext/lib/test-unit-ext/notification.rb +79 -0
  36. data/test-unit-ext/lib/test-unit-ext/omission.rb +96 -0
  37. data/test-unit-ext/lib/test-unit-ext/pending.rb +97 -0
  38. data/{test → test-unit-ext/lib}/test-unit-ext/priority.rb +25 -53
  39. data/test-unit-ext/lib/test-unit-ext/version.rb +3 -0
  40. data/test-unit-ext/lib/test-unit-ext/xml-report.rb +224 -0
  41. data/test-unit-ext/lib/test-unit-ext.rb +16 -0
  42. data/test-unit-ext/misc/rd2html.rb +42 -0
  43. data/test-unit-ext/test/run-test.rb +14 -0
  44. data/test-unit-ext/test/test_attributes.rb +139 -0
  45. data/test-unit-ext/test/test_color.rb +39 -0
  46. data/test-unit-ext/test/test_diff.rb +475 -0
  47. data/test-unit-ext/test/test_notification.rb +32 -0
  48. data/test-unit-ext/test/test_omission.rb +64 -0
  49. data/test-unit-ext/test/test_pending.rb +64 -0
  50. data/test-unit-ext/test/test_priority.rb +88 -0
  51. data/test-unit-ext/test/test_xml_report.rb +161 -0
  52. metadata +34 -7
  53. 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
@@ -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