unified 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/.gitignore +17 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +33 -0
  6. data/Rakefile +1 -0
  7. data/lib/unified.rb +8 -0
  8. data/lib/unified/chunk.rb +88 -0
  9. data/lib/unified/diff.rb +74 -0
  10. data/lib/unified/line.rb +21 -0
  11. data/lib/unified/parser.rb +53 -0
  12. data/lib/unified/transformer.rb +25 -0
  13. data/lib/unified/version.rb +3 -0
  14. data/spec/chunk_spec.rb +134 -0
  15. data/spec/diff_spec.rb +203 -0
  16. data/spec/examples/invalid/no_chunk_header.diff +10 -0
  17. data/spec/examples/invalid/no_content.diff +0 -0
  18. data/spec/examples/invalid/no_modifications.diff +11 -0
  19. data/spec/examples/invalid/no_modified_file_header.diff +6 -0
  20. data/spec/examples/invalid/no_original_file_header.diff +6 -0
  21. data/spec/examples/valid/.DS_Store +0 -0
  22. data/spec/examples/valid/commit_revision.diff +5 -0
  23. data/spec/examples/valid/modified_filename.diff +7 -0
  24. data/spec/examples/valid/more_additions.diff +7 -0
  25. data/spec/examples/valid/more_deletions.diff +10 -0
  26. data/spec/examples/valid/multi_chunk.diff +19 -0
  27. data/spec/examples/valid/new_file.diff +8 -0
  28. data/spec/examples/valid/no_newline.diff +29 -0
  29. data/spec/examples/valid/null_git.diff +8 -0
  30. data/spec/examples/valid/removed_file.diff +8 -0
  31. data/spec/examples/valid/simple.diff +11 -0
  32. data/spec/examples/valid/simple_git.diff +29 -0
  33. data/spec/line_spec.rb +45 -0
  34. data/spec/parser_spec.rb +344 -0
  35. data/spec/spec_helper.rb +21 -0
  36. data/spec/support/matchers/parse_matchers.rb +53 -0
  37. data/spec/transformer_spec.rb +82 -0
  38. data/unified.gemspec +26 -0
  39. metadata +173 -0
