File: test_case.rb

package info (click to toggle)
sass-spec 3.6.3-1.2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 23,004 kB
  • sloc: ruby: 1,620; perl: 428; sh: 96; makefile: 16; javascript: 3
file content (196 lines) | stat: -rw-r--r-- 6,427 bytes parent folder | download | duplicates (2)
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
require 'pathname'
require_relative 'util'

# This represents a specific test case.
class SassSpec::TestCase
  attr_reader :metadata, :dir, :impl

  # The path to the input file for this test case.
  #
  # Note that this file is not guaranteed to exist on disk, since this test case
  # may be loaded from an HRX file.
  attr_reader :input_path

  # The Sass or SCSS input for this test case.
  attr_reader :input

  # The normalized expected CSS output. This is `nil` if the test is expected to
  # fail, or if it's malformed and has no expectations.
  attr_reader :expected

  # The normalized expected warning text. This is `nil` if the test is expected
  # to fail, or if it's malformed and has no expectations. It's `""` if the test
  # is expected to succeed without warnings.
  attr_reader :expected_warning

  # The normalized expected error message. This is `nil` if the test is expected
  # to succeed, or if it's malformed and has no expectations.
  attr_reader :expected_error

  # Returns the test case for the given SassSpec::Directory and implementation
  # name.
  def initialize(dir, impl)
    @dir = dir
    @impl = impl
    @metadata = SassSpec::TestCaseMetadata.new(dir)
    @result = false

    input_files = @dir.glob("input.*")
    if input_files.empty?
      raise ArgumentError.new("No input file found in #{@dir}")
    elsif input_files.size > 1
      raise ArgumentError.new("Multiple input files found in #{@dir}")
    end
    @input = @dir.read(input_files.first)

    # If there's an impl-specific output file, then this is a success test.
    # Otherwise, it's an error test if there's *any* error file, impl-specific
    # or otherwise.
    if !file?("output.css", impl: true) && file?("error", impl: :auto)
      @expected_error = SassSpec::Util.normalize_error(read("error", impl: :auto))
    else
      if file?("output.css", impl: :auto)
        output = read("output.css", impl: :auto)
        # we seem to get CP850 otherwise
        # this provokes equal test to fail
        output = String.new(output).force_encoding('ASCII-8BIT')
        @expected = SassSpec::Util.normalize_output(output)
      end

      @expected_warning =
        if file?("warning", impl: :auto)
          SassSpec::Util.normalize_error(read("warning", impl: :auto))
        else
          ""
        end
    end
  end

  def result?
    @result
  end

  def finalize(result)
    @result = result
  end

  def name
    @metadata.name
  end

  def should_fail?
    !!expected_error
  end

  def todo?
    @metadata.todo?(impl)
  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

  # Returns whether a file exists at `path` within this test case's directory.
  #
  # If `impl` is `true`, this adds an implementation-specific suffix to the
  # path. If `impl` is false (the default), it does not. If `impl` is `:auto`,
  # it returns true if either the implementation-specific file or the base file
  # exists.
  def file?(path, impl: false)
    path = _path(path, impl)
    path && @dir.file?(path)
  end

  # Reads the file at `path` within this test case's directory.
  #
  # If `impl` is `true`, this adds an implementation-specific suffix to the
  # path. If `impl` is false (the default), it does not. If `impl` is `:auto`,
  # it returns the contents of implementation-specific file if it exists, or the
  # base file if *it* exists, or throws an error if neither exists.
  def read(path, impl: false)
    unless resolved_path = _path(path, impl)
      raise "#@dir/#{path} doesn't exist"
    end

    @dir.read(resolved_path)
  end

  # Writes `contents` to `path` within this test case's directory.
  #
  # If `impl` is `true`, this adds an implementation-specific suffix to the
  # path. If `impl` is false (the default), it does not. If `impl` is `:auto`,
  # it writes to the implementation-specific file if it exists, and the base
  # file otherwise.
  def write(path, contents, impl: false)
    @dir.write(_path(path, impl) || path, contents)
  end

  # Deletes the file at `path` within this test case's directory.
  #
  # If `if_exists` is `true`, don't throw an error if the file doesn't exist.
  #
  # If `impl` is `true`, this adds an implementation-specific suffix to the
  # path. If `impl` is false (the default), it does not. If `impl` is `:auto`,
  # it deletes the implementation-specific file if it exists, or the base file
  # if *it* exists, or throws an error if neither exists.
  def delete(path, if_exists: false, impl: false)
    unless resolved_path = _path(path, impl)
      return if if_exists
      raise "#@dir/#{path} doesn't exist"
    end

    @dir.delete(resolved_path, if_exists: if_exists)
  end

  # Renames the file at `old` to `new` within this test case's directory.
  #
  # If `impl` is `true`, this adds an implementation-specific suffix to both
  # paths. If `impl` is false (the default), it does not. If `impl` is `:auto`,
  # it renames the implementation-specific file if it exists to an
  # implementation-specific version of `new`, or the base file if *it* exists to
  # `new`, or throws an error if neither exist.
  def rename(old, new, impl: false)
    unless resolved_old = _path(old, impl)
      raise "#@dir/#{old} doesn't exist"
    end

    new = _path(new, true) if resolved_old.include?("-#{@impl}")
    @dir.rename(resolved_old, new)
  end

  # Invokes EngineAdapter#compile on this test case and returns the result.
  def run(engine_adapter)
    @dir.with_real_files do
      engine_adapter.compile(
        File.join(@dir.path, @dir.glob("input.*").first),
        @metadata.precision || 10)
    end
  end

  private

  # Returns the path of `path` within `dir`. 
  #
  # If `impl` is `true`, this adds an implementation-specific suffix to the
  # path. If `impl` is false (the default), it does not. If `impl` is `:auto`,
  # it returns the implementation-specific file if it exists, or the base file
  # if *it* exists, or `nil` if neither exists.
  def _path(path, impl)
    return path unless impl

    extension = File.extname(path)
    path_without_extension = extension.empty? ? path : path[0...-extension.length]
    path_with_impl = "#{path_without_extension}-#{@impl}#{extension}"

    return path_with_impl if impl == true || @dir.file?(path_with_impl)
    return path if @dir.file?(path)
  end
end