diff-lcs 1.6.2 → 2.0.0.beta.2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +65 -4
- data/CONTRIBUTING.md +91 -35
- data/CONTRIBUTORS.md +19 -9
- data/LICENCE.md +39 -11
- data/Manifest.txt +91 -83
- data/README.md +13 -9
- data/Rakefile +99 -73
- data/SECURITY.md +22 -27
- data/integration/compare/array_diff_spec.rb +10 -0
- data/integration/compare/hash_diff_spec.rb +25 -0
- data/integration/compare/string_diff_spec.rb +10 -0
- data/integration/rspec_differ_spec.rb +26 -0
- data/integration/rspec_expectations_spec.rb +32 -0
- data/integration/runner +20 -0
- data/lib/diff/lcs/block.rb +29 -24
- data/lib/diff/lcs/callbacks.rb +240 -242
- data/lib/diff/lcs/change.rb +102 -104
- data/lib/diff/lcs/hunk.rb +92 -155
- data/lib/diff/lcs/internals.rb +92 -96
- data/lib/diff/lcs/ldiff.rb +30 -38
- data/lib/diff/lcs/version.rb +1 -1
- data/lib/diff/lcs.rb +439 -466
- data/licenses/dco.txt +34 -0
- data/spec/hunk_spec.rb +32 -45
- data/spec/lcs_spec.rb +6 -6
- data/spec/ldiff_spec.rb +8 -8
- data/spec/spec_helper.rb +17 -27
- data/test/fixtures/ldiff/output.diff-c +7 -0
- data/test/fixtures/ldiff/output.diff-u +5 -0
- data/test/fixtures/ldiff/output.diff.bin2 +1 -0
- data/test/fixtures/ldiff/output.diff.bin2-c +1 -0
- data/test/fixtures/ldiff/output.diff.bin2-e +1 -0
- data/test/fixtures/ldiff/output.diff.bin2-f +1 -0
- data/test/fixtures/ldiff/output.diff.bin2-u +1 -0
- data/{spec → test}/fixtures/ldiff/output.diff.chef-c +2 -2
- data/test/fixtures/ldiff/output.diff.chef-u +9 -0
- data/{spec → test}/fixtures/ldiff/output.diff.chef2-c +2 -2
- data/{spec → test}/fixtures/ldiff/output.diff.chef2-u +2 -2
- data/test/fixtures/ldiff/output.diff.empty.vs.four_lines-c +9 -0
- data/test/fixtures/ldiff/output.diff.empty.vs.four_lines-u +7 -0
- data/test/fixtures/ldiff/output.diff.four_lines.vs.empty-c +9 -0
- data/test/fixtures/ldiff/output.diff.four_lines.vs.empty-u +7 -0
- data/test/fixtures/ldiff/output.diff.issue95_trailing_context-c +9 -0
- data/test/fixtures/ldiff/output.diff.issue95_trailing_context-u +6 -0
- data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line1-c +2 -2
- data/test/fixtures/ldiff/output.diff.missing_new_line1-u +9 -0
- data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line2-c +2 -2
- data/test/fixtures/ldiff/output.diff.missing_new_line2-u +9 -0
- data/test/test_block.rb +34 -0
- data/test/test_change.rb +234 -0
- data/test/test_diff.rb +53 -0
- data/test/test_helper.rb +225 -0
- data/test/test_hunk.rb +72 -0
- data/test/test_issues.rb +168 -0
- data/test/test_lcs.rb +47 -0
- data/test/test_ldiff.rb +89 -0
- data/test/test_patch.rb +362 -0
- data/test/test_sdiff.rb +167 -0
- data/test/test_traverse_balanced.rb +322 -0
- data/test/test_traverse_sequences.rb +187 -0
- metadata +199 -110
- data/.rspec +0 -1
- data/bin/htmldiff +0 -35
- data/lib/diff/lcs/backports.rb +0 -13
- data/lib/diff/lcs/htmldiff.rb +0 -160
- data/mise.toml +0 -5
- data/spec/fixtures/ldiff/output.diff-c +0 -7
- data/spec/fixtures/ldiff/output.diff-e +0 -3
- data/spec/fixtures/ldiff/output.diff-f +0 -3
- data/spec/fixtures/ldiff/output.diff-u +0 -5
- data/spec/fixtures/ldiff/output.diff.bin2 +0 -1
- data/spec/fixtures/ldiff/output.diff.bin2-c +0 -1
- data/spec/fixtures/ldiff/output.diff.bin2-e +0 -1
- data/spec/fixtures/ldiff/output.diff.bin2-f +0 -1
- data/spec/fixtures/ldiff/output.diff.bin2-u +0 -1
- data/spec/fixtures/ldiff/output.diff.chef-e +0 -3
- data/spec/fixtures/ldiff/output.diff.chef-f +0 -3
- data/spec/fixtures/ldiff/output.diff.chef-u +0 -9
- data/spec/fixtures/ldiff/output.diff.chef2-e +0 -7
- data/spec/fixtures/ldiff/output.diff.chef2-f +0 -7
- data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines-c +0 -9
- data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines-u +0 -7
- data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty-c +0 -9
- data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty-u +0 -7
- data/spec/fixtures/ldiff/output.diff.issue95_trailing_context-c +0 -9
- data/spec/fixtures/ldiff/output.diff.issue95_trailing_context-u +0 -6
- data/spec/fixtures/ldiff/output.diff.missing_new_line1-u +0 -9
- data/spec/fixtures/ldiff/output.diff.missing_new_line2-u +0 -9
- /data/{docs → licenses}/COPYING.txt +0 -0
- /data/{docs → licenses}/artistic.txt +0 -0
- /data/{spec → test}/fixtures/123_x +0 -0
- /data/{spec → test}/fixtures/456_x +0 -0
- /data/{spec → test}/fixtures/aX +0 -0
- /data/{spec → test}/fixtures/bXaX +0 -0
- /data/{spec → test}/fixtures/ds1.csv +0 -0
- /data/{spec → test}/fixtures/ds2.csv +0 -0
- /data/{spec → test}/fixtures/empty +0 -0
- /data/{spec → test}/fixtures/file1.bin +0 -0
- /data/{spec → test}/fixtures/file2.bin +0 -0
- /data/{spec → test}/fixtures/four_lines +0 -0
- /data/{spec → test}/fixtures/four_lines_with_missing_new_line +0 -0
- /data/{spec → test}/fixtures/ldiff/diff.missing_new_line1-e +0 -0
- /data/{spec → test}/fixtures/ldiff/diff.missing_new_line1-f +0 -0
- /data/{spec → test}/fixtures/ldiff/diff.missing_new_line2-e +0 -0
- /data/{spec → test}/fixtures/ldiff/diff.missing_new_line2-f +0 -0
- /data/{spec → test}/fixtures/ldiff/error.diff.chef-e +0 -0
- /data/{spec → test}/fixtures/ldiff/error.diff.chef-f +0 -0
- /data/{spec → test}/fixtures/ldiff/error.diff.missing_new_line1-e +0 -0
- /data/{spec → test}/fixtures/ldiff/error.diff.missing_new_line1-f +0 -0
- /data/{spec → test}/fixtures/ldiff/error.diff.missing_new_line2-e +0 -0
- /data/{spec → test}/fixtures/ldiff/error.diff.missing_new_line2-f +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.bin1 +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.bin1-c +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.bin1-e +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.bin1-f +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.bin1-u +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.chef +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.chef2 +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.chef2-d +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.empty.vs.four_lines +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.empty.vs.four_lines-e +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.empty.vs.four_lines-f +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.four_lines.vs.empty +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.four_lines.vs.empty-e +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.four_lines.vs.empty-f +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.issue95_trailing_context +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.issue95_trailing_context-e +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.issue95_trailing_context-f +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line1 +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line1-e +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line1-f +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line2 +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line2-e +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line2-f +0 -0
- /data/{spec → test}/fixtures/new-chef +0 -0
- /data/{spec → test}/fixtures/new-chef2 +0 -0
- /data/{spec → test}/fixtures/old-chef +0 -0
- /data/{spec → test}/fixtures/old-chef2 +0 -0
data/Rakefile
CHANGED
|
@@ -1,52 +1,24 @@
|
|
|
1
1
|
require "rubygems"
|
|
2
|
-
require "rspec"
|
|
3
|
-
require "rspec/core/rake_task"
|
|
4
2
|
require "hoe"
|
|
5
3
|
require "rake/clean"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
BUILD_DOCS = MAINTENANCE || ENV["DOCS"] == "true"
|
|
9
|
-
TRUSTED_RELEASE = ENV["rubygems_release_gem"] == "true"
|
|
4
|
+
require "rdoc/task"
|
|
5
|
+
require "minitest/test_task"
|
|
10
6
|
|
|
11
7
|
Hoe.plugin :halostatue
|
|
12
|
-
Hoe.plugin :rubygems
|
|
13
8
|
|
|
14
9
|
Hoe.plugins.delete :debug
|
|
15
10
|
Hoe.plugins.delete :newb
|
|
11
|
+
Hoe.plugins.delete :publish
|
|
16
12
|
Hoe.plugins.delete :signing
|
|
17
|
-
Hoe.plugins.delete :
|
|
18
|
-
|
|
19
|
-
if RUBY_VERSION < "1.9"
|
|
20
|
-
class Array # :nodoc:
|
|
21
|
-
def to_h
|
|
22
|
-
Hash[*flatten(1)]
|
|
23
|
-
end
|
|
24
|
-
end
|
|
13
|
+
Hoe.plugins.delete :test
|
|
25
14
|
|
|
26
|
-
|
|
27
|
-
def metadata=(*)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def default_value(*)
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
class Object # :nodoc:
|
|
35
|
-
def caller_locations(*)
|
|
36
|
-
[]
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
_spec = Hoe.spec "diff-lcs" do
|
|
15
|
+
hoe = Hoe.spec "diff-lcs" do
|
|
42
16
|
developer("Austin Ziegler", "halostatue@gmail.com")
|
|
43
17
|
|
|
44
|
-
self.trusted_release =
|
|
18
|
+
self.trusted_release = ENV["rubygems_release_gem"] == "true"
|
|
45
19
|
|
|
46
|
-
require_ruby_version ">=
|
|
20
|
+
require_ruby_version ">= 3.2.0", "< 5"
|
|
47
21
|
|
|
48
|
-
self.history_file = "CHANGELOG.md"
|
|
49
|
-
self.readme_file = "README.md"
|
|
50
22
|
self.licenses = ["MIT", "Artistic-1.0-Perl", "GPL-2.0-or-later"]
|
|
51
23
|
|
|
52
24
|
spec_extras[:metadata] = ->(val) {
|
|
@@ -54,62 +26,116 @@ _spec = Hoe.spec "diff-lcs" do
|
|
|
54
26
|
}
|
|
55
27
|
|
|
56
28
|
extra_dev_deps << ["hoe", "~> 4.0"]
|
|
57
|
-
extra_dev_deps << ["hoe-halostatue", "~>
|
|
58
|
-
extra_dev_deps << ["
|
|
59
|
-
extra_dev_deps << ["
|
|
29
|
+
extra_dev_deps << ["hoe-halostatue", "~> 3.0"]
|
|
30
|
+
extra_dev_deps << ["minitest", "~> 6.0"]
|
|
31
|
+
extra_dev_deps << ["minitest-autotest", "~> 1.0"]
|
|
32
|
+
extra_dev_deps << ["minitest-focus", "~> 1.1"]
|
|
60
33
|
extra_dev_deps << ["rake", ">= 10.0", "< 14"]
|
|
61
|
-
extra_dev_deps << ["rdoc", ">= 6.
|
|
34
|
+
extra_dev_deps << ["rdoc", ">= 6.0", "< 8"]
|
|
35
|
+
extra_dev_deps << ["simplecov", "~> 0.9"]
|
|
36
|
+
extra_dev_deps << ["simplecov-lcov", "~> 0.9"]
|
|
37
|
+
extra_dev_deps << ["standard", "~> 1.50"]
|
|
38
|
+
extra_dev_deps << ["standard-thread_safety", "~> 1.0"]
|
|
39
|
+
extra_dev_deps << ["fasterer", "~> 0.11"]
|
|
62
40
|
end
|
|
63
41
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
42
|
+
Minitest::TestTask.create :test
|
|
43
|
+
Minitest::TestTask.create :coverage do |t|
|
|
44
|
+
formatters = <<-RUBY.split($/).join(" ")
|
|
45
|
+
SimpleCov::Formatter::MultiFormatter.new([
|
|
46
|
+
SimpleCov::Formatter::HTMLFormatter,
|
|
47
|
+
SimpleCov::Formatter::LcovFormatter,
|
|
48
|
+
SimpleCov::Formatter::SimpleFormatter
|
|
49
|
+
])
|
|
50
|
+
RUBY
|
|
51
|
+
t.test_prelude = <<-RUBY.split($/).join("; ")
|
|
52
|
+
require "simplecov"
|
|
53
|
+
require "simplecov-lcov"
|
|
54
|
+
|
|
55
|
+
SimpleCov::Formatter::LcovFormatter.config do |config|
|
|
56
|
+
config.report_with_single_file = true
|
|
57
|
+
config.lcov_file_name = "lcov.info"
|
|
69
58
|
end
|
|
70
|
-
end
|
|
71
59
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
60
|
+
SimpleCov.start "test_frameworks" do
|
|
61
|
+
enable_coverage :branch
|
|
62
|
+
primary_coverage :branch
|
|
63
|
+
formatter #{formatters}
|
|
64
|
+
end
|
|
65
|
+
RUBY
|
|
76
66
|
end
|
|
77
67
|
|
|
68
|
+
task default: :test
|
|
69
|
+
|
|
78
70
|
task :version do
|
|
79
71
|
require "diff/lcs/version"
|
|
80
72
|
puts Diff::LCS::VERSION
|
|
81
73
|
end
|
|
82
74
|
|
|
83
|
-
|
|
75
|
+
RDoc::Task.new do |config|
|
|
76
|
+
config.title = "diff-lcs"
|
|
77
|
+
config.main = "README.md"
|
|
78
|
+
config.rdoc_dir = "doc"
|
|
79
|
+
config.rdoc_files = hoe.spec.require_paths + hoe.spec.extra_rdoc_files -
|
|
80
|
+
FileList["integration/golden/*.txt", "Manifest.txt"].to_a
|
|
81
|
+
config.markup = "markdown"
|
|
82
|
+
end
|
|
83
|
+
task docs: :rerdoc
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
# standard:enable Style/HashSyntax
|
|
85
|
+
def rspec_to_golden(file)
|
|
86
|
+
File.join("integration/golden", File.basename(file, "_spec.rb")) + ".txt"
|
|
87
|
+
end
|
|
89
88
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
89
|
+
def normalize_rspec_output(data)
|
|
90
|
+
data
|
|
91
|
+
.gsub(/Randomized with seed \d+/, "Randomized with seed XXXXX")
|
|
92
|
+
.gsub(/Finished in [\d.]+ seconds/, "Finished in X.XXXXX seconds")
|
|
93
|
+
.gsub(/files took [\d.]+ seconds to load/, "files took X.XXXXX seconds to load")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def unbundled(&block)
|
|
97
|
+
if defined?(Bundler)
|
|
98
|
+
Bundler.with_unbundled_env(&block)
|
|
99
|
+
else
|
|
100
|
+
block.call
|
|
97
101
|
end
|
|
98
102
|
end
|
|
99
103
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
rspecs = FileList["integration/compare/*_spec.rb"]
|
|
105
|
+
|
|
106
|
+
namespace :integration do
|
|
107
|
+
desc "Compare RSpec output with and without diff-lcs 2"
|
|
108
|
+
task :compare do
|
|
109
|
+
require "tempfile"
|
|
110
|
+
base = Tempfile.create("baseline") { _1.path }
|
|
111
|
+
work = Tempfile.create("working") { _1.path }
|
|
112
|
+
|
|
113
|
+
unbundled { sh "gem install rspec" }
|
|
107
114
|
|
|
108
|
-
|
|
109
|
-
|
|
115
|
+
rspecs.to_a.each do |rspec_file|
|
|
116
|
+
basename = File.basename(rspec_file, "_spec.rb")
|
|
110
117
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
118
|
+
base_contents = unbundled { `integration/runner rspec #{rspec_file} 2>&1` }
|
|
119
|
+
base_contents = normalize_rspec_output(base_contents)
|
|
120
|
+
|
|
121
|
+
work_contents = unbundled { `integration/runner rspec -Ilib -rdiff/lcs #{rspec_file} 2>&1` }
|
|
122
|
+
work_contents = normalize_rspec_output(work_contents)
|
|
123
|
+
|
|
124
|
+
if base_contents == work_contents
|
|
125
|
+
puts "#{basename}: OK"
|
|
126
|
+
else
|
|
127
|
+
puts "#{basename}: FAIL"
|
|
128
|
+
|
|
129
|
+
File.write(base, base_contents)
|
|
130
|
+
File.write(work, work_contents)
|
|
131
|
+
|
|
132
|
+
unbundled { sh "integration/runner -Ilib bin/ldiff -U #{base} #{work}" }
|
|
133
|
+
end
|
|
134
|
+
end
|
|
114
135
|
end
|
|
115
136
|
end
|
|
137
|
+
|
|
138
|
+
desc "Run RSpec integration tests with diff-lcs 2.0"
|
|
139
|
+
task integration: ["integration:compare"] do
|
|
140
|
+
sh "rspec -Ilib -r diff/lcs integration/*_spec.rb"
|
|
141
|
+
end
|
data/SECURITY.md
CHANGED
|
@@ -1,41 +1,36 @@
|
|
|
1
1
|
# diff-lcs Security
|
|
2
2
|
|
|
3
|
+
## LLM-Generated Security Report Policy
|
|
4
|
+
|
|
5
|
+
Absolutely no security reports will be accepted that have been generated by LLM
|
|
6
|
+
agents.
|
|
7
|
+
|
|
3
8
|
## Supported Versions
|
|
4
9
|
|
|
5
|
-
Security reports are accepted for the most recent major release
|
|
6
|
-
|
|
7
|
-
major release, the previous version will receive full support for six months and
|
|
8
|
-
security support for an additional six months (for a total of twelve months).
|
|
10
|
+
Security reports are accepted for the most recent major release, with a limited
|
|
11
|
+
window of support after the initial major release.
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
will
|
|
13
|
+
- Bug reports will be accepted up to three months after release.
|
|
14
|
+
- Security reports will be accepted up to six months after release.
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
>
|
|
16
|
-
> versions of Ruby only.
|
|
16
|
+
All issues raised must be demonstrated on the minimum supported Ruby version.
|
|
17
|
+
|
|
18
|
+
> [!important]
|
|
17
19
|
>
|
|
18
|
-
>
|
|
19
|
-
>
|
|
20
|
-
> | 2025 | +6 months | +12 months |
|
|
20
|
+
> Because diff-lcs 1 has been the only version for over twenty years, security
|
|
21
|
+
> reports will be accepted for one year after the release of diff-lcs 2.
|
|
21
22
|
>
|
|
22
|
-
>
|
|
23
|
-
>
|
|
24
|
-
> 2026-
|
|
23
|
+
> | Version | Release Date | Support Ends | Security Support Ends |
|
|
24
|
+
> | ------- | ------------ | ------------ | --------------------- |
|
|
25
|
+
> | 1.x | 2010 | 2026-04-30 | 2027-01-31 |
|
|
26
|
+
> | 2.x | 2026-01-31 | - | - |
|
|
25
27
|
|
|
26
28
|
## Reporting a Vulnerability
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
coordinate the fix and disclosure.
|
|
30
|
-
|
|
31
|
-
Alternatively, Send an email to [diff-lcs@halostatue.ca][email] with the text
|
|
32
|
-
`Diff::LCS` in the subject. Emails sent to this address should be encrypted
|
|
33
|
-
using [age][age] with the following public key:
|
|
30
|
+
Report vulnerabilities via the [Tidelift security contact][tidelift]. Tidelift
|
|
31
|
+
will coordinate the fix and disclosure.
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
age1fc6ngxmn02m62fej5cl30lrvwmxn4k3q2atqu53aatekmnqfwumqj4g93w
|
|
37
|
-
```
|
|
33
|
+
Alternatively, create a [private vulnerability report][advisory] with GitHub.
|
|
38
34
|
|
|
35
|
+
[advisory]: https://github.com/halostatue/diff-lcs/security/advisories/new
|
|
39
36
|
[tidelift]: https://tidelift.com/security
|
|
40
|
-
[email]: mailto:diff-lcs@halostatue.ca
|
|
41
|
-
[age]: https://github.com/FiloSottile/age
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe "Array diff failure" do
|
|
4
|
+
it "shows diff for array with different elements" do
|
|
5
|
+
expected = ["apple", "banana", "cherry", "date", "elderberry"]
|
|
6
|
+
actual = ["apple", "blueberry", "cherry", "date", "elderberry"]
|
|
7
|
+
|
|
8
|
+
expect(actual).to eq(expected)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe "Hash diff failure" do
|
|
4
|
+
it "shows diff for nested hash mismatch" do
|
|
5
|
+
expected = {
|
|
6
|
+
name: "John",
|
|
7
|
+
age: 30,
|
|
8
|
+
address: {
|
|
9
|
+
city: "New York",
|
|
10
|
+
zip: "10001"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
actual = {
|
|
15
|
+
name: "John",
|
|
16
|
+
age: 35,
|
|
17
|
+
address: {
|
|
18
|
+
city: "Boston",
|
|
19
|
+
zip: "10001"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
expect(actual).to eq(expected)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe "String diff failure" do
|
|
4
|
+
it "shows diff for multiline string mismatch" do
|
|
5
|
+
expected = "line1\nline2\nline3\nline4\nline5\n"
|
|
6
|
+
actual = "line1\nchanged\nline3\nline4\nline5\n"
|
|
7
|
+
|
|
8
|
+
expect(actual).to eq(expected)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Integration test to verify diff-lcs 2.0 works with RSpec's differ
|
|
4
|
+
# This runs RSpec with diff-lcs 1.x installed but loads the repo version
|
|
5
|
+
|
|
6
|
+
RSpec.describe "Diff::LCS 2.0 with RSpec::Support::Differ" do
|
|
7
|
+
let(:differ) { RSpec::Support::Differ.new }
|
|
8
|
+
|
|
9
|
+
it "produces diff output for multiline strings" do
|
|
10
|
+
expected = "foo\nzap\nbar\n"
|
|
11
|
+
actual = "foo\nbar\nzap\n"
|
|
12
|
+
|
|
13
|
+
diff = differ.diff(actual, expected)
|
|
14
|
+
|
|
15
|
+
expect(diff).to be_a(String)
|
|
16
|
+
expect(diff).not_to be_empty
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "handles identical strings" do
|
|
20
|
+
str = "same\n"
|
|
21
|
+
diff = differ.diff(str, str)
|
|
22
|
+
|
|
23
|
+
# May return empty or just newline depending on implementation
|
|
24
|
+
expect(diff.strip).to be_empty
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Integration test for RSpec expectation failures that use diff-lcs
|
|
4
|
+
# Verifies that diff output is generated correctly with diff-lcs 2.0
|
|
5
|
+
|
|
6
|
+
RSpec.describe "Diff::LCS 2.0 with RSpec expectations" do
|
|
7
|
+
it "produces diff for failed multiline string equality" do
|
|
8
|
+
expect {
|
|
9
|
+
expect("foo\nbar\nbaz").to eq("foo\nqux\nbaz")
|
|
10
|
+
}.to raise_error(RSpec::Expectations::ExpectationNotMetError) do |error|
|
|
11
|
+
expect(error.message).to include("Diff:")
|
|
12
|
+
expect(error.message).to match(/[-+](qux|bar)/)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "produces diff for failed hash equality" do
|
|
17
|
+
expect {
|
|
18
|
+
expect({a: 1, b: 2}).to eq({a: 1, b: 3})
|
|
19
|
+
}.to raise_error(RSpec::Expectations::ExpectationNotMetError) do |error|
|
|
20
|
+
expect(error.message).to include("Diff:")
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "does not crash when comparing complex objects" do
|
|
25
|
+
obj1 = {a: [1, 2, 3], b: "test"}
|
|
26
|
+
obj2 = {a: [1, 4, 3], b: "test"}
|
|
27
|
+
|
|
28
|
+
expect {
|
|
29
|
+
expect(obj1).to eq(obj2)
|
|
30
|
+
}.to raise_error(RSpec::Expectations::ExpectationNotMetError)
|
|
31
|
+
end
|
|
32
|
+
end
|
data/integration/runner
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "rbconfig"
|
|
4
|
+
require "rubygems"
|
|
5
|
+
|
|
6
|
+
if ENV["CI"]
|
|
7
|
+
vendor_dir = Dir.glob(File.join(Dir.pwd, "vendor", "bundle", "{ruby,jruby,truffleruby}", "*")).max
|
|
8
|
+
unless vendor_dir && Dir.exist?(vendor_dir)
|
|
9
|
+
fail "vendor bundle not found; expected vendor/bundle/ruby/*"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Prefer vendored gems, fall back to system gem dir
|
|
13
|
+
ENV["GEM_HOME"] = vendor_dir
|
|
14
|
+
ENV["GEM_PATH"] = "#{vendor_dir}:#{Gem.default_dir}"
|
|
15
|
+
|
|
16
|
+
# Ensure Gem.path is updated for the running process (affects Gem.lookup)
|
|
17
|
+
Gem.use_paths(ENV["GEM_HOME"], ENV["GEM_PATH"].split(":"))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
exec RbConfig.ruby, "-S", *ARGV
|
data/lib/diff/lcs/block.rb
CHANGED
|
@@ -1,37 +1,42 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
# Basically, this is just a list of changes, where each change adds or
|
|
5
|
-
# deletes a single item. Used by bin/ldiff.
|
|
6
|
-
class Diff::LCS::Block
|
|
7
|
-
attr_reader :changes, :insert, :remove
|
|
3
|
+
Diff::LCS::Block = Data.define(:changes, :insert, :remove) # :nodoc:
|
|
8
4
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
# A block is an operation removing, adding, or changing a group of items, a list of
|
|
6
|
+
# changes, where each change adds or deletes a single item.
|
|
7
|
+
#
|
|
8
|
+
# Used by bin/ldiff.
|
|
9
|
+
class Diff::LCS::Block
|
|
10
|
+
def self.from_chunk(chunk)
|
|
11
|
+
changes, insert, remove = [], [], []
|
|
13
12
|
|
|
14
|
-
chunk.each do
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
chunk.each do
|
|
14
|
+
changes << _1
|
|
15
|
+
remove << _1 if _1.deleting?
|
|
16
|
+
insert << _1 if _1.adding?
|
|
18
17
|
end
|
|
18
|
+
|
|
19
|
+
new(changes: changes.freeze, remove: remove.freeze, insert: insert.freeze)
|
|
19
20
|
end
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
class << self
|
|
23
|
+
private :new, :[]
|
|
23
24
|
end
|
|
24
25
|
|
|
26
|
+
private :with
|
|
27
|
+
|
|
28
|
+
def diff_size = insert.size - remove.size
|
|
29
|
+
|
|
25
30
|
def op
|
|
26
|
-
case [
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
case [remove, insert]
|
|
32
|
+
# Unchanged
|
|
33
|
+
in [[], []] then "^"
|
|
34
|
+
# Delete
|
|
35
|
+
in [_, []] then "-"
|
|
36
|
+
# Insert
|
|
37
|
+
in [[], _] then "+"
|
|
38
|
+
# Conflict
|
|
39
|
+
in [_, _] then "!"
|
|
35
40
|
end
|
|
36
41
|
end
|
|
37
42
|
end
|