assert_same 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ *~
2
+ *.gem
3
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010-2011 Pluron, Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,5 @@
1
+ assert_same
2
+ ===========
3
+
4
+ Please read the API docs in lib/assert_same.rb
5
+
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'bundler'
5
+
6
+ Bundler::GemHelper.install_tasks
7
+
8
+ desc 'Default: run unit tests.'
9
+ task :default => :test
10
+
11
+ desc 'Test assert_same.'
12
+ Rake::TestTask.new(:test) do |t|
13
+ t.libs << 'lib'
14
+ t.pattern = 'test/**/*_test.rb'
15
+ t.verbose = true
16
+ end
17
+
18
+ desc 'Generate documentation for assert_same.'
19
+ Rake::RDocTask.new(:rdoc) do |rdoc|
20
+ rdoc.rdoc_dir = 'rdoc'
21
+ rdoc.title = 'assert_same'
22
+ rdoc.options << '--line-numbers' << '--inline-source'
23
+ rdoc.rdoc_files.include('README')
24
+ rdoc.rdoc_files.include('lib/**/*.rb')
25
+ end
@@ -0,0 +1,20 @@
1
+ require 'rubygems'
2
+
3
+ SPEC = Gem::Specification.new do |s|
4
+ s.name = "assert_same"
5
+ s.version = "0.1"
6
+ s.author = "Pluron, Inc."
7
+ s.email = "support@pluron.com"
8
+ s.homepage = "http://github.com/acunote/assert_same"
9
+ s.platform = Gem::Platform::RUBY
10
+ s.description = "assert_same assertion"
11
+ s.summary = "Assert which checks that two strings (expected and actual) are same and which can magically replace expected value with the actual in case the new behavior (and new actual value) is correct"
12
+
13
+ s.add_development_dependency('bundler', '>= 1.0.0')
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- test/*`.split("\n")
17
+
18
+ s.require_path = "lib"
19
+ s.has_rdoc = true
20
+ end
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + "/lib")
2
+ require 'assert_same'
data/lib/array_diff.rb ADDED
@@ -0,0 +1,331 @@
1
+ class ArrayDiff
2
+
3
+ VERSION = 0.3
4
+
5
+ def ArrayDiff.lcs(a, b)
6
+ astart = 0
7
+ bstart = 0
8
+ afinish = a.length-1
9
+ bfinish = b.length-1
10
+ mvector = []
11
+
12
+ # First we prune off any common elements at the beginning
13
+ while (astart <= afinish && bstart <= afinish && a[astart] == b[bstart])
14
+ mvector[astart] = bstart
15
+ astart += 1
16
+ bstart += 1
17
+ end
18
+
19
+ # now the end
20
+ while (astart <= afinish && bstart <= bfinish && a[afinish] == b[bfinish])
21
+ mvector[afinish] = bfinish
22
+ afinish -= 1
23
+ bfinish -= 1
24
+ end
25
+
26
+ bmatches = b.reverse_hash(bstart..bfinish)
27
+ thresh = []
28
+ links = []
29
+
30
+ (astart..afinish).each { |aindex|
31
+ aelem = a[aindex]
32
+ next unless bmatches.has_key? aelem
33
+ k = nil
34
+ bmatches[aelem].reverse.each { |bindex|
35
+ if k && (thresh[k] > bindex) && (thresh[k-1] < bindex)
36
+ thresh[k] = bindex
37
+ else
38
+ k = thresh.replacenextlarger(bindex, k)
39
+ end
40
+ links[k] = [ (k==0) ? nil : links[k-1], aindex, bindex ] if k
41
+ }
42
+ }
43
+
44
+ if !thresh.empty?
45
+ link = links[thresh.length-1]
46
+ while link
47
+ mvector[link[1]] = link[2]
48
+ link = link[0]
49
+ end
50
+ end
51
+
52
+ return mvector
53
+ end
54
+
55
+ def makediff(a, b)
56
+ mvector = ArrayDiff.lcs(a, b)
57
+ ai = bi = 0
58
+ while ai < mvector.length
59
+ bline = mvector[ai]
60
+ if bline
61
+ while bi < bline
62
+ discardb(bi, b[bi])
63
+ bi += 1
64
+ end
65
+ match(ai, bi)
66
+ bi += 1
67
+ else
68
+ discarda(ai, a[ai])
69
+ end
70
+ ai += 1
71
+ end
72
+ while ai < a.length
73
+ discarda(ai, a[ai])
74
+ ai += 1
75
+ end
76
+ while bi < b.length
77
+ discardb(bi, b[bi])
78
+ bi += 1
79
+ end
80
+ match(ai, bi)
81
+ 1
82
+ end
83
+
84
+ def compactdiffs
85
+ diffs = []
86
+ @diffs.each { |df|
87
+ i = 0
88
+ curdiff = []
89
+ while i < df.length
90
+ whot = df[i][0]
91
+ s = @isstring ? df[i][2].chr : [df[i][2]]
92
+ p = df[i][1]
93
+ last = df[i][1]
94
+ i += 1
95
+ while df[i] && df[i][0] == whot && df[i][1] == last+1
96
+ s << df[i][2]
97
+ last = df[i][1]
98
+ i += 1
99
+ end
100
+ curdiff.push [whot, p, s]
101
+ end
102
+ diffs.push curdiff
103
+ }
104
+ return diffs
105
+ end
106
+
107
+ attr_reader :diffs, :difftype
108
+
109
+ def initialize(diffs_or_a, b = nil, isstring = nil)
110
+ if b.nil?
111
+ @diffs = diffs_or_a
112
+ @isstring = isstring
113
+ else
114
+ @diffs = []
115
+ @curdiffs = []
116
+ makediff(diffs_or_a, b)
117
+ @difftype = diffs_or_a.class
118
+ end
119
+ end
120
+
121
+ def match(ai, bi)
122
+ @diffs.push @curdiffs unless @curdiffs.empty?
123
+ @curdiffs = []
124
+ end
125
+
126
+ def discarda(i, elem)
127
+ @curdiffs.push ['-', i, elem]
128
+ end
129
+
130
+ def discardb(i, elem)
131
+ @curdiffs.push ['+', i, elem]
132
+ end
133
+
134
+ def compact
135
+ return Diff.new(compactdiffs)
136
+ end
137
+
138
+ def compact!
139
+ @diffs = compactdiffs
140
+ end
141
+
142
+ def inspect
143
+ @diffs.inspect
144
+ end
145
+
146
+ def diffrange(a, b)
147
+ if (a == b)
148
+ "#{a}"
149
+ else
150
+ "#{a},#{b}"
151
+ end
152
+ end
153
+
154
+ def to_diff
155
+ offset = 0
156
+ @diffs.each { |b|
157
+ first = b[0][1]
158
+ length = b.length
159
+ action = b[0][0]
160
+ addcount = 0
161
+ remcount = 0
162
+ b.each { |l|
163
+ if l[0] == "+"
164
+ addcount += 1
165
+ elsif l[0] == "-"
166
+ remcount += 1
167
+ end
168
+ }
169
+ if addcount == 0
170
+ puts "#{diffrange(first+1, first+remcount)}d#{first+offset}"
171
+ elsif remcount == 0
172
+ puts "#{first-offset}a#{diffrange(first+1, first+addcount)}"
173
+ else
174
+ puts "#{diffrange(first+1, first+remcount)}c#{diffrange(first+offset+1, first+offset+addcount)}"
175
+ end
176
+ lastdel = (b[0][0] == "-")
177
+ b.each { |l|
178
+ if l[0] == "-"
179
+ offset -= 1
180
+ print "< "
181
+ elsif l[0] == "+"
182
+ offset += 1
183
+ if lastdel
184
+ lastdel = false
185
+ puts "---"
186
+ end
187
+ print "> "
188
+ end
189
+ print l[2]
190
+ print "\n"
191
+ }
192
+ }
193
+ end
194
+
195
+
196
+ end
197
+
198
+ module Diffable
199
+ def diff(b)
200
+ ArrayDiff.new(self, b)
201
+ end
202
+
203
+ # Create a hash that maps elements of the array to arrays of indices
204
+ # where the elements are found.
205
+
206
+ def reverse_hash(range = (0...self.length))
207
+ revmap = {}
208
+ range.each { |i|
209
+ elem = self[i]
210
+ if revmap.has_key? elem
211
+ revmap[elem].push i
212
+ else
213
+ revmap[elem] = [i]
214
+ end
215
+ }
216
+ return revmap
217
+ end
218
+
219
+ def replacenextlarger(value, high = nil)
220
+ high ||= self.length
221
+ if self.empty? || value > self[-1]
222
+ push value
223
+ return high
224
+ end
225
+ # binary search for replacement point
226
+ low = 0
227
+ while low < high
228
+ index = (high+low)/2
229
+ found = self[index]
230
+ return nil if value == found
231
+ if value > found
232
+ low = index + 1
233
+ else
234
+ high = index
235
+ end
236
+ end
237
+
238
+ self[low] = value
239
+ # $stderr << "replace #{value} : 0/#{low}/#{init_high} (#{steps} steps) (#{init_high-low} off )\n"
240
+ # $stderr.puts self.inspect
241
+ #gets
242
+ #p length - low
243
+ return low
244
+ end
245
+
246
+ def patch(diff)
247
+ newary = nil
248
+ if diff.difftype == String
249
+ newary = diff.difftype.new('')
250
+ else
251
+ newary = diff.difftype.new
252
+ end
253
+ ai = 0
254
+ bi = 0
255
+ diff.diffs.each { |d|
256
+ d.each { |mod|
257
+ case mod[0]
258
+ when '-'
259
+ while ai < mod[1]
260
+ newary << self[ai]
261
+ ai += 1
262
+ bi += 1
263
+ end
264
+ ai += 1
265
+ when '+'
266
+ while bi < mod[1]
267
+ newary << self[ai]
268
+ ai += 1
269
+ bi += 1
270
+ end
271
+ newary << mod[2]
272
+ bi += 1
273
+ else
274
+ raise "Unknown diff action"
275
+ end
276
+ }
277
+ }
278
+ while ai < self.length
279
+ newary << self[ai]
280
+ ai += 1
281
+ bi += 1
282
+ end
283
+ return newary
284
+ end
285
+ end
286
+
287
+ class Array
288
+ include Diffable
289
+ end
290
+
291
+ class String
292
+ include Diffable
293
+ end
294
+
295
+ =begin
296
+ = ArrayDiff
297
+ (({diff.rb})) - computes the differences between two arrays or
298
+ strings. Copyright (C) 2001 Lars Christensen
299
+ http://users.cybercity.dk/~dsl8950/ruby/diff.html
300
+
301
+ == Synopsis
302
+
303
+ diff = ArrayDiff.new(a, b)
304
+ b = a.patch(diff)
305
+
306
+ == Class ArrayDiff
307
+ === Class Methods
308
+ --- ArrayDiff.new(a, b)
309
+ --- a.diff(b)
310
+ Creates a Diff object which represent the differences between
311
+ ((|a|)) and ((|b|)). ((|a|)) and ((|b|)) can be either be arrays
312
+ of any objects, strings, or object of any class that include
313
+ module ((|Diffable|))
314
+
315
+ == Module Diffable
316
+ The module ((|Diffable|)) is intended to be included in any class for
317
+ which differences are to be computed. Diffable is included into String
318
+ and Array when (({diff.rb})) is (({require}))'d.
319
+
320
+ Classes including Diffable should implement (({[]})) to get element at
321
+ integer indices, (({<<})) to append elements to the object and
322
+ (({ClassName#new})) should accept 0 arguments to create a new empty
323
+ object.
324
+
325
+ === Instance Methods
326
+ --- Diffable#patch(diff)
327
+ Applies the differences from ((|diff|)) to the object ((|obj|))
328
+ and return the result. ((|obj|)) is not changed. ((|obj|)) and
329
+ can be either an array or a string, but must match the object
330
+ from which the ((|diff|)) was created.
331
+ =end
@@ -0,0 +1,273 @@
1
+ # Copyright (c) 2010-2011 Pluron, Inc.
2
+ require 'test/unit'
3
+ require 'text_diff'
4
+ require 'pathname'
5
+
6
+ class Test::Unit::TestCase
7
+
8
+ #Hash[filename][line_number] = offset
9
+ #For each line in the original file we store its offset (+N or -N lines)
10
+ #relative to the actual file
11
+ @@file_offsets = Hash.new { |hash, key| hash[key] = {} }
12
+
13
+ # assert_same: assert which checks that two strings (expected and actual) are same
14
+ # and which can "magically" replace expected value with the actual in case
15
+ # the new behavior (and new actual value) is correct
16
+ #
17
+ # == Usage ==
18
+ #
19
+ # Write this in the test source:
20
+ # assert_same something, <<-END
21
+ # foo
22
+ # bar
23
+ # zee
24
+ # END
25
+ #
26
+ # Then run tests as usual:
27
+ # rake test:units
28
+ # ruby test/unit/foo_test.rb
29
+ # ...
30
+ #
31
+ # When assert_same fails, you'll be able to:
32
+ # - review diff
33
+ # - (optionally) accept new actual value (this modifies the test source file)
34
+ #
35
+ # Additional options for test runs:
36
+ # --no-interactive skips all questions and just reports failures
37
+ # --autoaccept prints diffs and automatically accepts all new actual values
38
+ # --no-canonicalize turns off expected and actual value canonicalization (see below for details)
39
+ #
40
+ # Additional options can be passed during both single test file run and rake test run:
41
+ # ruby test/unit/foo_test.rb -- --autoaccept
42
+ # ruby test/unit/foo_test.rb -- --no-interactive
43
+ #
44
+ # rake test TESTOPTS="-- --autoaccept"
45
+ # rake test:units TESTOPTS="-- --no-canonicalize --autoaccept"
46
+ #
47
+ #
48
+ #
49
+ # == Canonicalization ==
50
+ #
51
+ # Before comparing expected and actual strings, assert_same canonicalizes both using these rules:
52
+ # - indentation is ignored (except for indentation
53
+ # relative to the first line of the expected/actual string)
54
+ # - ruby-style comments after "#" are ignored:
55
+ # both whole-line and end-of-line comments are supported
56
+ # - empty lines are ignored
57
+ # - trailing whitespaces are ignored
58
+ #
59
+ # You can turn canonicalization off with --no-canonicalize option. This is useful
60
+ # when you need to regenerate expected test strings.
61
+ # To regenerate the whole test suite, run:
62
+ # rake test TESTOPTS="-- --no-canonicalize --autoaccept"
63
+ #
64
+ # Example of assert_same with comments:
65
+ # assert_same something, <<-END
66
+ # # some tree
67
+ # foo 1
68
+ # foo 1.1
69
+ # foo 1.2 # some node
70
+ # bar 2
71
+ # bar 2.1
72
+ # END
73
+ #
74
+ #
75
+ #
76
+ # == Umportant Usage Rules ==
77
+ #
78
+ # Restrictions:
79
+ # - only END and EOS are supported as end of string sequence
80
+ # - it's a requirement that you have <<-END at the same line as assert_same
81
+ # - assert_same can't be within a block
82
+ #
83
+ # Storing expected output in files:
84
+ # - assert_same something, :log => <path_to_file>
85
+ # - path to file is relative to:
86
+ # - RAILS_ROOT (if that is defined)
87
+ # - current dir (if no RAILS_ROOT is defined)
88
+ # - file doesn't have to exist, it will be created if necessary
89
+ #
90
+ # Misc:
91
+ # - it's ok to have several assert_same's in the same test method, assert_same.
92
+ # correctly updates all assert_same's in the test file
93
+ # - it's ok to omit expected string, like this:
94
+ # assert_same something
95
+ # in fact, this is the preferred way to create assert_same tests - you write empty
96
+ # assert_same, run tests and they will fill expected values for you automatically
97
+ def assert_same(actual, expected = :autofill_expected_value)
98
+ if expected.class == String
99
+ expected ||= ""
100
+ mode = :expecting_string
101
+ elsif expected == :autofill_expected_value
102
+ expected = ""
103
+ mode = :autofill_expected_value
104
+ elsif expected.class == Hash
105
+ raise ":log key is missing" unless expected.has_key? :log
106
+ mode = :expecting_file
107
+ log_file = expected[:log]
108
+ if defined? RAILS_ROOT
109
+ log_file = File.expand_path(log_file, RAILS_ROOT)
110
+ else
111
+ log_file = File.expand_path(log_file, Dir.pwd)
112
+ end
113
+ expected = File.exists?(log_file) ? File.read(log_file) : ""
114
+ else
115
+ internal_error("Incorrect expected argument for assert_same. It must be either String or Hash.")
116
+ end
117
+
118
+ # interactive mode is turned on by default, except when
119
+ # - --no-interactive is given
120
+ # - STDIN is not a terminal device (i.e. we can't ask any questions)
121
+ interactive = !ARGV.include?("--no-interactive") && STDIN.tty?
122
+ canonicalize = !ARGV.include?("--no-canonicalize")
123
+ autoaccept = ARGV.include?("--autoaccept")
124
+
125
+ is_same_canonicalized, is_same, diff_canonicalized, diff = compare_for_assert_same(expected, actual)
126
+
127
+ if (canonicalize and !is_same_canonicalized) or (!canonicalize and !is_same)
128
+ diff_to_report = canonicalize ? diff_canonicalized : diff
129
+ if interactive
130
+ # print method name and short backtrace
131
+ failure = Test::Unit::Failure.new(name, filter_backtrace(caller(0)), diff_to_report)
132
+ puts "\n#{failure}"
133
+
134
+ if autoaccept
135
+ accept = true
136
+ else
137
+ print "Accept the new value: yes to all, no to all, yes, no? [Y/N/y/n] (y): "
138
+ STDOUT.flush
139
+ response = STDIN.gets.strip
140
+ accept = ["", "y", "Y"].include? response
141
+ ARGV << "--autoaccept" if response == "Y"
142
+ ARGV << "--no-interactive" if response == "N"
143
+ end
144
+
145
+ if accept
146
+ if [:expecting_string, :autofill_expected_value].include? mode
147
+ accept_string(actual, mode)
148
+ elsif mode == :expecting_file
149
+ accept_file(actual, log_file)
150
+ end
151
+ end
152
+ end
153
+ if accept
154
+ # when change is accepted, we should not report it as a failure because
155
+ # we want the test method to continue executing (in case there're more
156
+ # assert_same's in the method)
157
+ add_assertion
158
+ else
159
+ raise Test::Unit::AssertionFailedError.new(diff_to_report)
160
+ end
161
+ else
162
+ add_assertion
163
+ end
164
+ end
165
+
166
+ private
167
+
168
+ def accept_string(actual, mode)
169
+ file, method, line = get_caller_location(:depth => 3)
170
+
171
+ # read source file, construct the new source, replacing everything
172
+ # between "do" and "end" in assert_same's block
173
+ # using File::expand_path here because "file" can be either
174
+ # absolute path (when test is run with "rake test" runs)
175
+ # or relative path (when test is run via ruby <path_to_test_file>)
176
+ source = File.readlines(File::expand_path(file))
177
+
178
+ # file may be changed by previous accepted assert_same's, adjust line numbers
179
+ offset = @@file_offsets[file].keys.inject(0) do |sum, i|
180
+ line.to_i >= i ? sum + @@file_offsets[file][i] : sum
181
+ end
182
+
183
+ expected_text_end_line = expected_text_start_line = line.to_i + offset
184
+ unless mode == :autofill_expected_value
185
+ #if we're autofilling the value, END/EOS marker will not exist
186
+ #(second arg to assert_same is omitted)
187
+ #else we search for it
188
+ expected_text_end_line += 1 while !["END", "EOS"].include?(source[expected_text_end_line].strip)
189
+ end
190
+
191
+ expected_length = expected_text_end_line - expected_text_start_line
192
+
193
+ # indentation is the indentation of assert_same call + 4
194
+ indentation = source[expected_text_start_line-1] =~ /^(\s+)/ ? $1.length : 0
195
+ indentation += 4
196
+
197
+ if mode == :autofill_expected_value
198
+ # add second argument to assert_same if it's omitted
199
+ source[expected_text_start_line-1] = "#{source[expected_text_start_line-1].chop}, <<-END\n"
200
+ end
201
+ source = source[0, expected_text_start_line] +
202
+ actual.split("\n").map { |l| "#{" "*(indentation)}#{l}\n"} +
203
+ (mode == :autofill_expected_value ? ["#{" "*(indentation-4)}END\n"] : [])+
204
+ source[expected_text_end_line, source.length]
205
+
206
+ # recalculate line number adjustments
207
+ actual_length = actual.split("\n").length
208
+ actual_length += 1 if mode == :autofill_expected_value # END marker after actual value
209
+ @@file_offsets[file][line.to_i] = actual_length - expected_length
210
+
211
+ source_file = File.open(file, "w+")
212
+ source_file.write(source)
213
+ source_file.fsync
214
+ source_file.close
215
+ end
216
+
217
+ def accept_file(actual, log_file)
218
+ log = File.open(log_file, "w+")
219
+ log.write(actual)
220
+ log.fsync
221
+ log.close
222
+ end
223
+
224
+ def compare_for_assert_same(expected_verbatim, actual_verbatim)
225
+ expected_canonicalized, expected = canonicalize_for_assert_same(expected_verbatim)
226
+ actual_canonicalized, actual = canonicalize_for_assert_same(actual_verbatim)
227
+ diff_canonicalized = AssertSame::TextDiff.array_diff(expected_canonicalized, actual_canonicalized)
228
+ diff = AssertSame::TextDiff.array_diff(expected, actual)
229
+ [expected_canonicalized == actual_canonicalized, expected == actual, diff_canonicalized, diff]
230
+ end
231
+
232
+ def canonicalize_for_assert_same(text)
233
+ # make array of lines out of the text
234
+ result = text.split("\n")
235
+
236
+ # ignore leading newlines if any (trailing will be automatically ignored by split())
237
+ result.delete_at(0) if result[0] == ""
238
+
239
+ indentation = $1.length if result[0] and result[0] =~ /^(\s+)/
240
+
241
+ result.map! do |line|
242
+ # ignore indentation: we assume that the first line defines indentation
243
+ line.gsub!(/^\s{#{indentation}}/, '') if indentation
244
+ # ignore trailing spaces
245
+ line.gsub(/\s*$/, '')
246
+ end
247
+
248
+ # ignore comments
249
+ result_canonicalized= result.map do |line|
250
+ line.gsub(/\s*(#.*)?$/, '')
251
+ end
252
+ # ignore blank lines (usually they are lines with comments only)
253
+ result_canonicalized.delete_if { |line| line.nil? or line.empty? }
254
+
255
+ [result_canonicalized, result]
256
+ end
257
+
258
+ def get_caller_location(options = {:depth => 2})
259
+ caller_method = caller(options[:depth])[0]
260
+
261
+ #Sample output is:
262
+ #either full path when run as "rake test"
263
+ # /home/user/assert_same/test/unit/assure_test.rb:9:in `test_assure
264
+ #or relative path when run as ruby test/unit/assure_test.rb
265
+ # test/unit/assure_test.rb:9:in `test_assure
266
+ caller_method =~ /([^:]+):([0-9]+):in `(.+)'/
267
+ file = $1
268
+ line = $2
269
+ method = $3
270
+ [file, method, line]
271
+ end
272
+
273
+ end
data/lib/text_diff.rb ADDED
@@ -0,0 +1,109 @@
1
+ # Copyright (c) 2008 Pluron, Inc.
2
+
3
+ require 'array_diff'
4
+
5
+ module AssertSame
6
+
7
+ class TextDiff
8
+
9
+ def self.mapped_chunk(arr, from, to)
10
+ return [] if to < from
11
+ arr[from, to-from+1].map{|x| [' ', x]}
12
+ end
13
+
14
+ def self.chunk(arr, from, to)
15
+ result = []
16
+ if to - from > 2
17
+ result += self.mapped_chunk(arr, from, from+2) if from > 0
18
+ result << ['@', to-2]
19
+ result += self.mapped_chunk(arr, to-2, to)
20
+ else
21
+ result << ['@', from] if from == 0
22
+ result += self.mapped_chunk(arr, from, to)
23
+ end
24
+ end
25
+
26
+ def self.record_stat(arr, at, remcount, addcount, linecount, offset)
27
+ index = arr[at][1]+1
28
+ ['', "@@ -#{index},#{linecount-addcount}, +#{index+offset},#{linecount-remcount} @@"]
29
+ end
30
+
31
+ def self.diff(a_text, b_text)
32
+ a = a_text.split("\n")
33
+ b = b_text.split("\n")
34
+ self.array_diff(a, b)
35
+ end
36
+
37
+ def self.array_diff(a, b)
38
+ diff = ArrayDiff.new(a, b)
39
+
40
+ return nil if diff.diffs.empty?
41
+
42
+ result = []
43
+
44
+ from = to = nextfrom = 0
45
+ offset = 0
46
+ diff.diffs.each do |tuple|
47
+ first = tuple[0][1]
48
+ length = tuple.length
49
+ action = tuple[0][0]
50
+ addcount = 0
51
+ remcount = 0
52
+ tuple.each do |l|
53
+ if l[0] == "+"
54
+ addcount += 1
55
+ elsif l[0] == "-"
56
+ remcount += 1
57
+ end
58
+ end
59
+ if remcount == 0
60
+ to = first-offset-1
61
+ nextfrom = to+1
62
+ else
63
+ to = first-1
64
+ nextfrom = to+remcount+1
65
+ end
66
+ result += self.chunk(a, from, to)
67
+ from = nextfrom
68
+ lastdel = (tuple[0][0] == "-")
69
+ tuple.each do |l|
70
+ if l[0] == "-"
71
+ offset -= 1
72
+ else
73
+ offset += 1
74
+ end
75
+ result << [l[0], l[2]]
76
+ end
77
+ end
78
+ if (a.length - from) > 2
79
+ result += self.chunk(a, from, from+2)
80
+ elsif a.length > 0
81
+ result += self.chunk(a, from, a.length-1)
82
+ end
83
+ linecount = addcount = remcount = offset = current_offset = 0
84
+ info_index = nil
85
+ result.each_with_index do |l, i|
86
+ if l[0] == '@'
87
+ result[info_index] = self.record_stat(result, info_index, remcount, addcount,
88
+ linecount, offset) if info_index
89
+ info_index = i
90
+ offset += addcount - remcount
91
+ linecount = 0
92
+ addcount = remcount = 0
93
+ elsif l[0] == '-'
94
+ remcount += 1
95
+ linecount += 1
96
+ elsif l[0] == '+'
97
+ addcount += 1
98
+ linecount += 1
99
+ else
100
+ linecount += 1
101
+ end
102
+ end
103
+ result[info_index] = self.record_stat(result, info_index, remcount, addcount, linecount, offset) if info_index
104
+ return result.map{|x| x.join('')}.join("\n")
105
+ end
106
+
107
+ end
108
+
109
+ end
@@ -0,0 +1,18 @@
1
+ # Copyright (c) 2011 Pluron, Inc.
2
+
3
+ require 'test/unit'
4
+ require 'lib/assert_same'
5
+
6
+ class AssertSameTest < Test::Unit::TestCase
7
+
8
+ def test_basic_assert_same
9
+ assert_same "foo", <<-END
10
+ foo
11
+ END
12
+ end
13
+
14
+ def test_assert_same_with_files
15
+ assert_same "foo", :log => 'test/logs/assert_same_with_files.log.ref'
16
+ end
17
+
18
+ end
@@ -0,0 +1 @@
1
+ foo
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: assert_same
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ version: "0.1"
10
+ platform: ruby
11
+ authors:
12
+ - Pluron, Inc.
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2012-03-16 00:00:00 Z
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
20
+ name: bundler
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ hash: 23
28
+ segments:
29
+ - 1
30
+ - 0
31
+ - 0
32
+ version: 1.0.0
33
+ type: :development
34
+ version_requirements: *id001
35
+ description: assert_same assertion
36
+ email: support@pluron.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - .gitignore
45
+ - LICENSE
46
+ - README
47
+ - Rakefile
48
+ - assert_same.gemspec
49
+ - init.rb
50
+ - lib/array_diff.rb
51
+ - lib/assert_same.rb
52
+ - lib/text_diff.rb
53
+ - test/assert_same_test.rb
54
+ - test/logs/assert_same_with_files.log.ref
55
+ homepage: http://github.com/acunote/assert_same
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options: []
60
+
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ hash: 3
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ hash: 3
78
+ segments:
79
+ - 0
80
+ version: "0"
81
+ requirements: []
82
+
83
+ rubyforge_project:
84
+ rubygems_version: 1.8.15
85
+ signing_key:
86
+ specification_version: 3
87
+ summary: Assert which checks that two strings (expected and actual) are same and which can magically replace expected value with the actual in case the new behavior (and new actual value) is correct
88
+ test_files:
89
+ - test/assert_same_test.rb
90
+ - test/logs/assert_same_with_files.log.ref