wrong 0.6.3 → 0.7.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 (42) hide show
  1. data/README.markdown +10 -7
  2. data/lib/wrong.rb +1 -0
  3. data/lib/wrong/adapters/rspec.rb +4 -0
  4. data/lib/wrong/assert.rb +1 -2
  5. data/lib/wrong/chunk.rb +5 -170
  6. data/lib/wrong/close_to.rb +7 -1
  7. data/lib/wrong/config.rb +3 -3
  8. data/lib/wrong/d.rb +2 -1
  9. data/lib/wrong/failure_message.rb +109 -7
  10. data/lib/wrong/message/string_comparison.rb +1 -1
  11. data/lib/wrong/sexp_ext.rb +2 -1
  12. data/lib/wrong/terminal.rb +43 -0
  13. data/lib/wrong/version.rb +1 -1
  14. data/test/adapters/railsapp/app/controllers/application_controller.rb +3 -0
  15. data/test/adapters/railsapp/app/helpers/application_helper.rb +2 -0
  16. data/test/adapters/railsapp/config/application.rb +54 -0
  17. data/test/adapters/railsapp/config/boot.rb +6 -0
  18. data/test/adapters/railsapp/config/environment.rb +5 -0
  19. data/test/adapters/railsapp/config/environments/development.rb +30 -0
  20. data/test/adapters/railsapp/config/environments/production.rb +60 -0
  21. data/test/adapters/railsapp/config/environments/test.rb +42 -0
  22. data/test/adapters/railsapp/config/initializers/backtrace_silencers.rb +7 -0
  23. data/test/adapters/railsapp/config/initializers/inflections.rb +10 -0
  24. data/test/adapters/railsapp/config/initializers/mime_types.rb +5 -0
  25. data/test/adapters/railsapp/config/initializers/secret_token.rb +7 -0
  26. data/test/adapters/railsapp/config/initializers/session_store.rb +8 -0
  27. data/test/adapters/railsapp/config/initializers/wrap_parameters.rb +14 -0
  28. data/test/adapters/railsapp/config/routes.rb +58 -0
  29. data/test/adapters/railsapp/db/seeds.rb +7 -0
  30. data/test/adapters/railsapp/spec/spec_helper.rb +33 -0
  31. data/test/adapters/railsapp/spec/wrong_spec.rb +8 -0
  32. data/test/adapters/rspec_rails_test.rb +4 -4
  33. data/test/adapters/rspec_test.rb +2 -2
  34. data/test/assert_advanced_test.rb +9 -1
  35. data/test/chunk_test.rb +0 -200
  36. data/test/close_to_test.rb +34 -2
  37. data/test/d_test.rb +2 -2
  38. data/test/eventually_test.rb +27 -34
  39. data/test/failure_message_test.rb +212 -13
  40. data/test/test_helper.rb +2 -2
  41. metadata +47 -56
  42. data/lib/wrong/ruby2ruby_patch.rb +0 -37
@@ -100,6 +100,8 @@ If you want to compare floats, try this:
100
100
 
101
101
  If you don't want `close_to?` cluttering up `Float` in your test runs then use `include Wrong::Assert` instead of `include Wrong`.
102
102
 
103
+ `close_to?` also works on `Time`s, `Date`s, and `DateTime`s. (The default tolerance of 1 msec may be too small for you.)
104
+
103
105
  ### d
104
106
 
105
107
  We also implement the most amazing debugging method ever, `d`, which gives you a sort of mini-wrong wherever you want it
@@ -251,29 +253,30 @@ Wrong is compatible with RSpec and MiniTest::Spec, and probably Cucumber too, so
251
253
  Here's an RSpec example:
252
254
 
253
255
  require "wrong/adapters/rspec"
254
- Wrong.config.alias_assert :expect_that
256
+ Wrong.config.alias_assert :expect, override: true
255
257
 
256
258
  describe BleuCheese do
257
259
  it "stinks" do
258
- expect_that { BleuCheese.new.smell > 9000 }
260
+ cheese = BleuCheese.new
261
+ expect { cheese.smell > 9000 }
259
262
  end
260
263
  end
261
264
 
262
265
  This makes your code read like a BDD-style DSL, without RSpec's "should" syntax (which is, let's face it, pretty weird the first few hundred times you have to use it). Compare
263
266
 
264
- expect_that { BleuCheese.new.smell > 9000 }
267
+ expect { cheese.smell > 9000 }
265
268
 
266
269
  to
267
270
 
268
- BleuCheese.new.smell.should > 9000
271
+ cheese.smell.should be > 9000
269
272
 