@@ -0,0 +1,203 @@
1
+ require "unified/diff"
2
+ require "spec_helper"
3
+
4
+ describe "Unified::Diff" do
5
+
6
+ def lines
7
+ [" Line 1", "+Line 2", "-Line 3"]
8
+ end
9
+
10
+ def chunk(orig = 1, mod = 1, lines = lines)
11
+ Unified::Chunk.new(original: orig, modified: mod, lines: lines, section_header: "A header")
12
+ end
13
+
14
+ def diff(lines = lines)
15
+ Unified::Diff.new original_filename: "Original", modified_filename: "Modified",
16
+ original_revision: "OrigRev", modified_revision: "ModRev",
17
+ chunks: [chunk(1, 1, lines), chunk(25, 25, lines), chunk(50, 50, lines)]
18
+ end
19
+
20
+ describe ".parse!" do
21
+ it "parses all valid diff files" do
22
+ valid_diffs.each do |content|
23
+ expect { Unified::Diff.parse!(content) }.not_to raise_error
24
+ end
25
+ end
26
+
27
+ it "returns a Unified::Diff object" do
28
+ valid_diffs.each do |content|
29
+ expect(Unified::Diff.parse!(content)).to be_a_kind_of Unified::Diff
30
+ end
31
+ end
32
+
33
+ it "raises a Unified::ParseError for invalid diffs" do
34
+ invalid_diffs.each do |content|
35
+ expect { Unified::Diff.parse!(content) }.to raise_error(Unified::ParseError)
36
+ end
37
+ end
38
+
39
+ it "provides a line number and character for invalid diffs" do
40
+ invalid_diffs.each do |content|
41
+ begin
42
+ Unified::Diff.parse!(content)
43
+ rescue Unified::ParseError => e
44
+ e.message.should match /line \d+/
45
+ e.message.should match /char \d+/
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ describe "#original_filename" do
52
+ it "returns the correct value" do
53
+ diff.original_filename.should == "Original"
54
+ end
55
+ end
56
+
57
+ describe "#modified_filename" do
58
+ it "returns the correct value" do
59
+ diff.modified_filename.should == "Modified"
60
+ end
61
+ end
62
+
63
+ describe "#original_revision" do
64
+ it "returns the correct value" do
65
+ diff.original_revision.should == "OrigRev"
66
+ end
67
+ end
68
+
69
+ describe "#modified_revision" do
70
+ it "returns the correct value" do
71
+ diff.modified_revision.should == "ModRev"
72
+ end
73
+ end
74
+
75
+ describe "#chunks" do
76
+ it "returns the correct number of chunks" do
77
+ diff.chunks.size.should == 3
78
+ end
79
+ it "returns chunk objects" do
80
+ diff.chunks.each do |chunk|
81
+ chunk.should be_a_kind_of Unified::Chunk
82
+ end
83
+ end
84
+ end
85
+
86
+ describe "#number_of_added_lines" do
87
+ it "returns the correct value" do
88
+ diff.number_of_added_lines.should == 3
89
+ end
90
+ end
91
+
92
+ describe "#number_of_deleted_lines" do
93
+ it "returns the correct value" do
94
+ diff.number_of_deleted_lines.should == 3
95
+ end
96
+ end
97
+
98
+ describe "#number_of_unchanged_lines" do
99
+ it "returns the correct value" do
100
+ diff.number_of_unchanged_lines.should == 3
101
+ end
102
+ end
103
+
104
+ describe "#number_of_modified_lines" do
105
+ it "returns the number of additions plus the number of deletions" do
106
+ diff.number_of_modified_lines.should == 6
107
+ end
108
+ end
109
+
110
+ describe "#total_number_of_lines" do
111
+ it "returns the total number of lines" do
112
+ diff.total_number_of_lines.should == 9
113
+ end
114
+ end
115
+
116
+ describe "#proportion_of_deleted_lines" do
117
+ it "returns 0 when there are no deleted lines" do
118
+ lines = [" Line 1", "+Line 2"]
119
+ diff(lines).proportion_of_deleted_lines.should == 0
120
+ end
121
+
122
+ it "returns 100 when there are only deleted lines" do
123
+ lines = ["-Line 1", "-Line 2"]
124
+ diff(lines).proportion_of_deleted_lines.should == 100
125
+ end
126
+
127
+ it "returns 50 when there are equal added and deleted lines" do
128
+ lines = ["-Line 1", "+Line 2", " Line 3"]
129
+ diff(lines).proportion_of_deleted_lines.should == 50
130
+ end
131
+
132
+ it "rounds to the nearest integer when the lines are not evenly divisible" do
133
+ lines = ["-Line 1", "-Line 2", "+Line 3"]
134
+ diff(lines).proportion_of_deleted_lines.should == 67
135
+ end
136
+ end
137
+
138
+ describe "#proportion_of_added_lines" do
139
+ it "returns 0 when there are no added lines" do
140
+ lines = [" Line 1", "-Line 2"]
141
+ diff(lines).proportion_of_added_lines.should == 0
142
+ end
143
+
144
+ it "returns 100 when there are only added lines" do
145
+ lines = ["+Line 1", "+Line 2"]
146
+ diff(lines).proportion_of_added_lines.should == 100
147
+ end
148
+
149
+ it "returns 50 when there are equal added and added lines" do
150
+ lines = ["-Line 1", "+Line 2", " Line 3"]
151
+ diff(lines).proportion_of_added_lines.should == 50
152
+ end
153
+
154
+ it "rounds to the nearest integer when the lines are not evenly divisible" do
155
+ lines = ["-Line 1", "-Line 2", "+Line 3"]
156
+ diff(lines).proportion_of_added_lines.should == 33
157
+ end
158
+ end
159
+
160
+ describe "#each_chunk" do
161
+ it "iterates once for every chunk" do
162
+ index = 0
163
+ diff.each_chunk do
164
+ index += 1
165
+ end
166
+
167
+ index.should == 3
168
+ end
169
+
170
+ it "passes a chunk to the block" do
171
+ diff.each_chunk do |chunk|
172
+ expect(chunk).to be_a_kind_of Unified::Chunk
173
+ end
174
+ end
175
+ end
176
+
177
+ describe "#header" do
178
+ it "returns a valid diff header" do
179
+ diff.header.should == "--- Original\tOrigRev\n+++ Modified\tModRev"
180
+ end
181
+ end
182
+
183
+ describe "#to_s" do
184
+ it "returns the full diff output" do
185
+ diff.to_s.should == <<-EOF.strip
186
+ --- Original\tOrigRev
187
+ +++ Modified\tModRev
188
+ @@ -1,2 +1,2 @@ A header
189
+ Line 1
190
+ +Line 2
191
+ -Line 3
192
+ @@ -25,2 +25,2 @@ A header
193
+ Line 1
194
+ +Line 2
195
+ -Line 3
196
+ @@ -50,2 +50,2 @@ A header
197
+ Line 1
198
+ +Line 2
199
+ -Line 3
200
+ EOF
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,10 @@
1
+ --- Original 2013-03-23 21:06:27.000000000 +0000
2
+ +++ Original 2013-03-23 21:06:48.000000000 +0000
3
+ Line 1
4
+ Line 2
5
+ Line 3
6
+ -Line 5
7
+ -Line 6
8
+ -Line 7
9
+ +Line 4
10
+ +Line 5
File without changes
@@ -0,0 +1,11 @@
1
+ --- Original 2013-03-23 21:06:27.000000000 +0000
2
+ +++ Original 2013-03-23 21:06:48.000000000 +0000
3
+ @@ -1,4 +1,4 @@
4
+ Line 1
5
+ Line 2
6
+ Line 3
7
+ Line 5
8
+ Line 6
9
+ Line 7
10
+ Line 4
11
+ Line 5
@@ -0,0 +1,6 @@
1
+ --- Original 2013-03-23 21:06:48.000000000 +0000
2
+ @@ -1,4 +1,4 @@
3
+ -Line1
4
+ +Line 1
5
+ -Line 3
6
+ -Line 4
@@ -0,0 +1,6 @@
1
+ +++ Modified 2013-03-23 21:06:48.000000000 +0000
2
+ @@ -1,4 +1,4 @@
3
+ -Line1
4
+ +Line 1
5
+ -Line 3
6
+ -Line 4
@@ -0,0 +1,5 @@
1
+ --- File1 (revision 815)
2
+ +++ File1a (working copy)
3
+ @@ -1 +1 @@
4
+ -testing1
5
+ +tester1
@@ -0,0 +1,7 @@
1
+ --- Original 2013-03-23 21:06:27.000000000 +0000
2
+ +++ Modified 2013-03-23 21:06:48.000000000 +0000
3
+ @@ -1,4 +1,4 @@
4
+ -Line1
5
+ +Line 1
6
+ -Line 3
7
+ -Line 4
@@ -0,0 +1,7 @@
1
+ --- File1 (revision 815)
2
+ +++ File1a (working copy)
3
+ @@ -1,4 +1,4 @@
4
+ -testing1
5
+ +tester1
6
+ +One
7
+ +Two
@@ -0,0 +1,10 @@
1
+ --- Original 2013-03-23 21:06:27.000000000 +0000
2
+ +++ Original 2013-03-23 21:06:48.000000000 +0000
3
+ @@ -1,6 +1,4 @@
4
+ Line 1
5
+ Line 2
6
+ Line 3
7
+ -Line 5
8
+ -Line 6
9
+ -Line 7
10
+ +Line 4
@@ -0,0 +1,19 @@
1
+ --- multi_chunk 2013-03-24 14:38:43.000000000 +0000
2
+ +++ multi_chunk 2013-03-24 14:38:52.000000000 +0000
3
+ @@ -1,7 +1,7 @@
4
+ Line 1
5
+ Line 2
6
+ Line 3
7
+ -Line4
8
+ +Line 4
9
+ Line 5
10
+ Line 6
11
+ Line 7
12
+ @@ -15,6 +15,6 @@
13
+ Line 15
14
+ Line 16
15
+ Line 17
16
+ -Line18
17
+ +Line 18
18
+ Line 19
19
+ Line 20
@@ -0,0 +1,8 @@
1
+ --- spec/factories/diffs/new_file.diff 2013-03-30 21:32:55.000000000 +0000
2
+ +++ spec/factories/diffs/new_file.diff 2013-03-30 21:32:44.000000000 +0000
3
+ @@ -0,0 +1,5 @@
4
+ +This is a new file
5
+ +And these are
6
+ +all
7
+ +new
8
+ +lines
@@ -0,0 +1,29 @@
1
+ --- a/app/controllers/api/v1/code_reviews_controller.rb
2
+ +++ b/app/controllers/api/v1/code_reviews_controller.rb
3
+ @@ -1,24 +1,24 @@
4
+ module Api
5
+ module V1
6
+ class CodeReviewsController < ApplicationController
7
+ respond_to :json
8
+
9
+ def create
10
+ @review = CodeReview.create raw: params[:content]
11
+ render :json => {
12
+ url: code_review_path(@review),
13
+ expires_at: @review.expires_at
14
+ }
15
+ end
16
+
17
+ def show
18
+ - @review = CodeReview.not_expired.find_by_secure_hash! params[:id]
19
+ + @review = CodeReview.not_expired.find_by_token! params[:id]
20
+ sleep 0.5 and raise ActiveRecord::RecordNotFound if @review.nil?
21
+ render :json => {
22
+ content: @review.raw,
23
+ expires_at: @review.expires_at
24
+ }
25
+ end
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,8 @@
1
+ --- /dev/null
2
+ +++ /a/new/file
3
+ @@ -0,0 +1,5 @@
4
+ +This is a new file
5
+ +And these are
6
+ +all
7
+ +new
8
+ +lines
@@ -0,0 +1,8 @@
1
+ --- spec/factories/diffs/new_file.diff 2013-03-31 13:56:25.000000000 +0100
2
+ +++ spec/factories/diffs/new_file.diff 2013-03-31 14:05:49.000000000 +0100
3
+ @@ -1,5 +0,0 @@
4
+ -This is a removed file
5
+ -And these are
6
+ -all
7
+ -removed
8
+ -lines
@@ -0,0 +1,11 @@
1
+ --- Original 2013-03-23 21:06:27.000000000 +0000
2
+ +++ Original 2013-03-23 21:06:48.000000000 +0000
3
+ @@ -1,6 +1,5 @@ A section heading
4
+ Line 1
5
+ Line 2
6
+ Line 3
7
+ -Line 5
8
+ -Line 6
9
+ -Line 7
10
+ +Line 4
11
+ +Line 5
@@ -0,0 +1,29 @@
1
+ --- a/app/controllers/api/v1/code_reviews_controller.rb
2
+ +++ b/app/controllers/api/v1/code_reviews_controller.rb
3
+ @@ -1,24 +1,24 @@
4
+ module Api
5
+ module V1
6
+ class CodeReviewsController < ApplicationController
7
+ respond_to :json
8
+
9
+ def create
10
+ @review = CodeReview.create raw: params[:content]
11
+ render :json => {
12
+ url: code_review_path(@review),
13
+ expires_at: @review.expires_at
14
+ }
15
+ end
16
+
17
+ def show
18
+ - @review = CodeReview.not_expired.find_by_secure_hash! params[:id]
19
+ + @review = CodeReview.not_expired.find_by_token! params[:id]
20
+ sleep 0.5 and raise ActiveRecord::RecordNotFound if @review.nil?
21
+ render :json => {
22
+ content: @review.raw,
23
+ expires_at: @review.expires_at
24
+ }
25
+ end
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,45 @@
1
+ require 'unified/line'
2
+
3
+ describe "Unified::Line" do
4
+ it "extends from String" do
5
+ line = Unified::Line.new "A line"
6
+ line.should respond_to(:gsub)
7
+ line.should respond_to(:size)
8
+ end
9
+
10
+ context "additional methods" do
11
+ let(:addition) { Unified::Line.new "+An addition" }
12
+ let(:deletion) { Unified::Line.new "-A deletion" }
13
+ let(:unchanged) { Unified::Line.new " Unchanged" }
14
+
15
+ describe "#addition?" do
16
+ it "is true when a line begins with '+'" do
17
+ addition.addition?.should be_true
18
+ end
19
+ it "is false when a line doesn't begin with '-'" do
20
+ deletion.addition?.should be_false
21
+ unchanged.addition?.should be_false
22
+ end
23
+ end
24
+
25
+ describe "#deletion?" do
26
+ it "is true when a line begins with '-'" do
27
+ deletion.deletion?.should be_true
28
+ end
29
+ it "is false when a line doesn't begin with '-'" do
30
+ addition.deletion?.should be_false
31
+ unchanged.deletion?.should be_false
32
+ end
33
+ end
34
+
35
+ describe "#unchanged?" do
36
+ it "is true when a line begins with ' '" do
37
+ unchanged.unchanged?.should be_true
38
+ end
39
+ it "is false when a line doesn't begin with ' '" do
40
+ addition.unchanged?.should be_false
41
+ deletion.unchanged?.should be_false
42
+ end
43
+ end
44
+ end
45
+ end