Skip to content

Commit 22ed668

Browse files
committed
Improve spec test runner (mainly for windows)
Encoding was off and there was an issue with ruby sass replacing single `\r` chars with `\n`. Also updated cleaning functions to be more strict and friendly.
1 parent ce31d80 commit 22ed668

File tree

3 files changed

+74
-47
lines changed

3 files changed

+74
-47
lines changed

lib/sass_spec/engine_adapter.rb

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
require "open3"
2-
31
class EngineAdapter
42
def describe
53
not_implemented
@@ -39,13 +37,15 @@ def describe
3937
end
4038

4139
def version
42-
stdout, stderr, status = Open3.capture3("#{@command} -v")
40+
require 'open3'
41+
stdout, stderr, status = Open3.capture3("#{@command} -v", :binmode => true)
4342
stdout.to_s
4443
end
4544

4645

4746
def compile(sass_filename, style)
48-
stdout, stderr, status = Open3.capture3("#{@command} -t #{style} #{sass_filename}")
47+
require 'open3'
48+
stdout, stderr, status = Open3.capture3("#{@command} -t #{style} #{sass_filename}", :binmode => true)
4949
[stdout, stderr, status.exitstatus]
5050
end
5151
end
@@ -66,20 +66,34 @@ def version
6666

6767
def compile(sass_filename, style)
6868
require 'sass'
69+
# overloads STDERR
70+
stderr = StringIO.new
71+
# restore previous default encoding
72+
encoding = Encoding.default_external
73+
# Does not work as expected when tests are run in parallel
74+
# It runs tests in threads, so stderr is shared across them
75+
old_stderr, $stderr = $stderr, stderr
6976
begin
70-
captured_stderr = StringIO.new
71-
# Does not work as expected when tests are run in parallel
72-
real_stderr, $stderr = $stderr, captured_stderr
73-
begin
74-
css_output = Sass.compile_file(sass_filename.to_s, :style => style.to_sym)
75-
[css_output, captured_stderr.string, 0]
76-
rescue Sass::SyntaxError => e
77-
["", "Error: " + e.message.to_s, 1]
78-
rescue => e
79-
["", e.to_s, 2]
80-
end
81-
ensure
82-
$stderr = real_stderr
77+
Encoding.default_external = "UTF-8"
78+
css_output = Sass.compile_file(sass_filename.to_s, :style => style.to_sym)
79+
# strings come back as utf8 encoded
80+
# internaly we only work with bytes
81+
err_output = stderr.string
82+
err_output.force_encoding('ASCII-8BIT')
83+
css_output.force_encoding('ASCII-8BIT')
84+
[css_output, err_output, 0]
85+
rescue Sass::SyntaxError => e
86+
# prepend the prefix to the message
87+
# and indent all lines to match it
88+
err_output = "Error: " + e.message.to_s
89+
.gsub(/(?:\r?\n)(?!\z)/, "\n ")
90+
err_output.force_encoding('ASCII-8BIT')
91+
["", err_output, 65]
92+
rescue => e
93+
["", e.to_s, 2]
8394
end
95+
ensure
96+
$stderr = old_stderr
97+
Encoding.default_external = encoding
8498
end
8599
end

lib/sass_spec/test.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def run_spec_test(test_case, options = {})
1111

1212
if test_case.overwrite?
1313
if status != 0
14-
File.open(test_case.status_path, "w+") do |f|
14+
File.open(test_case.status_path, "w+", :binmode => true) do |f|
1515
f.write(status)
1616
f.close
1717
end
@@ -20,15 +20,15 @@ def run_spec_test(test_case, options = {})
2020
end
2121

2222
if error.length > 0
23-
File.open(test_case.error_path, "w+") do |f|
23+
File.open(test_case.error_path, "w+", :binmode => true) do |f|
2424
f.write(error)
2525
f.close
2626
end
2727
elsif (File.file?(test_case.error_path))
2828
File.unlink(test_case.error_path)
2929
end
3030

