diff-lcs 1.5.1 → 2.0.0
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 +581 -0
- data/CODE_OF_CONDUCT.md +166 -0
- data/CONTRIBUTING.md +127 -0
- data/CONTRIBUTORS.md +59 -0
- data/LICENCE.md +68 -0
- data/Manifest.txt +99 -35
- data/README.md +105 -0
- data/Rakefile +107 -96
- data/SECURITY.md +36 -0
- 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 +110 -157
- data/lib/diff/lcs/internals.rb +92 -96
- data/lib/diff/lcs/ldiff.rb +81 -73
- data/lib/diff/lcs/version.rb +7 -0
- data/lib/diff/lcs.rb +440 -466
- data/{docs → licenses}/artistic.txt +1 -1
- data/licenses/dco.txt +34 -0
- data/spec/hunk_spec.rb +33 -46
- data/spec/issues_spec.rb +32 -32
- data/spec/lcs_spec.rb +6 -6
- data/spec/ldiff_spec.rb +27 -16
- data/spec/patch_spec.rb +1 -1
- data/spec/spec_helper.rb +98 -108
- data/test/fixtures/123_x +2 -0
- data/test/fixtures/456_x +2 -0
- data/test/fixtures/empty +0 -0
- data/test/fixtures/file1.bin +0 -0
- data/test/fixtures/file2.bin +0 -0
- data/test/fixtures/four_lines +4 -0
- data/test/fixtures/four_lines_with_missing_new_line +4 -0
- data/test/fixtures/ldiff/diff.missing_new_line1-e +1 -0
- data/test/fixtures/ldiff/diff.missing_new_line1-f +1 -0
- data/test/fixtures/ldiff/diff.missing_new_line2-e +1 -0
- data/test/fixtures/ldiff/diff.missing_new_line2-f +1 -0
- data/test/fixtures/ldiff/error.diff.chef-e +2 -0
- data/test/fixtures/ldiff/error.diff.chef-f +2 -0
- data/test/fixtures/ldiff/error.diff.missing_new_line1-e +1 -0
- data/test/fixtures/ldiff/error.diff.missing_new_line1-f +1 -0
- data/test/fixtures/ldiff/error.diff.missing_new_line2-e +1 -0
- data/test/fixtures/ldiff/error.diff.missing_new_line2-f +1 -0
- data/test/fixtures/ldiff/output.diff-c +7 -0
- data/test/fixtures/ldiff/output.diff-u +5 -0
- data/test/fixtures/ldiff/output.diff.bin1 +0 -0
- data/test/fixtures/ldiff/output.diff.bin1-c +0 -0
- data/test/fixtures/ldiff/output.diff.bin1-e +0 -0
- data/test/fixtures/ldiff/output.diff.bin1-f +0 -0
- data/test/fixtures/ldiff/output.diff.bin1-u +0 -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 +5 -0
- data/test/fixtures/ldiff/output.diff.empty.vs.four_lines-c +9 -0
- data/test/fixtures/ldiff/output.diff.empty.vs.four_lines-e +6 -0
- data/test/fixtures/ldiff/output.diff.empty.vs.four_lines-f +6 -0
- data/test/fixtures/ldiff/output.diff.empty.vs.four_lines-u +7 -0
- data/test/fixtures/ldiff/output.diff.four_lines.vs.empty +5 -0
- data/test/fixtures/ldiff/output.diff.four_lines.vs.empty-c +9 -0
- data/test/fixtures/ldiff/output.diff.four_lines.vs.empty-e +1 -0
- data/test/fixtures/ldiff/output.diff.four_lines.vs.empty-f +1 -0
- data/test/fixtures/ldiff/output.diff.four_lines.vs.empty-u +7 -0
- data/test/fixtures/ldiff/output.diff.issue95_trailing_context +4 -0
- data/test/fixtures/ldiff/output.diff.issue95_trailing_context-c +9 -0
- data/{spec/fixtures/ldiff/output.diff-e → test/fixtures/ldiff/output.diff.issue95_trailing_context-e} +1 -1
- data/{spec/fixtures/ldiff/output.diff-f → test/fixtures/ldiff/output.diff.issue95_trailing_context-f} +1 -1
- data/test/fixtures/ldiff/output.diff.issue95_trailing_context-u +6 -0
- data/test/fixtures/ldiff/output.diff.missing_new_line1 +5 -0
- data/test/fixtures/ldiff/output.diff.missing_new_line1-c +14 -0
- data/test/fixtures/ldiff/output.diff.missing_new_line1-e +0 -0
- data/test/fixtures/ldiff/output.diff.missing_new_line1-f +0 -0
- data/test/fixtures/ldiff/output.diff.missing_new_line1-u +9 -0
- data/test/fixtures/ldiff/output.diff.missing_new_line2 +5 -0
- data/test/fixtures/ldiff/output.diff.missing_new_line2-c +14 -0
- data/test/fixtures/ldiff/output.diff.missing_new_line2-e +0 -0
- data/test/fixtures/ldiff/output.diff.missing_new_line2-f +0 -0
- 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 +211 -103
- data/.rspec +0 -1
- data/Code-of-Conduct.md +0 -74
- data/Contributing.md +0 -121
- data/History.md +0 -431
- data/License.md +0 -41
- data/README.rdoc +0 -84
- data/bin/htmldiff +0 -35
- data/lib/diff/lcs/backports.rb +0 -9
- data/lib/diff/lcs/htmldiff.rb +0 -158
- data/spec/fixtures/ldiff/output.diff-c +0 -7
- data/spec/fixtures/ldiff/output.diff-u +0 -5
- 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/{docs → licenses}/COPYING.txt +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/ldiff/output.diff +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/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,130 +1,141 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
1
|
require "rubygems"
|
|
4
|
-
require "rspec"
|
|
5
|
-
require "rspec/core/rake_task"
|
|
6
2
|
require "hoe"
|
|
3
|
+
require "rake/clean"
|
|
4
|
+
require "rdoc/task"
|
|
5
|
+
require "minitest/test_task"
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
class Hoe
|
|
10
|
-
def with_config
|
|
11
|
-
config = Hoe::DEFAULT_CONFIG
|
|
7
|
+
Hoe.plugin :halostatue
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
Hoe.plugins.delete :debug
|
|
10
|
+
Hoe.plugins.delete :newb
|
|
11
|
+
Hoe.plugins.delete :publish
|
|
12
|
+
Hoe.plugins.delete :signing
|
|
13
|
+
Hoe.plugins.delete :test
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
hoe = Hoe.spec "diff-lcs" do
|
|
16
|
+
developer("Austin Ziegler", "halostatue@gmail.com")
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
end
|
|
18
|
+
self.trusted_release = ENV["rubygems_release_gem"] == "true"
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
File.exist?(name) ? safe_load_yaml(name) : {}
|
|
25
|
-
end
|
|
20
|
+
require_ruby_version ">= 3.2.0", "< 5"
|
|
26
21
|
|
|
27
|
-
|
|
28
|
-
return safe_load_yaml_file(name) if YAML.respond_to?(:safe_load_file)
|
|
22
|
+
self.licenses = ["MIT", "Artistic-1.0-Perl", "GPL-2.0-or-later"]
|
|
29
23
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
24
|
+
spec_extras[:metadata] = ->(val) {
|
|
25
|
+
val["rubygems_mfa_required"] = "true"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
extra_dev_deps << ["hoe", "~> 4.0"]
|
|
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"]
|
|
33
|
+
extra_dev_deps << ["rake", ">= 10.0", "< 14"]
|
|
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"]
|
|
40
|
+
end
|
|
41
|
+
|
|
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"
|
|
34
58
|
end
|
|
35
59
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
60
|
+
SimpleCov.start "test_frameworks" do
|
|
61
|
+
enable_coverage :branch
|
|
62
|
+
primary_coverage :branch
|
|
63
|
+
formatter #{formatters}
|
|
40
64
|
end
|
|
65
|
+
RUBY
|
|
41
66
|
end
|
|
42
67
|
|
|
43
|
-
|
|
44
|
-
Hoe.plugin :doofus
|
|
45
|
-
Hoe.plugin :gemspec2
|
|
46
|
-
Hoe.plugin :git
|
|
68
|
+
task default: :test
|
|
47
69
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
end
|
|
53
|
-
end
|
|
70
|
+
task :version do
|
|
71
|
+
require "diff/lcs/version"
|
|
72
|
+
puts Diff::LCS::VERSION
|
|
73
|
+
end
|
|
54
74
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
|
58
84
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
85
|
+
def rspec_to_golden(file)
|
|
86
|
+
File.join("integration/golden", File.basename(file, "_spec.rb")) + ".txt"
|
|
87
|
+
end
|
|
62
88
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
|
67
101
|
end
|
|
68
102
|
end
|
|
69
103
|
|
|
70
|
-
|
|
71
|
-
developer("Austin Ziegler", "halostatue@gmail.com")
|
|
104
|
+
rspecs = FileList["integration/compare/*_spec.rb"]
|
|
72
105
|
|
|
73
|
-
|
|
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 }
|
|
74
112
|
|
|
75
|
-
|
|
76
|
-
self.readme_file = "README.rdoc"
|
|
77
|
-
self.licenses = ["MIT", "Artistic-2.0", "GPL-2.0-or-later"]
|
|
113
|
+
unbundled { sh "gem install rspec" }
|
|
78
114
|
|
|
79
|
-
|
|
115
|
+
rspecs.to_a.each do |rspec_file|
|
|
116
|
+
basename = File.basename(rspec_file, "_spec.rb")
|
|
80
117
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
extra_dev_deps << ["hoe-gemspec2", "~> 1.1"]
|
|
84
|
-
extra_dev_deps << ["hoe-git2", "~> 1.7"]
|
|
85
|
-
extra_dev_deps << ["hoe-rubygems", "~> 1.0"]
|
|
86
|
-
extra_dev_deps << ["rspec", ">= 2.0", "< 4"]
|
|
87
|
-
extra_dev_deps << ["rake", ">= 10.0", "< 14"]
|
|
88
|
-
extra_dev_deps << ["rdoc", ">= 6.3.1", "< 7"]
|
|
89
|
-
end
|
|
118
|
+
base_contents = unbundled { `integration/runner rspec #{rspec_file} 2>&1` }
|
|
119
|
+
base_contents = normalize_rspec_output(base_contents)
|
|
90
120
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
rspec_dirs = %w[spec lib].join(":")
|
|
94
|
-
t.rspec_opts = ["-I#{rspec_dirs}"]
|
|
95
|
-
end
|
|
121
|
+
work_contents = unbundled { `integration/runner rspec -Ilib -rdiff/lcs #{rspec_file} 2>&1` }
|
|
122
|
+
work_contents = normalize_rspec_output(work_contents)
|
|
96
123
|
|
|
97
|
-
|
|
124
|
+
if base_contents == work_contents
|
|
125
|
+
puts "#{basename}: OK"
|
|
126
|
+
else
|
|
127
|
+
puts "#{basename}: FAIL"
|
|
98
128
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
task :test => :spec unless Rake::Task["test"].prereqs.include?("spec")
|
|
102
|
-
# standard:enable Style/HashSyntax
|
|
129
|
+
File.write(base, base_contents)
|
|
130
|
+
File.write(work, work_contents)
|
|
103
131
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
desc "Runs test coverage. Only works Ruby 2.0+ and assumes 'simplecov' is installed."
|
|
107
|
-
task :coverage do
|
|
108
|
-
ENV["COVERAGE"] = "yes"
|
|
109
|
-
Rake::Task["spec"].execute
|
|
132
|
+
unbundled { sh "integration/runner -Ilib bin/ldiff -U #{base} #{work}" }
|
|
133
|
+
end
|
|
110
134
|
end
|
|
111
135
|
end
|
|
112
136
|
end
|
|
113
137
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
You are starting a barebones Ruby 1.8 docker environment. You will need to
|
|
118
|
-
do the following:
|
|
119
|
-
|
|
120
|
-
- mv Gemfile.lock{,.v2}
|
|
121
|
-
- gem install bundler --version 1.17.2 --no-ri --no-rdoc
|
|
122
|
-
- ruby -S bundle
|
|
123
|
-
- rake
|
|
124
|
-
|
|
125
|
-
Don't forget to restore your Gemfile.lock after testing.
|
|
126
|
-
|
|
127
|
-
MESSAGE
|
|
128
|
-
# standard:enable Layout/HeredocIndentation
|
|
129
|
-
sh "docker run -it --rm -v #{Dir.pwd}:/root/diff-lcs bellbind/docker-ruby18-rails2 bash -l"
|
|
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"
|
|
130
141
|
end
|
data/SECURITY.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# diff-lcs Security
|
|
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
|
+
|
|
8
|
+
## Supported Versions
|
|
9
|
+
|
|
10
|
+
Security reports are accepted for the most recent major release, with a limited
|
|
11
|
+
window of support after the initial major release.
|
|
12
|
+
|
|
13
|
+
- Bug reports will be accepted up to three months after release.
|
|
14
|
+
- Security reports will be accepted up to six months after release.
|
|
15
|
+
|
|
16
|
+
All issues raised must be demonstrated on the minimum supported Ruby version.
|
|
17
|
+
|
|
18
|
+
> [!important]
|
|
19
|
+
>
|
|
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.
|
|
22
|
+
>
|
|
23
|
+
> | Version | Release Date | Support Ends | Security Support Ends |
|
|
24
|
+
> | ------- | ------------ | ------------ | --------------------- |
|
|
25
|
+
> | 1.x | 2010 | 2026-05-01 | 2027-02-01 |
|
|
26
|
+
> | 2.x | 2026-02-01 | - | - |
|
|
27
|
+
|
|
28
|
+
## Reporting a Vulnerability
|
|
29
|
+
|
|
30
|
+
Report vulnerabilities via the [Tidelift security contact][tidelift]. Tidelift
|
|
31
|
+
will coordinate the fix and disclosure.
|
|
32
|
+
|
|
33
|
+
Alternatively, create a [private vulnerability report][advisory] with GitHub.
|
|
34
|
+
|
|
35
|
+
[advisory]: https://github.com/halostatue/diff-lcs/security/advisories/new
|
|
36
|
+
[tidelift]: https://tidelift.com/security
|
|
@@ -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
|