wrong 0.4.0 → 0.4.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +97 -49
- data/lib/wrong/adapters/minitest.rb +5 -2
- data/lib/wrong/adapters/rspec.rb +36 -15
- data/lib/wrong/assert.rb +1 -33
- data/lib/wrong/chunk.rb +34 -24
- data/lib/wrong/config.rb +42 -7
- data/lib/wrong/failure_message.rb +59 -0
- data/lib/wrong/helpers.rb +13 -12
- data/lib/wrong/message/array_diff.rb +1 -1
- data/lib/wrong/rainbow.rb +9 -4
- data/lib/wrong/sexp_ext.rb +6 -6
- data/lib/wrong/version.rb +1 -1
- data/lib/wrong.rb +14 -3
- data/test/adapters/minitest_test.rb +2 -3
- data/test/adapters/railsapp/app/controllers/application_controller.rb +3 -0
- data/test/adapters/railsapp/app/helpers/application_helper.rb +2 -0
- data/test/adapters/railsapp/autotest/discover.rb +2 -0
- data/test/adapters/railsapp/config/application.rb +42 -0
- data/test/adapters/railsapp/config/boot.rb +13 -0
- data/test/adapters/railsapp/config/environment.rb +5 -0
- data/test/adapters/railsapp/config/environments/development.rb +26 -0
- data/test/adapters/railsapp/config/environments/production.rb +49 -0
- data/test/adapters/railsapp/config/environments/test.rb +35 -0
- data/test/adapters/railsapp/config/initializers/backtrace_silencers.rb +7 -0
- data/test/adapters/railsapp/config/initializers/inflections.rb +10 -0
- data/test/adapters/railsapp/config/initializers/mime_types.rb +5 -0
- data/test/adapters/railsapp/config/initializers/secret_token.rb +7 -0
- data/test/adapters/railsapp/config/initializers/session_store.rb +8 -0
- data/test/adapters/railsapp/config/routes.rb +58 -0
- data/test/adapters/railsapp/db/seeds.rb +7 -0
- data/test/adapters/railsapp/spec/spec_helper.rb +28 -0
- data/test/adapters/railsapp/spec/wrong_spec.rb +8 -0
- data/test/adapters/rspec_rails_test.rb +58 -0
- data/test/adapters/rspec_test.rb +0 -61
- data/test/adapters/test_unit_test.rb +2 -2
- data/test/assert_advanced_test.rb +4 -4
- data/test/chunk_test.rb +14 -0
- data/test/config_test.rb +98 -51
- data/test/failure_message_test.rb +66 -16
- data/test/failures_test.rb +40 -86
- data/test/message/array_diff_test.rb +6 -2
- data/test/string_comparison_test.rb +3 -1
- data/test/test_helper.rb +26 -3
- metadata +190 -215
data/README.markdown
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
## Abstract ##
|
6
6
|
|
7
|
-
Wrong provides a general assert method that takes a predicate block. Assertion failure messages are rich in detail. The Wrong idea is to replace all those countless assert\_this
|
7
|
+
Wrong provides a general assert method that takes a predicate block. Assertion failure messages are rich in detail. The Wrong idea is to replace all those countless `assert\_this`, `assert\_that`, `should\_something` library methods which only exist to give a failure message that's not simply "assertion failed". Wrong replaces all of them in one fell swoop, since if you can write it in Ruby, Wrong can make a sensible failure message out of it.
|
8
8
|
|
9
9
|
We'd very much appreciate feedback and bug reports. There are plenty of things left to be done to make the results look uniformly clean and beautiful. We want your feedback, and especially to give us cases where either it blows up or the output is ugly or uninformative.
|
10
10
|
|
@@ -16,11 +16,7 @@ Inspired by [assert { 2.0 }](http://assert2.rubyforge.org/) but rewritten from s
|
|
16
16
|
|
17
17
|
gem install wrong
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
gem install wrong-jruby
|
22
|
-
|
23
|
-
which untangles some dependencies.
|
19
|
+
We have deployed gems for both Ruby and JRuby; if you get dependency issues on your platform, please let us know what Ruby interpreter and version you're using and what errors you get, and we'll try to track it down.
|
24
20
|
|
25
21
|
## Usage ##
|
26
22
|
|
@@ -61,13 +57,16 @@ If your assertion is more than a simple predicate, then Wrong will split it into
|
|
61
57
|
And a companion, 'deny':
|
62
58
|
|
63
59
|
deny{'abc'.include?('bc')}
|
64
|
-
==> Didn't expect "abc".include?("bc")
|
60
|
+
==> Didn't expect "abc".include?("bc")
|
65
61
|
|
66
62
|
There's also a convenience method for catching errors:
|
67
63
|
|
68
64
|
assert{ rescuing{raise "vanilla"}.message == "chocolate" }
|
69
65
|
==>
|
70
|
-
Expected (rescuing { raise("vanilla") }.message == "chocolate"), but
|
66
|
+
Expected (rescuing { raise("vanilla") }.message == "chocolate"), but
|
67
|
+
rescuing { raise("vanilla") }.message is "vanilla"
|
68
|
+
rescuing { raise("vanilla") } is #<RuntimeError: vanilla>
|
69
|
+
raise("vanilla") raises RuntimeError: vanilla
|
71
70
|
|
72
71
|
And one for capturing output streams:
|
73
72
|
|
@@ -93,7 +92,7 @@ We also implement the most amazing debugging method ever, `d`, which gives you a
|
|
93
92
|
d { x } # => prints "x is 7" to the console
|
94
93
|
d { x * 2 } # => prints "(x * 2) is 14" to the console
|
95
94
|
|
96
|
-
(`d` was originally implemented by Rob Sanheim in LogBuddy; as with Assert2 this is a rewrite and homage.) Remember, if you want `d` to work at runtime (e.g. in a webapp) then you must `include
|
95
|
+
(`d` was originally implemented by Rob Sanheim in LogBuddy; as with Assert2 this version is a rewrite and homage.) Remember, if you want `d` to work at runtime (e.g. in a webapp) then you must `include Wrong::D` inside your app, e.g. in your `environment.rb` file.
|
97
96
|
|
98
97
|
More examples are in the file `examples.rb` <http://github.com/alexch/wrong/blob/master/examples.rb>
|
99
98
|
|
@@ -118,7 +117,7 @@ will give you the `assert` and `deny` methods but not the formatters or `rescuin
|
|
118
117
|
require 'wrong/d'
|
119
118
|
include Wrong::D
|
120
119
|
|
121
|
-
To summarize: if you do `require 'wrong'` and `include Wrong` then you will get the whole ball of wax. Most people will probably want this since it's easier, but there is an alternative,
|
120
|
+
To summarize: if you do `require 'wrong'` and `include Wrong` then you will get the whole ball of wax. Most people will probably want this since it's easier, but there is an alternative, which is to `require` and `include` only what you want.
|
122
121
|
|
123
122
|
And beware: if you don't `require 'wrong'`, then `include Wrong` will not do anything at all.
|
124
123
|
|
@@ -139,13 +138,40 @@ or this
|
|
139
138
|
? The Wrong way has the advantage of being plain, transparent Ruby code, not an awkward DSL that moves "equal" out of its natural place between the comparands. Plus, WYSIWYG! You know just from looking at it that "equal" means `==`, not `eql?` or `===` or `=~`.
|
140
139
|
|
141
140
|
Moreover, much like TDD itself, Wrong encourages you to write cleaner code. If your assertion messages are not clear and "Englishy", then maybe it's time for you to refactor a bit -- extract an informatively named variable or method, maybe push some function onto its natural object *a la* the [Law of Demeter](http://en.wikipedia.org/wiki/Law_of_Demeter)...
|
141
|
+
Also, try not to call any methods with side effects inside an assert. In addition to being bad form, this can cause messed-up failure messages, since the side effects may occur several times in the process of building the message.
|
142
142
|
|
143
143
|
Wrong also lets you put the expected and actual values in any order you want! Consider the failure messages for
|
144
144
|
|
145
145
|
assert { current_user == "joe" } # => Expected (current_user == "joe") but current_user is "fred"
|
146
146
|
assert { "joe" == current_user } # => Expected ("joe" == current_user) but current_user is "fred"
|
147
147
|
|
148
|
-
You get
|
148
|
+
You get all the information you want, and none you don't want. At least, that's the plan! :-)
|
149
|
+
|
150
|
+
## BDD with Wrong ##
|
151
|
+
|
152
|
+
Wrong is compatible with RSpec and MiniTest::Spec, and probably Cucumber too, so you can use it inside your BDD framework of choice. To make your test code even BDD-er, try aliasing `assert` to either `should` or (Alex's favorite) `expect`.
|
153
|
+
|
154
|
+
Here's an RSpec example:
|
155
|
+
|
156
|
+
require "wrong"
|
157
|
+
require "wrong/adapters/rspec"
|
158
|
+
Wrong.config.alias_assert :expect
|
159
|
+
|
160
|
+
describe BleuCheese do
|
161
|
+
it "stinks" do
|
162
|
+
expect { BleuCheese.new.smell > 9000 }
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
This makes your code read like a BDD-style DSL, without RSpec's arcane "should" syntax (which is, let's face it, pretty weird the first few hundred times you have to use it). Compare
|
167
|
+
|
168
|
+
expect { BleuCheese.new.smell > 9000 }
|
169
|
+
|
170
|
+
to
|
171
|
+
|
172
|
+
BleuCheese.new.smell.should > 9000
|
173
|
+
|
174
|
+
and seriously, tell me which one more clearly describes the desired behavior. The object under test doesn't really have a `should` method, so why should it during a test? And in what human language is "should greater than" a valid phrase?
|
149
175
|
|
150
176
|
## Algorithm ##
|
151
177
|
|
@@ -153,16 +179,18 @@ So wait a second. How do we do it? Doesn't Ruby have [poor support for AST intro
|
|
153
179
|
|
154
180
|
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:
|
155
181
|
|
156
|
-
* It works! Tested in 1.8.6, 1.8.7, 1.9.1,
|
182
|
+
* It works! Tested in MRI 1.8.6, 1.8.7, 1.9.1, 1.9.2, and JRuby 1.5.3. (Thank you, [rvm](http://rvm.beginrescueend.com/)!)
|
157
183
|
* Your code needs to be in a file.
|
158
184
|
* If you're developing Ruby code without saving it to a mounted disk, then sorry, Wrong is not right for you.
|
159
|
-
* We monkey-patch IRB so if you do `irb -rwrong` it'll save off your session in
|
185
|
+
* We monkey-patch IRB so if you do `irb -rwrong` it'll save off your session in memory where Wrong can read it.
|
186
|
+
* It'd be nice if it could work inside a `-e` block but as far as we can tell, there's no way to grab that `-e` code from inside Ruby.
|
160
187
|
* It's a development-time testing library, not a production runtime library, so there are no security or filesystem issues.
|
161
188
|
* `eval` isn't evil, it's just misunderstood.
|
162
189
|
* It makes a few assumptions about the structure of your code, leading to some restrictions:
|
163
|
-
* You can't have more than one call to `assert` per line. (This should not be a problem since even if you're nesting asserts for some bizarre reason, we assume you know where your Return key is.
|
190
|
+
* You can't have more than one call to `assert` per line. (This should not be a problem since even if you're nesting asserts for some bizarre reason, we assume you know where your Return key is.)
|
164
191
|
* You can't use metaprogramming to write your assert blocks.
|
165
|
-
* All variables and methods must be available in the binding of the
|
192
|
+
* All variables and methods must be available in the binding of the assert block.
|
193
|
+
* Passing a proc around and eventually calling assert on it might not work in some Ruby implementations.
|
166
194
|
|
167
195
|
## Adapters ##
|
168
196
|
|
@@ -172,9 +200,9 @@ Currently we support
|
|
172
200
|
|
173
201
|
* Test::Unit - `require 'wrong/adapters/test_unit'`
|
174
202
|
* Minitest - `require 'wrong/adapters/minitest'`
|
175
|
-
* RSpec - `require 'wrong/adapters/rspec'`
|
203
|
+
* RSpec - `require 'wrong/adapters/rspec'` (now supports both 1.3 and 2.0)
|
176
204
|
|
177
|
-
To use these, put the appropriate `require` in your helper; it should extend the framework enough that you can use `assert { }` in your test cases without extra fussing around.
|
205
|
+
To use these, put the appropriate `require` in your helper, **after** requiring your test framework; it should extend the framework enough that you can use `assert { }` in your test cases without extra fussing around.
|
178
206
|
|
179
207
|
## Explanations ##
|
180
208
|
|
@@ -194,13 +222,12 @@ And if your assertion code isn't self-explanatory, then that's a hint that you m
|
|
194
222
|
|
195
223
|
When a failure occurs, the exception message contains all the details you might need to make sense of it. Here's the breakdown:
|
196
224
|
|
197
|
-
Expected [CLAIM], but
|
225
|
+
Expected [CLAIM], but
|
198
226
|
[FORMATTER]
|
199
227
|
[SUBEXP] is [VALUE]
|
200
228
|
...
|
201
229
|
|
202
230
|
* CLAIM is the code inside your assert block, normalized
|
203
|
-
* SUMMARY is a to-English translation of the claim, via the Predicated library. This tries to be very intelligible; e.g. translating "include?" into "does not include" and so on.
|
204
231
|
* If there is a formatter registered for this type of predicate, its output will come next. (See below.)
|
205
232
|
* SUBEXP is each of the subtrees of the claim, minus duplicates and truisms (e.g. literals).
|
206
233
|
* The word "is" is a very nice separator since it doesn't look like code, but is short enough to be easily visually parsed.
|
@@ -210,6 +237,8 @@ We hope this structure lets your eyes focus on the meaningful values and differe
|
|
210
237
|
|
211
238
|
(Why does VALUE use `inspect` and not `to_s`? Because `inspect` on standard objects like String and Array are sure to show all relevant details, such as white space, in a console-safe way, and we hope other libraries follow suit. Also, `to_s` often inserts line breaks and that messes up formatting and legibility.)
|
212
239
|
|
240
|
+
Wrong tries to maintain indentation to improve readability. If the inspected VALUE contains newlines, the succeeding lines will be indented to the correct level.
|
241
|
+
|
213
242
|
## Formatters ##
|
214
243
|
|
215
244
|
Enhancements for error messages sit under wrong/message.
|
@@ -217,22 +246,19 @@ Enhancements for error messages sit under wrong/message.
|
|
217
246
|
Currently we support special messages for
|
218
247
|
|
219
248
|
* String ==
|
220
|
-
*
|
249
|
+
* Array(ish) ==
|
221
250
|
* including nested string elements
|
222
251
|
|
223
|
-
To use
|
252
|
+
To use the Array formatter, you may also need to `gem install diff-lcs` (it's an optional dependency).
|
224
253
|
|
225
|
-
require "wrong/message/
|
254
|
+
require "wrong/message/string_comparison"
|
226
255
|
assert { "the quick brown fox jumped over the lazy dog" ==
|
227
256
|
"the quick brown hamster jumped over the lazy gerbil" }
|
228
257
|
==>
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
^^^
|
234
|
-
the quick brown hamster jumped over the lazy gerbil
|
235
|
-
^^^^^^^
|
258
|
+
Expected ("the quick brown fox jumped over the lazy dog" == "the quick brown hamster jumped over the lazy gerbil"), but
|
259
|
+
Strings differ at position 16:
|
260
|
+
first: ..."quick brown fox jumped over the lazy dog"
|
261
|
+
second: ..."quick brown hamster jumped over the lazy gerbil"
|
236
262
|
--
|
237
263
|
|
238
264
|
require "wrong/message/array_diff"
|
@@ -246,36 +272,57 @@ To use these formatters, you have to explicitly `require` them! You may also nee
|
|
246
272
|
["venus", "earth", "pluto", "neptune"]
|
247
273
|
^ ^
|
248
274
|
|
249
|
-
|
275
|
+
## Config ##
|
276
|
+
|
277
|
+
These settings can either be set at runtime on the `Wrong.config` singleton, or inside a `.wrong` file in the current directory or a parent. In the `.wrong` file just pretend every line is preceded with `Wrong.config.` -- e.g. if there's a setting called `ice_cream`, you can do any of these in your `.wrong` file
|
278
|
+
|
279
|
+
ice_cream # => Wrong.config[:ice_cream] => true
|
280
|
+
ice_cream = true # => Wrong.config[:ice_cream] => true
|
281
|
+
ice_cream = "vanilla" # => Wrong.config[:ice_cream] => "vanilla"
|
282
|
+
|
283
|
+
or any of these at runtime:
|
284
|
+
|
285
|
+
Wrong.config.ice_cream # => Wrong.config[:ice_cream] => true
|
286
|
+
Wrong.config.ice_cream = true # => Wrong.config[:ice_cream] => true
|
287
|
+
Wrong.config.ice_cream = "vanilla" # => Wrong.config[:ice_cream] => "vanilla"
|
288
|
+
|
289
|
+
### Color ###
|
290
|
+
|
291
|
+
Apparently, no test framework is successful unless and until it supports console colors. So now we do. Call
|
292
|
+
|
293
|
+
Wrong.config.color
|
294
|
+
|
295
|
+
in your test helper or rakefile or wherever, or put
|
250
296
|
|
251
|
-
|
297
|
+
color
|
252
298
|
|
253
|
-
|
299
|
+
in your `.wrong` file and get ready to be **bedazzled**. If you need custom colors, let us know.
|
254
300
|
|
255
|
-
|
301
|
+
### Aliases ###
|
256
302
|
|
257
|
-
|
303
|
+
An end to the language wars! Name your "assert" and "deny" methods anything you want.
|
258
304
|
|
259
|
-
|
305
|
+
* In your code, use `Wrong.config.alias_assert` and `Wrong.config.alias_deny`
|
306
|
+
* In your `.wrong` file, put `alias_assert :expect` on a line by itself
|
260
307
|
|
261
|
-
|
308
|
+
Here are some suggestions:
|
262
309
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
310
|
+
alias_assert :expect
|
311
|
+
alias_assert :should # This looks nice in RSpec
|
312
|
+
alias_assert :confirm
|
313
|
+
alias_assert :be
|
267
314
|
|
268
|
-
|
269
|
-
|
315
|
+
alias_assert :is
|
316
|
+
alias_deny :aint
|
270
317
|
|
271
|
-
|
272
|
-
|
318
|
+
alias_assert :assure
|
319
|
+
alias_deny :refute
|
273
320
|
|
274
|
-
|
275
|
-
|
321
|
+
alias_assert :yep
|
322
|
+
alias_deny :nope
|
276
323
|
|
277
|
-
|
278
|
-
|
324
|
+
alias_assert :yay!
|
325
|
+
alias_deny :boo!
|
279
326
|
|
280
327
|
Just don't use "`aver`" since we took that one for an internal method in `Wrong::Assert`.
|
281
328
|
|
@@ -294,7 +341,8 @@ If you're in Ruby 1.8, you **really** shouldn't do it! But if you do, you can us
|
|
294
341
|
|
295
342
|
## Etc ##
|
296
343
|
|
297
|
-
*
|
344
|
+
* Mailing list: <http://groups.google.com/group/wrong-rb>
|
345
|
+
* Github project: <http://github.com/sconover/wrong>
|
298
346
|
* Tracker project: <http://www.pivotaltracker.com/projects/109993>
|
299
347
|
* the [Wrong way translation table (from RSpec and Test::Unit)](https://spreadsheets.google.com/pub?key=0AouPn6oLrimWdE0tZDVOWnFGMzVPZy0tWHZwdnhFYkE&hl=en&output=html). (Ask <alexch@gmail.com> if you want editing privileges.)
|
300
348
|
* the [Wrong slides](http://www.slideshare.net/alexchaffee/wrong-5069976) that Alex presented at Carbon Five and GoGaRuCo
|
@@ -1,7 +1,9 @@
|
|
1
|
-
require "wrong"
|
1
|
+
require "wrong/assert"
|
2
|
+
require "wrong/helpers"
|
2
3
|
|
3
4
|
class MiniTest::Unit::TestCase
|
4
|
-
include Wrong
|
5
|
+
include Wrong::Assert
|
6
|
+
include Wrong::Helpers
|
5
7
|
|
6
8
|
def failure_class
|
7
9
|
MiniTest::Assertion
|
@@ -11,4 +13,5 @@ class MiniTest::Unit::TestCase
|
|
11
13
|
self._assertions += 1 # increment minitest's assert count
|
12
14
|
super(valence, explanation, depth + 1) # apparently this passes along the default block
|
13
15
|
end
|
16
|
+
|
14
17
|
end
|
data/lib/wrong/adapters/rspec.rb
CHANGED
@@ -1,21 +1,42 @@
|
|
1
1
|
require "wrong"
|
2
2
|
|
3
|
-
if Object.const_defined? :
|
4
|
-
|
5
|
-
include Wrong
|
3
|
+
if Object.const_defined? :RSpec
|
4
|
+
# RSpec 2
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
6
|
+
if RSpec.const_defined? :Rails
|
7
|
+
# RSpec 2 plus Rails 3
|
8
|
+
module RSpec::Rails::TestUnitAssertionAdapter
|
9
|
+
included do
|
10
|
+
define_assertion_delegators
|
11
|
+
class_eval do
|
12
|
+
remove_method :assert
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module RSpec
|
19
|
+
module Core
|
20
|
+
class ExampleGroup
|
21
|
+
include Wrong
|
22
|
+
|
23
|
+
def failure_class
|
24
|
+
RSpec::Expectations::ExpectationNotMetError
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
elsif Object.const_defined? :Spec
|
31
|
+
# RSpec 1
|
32
|
+
Spec::Runner.configure do |config|
|
33
|
+
include Wrong
|
34
|
+
|
35
|
+
def failure_class
|
36
|
+
Spec::Expectations::ExpectationNotMetError
|
37
|
+
end
|
38
|
+
end
|
14
39
|
|
15
|
-
def failure_class
|
16
|
-
RSpec::Expectations::ExpectationNotMetError
|
17
|
-
end
|
18
|
-
end
|
19
40
|
else
|
20
|
-
|
41
|
+
raise "Wrong's RSpec adapter can't find RSpec. Please require 'spec' or 'rspec' before requiring 'wrong/adapters/rspec'."
|
21
42
|
end
|
data/lib/wrong/assert.rb
CHANGED
@@ -48,10 +48,6 @@ module Wrong
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
-
def summary(method_sym, predicate)
|
52
|
-
method_sym == :deny ? predicate.to_sentence : predicate.to_negative_sentence
|
53
|
-
end
|
54
|
-
|
55
51
|
protected
|
56
52
|
|
57
53
|
# for debugging -- if we couldn't make a predicate out of the code block, then this was why
|
@@ -59,34 +55,6 @@ module Wrong
|
|
59
55
|
@@last_predicated_error ||= nil
|
60
56
|
end
|
61
57
|
|
62
|
-
# todo: move some/all of this into FailureMessage
|
63
|
-
def full_message(chunk, block, valence, explanation)
|
64
|
-
code = chunk.code
|
65
|
-
|
66
|
-
predicate = begin
|
67
|
-
Predicated::Predicate.from_ruby_code_string(code, block.binding)
|
68
|
-
rescue Predicated::Predicate::DontKnowWhatToDoWithThisSexpError, Exception => e
|
69
|
-
# save it off for debugging
|
70
|
-
@@last_predicated_error = e
|
71
|
-
nil
|
72
|
-
end
|
73
|
-
|
74
|
-
code = code.color(:blue) if Wrong.config[:color]
|
75
|
-
message = ""
|
76
|
-
message << "#{explanation}: " if explanation
|
77
|
-
message << "#{valence == :deny ? "Didn't expect" : "Expected"} #{code}, but "
|
78
|
-
if predicate && !(predicate.is_a? Predicated::Conjunction)
|
79
|
-
message << summary(valence, predicate)
|
80
|
-
if formatter = FailureMessage.formatter_for(predicate)
|
81
|
-
failure = formatter.describe
|
82
|
-
failure = failure.bold if Wrong.config[:color]
|
83
|
-
message << failure
|
84
|
-
end
|
85
|
-
end
|
86
|
-
message << chunk.details
|
87
|
-
message
|
88
|
-
end
|
89
|
-
|
90
58
|
def aver(valence, explanation = nil, depth = 0, &block)
|
91
59
|
require "wrong/rainbow" if Wrong.config[:color]
|
92
60
|
|
@@ -96,7 +64,7 @@ module Wrong
|
|
96
64
|
|
97
65
|
chunk = Wrong::Chunk.from_block(block, depth + 2)
|
98
66
|
|
99
|
-
message =
|
67
|
+
message = FailureMessage.new(chunk, valence, explanation).full
|
100
68
|
raise failure_class.new(message)
|
101
69
|
end
|
102
70
|
end
|
data/lib/wrong/chunk.rb
CHANGED
@@ -1,17 +1,16 @@
|
|
1
1
|
require 'ruby_parser'
|
2
2
|
require 'ruby2ruby'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
def require_optionally(library)
|
5
|
+
begin
|
6
|
+
require library
|
7
|
+
rescue LoadError => e
|
8
|
+
raise e unless e.message == "no such file to load -- #{library}"
|
9
|
+
end
|
8
10
|
end
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
rescue LoadError => e
|
13
|
-
raise e unless e.message == "no such file to load -- sourcify"
|
14
|
-
end
|
12
|
+
require_optionally "ParseTree"
|
13
|
+
require_optionally "sourcify"
|
15
14
|
|
16
15
|
require "wrong/config"
|
17
16
|
require "wrong/sexp_ext"
|
@@ -27,16 +26,20 @@ module Wrong
|
|
27
26
|
as_proc.source_location
|
28
27
|
else
|
29
28
|
# in Ruby 1.8, it reads the source location from the call stack
|
30
|
-
|
29
|
+
# # $stderr.puts "---"
|
30
|
+
# $stderr.puts caller.join("\n")
|
31
|
+
relevant_caller = caller[depth]
|
32
|
+
# $stderr.puts "*** #{relevant_caller}"
|
33
|
+
relevant_caller.split(":")
|
31
34
|
end
|
32
35
|
|
33
|
-
new(file, line, block)
|
36
|
+
new(file, line, &block)
|
34
37
|
end
|
35
38
|
|
36
39
|
attr_reader :file, :line_number, :block
|
37
40
|
|
38
41
|
# line parameter is 1-based
|
39
|
-
def initialize(file, line_number, block
|
42
|
+
def initialize(file, line_number, &block)
|
40
43
|
@file = file
|
41
44
|
@line_number = line_number.to_i
|
42
45
|
@block = block
|
@@ -72,17 +75,19 @@ module Wrong
|
|
72
75
|
end)
|
73
76
|
end
|
74
77
|
|
75
|
-
def read_source_file(file
|
76
|
-
|
78
|
+
def read_source_file(file)
|
79
|
+
Chunk.read_here_or_higher(file)
|
80
|
+
end
|
77
81
|
|
82
|
+
def self.read_here_or_higher(file, dir = ".")
|
83
|
+
File.read "#{dir}/#{file}"
|
78
84
|
rescue Errno::ENOENT, Errno::EACCES => e
|
79
85
|
# we may be in a chdir underneath where the file is, so move up one level and try again
|
80
86
|
parent = "#{dir}/..".gsub(/^(\.\/)*/, '')
|
81
87
|
if File.expand_path(dir) == File.expand_path(parent)
|
82
88
|
raise Errno::ENOENT, "couldn't find #{file}"
|
83
89
|
end
|
84
|
-
|
85
|
-
|
90
|
+
read_here_or_higher(file, parent)
|
86
91
|
end
|
87
92
|
|
88
93
|
# Algorithm:
|
@@ -131,7 +136,7 @@ module Wrong
|
|
131
136
|
self.claim.to_ruby
|
132
137
|
rescue => e
|
133
138
|
# note: this is untested; it's to recover from when we can't locate the code
|
134
|
-
message = "Failed assertion at #{
|
139
|
+
message = "Failed assertion at #{file}:#{line_number} [couldn't retrieve source code due to #{e.inspect}]"
|
135
140
|
raise failure_class.new(message)
|
136
141
|
end
|
137
142
|
|
@@ -142,7 +147,7 @@ module Wrong
|
|
142
147
|
# todo: extract some of this into Sexp
|
143
148
|
parts_list = []
|
144
149
|
begin
|
145
|
-
unless sexp.first
|
150
|
+
unless [:arglist, :lasgn, :iter].include? sexp.first
|
146
151
|
code = sexp.to_ruby.strip
|
147
152
|
parts_list << code unless code == "" || parts_list.include?(code)
|
148
153
|
end
|
@@ -166,6 +171,12 @@ module Wrong
|
|
166
171
|
end
|
167
172
|
|
168
173
|
def details
|
174
|
+
@details ||= build_details
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def build_details
|
169
180
|
require "wrong/rainbow" if Wrong.config[:color]
|
170
181
|
s = ""
|
171
182
|
parts = self.parts
|
@@ -196,10 +207,10 @@ module Wrong
|
|
196
207
|
raises = raises.bold.color(:red)
|
197
208
|
end
|
198
209
|
formatted_exeption = if e.message and e.message != e.class.to_s
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
210
|
+
indent(2, part, " ", raises, ": ", indent_all(3, e.message))
|
211
|
+
else
|
212
|
+
indent(2, part, " ", raises)
|
213
|
+
end
|
203
214
|
details << formatted_exeption
|
204
215
|
end
|
205
216
|
end
|
@@ -214,8 +225,7 @@ module Wrong
|
|
214
225
|
|
215
226
|
end
|
216
227
|
|
217
|
-
|
218
|
-
|
228
|
+
public # don't know exactly why this needs to be public but eval'ed code can't find it otherwise
|
219
229
|
def indent(indent, *s)
|
220
230
|
"#{" " * indent}#{s.join('')}"
|
221
231
|
end
|
data/lib/wrong/config.rb
CHANGED
@@ -1,25 +1,60 @@
|
|
1
|
+
require "wrong/chunk"
|
2
|
+
|
1
3
|
module Wrong
|
4
|
+
def self.load_config
|
5
|
+
settings = begin
|
6
|
+
Chunk.read_here_or_higher(".wrong")
|
7
|
+
rescue Errno::ENOENT => e
|
8
|
+
# couldn't find it
|
9
|
+
nil # In Ruby 1.8, "e" would be returned here otherwise
|
10
|
+
end
|
11
|
+
Config.new settings
|
12
|
+
end
|
13
|
+
|
2
14
|
def self.config
|
3
|
-
@config ||=
|
15
|
+
@config ||= load_config
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.config=(new_config)
|
19
|
+
@config = load_config
|
4
20
|
end
|
5
21
|
|
6
22
|
class Config < Hash
|
23
|
+
def initialize(string = nil)
|
24
|
+
self[:aliases] = {:assert => [:assert], :deny => [:deny]}
|
25
|
+
if string
|
26
|
+
instance_eval string.gsub(/^(.*=)/, "self.\\1")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def method_missing(name, value = true)
|
31
|
+
name = name.to_s
|
32
|
+
if name =~ /=$/
|
33
|
+
name.gsub!(/=$/, '')
|
34
|
+
end
|
35
|
+
self[name.to_sym] = value
|
36
|
+
end
|
37
|
+
|
38
|
+
def alias_assert_or_deny(valence, extra_name)
|
39
|
+
Wrong::Assert.send(:alias_method, extra_name, valence)
|
40
|
+
new_method_name = extra_name.to_sym
|
41
|
+
self[:aliases][valence] << new_method_name unless self[:aliases][valence].include?(new_method_name)
|
42
|
+
end
|
43
|
+
|
7
44
|
def alias_assert(method_name)
|
8
|
-
|
9
|
-
self.assert_method_names << method_name.to_sym unless self.assert_method_names.include?(method_name)
|
45
|
+
alias_assert_or_deny(:assert, method_name)
|
10
46
|
end
|
11
47
|
|
12
48
|
def alias_deny(method_name)
|
13
|
-
|
14
|
-
self.deny_method_names << method_name.to_sym unless self.deny_method_names.include?(method_name)
|
49
|
+
alias_assert_or_deny(:deny, method_name)
|
15
50
|
end
|
16
51
|
|
17
52
|
def assert_method_names
|
18
|
-
|
53
|
+
self[:aliases][:assert]
|
19
54
|
end
|
20
55
|
|
21
56
|
def deny_method_names
|
22
|
-
|
57
|
+
self[:aliases][:deny]
|
23
58
|
end
|
24
59
|
|
25
60
|
def assert_methods
|
@@ -39,5 +39,64 @@ module Wrong
|
|
39
39
|
false
|
40
40
|
end
|
41
41
|
end
|
42
|
+
|
43
|
+
|
44
|
+
attr_accessor :chunk, :valence, :explanation
|
45
|
+
|
46
|
+
def initialize(chunk, valence, explanation)
|
47
|
+
@chunk, @valence, @explanation = chunk, valence, explanation
|
48
|
+
end
|
49
|
+
|
50
|
+
def basic
|
51
|
+
"#{valence == :deny ? "Didn't expect" : "Expected"} #{colored(:blue, chunk.code)}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def full
|
55
|
+
message = ""
|
56
|
+
message << "#{explanation}: " if explanation
|
57
|
+
message << basic
|
58
|
+
|
59
|
+
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
|
64
|
+
|
65
|
+
unless chunk.details.empty? and formatted_message.nil?
|
66
|
+
message << ", but"
|
67
|
+
end
|
68
|
+
|
69
|
+
message << formatted_message if formatted_message
|
70
|
+
message << chunk.details unless chunk.details.empty?
|
71
|
+
message
|
72
|
+
end
|
73
|
+
|
74
|
+
protected
|
75
|
+
def code
|
76
|
+
@code ||= chunk.code
|
77
|
+
end
|
78
|
+
|
79
|
+
def predicate
|
80
|
+
@predicate ||= begin
|
81
|
+
Predicated::Predicate.from_ruby_code_string(code, chunk.block.binding)
|
82
|
+
rescue Predicated::Predicate::DontKnowWhatToDoWithThisSexpError, Exception => e
|
83
|
+
# save it off for debugging
|
84
|
+
@@last_predicated_error = e
|
85
|
+
nil
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def colored(color, text)
|
90
|
+
if Wrong.config[:color]
|
91
|
+
if color == :bold
|
92
|
+
text.bold
|
93
|
+
else
|
94
|
+
text.color(color)
|
95
|
+
end
|
96
|
+
else
|
97
|
+
text
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
42
101
|
end
|
43
102
|
end
|