31-
File.open(test_case.expected_path, "w+") do |f|
31+
File.open(test_case.expected_path, "w+", :binmode => true) do |f|
3232
f.write(output)
3333
f.close
3434
end
@@ -83,12 +83,12 @@ def _clean_debug_path(error)
8383
pwd = Dir.pwd
8484
url = pwd.gsub(/\\/, '\/')
8585
error.gsub(/^.*?(input.scss:\d+ DEBUG:)/, '\1')
86-
.gsub(/[ ]+/, " ")
86+
.gsub(/\/+/, "/")
8787
.gsub(/#{Regexp.quote(url)}\//, "/sass/sass-spec/")
8888
.gsub(/#{Regexp.quote(pwd)}\//, "/sass/sass-spec/")
8989
.gsub(/(?:\/todo_|_todo\/)/, "/")
90-
.gsub(/\/libsass\-[a-z]+\-test\//, "/")
91-
.gsub(/\/libsass\-[a-z]+\-issue/, "/libsass-issue")
90+
.gsub(/\/libsass\-[a-z]+\-tests\//, "/")
91+
.gsub(/\/libsass\-[a-z]+\-issues/, "/libsass-issues")
9292
.strip
9393
end
9494

lib/sass_spec/test_case.rb

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,24 @@ def output
6767
stdout, stderr, status = engine.compile(@input_path, @output_style)
6868

6969
if @clean_test
70-
cleaned = _clean_output(stdout)
70+
clean_out = _clean_output(stdout)
7171
else
72-
cleaned = _norm_output(stdout)
72+
clean_out = _norm_output(stdout)
7373
end
74-
@output ||= [stdout, cleaned, stderr, status]
74+
75+
stderr = _clean_error(stderr)
76+
# always replace windows linefeeds
77+
stdout = stdout.gsub(/(\r\n)/, "\n")
78+
stderr = stderr.gsub(/(\r\n)/, "\n")
79+
80+
@output ||= [stdout, clean_out, stderr, status]
7581
end
7682

7783
def expected
78-
output = File.read(@expected_path, :encoding => "utf-8")
84+
output = File.read(@expected_path, :binmode => true)
85+
# we seem to get CP850 otherwise
86+
# this provokes equal test to fail
87+
output.force_encoding('ASCII-8BIT')
7988
if @clean_test
8089
@expected ||= _clean_output(output)
8190
else
@@ -84,12 +93,12 @@ def expected
8493
end
8594

8695
def expected_error
87-
@expected_error = _clean_error(File.read(@error_path, :encoding => "utf-8"))
96+
@expected_error = _clean_error(File.read(@error_path, :binmode => true))
8897
end
8998

9099
def expected_status
91100
if should_fail?
92-
@expected_status = File.read(@status_path, :encoding => "utf-8").to_i
101+
@expected_status = File.read(@status_path).to_i
93102
else
94103
@expected_status = 0
95104
end
@@ -99,35 +108,39 @@ def engine
99108
@options[:engine_adapter]
100109
end
101110

111+
# normalization happens for every test
102112
def _norm_output(css)
103-
css = css.force_encoding('iso-8859-1').encode('utf-8')
104-
css.gsub(/(?:\r?\n)+/, "\n")
105-
.strip
113+
# we dont want to test for linux or windows line-feeds
114+
# but make sure we do not remove single cariage returns
115+
css = css.gsub(/(?:\r?\n)+/, "\n")
116+
end
117+
def _norm_error(err)
118+
# we dont want to test for linux or windows line-feeds
119+
# but make sure we do not remove single cariage returns
120+
err = err.gsub(/(?:\r?\n)+/, "\n")
121+
# remove all newlines at the end of the file
122+
err = err.sub(/(?:\r?\n)+\z/, "")
106123
end
107124

125+
# cleaning only happens when requested for test
126+
# done by creating `expected.type.clean` flag file
108127
def _clean_output(css)
109-
css = css.force_encoding('iso-8859-1').encode('utf-8')
110-
css.gsub(/\s+/, " ")
111-
.gsub(/ *\{/, " {\n")
128+
css.gsub(/ *\{/, " {\n")
112129
.gsub(/([;,]) */, "\\1\n")
113130
.gsub(/ *\} */, " }\n")
114131
.gsub(/;(?:\s*;)+/m, ";")
115132
.gsub(/;\r?\n }/m, " }")
133+
.gsub(/\r?\n/, "\n")
134+
.sub(/(?:\r?\n)+\z/, "")
116135
.strip
117136
end
118-
119137
def _clean_error(err)
120-
pwd = Dir.pwd
121-
url = pwd.gsub(/\\/, '/')
122-
err = err.force_encoding('iso-8859-1').encode('utf-8')
123-
err.gsub(/^.*?(input.scss:\d+ DEBUG:)/, '\1')
124-
.gsub(/[ ]+/, " ")
125-
.gsub(/#{Regexp.quote(url)}\//, "/sass/sass-spec/")
126-
.gsub(/#{Regexp.quote(pwd)}\//, "/sass/sass-spec/")
127-
.gsub(/(?:\/todo_|_todo\/)/, "/")
128-
.gsub(/\/libsass\-[a-z]+\-test\//, "/")
129-
.gsub(/\/libsass\-[a-z]+\-issue/, "/libsass-issue")
130-
.strip
138+
err.gsub(/(?:\/todo_|_todo\/)/, "/") # hide todo pre/suffix
139+
.gsub(/\/libsass\-[a-z]+\-tests\//, "/") # hide test directory
140+
.gsub(/\/libsass\-[a-z]+\-issues\//, "/libsass-issues/") # normalize issue specs
141+
.gsub(/[\w\/\-\\:]+?[\/\\]spec[\/\\]+/, "/sass/spec/") # normalize abs paths
142+
.sub(/(?:\r?\n)*\z/, "\n") # make sure we have exactly one trailing linefeed
143+
.sub(/\A(?:\r?[\n\s])+\z/, "") # clear the whole file if only whitespace
131144
end
132145

133146
end

0 commit comments

Comments
 (0)