diff-lcs 2.0.0.beta.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 +32 -5
- data/CODE_OF_CONDUCT.md +152 -114
- data/CONTRIBUTING.md +8 -3
- data/CONTRIBUTORS.md +19 -9
- data/Manifest.txt +88 -70
- data/README.md +30 -17
- data/Rakefile +90 -22
- data/SECURITY.md +3 -12
- 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/change.rb +21 -16
- data/lib/diff/lcs/ldiff.rb +9 -4
- data/lib/diff/lcs/version.rb +1 -1
- data/spec/hunk_spec.rb +32 -34
- data/spec/ldiff_spec.rb +7 -7
- data/spec/spec_helper.rb +4 -12
- 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 +136 -105
- data/spec/fixtures/ldiff/output.diff-c +0 -7
- 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-u +0 -9
- 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/{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/README.md
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
# Diff::LCS
|
|
2
2
|
|
|
3
|
-
-
|
|
4
|
-
|
|
5
|
-
- code :: https://github.com/halostatue/diff-lcs
|
|
6
|
-
- bugs :: https://github.com/halostatue/diff-lcs/issues
|
|
7
|
-
- rdoc :: http://rubydoc.info/github/halostatue/diff-lcs
|
|
3
|
+
[![RubyGems Version][shield-gems]][rubygems] ![Coveralls][shield-coveralls]
|
|
4
|
+
[![Build Status][shield-ci]][ci-workflow]
|
|
8
5
|
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
- code :: <https://github.com/halostatue/diff-lcs>
|
|
7
|
+
- issues :: <https://github.com/halostatue/diff-lcs/issues>
|
|
8
|
+
- docs :: <https://halostatue.github.io/diff-lcs/>
|
|
9
|
+
- changelog :: <https://github.com/halostatue/diff-lcs/blob/main/CHANGELOG.md>
|
|
12
10
|
|
|
13
11
|
## Description
|
|
14
12
|
|
|
@@ -16,15 +14,23 @@ Diff::LCS computes the difference between two Enumerable sequences using the
|
|
|
16
14
|
McIlroy-Hunt longest common subsequence (LCS) algorithm. It includes utilities
|
|
17
15
|
to create a simple HTML diff output format and a standard diff-like tool.
|
|
18
16
|
|
|
19
|
-
This is release
|
|
20
|
-
|
|
21
|
-
of
|
|
17
|
+
This is release 2.0, which has significant breaking changes (removal of
|
|
18
|
+
deprecations and workarounds) and requires at least Ruby 3.2 or higher. See full
|
|
19
|
+
details in the `CHANGELOG`, but users of diff-lcs should not notice any
|
|
20
|
+
significant changes to the APIs.
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
### Performance and Compatibility
|
|
23
|
+
|
|
24
|
+
I have not run any benchmarks, but the use of immutable Data classes and the
|
|
25
|
+
removal of a number of inner loop conditionals related to encoding and String
|
|
26
|
+
handling (which have been unnecessary since at least Ruby 2.1 but kept for
|
|
27
|
+
strict compatibility) should allow better optimization by modern Ruby
|
|
28
|
+
implementations.
|
|
29
|
+
|
|
30
|
+
If you are using RSpec for your test suite, you are unlikely to be able to use
|
|
31
|
+
Diff::LCS 2.0 because of the minimum Ruby version unless the developers of RSpec
|
|
32
|
+
loosen their version constraints. I cannot control this and have raised
|
|
33
|
+
[rspec/rspec#290][rspec-issue-290].
|
|
28
34
|
|
|
29
35
|
## Synopsis
|
|
30
36
|
|
|
@@ -88,5 +94,12 @@ Subsequences</em>, CACM, vol.20, no.5, pp.350-353, May 1977, with a few minor
|
|
|
88
94
|
improvements to improve the speed. A simplified description of the algorithm,
|
|
89
95
|
originally written for the Perl version, was written by Mark-Jason Dominus.
|
|
90
96
|
|
|
97
|
+
[ci-workflow]: https://github.com/halostatue/diff-lcs/actions/workflows/ci.yml
|
|
98
|
+
[coveralls]: https://coveralls.io/github/halostatue/diff-lcs?branch=main
|
|
99
|
+
[perl]: https://search.cpan.org/~nedkonz/Algorithm-Diff-1.15/
|
|
100
|
+
[rspec-issue-290]: https://github.com/rspec/rspec/issues/290
|
|
101
|
+
[rubygems]: https://rubygems.org/gems/diff-lcs
|
|
102
|
+
[shield-ci]: https://img.shields.io/github/actions/workflow/status/halostatue/diff-lcs/ci.yml?style=for-the-badge "Build Status"
|
|
103
|
+
[shield-coveralls]: https://img.shields.io/coverallsCoverage/github/halostatue/diff-lcs?style=for-the-badge
|
|
104
|
+
[shield-gems]: https://img.shields.io/gem/v/diff-lcs?style=for-the-badge "Version"
|
|
91
105
|
[smalltalk]: ftp://st.cs.uiuc.edu/pub/Smalltalk/MANCHESTER/manchester/4.0/diff.st
|
|
92
|
-
[perl]: http://search.cpan.org/~nedkonz/Algorithm-Diff-1.15/
|
data/Rakefile
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
require "rubygems"
|
|
2
|
-
require "rspec"
|
|
3
|
-
require "rspec/core/rake_task"
|
|
4
2
|
require "hoe"
|
|
5
3
|
require "rake/clean"
|
|
6
4
|
require "rdoc/task"
|
|
5
|
+
require "minitest/test_task"
|
|
7
6
|
|
|
8
7
|
Hoe.plugin :halostatue
|
|
9
|
-
Hoe.plugin :rubygems
|
|
10
8
|
|
|
11
9
|
Hoe.plugins.delete :debug
|
|
12
10
|
Hoe.plugins.delete :newb
|
|
13
11
|
Hoe.plugins.delete :publish
|
|
14
12
|
Hoe.plugins.delete :signing
|
|
13
|
+
Hoe.plugins.delete :test
|
|
15
14
|
|
|
16
15
|
hoe = Hoe.spec "diff-lcs" do
|
|
17
16
|
developer("Austin Ziegler", "halostatue@gmail.com")
|
|
@@ -27,10 +26,12 @@ hoe = Hoe.spec "diff-lcs" do
|
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
extra_dev_deps << ["hoe", "~> 4.0"]
|
|
30
|
-
extra_dev_deps << ["hoe-halostatue", "~>
|
|
31
|
-
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"]
|
|
32
33
|
extra_dev_deps << ["rake", ">= 10.0", "< 14"]
|
|
33
|
-
extra_dev_deps << ["rdoc", ">= 6.
|
|
34
|
+
extra_dev_deps << ["rdoc", ">= 6.0", "< 8"]
|
|
34
35
|
extra_dev_deps << ["simplecov", "~> 0.9"]
|
|
35
36
|
extra_dev_deps << ["simplecov-lcov", "~> 0.9"]
|
|
36
37
|
extra_dev_deps << ["standard", "~> 1.50"]
|
|
@@ -38,24 +39,33 @@ hoe = Hoe.spec "diff-lcs" do
|
|
|
38
39
|
extra_dev_deps << ["fasterer", "~> 0.11"]
|
|
39
40
|
end
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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"
|
|
46
54
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
Rake::Task["spec"].execute
|
|
55
|
+
SimpleCov::Formatter::LcovFormatter.config do |config|
|
|
56
|
+
config.report_with_single_file = true
|
|
57
|
+
config.lcov_file_name = "lcov.info"
|
|
51
58
|
end
|
|
52
|
-
end
|
|
53
59
|
|
|
54
|
-
|
|
55
|
-
|
|
60
|
+
SimpleCov.start "test_frameworks" do
|
|
61
|
+
enable_coverage :branch
|
|
62
|
+
primary_coverage :branch
|
|
63
|
+
formatter #{formatters}
|
|
64
|
+
end
|
|
65
|
+
RUBY
|
|
66
|
+
end
|
|
56
67
|
|
|
57
|
-
task default: :
|
|
58
|
-
task test: :spec unless Rake::Task["test"].prereqs.include?("spec")
|
|
68
|
+
task default: :test
|
|
59
69
|
|
|
60
70
|
task :version do
|
|
61
71
|
require "diff/lcs/version"
|
|
@@ -64,10 +74,68 @@ end
|
|
|
64
74
|
|
|
65
75
|
RDoc::Task.new do |config|
|
|
66
76
|
config.title = "diff-lcs"
|
|
67
|
-
# config.main = "lib/diff/lcs.rb"
|
|
68
77
|
config.main = "README.md"
|
|
69
78
|
config.rdoc_dir = "doc"
|
|
70
|
-
config.rdoc_files = hoe.spec.require_paths
|
|
79
|
+
config.rdoc_files = hoe.spec.require_paths + hoe.spec.extra_rdoc_files -
|
|
80
|
+
FileList["integration/golden/*.txt", "Manifest.txt"].to_a
|
|
71
81
|
config.markup = "markdown"
|
|
72
82
|
end
|
|
73
83
|
task docs: :rerdoc
|
|
84
|
+
|
|
85
|
+
def rspec_to_golden(file)
|
|
86
|
+
File.join("integration/golden", File.basename(file, "_spec.rb")) + ".txt"
|
|
87
|
+
end
|
|
88
|
+
|
|
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
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
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" }
|
|
114
|
+
|
|
115
|
+
rspecs.to_a.each do |rspec_file|
|
|
116
|
+
basename = File.basename(rspec_file, "_spec.rb")
|
|
117
|
+
|
|
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
|
|
135
|
+
end
|
|
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
|
@@ -22,24 +22,15 @@ All issues raised must be demonstrated on the minimum supported Ruby version.
|
|
|
22
22
|
>
|
|
23
23
|
> | Version | Release Date | Support Ends | Security Support Ends |
|
|
24
24
|
> | ------- | ------------ | ------------ | --------------------- |
|
|
25
|
-
> | 1.x | 2010 | 2026-
|
|
26
|
-
> | 2.x | 2026-01
|
|
25
|
+
> | 1.x | 2010 | 2026-05-01 | 2027-02-01 |
|
|
26
|
+
> | 2.x | 2026-02-01 | - | - |
|
|
27
27
|
|
|
28
28
|
## Reporting a Vulnerability
|
|
29
29
|
|
|
30
30
|
Report vulnerabilities via the [Tidelift security contact][tidelift]. Tidelift
|
|
31
31
|
will coordinate the fix and disclosure.
|
|
32
32
|
|
|
33
|
-
Alternatively, create a [private vulnerability report][advisory] with GitHub
|
|
34
|
-
send an email to [security@ruby.halostatue.ca][email] with the text `diff-lcs`
|
|
35
|
-
in the subject. Emails sent to this address should be encrypted using [age][age]
|
|
36
|
-
with the following public key:
|
|
37
|
-
|
|
38
|
-
```
|
|
39
|
-
age1fc6ngxmn02m62fej5cl30lrvwmxn4k3q2atqu53aatekmnqfwumqj4g93w
|
|
40
|
-
```
|
|
33
|
+
Alternatively, create a [private vulnerability report][advisory] with GitHub.
|
|
41
34
|
|
|
42
35
|
[advisory]: https://github.com/halostatue/diff-lcs/security/advisories/new
|
|
43
|
-
[age]: https://github.com/FiloSottile/age
|
|
44
|
-
[email]: mailto:security@ruby.halostatue.ca
|
|
45
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/change.rb
CHANGED
|
@@ -10,7 +10,7 @@ class Diff::LCS::Change
|
|
|
10
10
|
# (changed), '<' (tail changes from first sequence), or '>' (tail changes from second
|
|
11
11
|
# sequence). The last two ('<>') are only found with Diff::LCS::diff and
|
|
12
12
|
# Diff::LCS::sdiff.
|
|
13
|
-
VALID_ACTIONS = %w[
|
|
13
|
+
VALID_ACTIONS = %w[= - + ! > <].freeze
|
|
14
14
|
|
|
15
15
|
def self.valid_action?(action) = VALID_ACTIONS.include?(action)
|
|
16
16
|
|
|
@@ -37,6 +37,9 @@ class Diff::LCS::Change
|
|
|
37
37
|
|
|
38
38
|
def to_a = [action, position, element]
|
|
39
39
|
alias_method :to_ary, :to_a
|
|
40
|
+
alias_method :deconstruct, :to_a
|
|
41
|
+
|
|
42
|
+
def deconstruct_keys(_) = {action:, position:, element:}
|
|
40
43
|
|
|
41
44
|
def self.from_a(arr)
|
|
42
45
|
case arr
|
|
@@ -59,8 +62,8 @@ class Diff::LCS::Change
|
|
|
59
62
|
end
|
|
60
63
|
|
|
61
64
|
def <=>(other)
|
|
62
|
-
r =
|
|
63
|
-
r =
|
|
65
|
+
r = position <=> other.position
|
|
66
|
+
r = VALID_ACTIONS.index(action) <=> VALID_ACTIONS.index(other.action) if r.zero?
|
|
64
67
|
r = element <=> other.element if r.zero?
|
|
65
68
|
r
|
|
66
69
|
end
|
|
@@ -111,28 +114,27 @@ class Diff::LCS::ContextChange
|
|
|
111
114
|
|
|
112
115
|
def to_a = [action, [old_position, old_element], [new_position, new_element]]
|
|
113
116
|
alias_method :to_ary, :to_a
|
|
117
|
+
alias_method :deconstruct, :to_a
|
|
118
|
+
|
|
119
|
+
def deconstruct_keys(_) = {action:, old_position:, old_element:, new_position:, new_element:}
|
|
114
120
|
|
|
115
121
|
def self.from_a(arr) = Diff::LCS::Change.from_a(arr)
|
|
116
122
|
|
|
117
123
|
# Simplifies a context change for use in some diff callbacks. '<' actions are converted
|
|
118
124
|
# to '-' and '>' actions are converted to '+'.
|
|
119
125
|
def self.simplify(event)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
case ea[0]
|
|
126
|
+
case event.action
|
|
123
127
|
when "-"
|
|
124
|
-
|
|
128
|
+
event.with(new_element: nil)
|
|
125
129
|
when "<"
|
|
126
|
-
|
|
127
|
-
ea[2][1] = nil
|
|
130
|
+
event.with(action: "-", new_element: nil)
|
|
128
131
|
when "+"
|
|
129
|
-
|
|
132
|
+
event.with(old_element: nil)
|
|
130
133
|
when ">"
|
|
131
|
-
|
|
132
|
-
|
|
134
|
+
event.with(action: "+", old_element: nil)
|
|
135
|
+
else
|
|
136
|
+
event
|
|
133
137
|
end
|
|
134
|
-
|
|
135
|
-
from_a(ea)
|
|
136
138
|
end
|
|
137
139
|
|
|
138
140
|
def ==(other)
|
|
@@ -145,9 +147,12 @@ class Diff::LCS::ContextChange
|
|
|
145
147
|
end
|
|
146
148
|
|
|
147
149
|
def <=>(other)
|
|
148
|
-
r =
|
|
149
|
-
r = old_position <=> other.old_position if r.zero?
|
|
150
|
+
r = old_position <=> other.old_position
|
|
150
151
|
r = new_position <=> other.new_position if r.zero?
|
|
152
|
+
if r.zero?
|
|
153
|
+
r = Diff::LCS::Change::VALID_ACTIONS.index(action) <=>
|
|
154
|
+
Diff::LCS::Change::VALID_ACTIONS.index(other.action)
|
|
155
|
+
end
|
|
151
156
|
r = old_element <=> other.old_element if r.zero?
|
|
152
157
|
r = new_element <=> other.new_element if r.zero?
|
|
153
158
|
r
|
data/lib/diff/lcs/ldiff.rb
CHANGED
|
@@ -16,9 +16,14 @@ class Diff::LCS::Ldiff # :nodoc:
|
|
|
16
16
|
MIT licence.
|
|
17
17
|
COPYRIGHT
|
|
18
18
|
|
|
19
|
-
InputInfo = Struct.new(:filename, :data, :
|
|
19
|
+
InputInfo = Struct.new(:filename, :data, :mtime) do
|
|
20
20
|
def initialize(filename)
|
|
21
|
-
|
|
21
|
+
if filename == "-"
|
|
22
|
+
super("<stdin>", $stdin.read, Time.now)
|
|
23
|
+
return
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
super(filename, ::File.read(filename), ::File.stat(filename).mtime)
|
|
22
27
|
end
|
|
23
28
|
end
|
|
24
29
|
|
|
@@ -142,9 +147,9 @@ class Diff::LCS::Ldiff # :nodoc:
|
|
|
142
147
|
output << "Files #{info_old.filename} and #{info_new.filename} differ\n"
|
|
143
148
|
return true
|
|
144
149
|
when :unified, :context
|
|
145
|
-
ft = info_old.
|
|
150
|
+
ft = info_old.mtime.localtime.strftime("%Y-%m-%d %H:%M:%S.000000000 %z")
|
|
146
151
|
output << "#{char_old} #{info_old.filename}\t#{ft}\n"
|
|
147
|
-
ft = info_new.
|
|
152
|
+
ft = info_new.mtime.localtime.strftime("%Y-%m-%d %H:%M:%S.000000000 %z")
|
|
148
153
|
output << "#{char_new} #{info_new.filename}\t#{ft}\n"
|
|
149
154
|
when :ed
|
|
150
155
|
output = []
|
data/lib/diff/lcs/version.rb
CHANGED
data/spec/hunk_spec.rb
CHANGED
|
@@ -2,71 +2,69 @@
|
|
|
2
2
|
|
|
3
3
|
require "spec_helper"
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
require "diff/lcs/hunk"
|
|
5
|
+
require "diff/lcs/hunk"
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
describe Diff::LCS::Hunk do
|
|
8
|
+
let(:old_data) { ["Tu a un carté avec {count} itéms".encode("UTF-16LE")] }
|
|
9
|
+
let(:new_data) { ["Tu a un carte avec {count} items".encode("UTF-16LE")] }
|
|
10
|
+
let(:pieces) { Diff::LCS.diff old_data, new_data }
|
|
11
|
+
let(:hunk) { Diff::LCS::Hunk.new(old_data, new_data, pieces[0], 3, 0) }
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
it "produces a unified diff from the two pieces" do
|
|
14
|
+
expected = <<-EXPECTED.gsub(/^\s+/, "").encode("UTF-16LE").chomp
|
|
16
15
|
@@ -1 +1 @@
|
|
17
16
|
-Tu a un carté avec {count} itéms
|
|
18
17
|
+Tu a un carte avec {count} items
|
|
19
|
-
|
|
18
|
+
EXPECTED
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
expect(hunk.diff(:unified)).to eq(expected)
|
|
21
|
+
end
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
it "produces a unified diff from the two pieces (last entry)" do
|
|
24
|
+
expected = <<-EXPECTED.gsub(/^\s+/, "").encode("UTF-16LE").chomp
|
|
26
25
|
@@ -1 +1 @@
|
|
27
26
|
-Tu a un carté avec {count} itéms
|
|
28
27
|
+Tu a un carte avec {count} items
|
|
29
28
|
\
|
|
30
|
-
|
|
29
|
+
EXPECTED
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
expect(hunk.diff(:unified, true)).to eq(expected)
|
|
32
|
+
end
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
it "produces a context diff from the two pieces" do
|
|
35
|
+
expected = <<-EXPECTED.gsub(/^\s+/, "").encode("UTF-16LE").chomp
|
|
37
36
|
***************
|
|
38
37
|
*** 1 ****
|
|
39
38
|
! Tu a un carté avec {count} itéms
|
|
40
39
|
--- 1 ----
|
|
41
40
|
! Tu a un carte avec {count} items
|
|
42
|
-
|
|
41
|
+
EXPECTED
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
expect(hunk.diff(:context)).to eq(expected)
|
|
44
|
+
end
|
|
46
45
|
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
it "produces an old diff from the two pieces" do
|
|
47
|
+
expected = <<-EXPECTED.gsub(/^ +/, "").encode("UTF-16LE").chomp
|
|
49
48
|
1c1
|
|
50
49
|
< Tu a un carté avec {count} itéms
|
|
51
50
|
---
|
|
52
51
|
> Tu a un carte avec {count} items
|
|
53
52
|
|
|
54
|
-
|
|
53
|
+
EXPECTED
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
expect(hunk.diff(:old)).to eq(expected)
|
|
56
|
+
end
|
|
58
57
|
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
context "with empty first data set" do
|
|
59
|
+
let(:old_data) { [] }
|
|
61
60
|
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
it "produces a unified diff" do
|
|
62
|
+
expected = <<-EXPECTED.gsub(/^\s+/, "").encode("UTF-16LE").chomp
|
|
64
63
|
@@ -0,0 +1 @@
|
|
65
64
|
+Tu a un carte avec {count} items
|
|
66
|
-
|
|
65
|
+
EXPECTED
|
|
67
66
|
|
|
68
|
-
|
|
69
|
-
end
|
|
67
|
+
expect(hunk.diff(:unified)).to eq(expected)
|
|
70
68
|
end
|
|
71
69
|
end
|
|
72
70
|
end
|
data/spec/ldiff_spec.rb
CHANGED
|
@@ -27,11 +27,11 @@ RSpec.describe "bin/ldiff" do
|
|
|
27
27
|
def self.test_ldiff(fixture)
|
|
28
28
|
desc = [
|
|
29
29
|
fixture[:flag],
|
|
30
|
-
"
|
|
31
|
-
"
|
|
30
|
+
"test/fixtures/#{fixture[:left]}",
|
|
31
|
+
"test/fixtures/#{fixture[:right]}",
|
|
32
32
|
"#",
|
|
33
33
|
"=>",
|
|
34
|
-
"
|
|
34
|
+
"test/fixtures/ldiff/output.#{fixture[:name]}#{fixture[:flag]}"
|
|
35
35
|
].join(" ")
|
|
36
36
|
|
|
37
37
|
it desc do
|
|
@@ -49,7 +49,7 @@ RSpec.describe "bin/ldiff" do
|
|
|
49
49
|
def read_fixture(options, mode: "output", allow_missing: false)
|
|
50
50
|
fixture = options.fetch(:name)
|
|
51
51
|
flag = options.fetch(:flag)
|
|
52
|
-
name = "
|
|
52
|
+
name = "test/fixtures/ldiff/#{mode}.#{fixture}#{flag}"
|
|
53
53
|
|
|
54
54
|
return "" if !::File.exist?(name) && allow_missing
|
|
55
55
|
|
|
@@ -74,7 +74,7 @@ RSpec.describe "bin/ldiff" do
|
|
|
74
74
|
^
|
|
75
75
|
[-+*]{3}
|
|
76
76
|
\s*
|
|
77
|
-
|
|
77
|
+
test/fixtures/(\S+)
|
|
78
78
|
\s*
|
|
79
79
|
\d{4}-\d\d-\d\d
|
|
80
80
|
\s*
|
|
@@ -82,7 +82,7 @@ RSpec.describe "bin/ldiff" do
|
|
|
82
82
|
\s*
|
|
83
83
|
(?:[-+]\d{4}|Z)
|
|
84
84
|
}x,
|
|
85
|
-
'***
|
|
85
|
+
'*** test/fixtures/\1 0000-00-00 :00 =>:00 =>00.000000000 -0000'
|
|
86
86
|
)
|
|
87
87
|
end
|
|
88
88
|
|
|
@@ -92,7 +92,7 @@ RSpec.describe "bin/ldiff" do
|
|
|
92
92
|
right = options.fetch(:right)
|
|
93
93
|
|
|
94
94
|
stdout, stderr = capture_subprocess_io do
|
|
95
|
-
system("ruby -Ilib bin/ldiff #{flag}
|
|
95
|
+
system("ruby -Ilib bin/ldiff #{flag} test/fixtures/#{left} test/fixtures/#{right}")
|
|
96
96
|
end
|
|
97
97
|
|
|
98
98
|
[clean_data(stdout, flag), stderr, $?.exitstatus]
|