lib/CLIntegracon/file_tree_spec.rb in clintegracon-0.6.1 vs lib/CLIntegracon/file_tree_spec.rb in clintegracon-0.7.0
- old
+ new
@@ -1,10 +1,18 @@
require 'pathname'
require 'CLIntegracon/diff'
require 'CLIntegracon/formatter'
module CLIntegracon
+ # FileTreeSpec represents a single specification, which is mirrored
+ # on the file system in the spec directory by a direct children.
+ # It contains a before directory (#before_path) and an after
+ # directory (#after_path) or if it is initialized with a #base_spec,
+ # the before directory of this spec is used. The before directory
+ # contents in the #spec_path of the child spec, can contain further
+ # files, which overwrite, if given, the inherited contents.
+ #
class FileTreeSpec
# @return [FileTreeSpecContext]
# The context, which configures path and file behaviors
attr_reader :context
@@ -35,30 +43,53 @@
# The concrete temp directory for this spec
def temp_path
context.temp_path + spec_folder
end
+ # @return [String|NilClass]
+ # The name of an optional #base_spec.
+ attr_reader :base_spec_name
+
+ # Return whether this spec is based on another spec.
+ #
+ # @return [Bool]
+ #
+ def has_base?
+ !base_spec_name.nil?
+ end
+
+ # @return [FileTreeSpec|NilClass]
+ # The spec on whose #after_path will be used as #before_path
+ # for this spec.
+ def base_spec
+ has_base? ? context.spec(base_spec_name) : nil
+ end
+
# Init a spec with a given context
#
- # @param [FileTreeSpecContext] context
- # The context, which configures path and file behaviors
+ # @param [FileTreeSpecContext] context
+ # The context, which configures path and file behaviors
#
- # @param [String] spec_folder
- # The concrete spec folder
+ # @param [String] spec_folder
+ # The concrete spec folder
#
- def initialize(context, spec_folder)
+ # @param [String] based_on
+ # @see #base_spec_name
+ #
+ def initialize(context, spec_folder, based_on: nil)
@context = context
@spec_folder = spec_folder
+ @base_spec_name = based_on
end
# Run this spec
#
- # @param [Block<(FileTreeSpec)->()>] block
- # The block, which will be executed after chdir into the created temporary
- # directory. In this block you will likely run your modifications to the
- # file system and use the received FileTreeSpec instance to make asserts
- # with the test framework of your choice.
+ # @param [Block<(FileTreeSpec)->()>] block
+ # The block, which will be executed after chdir into the created temporary
+ # directory. In this block you will likely run your modifications to the
+ # file system and use the received FileTreeSpec instance to make asserts
+ # with the test framework of your choice.
#
def run(&block)
prepare!
copy_files!
@@ -69,28 +100,26 @@
end
# Compares the expected and produced directory by using the rules
# defined in the context
#
- # @param [Block<(Diff)->()>] diff_block
- # The block, where you will likely define a test for each file to compare.
- # It will receive a Diff of each of the expected and produced files.
+ # @param [Block<(Diff)->()>] diff_block
+ # The block, where you will likely define a test for each file to compare.
+ # It will receive a Diff of each of the expected and produced files.
#
def compare(&diff_block)
transform_paths!
glob_all(after_path).each do |relative_path|
expected = after_path + relative_path
next unless expected.file?
+ next if context.ignores?(relative_path)
- block = special_behavior_for_path relative_path
- next if block == context.class.nop
+ block = context.preprocessors_for(relative_path).first
+ diff = diff_files(expected, relative_path, &block)
- diff = diff_files(expected, relative_path)
- diff.preparator = block unless block.nil?
-
diff_block.call diff
end
end
# Compares the expected and produced directory by using the rules
@@ -98,24 +127,24 @@
#
# This is separate because you probably don't want to define an extra
# test case for each file, which wasn't expected at all. So you can
# keep your test cases consistent.
#
- # @param [Block<(Array)->()>] diff_block
- # The block, where you will likely define a test that no unexpected files exists.
- # It will receive an Array.
+ # @param [Block<(Array)->()>] diff_block
+ # The block, where you will likely define a test that no unexpected files exists.
+ # It will receive an Array.
#
def check_unexpected_files(&block)
expected_files = glob_all after_path
produced_files = glob_all
unexpected_files = produced_files - expected_files
# Select only files
- unexpected_files.reject! { |path| !path.file? }
+ unexpected_files.select! { |path| path.file? }
# Filter ignored paths
- unexpected_files.reject! { |path| special_behavior_for_path(path) == context.class.nop }
+ unexpected_files.reject! { |path| context.ignores?(path) }
block.call unexpected_files
end
# Return a Formatter
@@ -139,75 +168,66 @@
# Copies the before subdirectory of the given tests folder in the temporary
# directory.
#
def copy_files!
- source = before_path
destination = temp_path
- FileUtils.cp_r("#{source}/.", destination)
+
+ if has_base?
+ FileUtils.cp_r("#{base_spec.after_path}/.", destination)
+ end
+
+ begin
+ FileUtils.cp_r("#{before_path}/.", destination)
+ rescue Errno::ENOENT => e
+ raise e unless has_base?
+ end
end
# Applies the in the context configured transformations.
#
def transform_paths!
- context.transform_paths.each do |path, block|
- Dir.glob(path) do |produced_path|
- produced = Pathname(produced_path)
- block.call(produced)
+ glob_all.each do |path|
+ context.transformers_for(path).each do |transformer|
+ transformer.call(path)
end
end
end
# Searches recursively for all files and take care for including hidden files
# if this is configured in the context.
#
- # @param [String] path
- # The relative or absolute path to search in (optional)
+ # @param [String] path
+ # The relative or absolute path to search in (optional)
#
# @return [Array<Pathname>]
#
def glob_all(path=nil)
Dir.chdir path || '.' do
- Dir.glob("**/*", context.include_hidden_files? ? File::FNM_DOTMATCH : 0).sort.map { |path|
- Pathname(path)
- }
- end
- end
-
- # Find the special behavior for a given path
- #
- # @return [Block<(Pathname) -> to_s>]
- # This block takes the Pathname and transforms the file in a better comparable
- # state. If it returns nil, the file is ignored.
- #
- def special_behavior_for_path(path)
- context.special_paths.each do |key, block|
- matched = if key.is_a?(Regexp)
- path.to_s.match(key)
- else
- File.fnmatch(key, path)
+ Dir.glob("**/*", context.include_hidden_files? ? File::FNM_DOTMATCH : 0).sort.map do |p|
+ Pathname(p)
end
- next unless matched
- return block
end
- return nil
end
# Compares two files to check if they are identical and produces a clear diff
# to highlight the differences.
#
- # @param [Pathname] expected
- # The file in the after directory
+ # @param [Pathname] expected
+ # The file in the after directory
#
- # @param [Pathname] relative_path
- # The file in the temp directory
+ # @param [Pathname] relative_path
+ # The file in the temp directory
#
+ # @param [Block<(Pathname)->(to_s)>] block
+ # the block, which transforms the files in a better comparable form
+ #
# @return [Diff]
# An object holding a diff
#
- def diff_files(expected, relative_path)
+ def diff_files(expected, relative_path, &block)
produced = temp_path + relative_path
- Diff.new(expected, produced, relative_path)
+ Diff.new(expected, produced, relative_path, &block)
end
end
end