require 'pathname'
require 'yaml'
module SassSpec
class TestCaseMetadata
def self.cache
@metadata_cache ||= {}
end
# If you change this, also change Annotate::CLI#annotate_path
def self.merge_options(existing_opts, new_opts)
existing_opts = existing_opts.dup
new_opts.each do |key, value|
if added_key = key[/^add_(.*)/, 1]
added_key = added_key.to_sym
(existing_opts[added_key] ||= [])
.concat(value)
.uniq!
elsif removed_key = key[/^remove_((?:warning_)?todo)/, 1]
removed_key = removed_key.to_sym
(existing_opts[removed_key] ||= [])
.delete_if {|name| value.include?(_normalize_todo(name))}
existing_opts.delete(removed_key) if existing_opts[removed_key].empty?
elsif removed_key = key[/^remove_(.*)/, 1]
removed_key = removed_key.to_sym
(existing_opts[removed_key] ||= [])
.delete_if {|name| value.include?(name)}
existing_opts.delete(removed_key) if existing_opts[removed_key].empty?
elsif value.nil?
existing_opts.delete(key)
else
existing_opts[key] = value
end
end
existing_opts
end
ACCUMULATED_OPTIONS = [:todo, :warning_todo, :ignore_for, :ignore_warning_for, :only_on, :warning_only_on]
attr_reader :options
# The name of the test.
#
# This is a standardized format of the test's directory name.
attr_reader :name
# Parses metadata for the test case at the given SassSpec::Directory.
def initialize(test_case_dir)
@name = test_case_dir.to_s
@options = resolve_options(test_case_dir)
end
def resolve_options(dir)
self.class.cache[dir.path] ||= _resolve_options(dir).tap do |options|
_normalize_todos(options, :todo)
_normalize_todos(options, :warning_todo)
end.freeze
end
def _resolve_options(dir)
self_options = dir.file?("options.yml") ? YAML.load(dir.read("options.yml")) : {}
raise "#{dir.path}/options.yml is not a map!" unless self_options.is_a?(Hash)
return self_options unless parent = dir.parent
parent_options = resolve_options(parent)
rv = parent_options.merge(self_options) do |key, parent_value, self_value|
if ACCUMULATED_OPTIONS.include?(key)
(Array(parent_value) + Array(self_value)).uniq
else
self_value
end
end
rv
end
# Normalize a list of TODOs to convert GitHub issue references to
# implementation names.
def _normalize_todos(options, field)
if options.include?(field)
options[field] = options[field]
.map {|name| self.class._normalize_todo(name)}
.to_set
end
end
# Normalize a single TODO value to convert a GitHub issue reference to an
# implementation name.
def self._normalize_todo(value)
value =~ %r{^sass/(.*)#} ? $1 : value
end
def todo?(impl)
@options[:todo] && @options[:todo].include?(impl)
end
def warning_todo?(impl)
@options[:warning_todo] && @options[:warning_todo].include?(impl)
end
def all_warning_todos
@options[:warning_todo] || []
end
def ignore_for?(impl)
(@options[:ignore_for] && @options[:ignore_for].include?(impl)) ||
(@options[:only_on] && !@options[:only_on].include?(impl))
end
def ignore_warning_for?(impl)
(@options[:ignore_warning_for] && @options[:ignore_warning_for].include?(impl)) ||
(@options[:warning_only_on] && @options[:warning_only_on].include?(impl))
end
def warnings_ignored_for
@options[:ignore_warning_for] || []
end
def precision
@options[:precision]
end
def valid_for_impl?(impl)
!ignore_for?(impl)
end
end
end