quality_extensions 1.1.4 → 1.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/lib/Xfind_bug_test.rb +28 -0
  2. data/lib/quality_extensions/array/all_same.rb +40 -0
  3. data/lib/quality_extensions/array/delete_if_bang.rb +145 -0
  4. data/lib/quality_extensions/array/expand_ranges.rb +3 -53
  5. data/lib/quality_extensions/array/include_any_of.rb +23 -0
  6. data/lib/quality_extensions/array/justify.rb +305 -0
  7. data/lib/quality_extensions/array/{average.rb → mean.rb} +1 -1
  8. data/lib/quality_extensions/array/select_if_bang.rb +0 -0
  9. data/lib/quality_extensions/array/sum.rb +30 -0
  10. data/lib/quality_extensions/enumerable/all_same.rb +43 -0
  11. data/lib/quality_extensions/enumerable/group_by_and_map.rb +110 -0
  12. data/lib/quality_extensions/enumerable/select_bang.rb +49 -0
  13. data/lib/quality_extensions/enumerable/select_while.rb +337 -52
  14. data/lib/quality_extensions/enumerable/select_with_index.rb +145 -0
  15. data/lib/quality_extensions/hash/hash_select.rb +5 -2
  16. data/lib/quality_extensions/hash/merge_if.rb +48 -0
  17. data/lib/quality_extensions/kernel/example_printer.rb +10 -3
  18. data/lib/quality_extensions/kernel/require_all.rb +24 -8
  19. data/lib/quality_extensions/kernel/sleep_loudly.rb +108 -0
  20. data/lib/quality_extensions/kernel/uninterruptable.rb +22 -0
  21. data/lib/quality_extensions/matrix/indexable.rb +68 -0
  22. data/lib/quality_extensions/matrix/linked_vectors.rb +137 -0
  23. data/lib/quality_extensions/module/class_methods.rb +2 -0
  24. data/lib/quality_extensions/object/non.rb +12 -0
  25. data/lib/quality_extensions/pathname.rb +435 -7
  26. data/lib/quality_extensions/range_list.rb +222 -0
  27. data/lib/quality_extensions/safe_nil.rb +5 -3
  28. data/lib/quality_extensions/string/each_char_with_index.rb +1 -2
  29. data/lib/quality_extensions/string/integer_eh.rb +3 -0
  30. data/lib/quality_extensions/string/numeric_eh.rb +74 -0
  31. data/lib/quality_extensions/string/safe_in_comment.rb +38 -0
  32. data/lib/quality_extensions/string/safe_numeric_conversion.rb +102 -0
  33. data/lib/quality_extensions/string/shell_escape.rb +4 -4
  34. data/lib/quality_extensions/string/with_knowledge_of_color.rb +8 -0
  35. data/lib/quality_extensions/table.rb +116 -0
  36. data/lib/quality_extensions/template.rb +4 -5
  37. data/lib/quality_extensions/template.rb_test_unit.rb +33 -0
  38. data/lib/quality_extensions/test/difference_highlighting-minitest.rb +321 -0
  39. data/lib/quality_extensions/test/difference_highlighting-test_unit.rb +325 -0
  40. data/lib/quality_extensions/test/difference_highlighting.rb +6 -314
  41. data/lib/quality_extensions/timeout/countdown_timer.rb +1 -0
  42. data/lib/quality_extensions/vector/enumerable.rb +51 -0
  43. metadata +35 -5