270
- and consider which one more clearly describes the desired behavior. The object under test doesn't really have a `should` method, so why should it magically get one during a test? And in what human language is "should greater than" a valid phrase?
273
+ and consider which one more clearly describes the desired behavior. The object under test doesn't really have a `should` method, so why should it magically get one during a test?
271
274
 
272
275
  Warning: currently the use of `alias_assert :expect` is **not** compatible with RSpec, since RSpec also defines `expect` as a synonym for `lambda`. If you really want to use `expect` as an alias form `assert`, then use `Wrong.config.alias_assert :expect, :override => true`. See [issue #6](https://github.com/sconover/wrong/issues/6) for more details.
273
276
 
274
277
  ## Algorithm ##
275
278
 
276
- So wait a second. How do we do it? Doesn't Ruby have [poor support for AST introspection](http://blog.zenspider.com/2009/04/parsetree-eol.html)? Well, yes, it does, so we cheat: we figure out what file and line the assert block is defined in, then open the file, read the code, and parse it directly using Ryan Davis' amazing [RubyParser](http://parsetree.rubyforge.org/ruby_parser/) and [Ruby2Ruby](http://seattlerb.rubyforge.org/ruby2ruby/). You can bask in the kludge by examining `chunk.rb` and `assert.rb`. If you find some code it can't parse, please send it our way. As a failsafe we also use Sourcify, which has yet another home baked RACC parser, so we have many chances to parse your code.
279
+ So wait a second. How do we do it? Doesn't Ruby have [poor support for AST introspection](http://blog.zenspider.com/2009/04/parsetree-eol.html)? Well, yes, it does, so we cheat: we figure out what file and line the assert block is defined in, then open the file, read the code, and parse it directly using Ryan Davis' amazing [RubyParser](http://parsetree.rubyforge.org/ruby_parser/) and [Ruby2Ruby](http://seattlerb.rubyforge.org/ruby2ruby/). You can bask in the kludge by examining `chunk.rb` and `assert.rb`. If you find some code it can't parse, please send it our way.
277
280
 
278
281
  Before you get your knickers in a twist about how this is totally unacceptable because it doesn't support this or that use case, here are our caveats and excuses:
279
282
 
@@ -462,7 +465,7 @@ If you're in Ruby 1.8, you **really** shouldn't do it! But if you do, you can us
462
465
 
463
466
  ## Bugs ##
464
467
 
465
- * assert doesn't work (can't find the source code) from inside a "Dir.chdir" block
468
+ * see Github Issues <http://github.com/sconover/wrong/issues>
466
469
 
467
470
  ## todo ##
468
471
 
@@ -5,6 +5,7 @@ require "predicated"
5
5
  require "wrong/assert"
6
6
  require "wrong/helpers"
7
7
  require "wrong/chunk"
8
+ require "wrong/terminal"
8
9
  require "wrong/sexp_ext"
9
10
  require "wrong/version"
10
11
  require "wrong/config"
@@ -1,4 +1,5 @@
1
1
  require "wrong"
2
+ require "wrong/terminal"
2
3
 
3
4
  if Object.const_defined? :RSpec
4
5
  # RSpec 2
@@ -58,6 +59,9 @@ if Object.const_defined? :RSpec
58
59
  end
59
60
  end
60
61
 
62
+ # tweak terminal width so exceptions wrap properly inside RSpec output
63
+ Wrong::Terminal.width -= 7
64
+
61
65
  elsif Object.const_defined? :Spec
62
66
  # RSpec 1
63
67
  Spec::Runner.configure do |config|
@@ -5,7 +5,6 @@ require "predicated/to/sentence"
5
5
  require "wrong/chunk"
6
6
  require "wrong/config"
7
7
  require "wrong/failure_message"
8
- require "wrong/ruby2ruby_patch" # need to patch it after some other stuff loads
9
8
  require "wrong/rainbow"
10
9
 
11
10
  module Wrong
@@ -68,7 +67,7 @@ module Wrong
68
67
  value = !value if valence == :deny
69
68
  if value
70
69
  if Wrong.config[:verbose]
71
- code = Wrong::Chunk.from_block(block, depth + 2).code
70
+ code = Wrong::Chunk.from_block(block, depth + 2).code
72
71
  if Wrong.config[:color]
73
72
  explanation = explanation.color(:blue) if explanation
74
73
  code = code.color(:green)
@@ -2,18 +2,6 @@ require 'ruby_parser'
2
2
  require 'ruby2ruby'
3
3
  require 'pp'
4
4
 
5
- def require_optionally(library)
6
- begin
7
- require library
8
- rescue LoadError => e
9
- raise e unless e.message == "no such file to load -- #{library}" or
10
- e.message == "cannot load such file -- #{library}" # 1.9.3 changed the error message
11
- end
12
- end
13
-
14
- require_optionally "ParseTree"
15
- require_optionally "sourcify"
16
-
17
5
  require "wrong/config"
18
6
  require "wrong/sexp_ext"
19
7
  require "wrong/capturing"
@@ -60,21 +48,11 @@ module Wrong
60
48
  end
61
49
 
62
50
  def build_sexp
63
- sexp = begin
64
- unless @block.nil? or @block.is_a?(String) or !Object.const_defined?(:Sourcify)
65
- # first try sourcify
66
- @block.to_sexp[3] # the [3] is to strip out the "proc {" sourcify adds to everything
67
- end
68
- rescue Exception => e
69
- # sourcify failed, so fall through
70
- end
71
-
72
- # next try glomming
73
- sexp ||= glom(if @file == "(irb)"
74
- IRB.CurrentContext.all_lines
75
- else
76
- read_source_file(@file)
77
- end)
51
+ glom(if @file == "(irb)"
52
+ IRB.CurrentContext.all_lines
53
+ else
54
+ read_source_file(@file)
55
+ end)
78
56
  end
79
57
 
80
58
  def read_source_file(file)
@@ -95,7 +73,6 @@ module Wrong
95
73
  while sexp.nil? && line_index + c < lines.size
96
74
  begin
97
75
  @chunk = lines[line_index..line_index+c].join("\n")
98
-
99
76
  capturing(:stderr) do # new RubyParser is too loud
100
77
  sexp = @parser.parse(@chunk)
101
78
  end
@@ -164,148 +141,6 @@ module Wrong
164
141
  end
165
142
  end
166
143
 
167
- def details
168
- @details ||= build_details
169
- end
170
-
171
- def pretty_value(value, starting_col = 0, indent_wrapped_lines = 6, width = Chunk.terminal_width)
172
- # inspected = value.inspect
173
-
174
- # note that if the first line overflows due to the starting column then pp won't wrap it right
175
- inspected = PP.pp(value, "", width - starting_col).chomp
176
-
177
- # this bit might be redundant with the pp call now
178
- indented = indent_all(6, inspected)
179
- if width
180
- wrap_and_indent(indented, starting_col, indent_wrapped_lines, width)
181
- else
182
- indented
183
- end
184
- end
185
-
186
- private
187
-
188
- # todo: move to FailureMessage?
189
- def build_details
190
- require "wrong/rainbow" if Wrong.config[:color]
191
- s = ""
192
- parts = self.parts
193
- parts.shift # remove the first part, since it's the same as the code
194
-
195
- details = []
196
-
197
- if parts.size > 0
198
- parts.each do |part|
199
- begin
200
- value = eval(part, block.binding)
201
- unless part == value.inspect # this skips literals or tautologies
202
- if part =~ /\n/m
203
- part.gsub!(/\n/, newline(2))
204
- part += newline(3)
205
- end
206
- value = pretty_value(value, (4 + part.length + 4))
207
- if Wrong.config[:color]
208
- part = part.color(:blue)
209
- value = value.color(:magenta)
210
- end
211
- details << indent(4, part, " is ", value)
212
- end
213
- rescue Exception => e
214
- raises = "raises #{e.class}"
215
- if Wrong.config[:color]
216
- part = part.color(:blue)
217
- raises = raises.bold.color(:red)
218
- end
219
- formatted_exeption = if e.message and e.message != e.class.to_s
220
- indent(4, part, " ", raises, ": ", indent_all(6, e.message))
221
- else
222
- indent(4, part, " ", raises)
223
- end
224
- details << formatted_exeption
225
- end
226
- end
227
- end
228
-
229
- details.uniq!
230
- if details.empty?
231
- ""
232
- else
233
- "\n" + details.join("\n") + "\n"
234
- end
235
-
236
- end
237
-
238
- public # don't know exactly why this needs to be public but eval'ed code can't find it otherwise
239
- def indent(indent, *s)
240
- "#{" " * indent}#{s.join('')}"
241
- end
242
-
243
- def newline(indent)
244
- "\n" + self.indent(indent)
245
- end
246
-
247
- def indent_all(amount, s)
248
- s.gsub("\n", "\n#{indent(amount)}")
249
- end
250
-
251
- def wrap_and_indent(indented, starting_col, indent_wrapped_lines, full_width)
252
- first_line = true
253
- width = full_width - starting_col # the first line is essentially shorter
254
- indented.split("\n").map do |line|
255
- s = ""
256
- while line.length > width
257
- s << line[0...width]
258
- s << newline(indent_wrapped_lines)
259
- line = line[width..-1]
260
- if first_line
261
- width += starting_col - indent_wrapped_lines
262
- first_line = false
263
- end
264
- end
265
- s << line
266
- s
267
- end.join("\n")
268
- end
269
-
270
- # Returns [width, height] of terminal when detected, nil if not detected.
271
- # Think of this as a simpler version of Highline's Highline::SystemExtensions.terminal_size()
272
- # Lifted from https://github.com/cldwalker/hirb/blob/master/lib/hirb/util.rb#L59
273
- #
274
- # See also http://stackoverflow.com/questions/2068859/how-to-get-the-width-of-terminal-window-in-ruby
275
- # https://github.com/genki/ruby-terminfo/blob/master/lib/terminfo.rb
276
- # http://www.mkssoftware.com/docs/man1/stty.1.asp
277
-
278
-
279
- def self.terminal_size
280
- @@terminal_size ||= begin
281
- if (ENV['COLUMNS'] =~ /^\d+$/) && (ENV['LINES'] =~ /^\d+$/)
282
- [ENV['COLUMNS'].to_i, ENV['LINES'].to_i]
283
- elsif (RUBY_PLATFORM =~ /java/ || (!STDIN.tty? && ENV['TERM'])) && command_exists?('tput')
284
- [`tput cols`.to_i, `tput lines`.to_i]
285
- elsif STDIN.tty? && command_exists?('stty')
286
- `stty size`.scan(/\d+/).map { |s| s.to_i }.reverse
287
- else
288
- nil
289
- end
290
- rescue
291
- nil
292
- end
293
- end
294
-
295
- def self.terminal_width
296
- (@terminal_width ||= nil) || (terminal_size && terminal_size.first) || 80
297
- end
298
-
299
- def self.terminal_width= forced_with
300
- @terminal_width = forced_with
301
- end
302
-
303
- # Determines if a shell command exists by searching for it in ENV['PATH'].
304
- def self.command_exists?(command)
305
- ENV['PATH'].split(File::PATH_SEPARATOR).any? {|d| File.exists? File.join(d, command) }
306
- end
307
-
308
-
309
144
  end
310
145
 
311
146
  end
@@ -1,8 +1,14 @@
1
1
  module Wrong
2
2
  module CloseTo
3
3
  def close_to?(other, tolerance = 0.001)
4
- (self.to_f - other.to_f).abs < tolerance
4
+ if respond_to? :to_f
5
+ (self.to_f - other.to_f).abs < tolerance
6
+ elsif respond_to? :to_time
7
+ self.to_time.close_to?( other.to_time, tolerance)
8
+ end
5
9
  end
6
10
  end
7
11
  Numeric.send :include, CloseTo
12
+ Date.send :include, CloseTo
13
+ Time.send :include, CloseTo
8
14
  end
@@ -10,7 +10,7 @@ module Wrong
10
10
  end
11
11
  Config.new settings
12
12
  end
13
-
13
+
14
14
  def self.config
15
15
  @config ||= load_config
16
16
  end
@@ -72,8 +72,8 @@ module Wrong
72
72
  self[:aliases][:deny]
73
73
  end
74
74
 
75
- def assert_methods
76
- assert_method_names + deny_method_names
75
+ def hidden_methods
76
+ assert_method_names + deny_method_names + [:eventually]
77
77
  end
78
78
  end
79
79
  end
@@ -21,6 +21,7 @@ module Wrong
21
21
 
22
22
  # look for a "d" inside the block
23
23
  sexp.each_subexp do |subexp|
24
+ #sexp.deep_each do |subexp| # todo: try to use deep_each
24
25
  if subexp.d?
25
26
  sexp = subexp[3] # swap in the block part of the nested d call
26
27
  end
@@ -28,7 +29,7 @@ module Wrong
28
29
 
29
30
  code = sexp.to_ruby
30
31
  value = eval(code, block.binding, called_from[0], called_from[1].to_i)
31
- width = Chunk.terminal_width
32
+ width = Terminal.width
32
33
  value = PP.pp(value, "", width - (code.size + 3)).chomp
33
34
 
34
35
  if Wrong.config[:color]
@@ -1,3 +1,6 @@
1
+ require "wrong/chunk"
2
+ require "wrong/terminal"
3
+
1
4
  module Wrong
2
5
  class FailureMessage
3
6
  @@formatters = []
@@ -40,7 +43,6 @@ module Wrong
40
43
  end
41
44
  end
42
45
 
43
-
44
46
  attr_accessor :chunk, :valence, :explanation
45
47
 
46
48
  def initialize(chunk, valence, explanation)
@@ -57,20 +59,89 @@ module Wrong
57
59
  message << basic
58
60
 
59
61
  formatted_message = if predicate && !(predicate.is_a? Predicated::Conjunction)
60
- if formatter = FailureMessage.formatter_for(predicate)
61
- colored(:bold, formatter.describe)
62
- end
63
- end
62
+ if formatter = FailureMessage.formatter_for(predicate)
63
+ colored(:bold, formatter.describe)
64
+ end
65
+ end
64
66
 
65
- unless chunk.details.empty? and formatted_message.nil?
67
+ unless details.empty? and formatted_message.nil?
66
68
  message << ", but"
67
69
  end
68
70
 
69
71
  message << formatted_message if formatted_message
70
- message << chunk.details unless chunk.details.empty?
72
+ message << details unless details.empty?
71
73
  message
72
74
  end
73
75
 
76
+ def details
77
+ @details ||= begin
78
+ require "wrong/rainbow" if Wrong.config[:color]
79
+ s = ""
80
+ parts = chunk.parts
81
+
82
+ parts.shift while parts.first == "()" # the parser adds this sometimes
83
+ parts.shift # remove the first part, since it's the same as the code
84
+
85
+ details = []
86
+
87
+ if parts.size > 0
88
+ parts.each do |part|
89
+ begin
90
+ value = eval(part, chunk.block.binding)
91
+ unless part == value.inspect # this skips literals or tautologies
92
+ if part =~ /\n/m
93
+ part.gsub!(/\n/, newline(2))
94
+ part += newline(3)
95
+ end
96
+ value = pretty_value(value, (4 + part.length + 4))
97
+ if Wrong.config[:color]
98
+ part = part.color(:blue)
99
+ value = value.color(:magenta)
100
+ end
101
+ details << indent(4, part, " is ", value)
102
+ end
103
+ rescue Exception => e
104
+ raises = "raises #{e.class}"
105
+ if Wrong.config[:color]
106
+ part = part.color(:blue)
107
+ raises = raises.bold.color(:red)
108
+ end
109
+ formatted_exeption = if e.message and e.message != e.class.to_s
110
+ indent(4, part, " ", raises, ": ", indent_all(6, e.message))
111
+ else
112
+ indent(4, part, " ", raises)
113
+ end
114
+ details << formatted_exeption
115
+ end
116
+ end
117
+ end
118
+
119
+ details.uniq!
120
+ if details.empty?
121
+ ""
122
+ else
123
+ "\n" + details.join("\n") + "\n"
124
+ end
125
+ end
126
+
127
+ end
128
+
129
+ # todo: use awesome_print
130
+ def pretty_value(value, starting_col = 0, indent_wrapped_lines = 6, width = Terminal.width)
131
+ # inspected = value.inspect
132
+
133
+ # note that if the first line overflows due to the starting column then pp won't wrap it right
134
+ inspected = PP.pp(value, "", width - starting_col).chomp
135
+
136
+ # this bit might be redundant with the pp call now
137
+ indented = indent_all(6, inspected)
138
+ if width
139
+ wrap_and_indent(indented, starting_col, indent_wrapped_lines, width)
140
+ else
141
+ indented
142
+ end
143
+ end
144
+
74
145
  protected
75
146
  def code
76
147
  @code ||= chunk.code
@@ -98,5 +169,36 @@ module Wrong
98
169
  end
99
170
  end
100
171
 
172
+ def indent(indent, *s)
173
+ "#{" " * indent}#{s.join('')}"
174
+ end
175
+
176
+ def newline(indent)
177
+ "\n" + self.indent(indent)
178
+ end
179
+
180
+ def indent_all(amount, s)
181
+ s.gsub("\n", "\n#{indent(amount)}")
182
+ end
183
+
184
+ def wrap_and_indent(indented, starting_col, indent_wrapped_lines, full_width)
185
+ first_line = true
186
+ width = full_width - starting_col # the first line is essentially shorter
187
+ indented.split("\n").map do |line|
188
+ s = ""
189
+ while line.length > width
190
+ s << line[0...width]
191
+ s << newline(indent_wrapped_lines)
192
+ line = line[width..-1]
193
+ if first_line
194
+ width += starting_col - indent_wrapped_lines
195
+ first_line = false
196
+ end
197
+ end
198
+ s << line
199
+ s
200
+ end.join("\n")
201
+ end
202
+
101
203
  end
102
204
  end