lib/assert_same.rb in assert_same-0.3 vs lib/assert_same.rb in assert_same-0.5
- old
+ new
@@ -47,10 +47,19 @@
$assert_same_options << "--autoaccept" if ARGV.include?("--autoaccept")
end
+#Use this to raise internal error with a given message
+#You can define your own method for your application
+unless defined? internal_error
+ def internal_error(message = 'internal error')
+ raise message
+ end
+end
+
+
module Test::Unit::Assertions
#Hash[filename][line_number] = offset
#For each line in the original file we store its offset (+N or -N lines)
#relative to the actual file
@@ -66,10 +75,18 @@
# assert_same something, <<-END
# foo
# bar
# zee
# END
+ #
+ # You can also use assert_same for blocks. Then you can assert block result or raised exception
+ # assert_same(<<-END) do
+ # Exception NoMethodError: undefined method `+' for nil:NilClass
+ # END
+ # # Code block starts here
+ # c = nil + 1
+ # end
#
# Then run tests as usual:
# rake test:units
# ruby test/unit/foo_test.rb
# ...
@@ -148,29 +165,43 @@
# correctly updates all assert_same's in the test file
# - it's ok to omit expected string, like this:
# assert_same something
# in fact, this is the preferred way to create assert_same tests - you write empty
# assert_same, run tests and they will fill expected values for you automatically
- def assert_same(actual, expected = :autofill_expected_value)
- if expected.class == String
- expected ||= ""
- mode = :expecting_string
- elsif expected == :autofill_expected_value
+ def assert_same(*args)
+ if block_given?
+ mode = :block
+ expected = args[0]
+ actual = ""
+ begin
+ actual = yield.to_s
+ rescue Exception => e
+ actual = "Exception #{e.class}: #{e.message}"
+ end
+ else
+ mode = :scalar
+ expected = args[1]
+ actual = args[0]
+ end
+
+ if expected.nil?
expected = ""
- mode = :autofill_expected_value
+ change = :create_expected_string
+ elsif expected.class == String
+ change = :update_expected_string
elsif expected.class == Hash
raise ":log key is missing" unless expected.has_key? :log
- mode = :expecting_file
log_file = expected[:log]
if defined? RAILS_ROOT
log_file = File.expand_path(log_file, RAILS_ROOT)
else
log_file = File.expand_path(log_file, Dir.pwd)
end
expected = File.exists?(log_file) ? File.read(log_file) : ""
+ change = :update_file_with_expected_string
else
- internal_error("Incorrect expected argument for assert_same. It must be either String or Hash.")
+ internal_error("Invalid expected class #{excepted.class}")
end
# interactive mode is turned on by default, except when
# - --no-interactive is given
# - STDIN is not a terminal device (i.e. we can't ask any questions)
@@ -196,14 +227,16 @@
$assert_same_options << "--autoaccept" if response == "Y"
$assert_same_options << "--no-interactive" if response == "N"
end
if accept
- if [:expecting_string, :autofill_expected_value].include? mode
- accept_string(actual, mode)
- elsif mode == :expecting_file
+ if [:create_expected_string, :update_expected_string].include? change
+ accept_string(actual, change, mode)
+ elsif change == :update_file_with_expected_string
accept_file(actual, log_file)
+ else
+ internal_error("Invalid change #{change}")
end
end
end
if accept
# when change is accepted, we should not report it as a failure because
@@ -244,11 +277,14 @@
else
raise MiniTest::Assertion.new(diff)
end
end
- def accept_string(actual, mode)
+ # actual - actual value of the scalar or result of the executed block
+ # change - what to do with expected value (:create_expected_string or :update_expected_string)
+ # mode - describes signature of assert_same call by type of main argument (:block or :scalar)
+ def accept_string(actual, change, mode)
file, method, line = get_caller_location(:depth => 3)
# read source file, construct the new source, replacing everything
# between "do" and "end" in assert_same's block
# using File::expand_path here because "file" can be either
@@ -260,34 +296,43 @@
offset = @@file_offsets[file].keys.inject(0) do |sum, i|
line.to_i >= i ? sum + @@file_offsets[file][i] : sum
end
expected_text_end_line = expected_text_start_line = line.to_i + offset
- unless mode == :autofill_expected_value
- #if we're autofilling the value, END/EOS marker will not exist
- #(second arg to assert_same is omitted)
- #else we search for it
+ if change == :update_expected_string
+ #search for the end of expected value in code
expected_text_end_line += 1 while !["END", "EOS"].include?(source[expected_text_end_line].strip)
+ elsif change == :create_expected_string
+ # The is no expected value yet. expected_text_end_line is unknown
+ else
+ internal_error("Invalid change #{change}")
end
expected_length = expected_text_end_line - expected_text_start_line
# indentation is the indentation of assert_same call + 4
indentation = source[expected_text_start_line-1] =~ /^(\s+)/ ? $1.length : 0
indentation += 4
- if mode == :autofill_expected_value
- # add second argument to assert_same if it's omitted
- source[expected_text_start_line-1] = "#{source[expected_text_start_line-1].chop}, <<-END\n"
+ if change == :create_expected_string
+ if mode == :scalar
+ # add second argument to assert_same if it's omitted
+ source[expected_text_start_line-1] = "#{source[expected_text_start_line-1].chop}, <<-END\n"
+ elsif mode == :block
+ # add expected value as argument to assert_same before block call
+ source[expected_text_start_line-1] = source[expected_text_start_line-1].sub(/assert_same(\(.*?\))*/, "assert_same(<<-END)")
+ else
+ internal_error("Invalid mode #{mode}")
+ end
end
source = source[0, expected_text_start_line] +
actual.split("\n").map { |l| "#{" "*(indentation)}#{l}\n"} +
- (mode == :autofill_expected_value ? ["#{" "*(indentation-4)}END\n"] : [])+
+ (change == :create_expected_string ? ["#{" "*(indentation-4)}END\n"] : [])+
source[expected_text_end_line, source.length]
# recalculate line number adjustments
actual_length = actual.split("\n").length
- actual_length += 1 if mode == :autofill_expected_value # END marker after actual value
+ actual_length += 1 if change == :create_expected_string # END marker after expected value
@@file_offsets[file][line.to_i] = actual_length - expected_length
source_file = File.open(file, "w+")
source_file.write(source.join(''))
source_file.fsync