diff --git a/.wrong b/.wrong new file mode 100644 index 0000000..15efc10 --- /dev/null +++ b/.wrong @@ -0,0 +1,2 @@ +test_setting = "xyzzy" +color = false # sadly, we must disable this when testing wrong diff --git a/HISTORY.txt b/HISTORY.txt index 553d8b8..b61c1d6 100644 --- a/HISTORY.txt +++ b/HISTORY.txt @@ -1,3 +1,7 @@ +== 0.4.1 + +* reads settings from a .wrong file + == 0.4.0 * "require 'wrong'" and "include Wrong' now get everything; see README for details on how to get less than the whole enchilada diff --git a/README.markdown b/README.markdown index f2ad79a..fedf3b0 100644 --- a/README.markdown +++ b/README.markdown @@ -4,7 +4,7 @@ ## Abstract ## -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 more useful failure message than "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. +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. 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. @@ -16,11 +16,7 @@ Inspired by [assert { 2.0 }](http://assert2.rubyforge.org/) but rewritten from s gem install wrong -Under JRuby, the above may cause errors; if so, then try - - gem install wrong-jruby - -which untangles some dependencies. +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. ## Usage ## @@ -139,13 +135,14 @@ or this ? 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 `=~`. 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)... +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. Wrong also lets you put the expected and actual values in any order you want! Consider the failure messages for assert { current_user == "joe" } # => Expected (current_user == "joe") but current_user is "fred" assert { "joe" == current_user } # => Expected ("joe" == current_user) but current_user is "fred" -You get just the information you want, and none you don't want. At least, that's the plan! :-) +You get all the information you want, and none you don't want. At least, that's the plan! :-) ## Algorithm ## @@ -153,16 +150,18 @@ So wait a second. How do we do it? Doesn't Ruby have [poor support for AST intro 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: -* It works! Tested in 1.8.6, 1.8.7, 1.9.1, and 1.9.2-rc2. (Thank you, [rvm](http://rvm.beginrescueend.com/)!) +* 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/)!) * Your code needs to be in a file. * If you're developing Ruby code without saving it to a mounted disk, then sorry, Wrong is not right for you. - * We monkey-patch IRB so if you do `irb -rwrong` it'll save off your session in a place Wrong can read it. + * We monkey-patch IRB so if you do `irb -rwrong` it'll save off your session in memory where Wrong can read it. + * 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. * It's a development-time testing library, not a production runtime library, so there are no security or filesystem issues. * `eval` isn't evil, it's just misunderstood. * It makes a few assumptions about the structure of your code, leading to some restrictions: - * 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. And actually, technically you can put two asserts on a line, but it always describes the first one it sees, which means that if the second one executes, its failure message will be incorrect or broken.) + * 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.) * You can't use metaprogramming to write your assert blocks. - * All variables and methods must be available in the binding of the assertion block. + * All variables and methods must be available in the binding of the assert block. + * Passing a proc around and eventually calling assert on it might not work in some Ruby implementations. ## Adapters ## @@ -172,9 +171,9 @@ Currently we support * Test::Unit - `require 'wrong/adapters/test_unit'` * Minitest - `require 'wrong/adapters/minitest'` - * RSpec - `require 'wrong/adapters/rspec'` + * RSpec - `require 'wrong/adapters/rspec'` (now supports both 1.3 and 2.0) -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. +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. ## Explanations ## @@ -248,34 +247,52 @@ To use these formatters, you have to explicitly `require` them! You may also nee [Bug: turns out 'diff' and 'diff-lcs' are incompatible with each other. We're working on a fix.] -## Color ## +## Config ## + +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 + + ice_cream # => Wrong.config[:ice_cream] => true + ice_cream = true # => Wrong.config[:ice_cream] => true + ice_cream = "vanilla" # => Wrong.config[:ice_cream] => "vanilla" + +or any of these at runtime: + + Wrong.config.ice_cream # => Wrong.config[:ice_cream] => true + Wrong.config.ice_cream = true # => Wrong.config[:ice_cream] => true + Wrong.config.ice_cream = "vanilla" # => Wrong.config[:ice_cream] => "vanilla" + +### Color ### + +Apparently, no test framework is successful unless and until it supports console colors. So now we do. Call + + Wrong.config.color -Apparently, no test framework is successful unless and until it supports console colors. So now we do. Put +in your test helper or rakefile or wherever, or put - Wrong.config[:color] = true + color -in your test helper or rakefile or wherever and get ready to be **bedazzled**. If you need custom colors, let us know. +in your `.wrong` file and get ready to be **bedazzled**. If you need custom colors, let us know. -## Aliases ## +### Aliases ### -An end to the language wars! Name your "assert" and "deny" methods anything you want. Here are some suggestions: +An end to the language wars! Name your "assert" and "deny" methods anything you want. In your code, use `Wrong.config.alias_assert` and `Wrong.config.alias_deny`, and in your `.wrong` file, use Here are some suggestions: - Wrong.config.alias_assert(:expect) - Wrong.config.alias_assert(:should) # This looks nice with RSpec - Wrong.config.alias_assert(:confirm) - Wrong.config.alias_assert(:be) + alias_assert :expect + alias_assert :should # This looks nice in RSpec + alias_assert :confirm + alias_assert :be - Wrong.config.alias_assert(:is) - Wrong.config.alias_deny(:aint) + alias_assert :is + alias_deny :aint - Wrong.config.alias_assert(:assure) - Wrong.config.alias_deny(:refute) + alias_assert :assure + alias_deny :refute - Wrong.config.alias_assert(:yep) - Wrong.config.alias_deny(:nope) + alias_assert :yep + alias_deny :nope - Wrong.config.alias_assert(:yay!) - Wrong.config.alias_deny(:boo!) + alias_assert :yay! + alias_deny :boo! Just don't use "`aver`" since we took that one for an internal method in `Wrong::Assert`. @@ -294,7 +311,8 @@ If you're in Ruby 1.8, you **really** shouldn't do it! But if you do, you can us ## Etc ## -* Github projects: , +* Mailing list: +* Github project: * Tracker project: * the [Wrong way translation table (from RSpec and Test::Unit)](https://spreadsheets.google.com/pub?key=0AouPn6oLrimWdE0tZDVOWnFGMzVPZy0tWHZwdnhFYkE&hl=en&output=html). (Ask if you want editing privileges.) * the [Wrong slides](http://www.slideshare.net/alexchaffee/wrong-5069976) that Alex presented at Carbon Five and GoGaRuCo diff --git a/examples.rb b/examples.rb index f037658..4eccbd5 100644 --- a/examples.rb +++ b/examples.rb @@ -11,7 +11,7 @@ include Wrong -Wrong.config[:color] = true +Wrong.config.color # or just put the line "color" in a file called ".wrong" in the current dir def failing e = rescuing do diff --git a/lib/wrong/chunk.rb b/lib/wrong/chunk.rb index 3745243..87f5164 100644 --- a/lib/wrong/chunk.rb +++ b/lib/wrong/chunk.rb @@ -72,17 +72,19 @@ def build_sexp end) end - def read_source_file(file, dir = ".") - File.read "#{dir}/#{file}" + def read_source_file(file) + Chunk.read_here_or_higher(file) + end + def self.read_here_or_higher(file, dir = ".") + File.read "#{dir}/#{file}" rescue Errno::ENOENT, Errno::EACCES => e # we may be in a chdir underneath where the file is, so move up one level and try again parent = "#{dir}/..".gsub(/^(\.\/)*/, '') if File.expand_path(dir) == File.expand_path(parent) raise Errno::ENOENT, "couldn't find #{file}" end - read_source_file(file, parent) - + read_here_or_higher(file, parent) end # Algorithm: @@ -196,10 +198,10 @@ def details raises = raises.bold.color(:red) end formatted_exeption = if e.message and e.message != e.class.to_s - indent(2, part, " ", raises, ": ", indent_all(3, e.message)) - else - indent(2, part, " ", raises) - end + indent(2, part, " ", raises, ": ", indent_all(3, e.message)) + else + indent(2, part, " ", raises) + end details << formatted_exeption end end diff --git a/lib/wrong/config.rb b/lib/wrong/config.rb index 0b12d9e..b7a56b2 100644 --- a/lib/wrong/config.rb +++ b/lib/wrong/config.rb @@ -1,9 +1,38 @@ +require "wrong/chunk" + module Wrong + def self.load_config + settings = begin + Chunk.read_here_or_higher(".wrong") + rescue Errno::ENOENT => e + # couldn't find it + end + Config.new settings + end + def self.config - @config ||= Config.new + @config ||= load_config + end + + def self.config=(new_config) + @config = load_config end class Config < Hash + def initialize(string = nil) + if string + instance_eval string.gsub(/^(.*=)/, "self.\\1") + end + end + + def method_missing(name, value = true) + name = name.to_s + if name =~ /=$/ + name.gsub!(/=$/, '') + end + self[name.to_sym] = value + end + def alias_assert(method_name) Wrong::Assert.send(:alias_method, method_name, :assert) self.assert_method_names << method_name.to_sym unless self.assert_method_names.include?(method_name) diff --git a/lib/wrong/sexp_ext.rb b/lib/wrong/sexp_ext.rb index ca5d517..0115bda 100644 --- a/lib/wrong/sexp_ext.rb +++ b/lib/wrong/sexp_ext.rb @@ -12,8 +12,8 @@ def to_ruby # visit every node in the tree, including the root, that is an Sexp # todo: test - def each_subexp(&block) - yield self + def each_subexp(include_root = true, &block) + yield self if include_root each do |child| if child.is_a?(Sexp) child.each_subexp(&block) @@ -42,7 +42,7 @@ def assertion private def nested_assertions assertions = [] - self.each_of_type(:iter) { |sexp| assertions << sexp if sexp.assertion? } + self.each_subexp(false) { |sexp| assertions << sexp if sexp.assertion? } assertions end diff --git a/lib/wrong/version.rb b/lib/wrong/version.rb index 2fbce2c..4b5e9f4 100644 --- a/lib/wrong/version.rb +++ b/lib/wrong/version.rb @@ -1,3 +1,3 @@ module Wrong - VERSION = "0.4.0" unless defined?(Wrong::VERSION) + VERSION = "0.4.1" unless defined?(Wrong::VERSION) end diff --git a/test/assert_advanced_test.rb b/test/assert_advanced_test.rb index 2b25caa..7f335be 100644 --- a/test/assert_advanced_test.rb +++ b/test/assert_advanced_test.rb @@ -20,18 +20,18 @@ def assert_later(&p) end end - xit "can parse a here doc defined inside the block" do + it "can parse a here doc defined inside the block" do # todo: test in Chunk too - assert { "123\n456" == <<-TEXT + assert { "123\n456\n" == <<-TEXT 123 456 TEXT } end - xit "can parse a here doc defined outside the block" do + it "can parse a here doc defined outside the block" do # todo: test in Chunk too - assert { "123\n456" == <<-TEXT } + assert { "123\n456\n" == <<-TEXT } 123 456 TEXT diff --git a/test/config_test.rb b/test/config_test.rb index 2ed3740..d07e830 100644 --- a/test/config_test.rb +++ b/test/config_test.rb @@ -10,80 +10,120 @@ include Wrong before do - Wrong.config.clear + @config = Wrong::Config.new end - it "singleton" do - c = Wrong.config - assert { c.is_a?(Wrong::Config) } - c2 = Wrong.config - assert { c.object_id == c2.object_id } + it "has magic setters" do + config = Wrong::Config.new + config.foo = "bar" + assert { config[:foo] == "bar" } + + config.baz + assert { config[:baz] } + end + + it "reads from a string" do + config = Wrong::Config.new <<-SETTINGS +cold +flavor = "chocolate" +alias_assert :yum + SETTINGS + assert { config[:cold] } + assert { config[:flavor] == "chocolate" } + assert { config.assert_method_names.include? :yum } end -# it "reads from a .wrong file" + describe "Wrong.config" do + it "is a singleton" do + c = Wrong.config + assert { c.is_a?(Wrong::Config) } + c2 = Wrong.config + assert { c.object_id == c2.object_id } + end + + it "reads from a .wrong file in the current directory" do + wrong_settings = File.read(".wrong") + assert { wrong_settings != "" } + assert { Wrong.config[:test_setting] == "xyzzy" } + end + + it "reads from a .wrong file in a parent directory" do + wrong_settings = File.read(".wrong") + Dir.chdir("test") do # move into a subdirectory + assert { Wrong.load_config[:test_setting] == "xyzzy" } + end + end + + end it "getting an undeclared setting" do - assert { Wrong.config[:foo].nil? } + assert { @config[:foo].nil? } end it "setting and getting" do - Wrong.config[:foo] = "bar" - assert { Wrong.config[:foo] == "bar" } + @config[:foo] = "bar" + assert { @config[:foo] == "bar" } end - describe "adding aliases for assert" do - before do - Wrong.config.alias_assert(:is) + describe "adding aliases" do + after do + Wrong.config = Wrong::Config.new end - it "succeeds" do - is { 2 + 2 == 4 } - end + describe "for assert" do + before do + Wrong.config.alias_assert(:is) + end + + it "succeeds" do + is { 2 + 2 == 4 } + end - it "fails" do - e = rescuing { - is("math is hard") { 2 + 2 == 5 } - } - expected = <<-FAIL + it "fails" do + e = rescuing { + is("math is hard") { 2 + 2 == 5 } + } + expected = <<-FAIL math is hard: Expected ((2 + 2) == 5), but 4 is not equal to 5 (2 + 2) is 4 - FAIL - assert { e.message == expected } + FAIL + assert { e.message == expected } + end + + it "doesn't keep aliasing the same word" do + @config.alias_assert(:is) + @config.alias_assert(:is) + assert { @config.assert_method_names == [:assert, :is] } + end end - it "doesn't keep aliasing the same word" do - Wrong.config.alias_assert(:is) - Wrong.config.alias_assert(:is) - assert { Wrong.config.assert_method_names == [:assert, :is] } - end - end - - describe "adding aliases for deny" do - before do - Wrong.config.alias_deny(:aint) - end + describe "for deny" do + before do + Wrong.config.alias_deny(:aint) + end - it "succeeds" do - aint { 2 + 2 == 5 } - end + it "succeeds" do + aint { 2 + 2 == 5 } + end - it "fails" do - e = rescuing { - aint("math is hard") { 2 + 2 == 4 } - } - expected = <<-FAIL + it "fails" do + e = rescuing { + aint("math is hard") { 2 + 2 == 4 } + } + expected = <<-FAIL math is hard: Didn't expect ((2 + 2) == 4), but 4 is equal to 4 (2 + 2) is 4 - FAIL - assert { e.message == expected } - end + FAIL + assert { e.message == expected } + end + + it "doesn't keep aliasing the same word" do + @config.alias_deny(:aint) + @config.alias_deny(:aint) + assert { @config.deny_method_names == [:deny, :aint] } + end - it "doesn't keep aliasing the same word" do - Wrong.config.alias_deny(:aint) - Wrong.config.alias_deny(:aint) - assert { Wrong.config.deny_method_names == [:deny, :aint] } end end - end diff --git a/test/test_helper.rb b/test/test_helper.rb index a0db641..5ecbf92 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,4 +1,8 @@ -puts "RUBY_VERSION=#{RUBY_VERSION}#{" (JRuby)" if Object.const_defined?(:JRuby)}" +puts(if Object.const_defined? :RUBY_DESCRIPTION + RUBY_DESCRIPTION +else + "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE} patchlevel #{RUBY_PATCHLEVEL}) [#{RUBY_PLATFORM}]" +end) dir = File.dirname(__FILE__) $LOAD_PATH.unshift "#{dir}/../lib" @@ -37,7 +41,7 @@ def xdescribe(str) class MiniTest::Spec include MiniTest::Assertions - + class << self def xit(str) puts "x'd out test \"#{str}\"" diff --git a/which_ruby.rb b/which_ruby.rb new file mode 100644 index 0000000..1e9d758 --- /dev/null +++ b/which_ruby.rb @@ -0,0 +1,5 @@ +if Object.const_defined? :RUBY_DESCRIPTION + puts RUBY_DESCRIPTION +else + puts "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE} patchlevel #{RUBY_PATCHLEVEL}) [#{RUBY_PLATFORM}]" +end