@@ -24,62 +24,127 @@ def call
24
24
""
25
25
else
26
26
if expected . is_a? ( String ) && actual . is_a? ( String )
27
- if expected . include? ( "\n " ) || actual . include? ( "\n " )
28
- a = expected . split ( /\n / ) . map { |line | "#{ line } ⏎" }
29
- b = actual . split ( /\n / ) . map { |line | "#{ line } ⏎" }
30
- opcodes = sequence_matcher . diff_opcodes ( a , b )
31
-
32
- diff = opcodes . flat_map do |code , a_start , a_end , b_start , b_end |
33
- if code == :equal
34
- b [ b_start ..b_end ] . map { |line | normal ( " #{ line } " ) }
35
- elsif code == :insert
36
- b [ b_start ..b_end ] . map { |line | inserted ( "+ #{ line } " ) }
37
- else
38
- a [ a_start ..a_end ] . map { |line | deleted ( "- #{ line } " ) }
39
- end
40
- end . join ( "\n " )
41
-
42
- if expected != actual
43
- <<~OUTPUT
44
- Differing strings.
45
-
46
- Expected: #{ expected . inspect }
47
- Got: #{ actual . inspect }
48
-
49
- Diff:
50
-
51
- #{ diff }
52
- OUTPUT
53
- else
54
- ""
55
- end
56
- else
57
- <<~OUTPUT
58
- Differing strings.
27
+ diff_strings
28
+ elsif expected . is_a? ( Array ) && actual . is_a? ( Array )
29
+ diff_arrays
30
+ else
31
+ diff_objects
32
+ end
33
+ end
34
+ end
59
35
60
- Expected: #{ expected . inspect }
61
- Got: #{ actual . inspect }
36
+ private
62
37
63
- Diff:
38
+ attr_reader :expected , :actual , :color , :sequence_matcher
64
39
65
- #{ deleted ( "- #{ expected } " ) }
66
- #{ inserted ( "+ #{ actual } " ) }
67
- OUTPUT
40
+ def diff_strings
41
+ if expected . include? ( "\n " ) || actual . include? ( "\n " )
42
+ a = expected . split ( /\n / ) . map { |line | "#{ line } ⏎" }
43
+ b = actual . split ( /\n / ) . map { |line | "#{ line } ⏎" }
44
+ opcodes = sequence_matcher . diff_opcodes ( a , b )
45
+
46
+ diff = opcodes . flat_map do |code , a_start , a_end , b_start , b_end |
47
+ if code == :equal
48
+ b [ b_start ..b_end ] . map { |line | normal ( " #{ line } " ) }
49
+ elsif code == :insert
50
+ b [ b_start ..b_end ] . map { |line | inserted ( "+ #{ line } " ) }
51
+ else
52
+ a [ a_start ..a_end ] . map { |line | deleted ( "- #{ line } " ) }
68
53
end
54
+ end . join ( "\n " )
55
+
56
+ <<~OUTPUT
57
+ Differing strings.
58
+
59
+ Expected: #{ expected . inspect }
60
+ Actual: #{ actual . inspect }
61
+
62
+ Diff:
63
+
64
+ #{ diff }
65
+ OUTPUT
66
+ else
67
+ <<~OUTPUT
68
+ Differing strings.
69
+
70
+ Expected: #{ expected . inspect }
71
+ Actual: #{ actual . inspect }
72
+
73
+ Diff:
74
+
75
+ #{ deleted ( "- #{ expected } " ) }
76
+ #{ inserted ( "+ #{ actual } " ) }
77
+ OUTPUT
78
+ end
79
+ end
80
+
81
+ def diff_arrays
82
+ a , b = expected , actual
83
+ opcodes = sequence_matcher . diff_opcodes ( a , b )
84
+
85
+ normalized_opcodes = opcodes . flat_map do |code , a_start , a_end , b_start , b_end |
86
+ if code == :delete
87
+ { state : :deleted , index_range : ( a_start ..a_end ) , collection : expected }
88
+ elsif code == :insert
89
+ { state : :inserted , index_range : ( b_start ..b_end ) , collection : actual }
69
90
else
70
- <<~OUTPUT
71
- Differing objects.
91
+ { state : :equal , index_range : ( b_start ..b_end ) , collection : actual }
92
+ end
93
+ end
72
94
73
- Expected: #{ expected . inspect }
74
- Got: #{ actual . inspect }
75
- OUTPUT
95
+ i = 0
96
+ details = [ ]
97
+ while i < normalized_opcodes . length
98
+ first_event = normalized_opcodes [ i ]
99
+ second_event = normalized_opcodes [ i + 1 ]
100
+ if (
101
+ first_event [ :state ] == :deleted &&
102
+ first_event [ :index_range ] . size == 1 &&
103
+ second_event &&
104
+ second_event [ :state ] == :inserted &&
105
+ second_event [ :index_range ] . size == 1
106
+ )
107
+ index = first_event [ :index_range ] . first
108
+ details << "- *[#{ index } ]: Differing #{ plural_type_for ( actual [ index ] ) } .\n " +
109
+ " Expected: #{ expected [ index ] . inspect } \n " +
110
+ " Actual: #{ actual [ index ] . inspect } "
111
+ i += 2
112
+ else
113
+ if first_event [ :state ] == :inserted
114
+ first_event [ :index_range ] . each do |index |
115
+ details << "- *[? -> #{ index } ]: " +
116
+ "Actual has extra element #{ actual [ index ] . inspect } ."
117
+ end
118
+ elsif first_event [ :state ] == :deleted
119
+ first_event [ :index_range ] . each do |index |
120
+ details << "- *[#{ index } -> ?]: " +
121
+ "Actual is missing element #{ expected [ index ] . inspect } ."
122
+ end
123
+ end
124
+ i += 1
76
125
end
77
126
end
127
+
128
+ <<~OUTPUT
129
+ Differing arrays.
130
+
131
+ Expected: #{ expected . inspect }
132
+ Actual: #{ actual . inspect }
133
+
134
+ Details:
135
+
136
+ #{ details . join ( "\n " ) }
137
+ OUTPUT
78
138
end
79
139
80
- private
140
+ def diff_objects
141
+ <<~OUTPUT
142
+ Differing #{ plural_type_for ( actual ) } .
81
143
82
- attr_reader :expected , :actual , :color , :sequence_matcher
144
+ Expected: #{ expected . inspect }
145
+ Actual: #{ actual . inspect }
146
+ OUTPUT
147
+ end
83
148
84
149
def normal ( text )
85
150
Csi ::ColorHelper . plain ( text )
@@ -92,5 +157,13 @@ def inserted(text)
92
157
def deleted ( text )
93
158
Csi ::ColorHelper . light_red_bg ( text )
94
159
end
160
+
161
+ def plural_type_for ( value )
162
+ case value
163
+ when Numeric then "numbers"
164
+ when String then "strings"
165
+ else "objects"
166
+ end
167
+ end
95
168
end
96
169
end
0 commit comments