@@ -0,0 +1,321 @@
1
+ #--
2
+ # Author:: Tyler Rick
3
+ # Copyright:: Copyright (c) 2007 QualitySmith, Inc., 2009 Tyler Rick
4
+ # License:: Ruby License
5
+ # Submit to Facets?::
6
+ # Developer notes::
7
+ # To do:
8
+ # * do the same for refute_equal, which unfortunately does not simply wrap assert_equal
9
+ #++
10
+ # This file adds a bit of color to your failed string comparisons (assert_equal).
11
+ # Differences will be highlighted for you in color so that you can instantly find them.
12
+
13
+
14
+ $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
15
+ require 'rubygems'
16
+ gem 'colored'
17
+ require 'colored'
18
+ gem 'facets'
19
+ require 'facets/module/alias_method_chain'
20
+ require 'quality_extensions/object/send_if'
21
+ require 'quality_extensions/string/each_char_with_index'
22
+ require 'quality_extensions/module/bool_attr_accessor'
23
+ require 'quality_extensions/module/guard_method'
24
+ require 'quality_extensions/colored/toggleability'
25
+
26
+ require 'minitest/unit'
27
+
28
+ class String
29
+ # For all of the next 3 methods, we will underline spaces so that they are actually visible on the screen.
30
+ # Newlines will be replaced with '\n'"\n".
31
+
32
+ # This is a (sub)string that is common to both expected and actual
33
+ def highlight_commonality
34
+ self.
35
+ make_control_characters_visible.
36
+ make_spaces_visible(:green).
37
+ green.bold
38
+ #send_unless(self == ' ', :bold) # spaces are not bold; '_'s (and everything else) are
39
+ end
40
+ # This is a (sub)string that is different between expected and actual
41
+ def highlight_difference
42
+ self.
43
+ make_control_characters_visible.
44
+ make_spaces_visible(:red).
45
+ red.bold
46
+ #send_unless(self == ' ', :bold) # spaces are not bold; '_'s (and everything else) are
47
+ end
48
+ # This is a (sub)string that exists only in *self*, not in the other string
49
+ def highlight_unique
50
+ self.
51
+ make_control_characters_visible.
52
+ make_spaces_visible(:magenta).
53
+ magenta
54
+ end
55
+ # This is a (sub)string that doesn't exist in self, only in the *other* string. It's just a placeholder character (a space) that represents a *missing* character.
56
+ def highlight_absence
57
+ self.white.on_cyan.bold
58
+ end
59
+ def make_spaces_visible(color)
60
+ #:todo: Make this optional? Might be useful if you are comparing things with lots of spaces and underscores and you want to be able to tell the difference between them...?
61
+ self.gsub(' ', ' '.send(:"on_#{color}"))
62
+ end
63
+ def make_control_characters_visible
64
+ self.gsub(/\n/, '\n'+"\n"). # Show '\n' in addition to actually doing the line break
65
+ gsub(/\r/, '\r'). # Just escape it...
66
+ gsub(/\t/, '\t')
67
+ #:todo: Add other control characters?
68
+ end
69
+ end
70
+
71
+ module MiniTest
72
+ # put in class Unit::TestCase ? but how could we access it from methods in Assertions module then?
73
+ @@inspect_strings = false
74
+ mguard_method :inspect_strings!, :@@inspect_strings
75
+
76
+ module Assertions
77
+
78
+ # The problem with the original convert() is that it always called #inspect on strings... which is fine if you really
79
+ # want to see all those \n's and such. But not so great if you want to visually compare the strings. And if you have
80
+ # ANSI color codes in the strings, it will escape those so that you see the codes (\e[33m1) rather than the nice
81
+ # colored strings that you (sometimes) *want* to see...
82
+ #
83
+ def mu_pp_with_option_to_not_use_inspect_for_strings(object)
84
+ if String === object
85
+ if MiniTest.inspect_strings?
86
+ # Use the original method, which used inspect
87
+ mu_pp_without_option_to_not_use_inspect_for_strings(object)
88
+ else
89
+ object
90
+ end
91
+ else
92
+ # We only care about strings. Everything else can just keep happening like it was before.
93
+ mu_pp_without_option_to_not_use_inspect_for_strings(object)
94
+ end
95
+ end
96
+ alias_method_chain :mu_pp, :option_to_not_use_inspect_for_strings
97
+
98
+
99
+
100
+ @@use_assert_equal_with_highlight = nil
101
+ mguard_method :use_assert_equal_with_highlight!, :@@use_assert_equal_with_highlight
102
+
103
+ # The built-in behavior for <tt>assert_equal</tt> method is great for comparing small strings, but not so great for long strings.
104
+ # If both the strings you are dealing with are both 20 paragraphs long, for example, and they differ by only one character,
105
+ # the task of locating and identifying the one character that is off is akin to finding a (literal) needle in a
106
+ # (literal) haystack (not fun!).
107
+ #
108
+ # link:include/assert_equal_with_difference_highlighting-wheres_waldo.png
109
+ #
110
+ # This replacement/wrapper for <tt>assert_equal</tt> aims to solve all of your string comparison woes (and eventually someday
111
+ # perhaps arrays and hashes as well), helping you to spot differences very quickly and to have fun doing it.
112
+ #
113
+ # Rather than simply showing you the raw (<tt>inspect</tt>ed) +expected+ and +actual+ and expecting you, the poor user, to
114
+ # painstakingly compare the two and figure out exactly which characters are different by yourself this method will *highlight*
115
+ # the differences for you, allowing you to spot them an instant or less!!
116
+ #
117
+ # link:include/assert_equal_with_difference_highlighting-there_he_is.png
118
+ #
119
+ # *Strings*:
120
+ # * Does a characterwise comparison between the two strings. That is, for each index, it will look at the character at that
121
+ # index and decide if it is the same or different than the character at the same location in the other string. There are
122
+ # 3 1/2 cases:
123
+ # * *Common* characters are displayed in _green_,
124
+ # * *Different* characters in _red_,
125
+ # * Characters that exist <b>in only one string</b> but not the other are displayed in _yellow_
126
+ # * A _cyan_ <tt>~</tt> will appear that location in the _other_ string, as a placeholder for the <b>missing character</b>.
127
+ # *Arrays*:
128
+ # * [:todo:]
129
+ # *Hashes*:
130
+ # * [:todo:]
131
+ #
132
+ # <b>Disabling/enabling highlighting</b>:
133
+ #
134
+ # By default, highlighting is only used when one or both of the strings being compared is long or spans multiple lines. You can override the default with the <tt>:higlight</tt> option:
135
+ # assert_equal 'really long string', 'another really long string', :highlight => true
136
+ # You can turn it on for all assert_equal assertions by calling
137
+ # MiniTest::Assertions::use_assert_equal_with_highlight!
138
+ # Or you can just turn it on or off for the duration of a block only:
139
+ # MiniTest::Assertions::use_assert_equal_with_highlight! do
140
+ # assert_equal 'really long string', 'another really long string'
141
+ # end
142
+ #
143
+ # *Notes*:
144
+ # * Spaces are displayed as with a bright colored background so that they are actually visible on the screen (so you can distinguish an empty line from a line with spaces on it, for example).
145
+ # * Newlines are displayed as the text <tt>\n</tt> followed by the actual newline. Other control characters (<tt>\t</tt>, <tt>\r</tt>) are escaped as well so that you can tell what character it is.
146
+ #
147
+ # <b>Difference in method signature</b> from <tt>assert_equal_without_difference_highlighting</tt> (the standard behavior):
148
+ # * The last argument (+options+) is expected to be a hash rather than message=nil, since I don't see the use in passing in a message if
149
+ # the _default_ message can be made useful enough.
150
+ # * However, for compatibility with existing assert_equal calls, it will check if the 3rd argument is a string and if it is will use it as the failure message.
151
+ # * If you to pass in a message in combination with other options, use <tt>:message => 'my message'</tt>
152
+ #
153
+ # *Advanced*:
154
+ # * If you want everything to be escaped (so you can see the color _codes_ instead of the color itself, for example), use <tt>:inspect_strings => true</tt>
155
+ #
156
+ def assert_equal_with_highlighting(expected, actual, options = {})
157
+ if options.is_a?(String)
158
+ message = options
159
+ options = {}
160
+ else
161
+ message = options.delete(:message) || nil
162
+ end
163
+ highlight = options.delete(:highlight)
164
+
165
+ Assertions.send_unless(highlight.nil?, :use_assert_equal_with_highlight!, highlight) do
166
+
167
+ if String===expected and String===actual and expected!=actual and
168
+ (Assertions.use_assert_equal_with_highlight? || [expected.length, actual.length].max > 80 || [expected, actual].any? {|a| a.include?("\n")}) and
169
+ !(Assertions.use_assert_equal_with_highlight? == false)
170
+
171
+ expected_with_highlighting = ''
172
+ actual_with_highlighting = ''
173
+ full_message = nil
174
+ String.color_on! do
175
+ longest_string = [expected, actual].max {|a, b| a.length <=> b.length}
176
+ longest_string.each_char_with_index do |i, exp|
177
+ exp = expected[i] ? expected[i].chr : nil
178
+ act = actual[i] ? actual[i].chr : nil
179
+ if act.nil?
180
+ expected_with_highlighting << exp.highlight_unique
181
+ actual_with_highlighting << '~'.highlight_absence
182
+ elsif exp.nil?
183
+ expected_with_highlighting << '~'.highlight_absence
184
+ actual_with_highlighting << act.highlight_unique
185
+ elsif exp != act
186
+ expected_with_highlighting << exp.highlight_difference
187
+ actual_with_highlighting << act.highlight_difference
188
+ else
189
+ expected_with_highlighting << exp.highlight_commonality
190
+ actual_with_highlighting << exp.highlight_commonality
191
+ end
192
+
193
+ end
194
+ full_message = message(message) { <<End }
195
+ #{(' '*50 + ' Expected: ' + ' '*50).blue.underline.on_white }
196
+ #{expected_with_highlighting}
197
+ #{(' '*50 + ' But was: ' + ' '*50).yellow.underline.on_red }
198
+ #{actual_with_highlighting}
199
+ End
200
+ end
201
+ MiniTest.inspect_strings!(options.delete(:inspect_strings) || false) do
202
+ assert(expected == actual, full_message)
203
+ end
204
+ else
205
+ assert_equal_without_highlighting(expected, actual, message)
206
+ end
207
+
208
+ end # use_assert_equal_with_highlight!
209
+
210
+ end # def assert_equal_with_highlighting
211
+ alias_method_chain :assert_equal, :highlighting
212
+
213
+ end # module Assertions
214
+ end
215
+
216
+ #puts MiniTest::Assertions.instance_methods
217
+
218
+
219
+
220
+
221
+
222
+
223
+
224
+
225
+ # _____ _
226
+ # |_ _|__ ___| |_
227
+ # | |/ _ \/ __| __|
228
+ # | | __/\__ \ |_
229
+ # |_|\___||___/\__|
230
+ #
231
+ =begin test
232
+ require 'minitest/autorun'
233
+ #MiniTest::Assertions::use_assert_equal_with_highlight!
234
+
235
+ # :todo: Currently these (intentionally failing) tests are just manual and require visual inspection. If possible it would be
236
+ # nice to capture the output of the failure and make an assertion against that. But I'm not sure how to do that...
237
+
238
+ class TheTest < MiniTest::Unit::TestCase
239
+ def test01_single_character_differences
240
+ assert_equal <<End, <<End
241
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed feugiat mi. In sagittis, augue non eleifend sodales, arcu urna congue sapien, aliquet molestie pede urna sit amet dolor. Etiam diam. Vestibulum ornare, felis et porta faucibus, magna sapien vulputate arcu, vel facilisis lectus ipsum et ipsum.
242
+
243
+ Vivamus massa odio, lacinia eu, euismod vitae, lobortis eu, erat. Duis tincidunt, neque ac_tincidunt convallis, nibh tellus sodales eros, ut tristique nunc purus in urna. Nullam semper. Fusce quis augue ut metus interdum congue. Duis id dolor eu mi pellentesque sagittis. Quisque imperdiet orci a odio.
244
+ End
245
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed feugiat mi. In sagittis, augue non eleifend sodales, arcu urna congue sapien, aliquet molestie pede urna sit amet dolor. Etiam diam. Vestibulum ornare, felis et porta faucibus, magna sapien vulputate arcu, vel facilisis lectus ipsum et ipsum.
246
+
247
+ Vivamus massa odio, lacinia eu, euismod vitae, lobortis eu, erat. Duis tincidunt, neque ac tincidunt convallis, nibh tellus sodales eros, ut tristique nunc purus in urna. Nullam semper. Fusce quis augue ut metus interdum congue. Duis id color eu mi pellentesque sagittis. Quisque imperdiet orci a odio.
248
+ End
249
+ end
250
+
251
+ def test02_difference_in_control_characters
252
+ assert_equal "Lorem ipsum dolor sit amet,\nconsectetuer adipiscing elit.\nSed feugiat mi.",
253
+ "Lorem ipsum_dolor sit amet, consectetuer\tadipiscing elit.\n\rSed feugiat mi.", :highlight => true
254
+ end
255
+
256
+ def test03_expected_is_longer
257
+ assert_equal '1234567890', '123', :highlight => true
258
+ end
259
+
260
+ def test04_actual_is_longer
261
+ assert_equal '123', '1234567890', :highlight => true
262
+ end
263
+
264
+ # Use the ones above this line as screenshot material...
265
+
266
+ def test05_one_is_much_longer
267
+ assert_equal <<End, <<End
268
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed feugiat mi. In sagittis, augue non eleifend sodales, arcu urna congue sapien, aliquet molestie pede urna sit amet dolor. Etiam diam. Vestibulum ornare, felis et porta faucibus, magna sapien vulputate arcu, vel facilisis lectus ipsum et ipsum. Sed dictum, dolor suscipit malesuada pharetra, orci augue lobortis lectus, porta porta magna magna ut dui. Duis viverra enim sed felis. Mauris semper volutpat pede. Integer lectus lorem, lacinia in, iaculis ut, euismod non, nulla. Nunc non libero eget diam congue ornare. Nunc dictum tellus sed turpis. Sed venenatis, pede non ultricies pharetra, dolor felis malesuada nisl, id imperdiet lorem dui vel velit.
269
+ End
270
+ Lorem ipsum dolor sit amet
271
+ End
272
+ end
273
+
274
+ def test06_only_minor_single_character_differences_but_then_it_gets_out_of_sync
275
+ assert_equal <<End, <<End
276
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed feugiat mi. In sagittis, augue non eleifend sodales, arcu urna congue sapien.
277
+
278
+ Vivamus massa odio, lacinia eu, euismod vitae, lobortis eu, erat. Duis tincidunt, neque ac tincidunt convallis, nibh tellus sodales eros, ut tristique nunc purus in urna. Nullam semper. Fusce quis augue ut metus interdum congue. Duis id dolor eu mi pellentesque sagittis. Quisque imperdiet orci a odio.
279
+ End
280
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed feugiat mi. In sagittis, argue non eleifend sodales, arcu urna conque sapien.
281
+
282
+ Vivamus massa odio, lacinia eu, euismod vitae, lobortis eu, erat. Duis tincidunt, neque ac tincidunt convallis, nibh tellus sodales eros, ut tristique nunc purus in urna. Nullam semper. Fusce quis augue ut metus interdum congue. Duis id dolor eu mi pellentesque sagittis. Quisque imperdiet orci a odio.
283
+ End
284
+ end
285
+
286
+ def test07_underscores_versus_underlines
287
+ assert_equal <<End, <<End
288
+ ___[Underscores]___[Underscores] [Spaces] _ _ _ _ _ [Mix]
289
+ End
290
+ [Spaces] ___[Underscores] [Spaces] _ _ __ _ _[Mix]
291
+ End
292
+ end
293
+
294
+ def test08_inspect_strings_true
295
+ assert_equal '1234567890', '123', :inspect_strings => true
296
+ end
297
+ def test09_inspect_highlight_false
298
+ assert_equal '1234567890', '123', :highlight => false
299
+ end
300
+ def test10_highlight_false
301
+ MiniTest::Assertions::use_assert_equal_with_highlight! false do
302
+ assert_equal 'really long string', 'another really long string'
303
+ end
304
+ end
305
+ def test11_highlight_true
306
+ MiniTest::Assertions::use_assert_equal_with_highlight! do
307
+ assert_equal 'really long string', 'another really long string'
308
+ end
309
+ end
310
+ def test12_compatibility__using_arg3_as_message
311
+ assert_equal 'really long string', 'another really long string', 'This is my message! Can you see it?'
312
+ end
313
+ def test13_that_assert_nil_still_works
314
+ # Exposed a bug that existed previously, where it tried to do "".delete(:message)
315
+ assert_nil nil
316
+ end
317
+
318
+ end
319
+ =end
320
+
321
+
@@ -0,0 +1,325 @@
1
+ #--
2
+ # Author:: Tyler Rick
3
+ # Copyright:: Copyright (c) 2007 QualitySmith, Inc.
4
+ # License:: Ruby License
5
+ # Submit to Facets?::
6
+ # Developer notes::
7
+ # To do:
8
+ # * what do we do about this new 'diff:' output from ./lib/test/unit/assertions.rb:905 (delayed_diff)?
9
+ #++
10
+ # This file adds a bit of color to your failed string comparisons (assert_equal).
11
+ # Differences will be highlighted for you in color so that you can instantly find them.
12
+
13
+ $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
14
+ require 'rubygems'
15
+ gem 'colored'
16
+ require 'colored'
17
+ gem 'facets'
18
+ require 'facets/module/alias_method_chain'
19
+ require 'quality_extensions/object/send_if'
20
+ require 'quality_extensions/string/each_char_with_index'
21
+ require 'quality_extensions/module/bool_attr_accessor'
22
+ require 'quality_extensions/module/guard_method'
23
+ require 'quality_extensions/colored/toggleability'
24
+
25
+ require 'test/unit'
26
+
27
+ class String
28
+ # For all of the next 3 methods, we will underline spaces so that they are actually visible on the screen.
29
+ # Newlines will be replaced with '\n'"\n".
30
+
31
+ # This is a (sub)string that is common to both expected and actual
32
+ def highlight_commonality
33
+ self.
34
+ make_control_characters_visible.
35
+ make_spaces_visible(:green).
36
+ green.bold
37
+ #send_unless(self == ' ', :bold) # spaces are not bold; '_'s (and everything else) are
38
+ end
39
+ # This is a (sub)string that is different between expected and actual
40
+ def highlight_difference
41
+ self.
42
+ make_control_characters_visible.
43
+ make_spaces_visible(:red).
44
+ red.bold
45
+ #send_unless(self == ' ', :bold) # spaces are not bold; '_'s (and everything else) are
46
+ end
47
+ # This is a (sub)string that exists only in *self*, not in the other string
48
+ def highlight_unique
49
+ self.
50
+ make_control_characters_visible.
51
+ make_spaces_visible(:magenta).
52
+ magenta
53
+ end
54
+ # This is a (sub)string that doesn't exist in self, only in the *other* string. It's just a placeholder character (a space) that represents a *missing* character.
55
+ def highlight_absence
56
+ self.white.on_cyan.bold
57
+ end
58
+ def make_spaces_visible(color)
59
+ #:todo: Make this optional? Might be useful if you are comparing things with lots of spaces and underscores and you want to be able to tell the difference between them...?
60
+ self.gsub(' ', ' '.send(:"on_#{color}"))
61
+ end
62
+ def make_control_characters_visible
63
+ self.gsub(/\n/, '\n'+"\n"). # Show '\n' in addition to actually doing the line break
64
+ gsub(/\r/, '\r'). # Just escape it...
65
+ gsub(/\t/, '\t')
66
+ #:todo: Add other control characters?
67
+ end
68
+ end
69
+
70
+ module Test
71
+ module Unit
72
+ module Assertions
73
+
74
+ class AssertionMessage
75
+ @@inspect_strings = false
76
+ mguard_method :inspect_strings!, :@@inspect_strings
77
+
78
+ # The problem with the original convert() is that it always called #inspect on strings... which is fine if you really
79
+ # want to see all those \n's and such. But not so great if you want to visually compare the strings. And if you have
80
+ # ANSI color codes in the strings, it will escape those so that you see the codes (\e[33m1) rather than the nice
81
+ # colored strings that you (sometimes) *want* to see...
82
+ #
83
+ def convert_with_option_to_not_use_inspect_for_strings(object)
84
+ if String === object
85
+ if self.class.inspect_strings?
86
+ # Use the original method, which used pp or inspect
87
+ convert_without_option_to_not_use_inspect_for_strings(object)
88
+ else
89
+ object
90
+ end
91
+ else
92
+ # We only care about strings. Everything else can just keep happening like it was before.
93
+ convert_without_option_to_not_use_inspect_for_strings(object)
94
+ end
95
+ end
96
+ alias_method_chain :convert, :option_to_not_use_inspect_for_strings
97
+ end # class AssertionMessage
98
+
99
+ end
100
+ end
101
+ end
102
+
103
+ module Test
104
+ module Unit
105
+ module Assertions
106
+ @@use_assert_equal_with_highlight = nil
107
+ mguard_method :use_assert_equal_with_highlight!, :@@use_assert_equal_with_highlight
108
+
109
+ # The built-in behavior for <tt>assert_equal</tt> method is great for comparing small strings, but not so great for long strings.
110
+ # If both the strings you are dealing with are both 20 paragraphs long, for example, and they differ by only one character,
111
+ # the task of locating and identifying the one character that is off is akin to finding a (literal) needle in a
112
+ # (literal) haystack (not fun!).
113
+ #
114
+ # link:include/assert_equal_with_difference_highlighting-wheres_waldo.png
115
+ #
116
+ # This replacement/wrapper for <tt>assert_equal</tt> aims to solve all of your string comparison woes (and eventually someday
117
+ # perhaps arrays and hashes as well), helping you to spot differences very quickly and to have fun doing it.
118
+ #
119
+ # Rather than simply showing you the raw (<tt>inspect</tt>ed) +expected+ and +actual+ and expecting you, the poor user, to
120
+ # painstakingly compare the two and figure out exactly which characters are different by yourself this method will *highlight*
121
+ # the differences for you, allowing you to spot them an instant or less!!
122
+ #
123
+ # link:include/assert_equal_with_difference_highlighting-there_he_is.png
124
+ #
125
+ # *Strings*:
126
+ # * Does a characterwise comparison between the two strings. That is, for each index, it will look at the character at that
127
+ # index and decide if it is the same or different than the character at the same location in the other string. There are
128
+ # 3 1/2 cases:
129
+ # * *Common* characters are displayed in _green_,
130
+ # * *Different* characters in _red_,
131
+ # * Characters that exist <b>in only one string</b> but not the other are displayed in _yellow_
132
+ # * A _cyan_ <tt>~</tt> will appear that location in the _other_ string, as a placeholder for the <b>missing character</b>.
133
+ # *Arrays*:
134
+ # * [:todo:]
135
+ # *Hashes*:
136
+ # * [:todo:]
137
+ #
138
+ # <b>Disabling/enabling highlighting</b>:
139
+ #
140
+ # By default, highlighting is only used when one or both of the strings being compared is long or spans multiple lines. You can override the default with the <tt>:higlight</tt> option:
141
+ # assert_equal 'really long string', 'another really long string', :highlight => true
142
+ # You can turn it on for all assert_equal assertions by calling
143
+ # Test::Unit::Assertions::use_assert_equal_with_highlight!
144
+ # Or you can just turn it on or off for the duration of a block only:
145
+ # Test::Unit::Assertions::use_assert_equal_with_highlight! do
146
+ # assert_equal 'really long string', 'another really long string'
147
+ # end
148
+ #
149
+ # *Notes*:
150
+ # * Spaces are displayed as with a bright colored background so that they are actually visible on the screen (so you can distinguish an empty line from a line with spaces on it, for example).
151
+ # * Newlines are displayed as the text <tt>\n</tt> followed by the actual newline. Other control characters (<tt>\t</tt>, <tt>\r</tt>) are escaped as well so that you can tell what character it is.
152
+ #
153
+ # <b>Difference in method signature</b> from <tt>assert_equal_without_difference_highlighting</tt> (the standard behavior):
154
+ # * The last argument (+options+) is expected to be a hash rather than message=nil, since I don't see the use in passing in a message if
155
+ # the _default_ message can be made useful enough.
156
+ # * However, for compatibility with existing assert_equal calls, it will check if the 3rd argument is a string and if it is will use it as the failure message.
157
+ # * If you to pass in a message in combination with other options, use <tt>:message => 'my message'</tt>
158
+ #
159
+ # *Advanced*:
160
+ # * If you want everything to be escaped (so you can see the color _codes_ instead of the color itself, for example), use <tt>:inspect_strings => true</tt>
161
+ #
162
+ def assert_equal_with_highlighting(expected, actual, options = {})
163
+ if options.is_a?(String)
164
+ message = options
165
+ options = {}
166
+ else
167
+ message = options.delete(:message) || nil
168
+ end
169
+ highlight = options.delete(:highlight)
170
+
171
+ Assertions.send_unless(highlight.nil?, :use_assert_equal_with_highlight!, highlight) do
172
+
173
+ if String===expected and String===actual and expected!=actual and
174
+ (Assertions.use_assert_equal_with_highlight? || [expected.length, actual.length].max > 80 || [expected, actual].any? {|a| a.include?("\n")}) and
175
+ !(Assertions.use_assert_equal_with_highlight? == false)
176
+ expected_with_highlighting = ''
177
+ actual_with_highlighting = ''
178
+ full_message = nil
179
+ String.color_on! do
180
+ longest_string = [expected, actual].max {|a, b| a.length <=> b.length}
181
+ longest_string.each_char_with_index do |i, exp|
182
+ exp = expected[i] ? expected[i].chr : nil
183
+ act = actual[i] ? actual[i].chr : nil
184
+ if act.nil?
185
+ expected_with_highlighting << exp.highlight_unique
186
+ actual_with_highlighting << '~'.highlight_absence
187
+ elsif exp.nil?
188
+ expected_with_highlighting << '~'.highlight_absence
189
+ actual_with_highlighting << act.highlight_unique
190
+ elsif exp != act
191
+ expected_with_highlighting << exp.highlight_difference
192
+ actual_with_highlighting << act.highlight_difference
193
+ else
194
+ expected_with_highlighting << exp.highlight_commonality
195
+ actual_with_highlighting << exp.highlight_commonality
196
+ end
197
+
198
+ end
199
+ full_message = build_message(message, <<End, expected_with_highlighting, actual_with_highlighting)
200
+ #{(' '*50 + ' Expected: ' + ' '*50).blue.underline.on_white }
201
+ ?
202
+ #{(' '*50 + ' But was: ' + ' '*50).yellow.underline.on_red }
203
+ ?
204
+ End
205
+ end
206
+ AssertionMessage.inspect_strings!(options.delete(:inspect_strings) || false) do
207
+ assert_block(full_message) { expected == actual }
208
+ end
209
+ else
210
+ assert_equal_without_highlighting(expected, actual, message)
211
+ end
212
+
213
+ end # use_assert_equal_with_highlight!
214
+
215
+ end # def assert_equal_with_highlighting
216
+ alias_method_chain :assert_equal, :highlighting
217
+
218
+ end
219
+ end
220
+ end
221
+
222
+
223
+
224
+
225
+
226
+
227
+
228
+
229
+ # _____ _
230
+ # |_ _|__ ___| |_
231
+ # | |/ _ \/ __| __|
232
+ # | | __/\__ \ |_
233
+ # |_|\___||___/\__|
234
+ #
235
+ =begin test
236
+ require 'test/unit'
237
+ #Test::Unit::Assertions::use_assert_equal_with_highlight!
238
+
239
+ # :todo: Currently these (intentionally failing) tests are just manual and require visual inspection. If possible it would be
240
+ # nice to capture the output of the failure and make an assertion against that. But I'm not sure how to do that...
241
+
242
+ class TheTest < Test::Unit::TestCase
243
+ def test01_single_character_differences
244
+ assert_equal <<End, <<End
245
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed feugiat mi. In sagittis, augue non eleifend sodales, arcu urna congue sapien, aliquet molestie pede urna sit amet dolor. Etiam diam. Vestibulum ornare, felis et porta faucibus, magna sapien vulputate arcu, vel facilisis lectus ipsum et ipsum.
246
+
247
+ Vivamus massa odio, lacinia eu, euismod vitae, lobortis eu, erat. Duis tincidunt, neque ac_tincidunt convallis, nibh tellus sodales eros, ut tristique nunc purus in urna. Nullam semper. Fusce quis augue ut metus interdum congue. Duis id dolor eu mi pellentesque sagittis. Quisque imperdiet orci a odio.
248
+ End
249
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed feugiat mi. In sagittis, augue non eleifend sodales, arcu urna congue sapien, aliquet molestie pede urna sit amet dolor. Etiam diam. Vestibulum ornare, felis et porta faucibus, magna sapien vulputate arcu, vel facilisis lectus ipsum et ipsum.
250
+
251
+ Vivamus massa odio, lacinia eu, euismod vitae, lobortis eu, erat. Duis tincidunt, neque ac tincidunt convallis, nibh tellus sodales eros, ut tristique nunc purus in urna. Nullam semper. Fusce quis augue ut metus interdum congue. Duis id color eu mi pellentesque sagittis. Quisque imperdiet orci a odio.
252
+ End
253
+ end
254
+
255
+ def test02_difference_in_control_characters
256
+ assert_equal "Lorem ipsum dolor sit amet,\nconsectetuer adipiscing elit.\nSed feugiat mi.",
257
+ "Lorem ipsum_dolor sit amet, consectetuer\tadipiscing elit.\n\rSed feugiat mi.", :highlight => true
258
+ end
259
+
260
+ def test03_expected_is_longer
261
+ assert_equal '1234567890', '123', :highlight => true
262
+ end
263
+
264
+ def test04_actual_is_longer
265
+ assert_equal '123', '1234567890', :highlight => true
266
+ end
267
+
268
+ # Use the ones above this line as screenshot material...
269
+
270
+ def test05_one_is_much_longer
271
+ assert_equal <<End, <<End
272
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed feugiat mi. In sagittis, augue non eleifend sodales, arcu urna congue sapien, aliquet molestie pede urna sit amet dolor. Etiam diam. Vestibulum ornare, felis et porta faucibus, magna sapien vulputate arcu, vel facilisis lectus ipsum et ipsum. Sed dictum, dolor suscipit malesuada pharetra, orci augue lobortis lectus, porta porta magna magna ut dui. Duis viverra enim sed felis. Mauris semper volutpat pede. Integer lectus lorem, lacinia in, iaculis ut, euismod non, nulla. Nunc non libero eget diam congue ornare. Nunc dictum tellus sed turpis. Sed venenatis, pede non ultricies pharetra, dolor felis malesuada nisl, id imperdiet lorem dui vel velit.
273
+ End
274
+ Lorem ipsum dolor sit amet
275
+ End
276
+ end
277
+
278
+ def test06_only_minor_single_character_differences_but_then_it_gets_out_of_sync
279
+ assert_equal <<End, <<End
280
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed feugiat mi. In sagittis, augue non eleifend sodales, arcu urna congue sapien.
281
+
282
+ Vivamus massa odio, lacinia eu, euismod vitae, lobortis eu, erat. Duis tincidunt, neque ac tincidunt convallis, nibh tellus sodales eros, ut tristique nunc purus in urna. Nullam semper. Fusce quis augue ut metus interdum congue. Duis id dolor eu mi pellentesque sagittis. Quisque imperdiet orci a odio.
283
+ End
284
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed feugiat mi. In sagittis, argue non eleifend sodales, arcu urna conque sapien.
285
+
286
+ Vivamus massa odio, lacinia eu, euismod vitae, lobortis eu, erat. Duis tincidunt, neque ac tincidunt convallis, nibh tellus sodales eros, ut tristique nunc purus in urna. Nullam semper. Fusce quis augue ut metus interdum congue. Duis id dolor eu mi pellentesque sagittis. Quisque imperdiet orci a odio.
287
+ End
288
+ end
289
+
290
+ def test07_underscores_versus_underlines
291
+ assert_equal <<End, <<End
292
+ ___[Underscores]___[Underscores] [Spaces] _ _ _ _ _ [Mix]
293
+ End
294
+ [Spaces] ___[Underscores] [Spaces] _ _ __ _ _[Mix]
295
+ End
296
+ end
297
+
298
+ def test08_inspect_strings_true
299
+ assert_equal '1234567890', '123', :inspect_strings => true
300
+ end
301
+ def test09_inspect_highlight_false
302
+ assert_equal '1234567890', '123', :highlight => false
303
+ end
304
+ def test10_highlight_false
305
+ Test::Unit::Assertions::use_assert_equal_with_highlight! false do
306
+ assert_equal 'really long string', 'another really long string'
307
+ end
308
+ end
309
+ def test11_highlight_true
310
+ Test::Unit::Assertions::use_assert_equal_with_highlight! do
311
+ assert_equal 'really long string', 'another really long string'
312
+ end
313
+ end
314
+ def test12_compatibility__using_arg3_as_message
315
+ assert_equal 'really long string', 'another really long string', 'This is my message! Can you see it?'
316
+ end
317
+ def test13_that_assert_nil_still_works
318
+ # Exposed a bug that existed previously, where it tried to do "".delete(:message)
319
+ assert_nil nil
320
+ end
321
+
322
+ end
323
+ =end
324
+
325
+