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