-
Notifications
You must be signed in to change notification settings - Fork 89
/
test_case.rb
238 lines (190 loc) · 5.23 KB
/
test_case.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
require 'pathname'
# This represents a specific test case.
class SassSpec::TestCase
attr_reader :folder, :input_path, :expected_path, :error_path, :status_path,
:options_path, :metadata
def initialize(folder, options = {})
@options = options
@folder = folder
@metadata = SassSpec::TestCaseMetadata.new(folder, File.expand_path(options[:spec_directory]))
@input_path = Pathname.new(find_input_path(folder))
@options_path = File.join(folder, "options.yml")
@result = false
end
def find_input_path(folder)
input_files = Dir.glob(File.join(folder, "input.*"))
if input_files.empty?
raise ArgumentError.new("No input file found in #{folder}")
elsif input_files.size > 1
raise ArgumentError.new("Multiple input files found in #{folder}: #{input_files.join(', ')}")
end
input_files.first
end
def result?
@result
end
def finalize(result)
@result = result
end
def name
@metadata.name
end
def folder
File.expand_path(@folder)
end
def expected_path
@expected_path ||= _expectation_path("expected_output.css")
end
def base_expected_path
File.join(@folder, "expected_output.css")
end
def impl_expected_path
File.join(@folder, "expected_output-#{impl}.css")
end
def error_path
@error_path ||= _expectation_path("error")
end
def base_error_path
File.join(@folder, "error")
end
def impl_error_path
File.join(@folder, "error-#{impl}")
end
def status_path
@status_path ||= _expectation_path("status")
end
def base_status_path
File.join(@folder, "status")
end
def impl_status_path
File.join(@folder, "status-#{impl}")
end
def precision
@metadata.precision || 5
end
def clean_test
@metadata.clean_output?
end
def output_style
@metadata.output_style
end
def verify_stderr?
!expected_error.empty?
end
def interactive?
@options[:interactive]
end
def migrate_version?
@options[:migrate_version]
end
def migrate_impl?
@options[:migrate_impl]
end
def should_fail?
expected_status != 0
end
def impl
@options[:engine_adapter].describe
end
def todo?
@metadata.todo?(impl)
end
def probe_todo?
# run todo tests but do not fail runner if one does not pass
@options[:probe_todo] && (todo? || warning_todo?)
end
def warning_todo?
@metadata.warning_todo?(impl)
end
def ignore?
@metadata.ignore_for?(impl)
end
def ignore_warning?
@metadata.ignore_warning_for?(impl)
end
def overwrite?
@options[:generate]
end
def output
if @output
return @output
end
stdout, stderr, status = engine.compile(input_path, output_style, precision)
if clean_test
clean_out = _clean_output(stdout)
else
clean_out = _norm_output(stdout)
end
stderr = _clean_error(stderr)
# always replace windows linefeeds
stdout = stdout.gsub(/(\r\n)/, "\n")
stderr = stderr.gsub(/(\r\n)/, "\n")
@output ||= [stdout, clean_out, stderr, status]
end
def input
@input ||= File.read(input_path, :binmode => true, :encoding => "ASCII-8BIT")
end
def expected
output = File.read(expected_path, :binmode => true, :encoding => "ASCII-8BIT")
# we seem to get CP850 otherwise
# this provokes equal test to fail
output.force_encoding('ASCII-8BIT')
if clean_test
@expected ||= _clean_output(output)
else
@expected ||= _norm_output(output)
end
end
def expected_error
@expected_error ||=
if File.file?(error_path)
_clean_error(File.read(error_path, :binmode => true,
:encoding => "ASCII-8BIT"))
else
""
end
end
def equivalent?(other_test_case)
output == other_test_case.output
end
def expected_status
@expected_status ||=
if File.file?(status_path)
@expected_status = File.read(status_path).to_i
else
@expected_status = 0
end
end
def engine
@options[:engine_adapter]
end
def _expectation_path(basename)
extension = File.extname(basename)
path = File.join(@folder, File.basename(basename, extension))
impl_path = "#{path}-#{impl}#{extension}"
File.file?(impl_path) ? impl_path : "#{path}#{extension}"
end
# normalization happens for every test
def _norm_output(css)
# we dont want to test for linux or windows line-feeds
# but make sure we do not remove single cariage returns
css = css.gsub(/(?:\r?\n)+/, "\n")
end
# cleaning only happens when requested for test
# done by creating `expected.type.clean` flag file
def _clean_output(css)
_norm_output(css)
.gsub(/\s+/, " ")
.gsub(/\s*,\s*/, ",")
end
# errors are always cleaned
# we also write them cleaned
def _clean_error(err)
err.gsub(/(?:\/todo_|_todo\/)/, "/") # hide todo pre/suffix
.gsub(/\/libsass\-[a-z]+\-tests\//, "/") # hide test directory
.gsub(/\/libsass\-[a-z]+\-issues\//, "/libsass-issues/") # normalize issue specs
.gsub(/(([\w\/.\-\\:]+?[\/\\])|([\/\\]|(?!:\s)))spec[\/\\]+/, "/sass/spec/") # normalize abs paths
.sub(/(?:\r?\n)*\z/, "\n") # make sure we have exactly one trailing linefeed
.sub(/\A(?:\r?\s)+\z/, "") # clear the whole file if only whitespace
end
end