class Ki::Tester
Modifies run time environment for tests and automatically restores original state
-
note: cleanup is triggered by calling the {#after} method
@see cleaners
@see tmpdir
@see catch_stdio
@see chdir
@see after
Attributes
List of Procs which should be executed @see after
Name of the test round that uses this tester
Public Class Methods
Copies contents of src to dest
-
excludes files and directories beginning with a '.'
@param [String] src source directory path @param [String] dest destination directory path @return [String] dest directory
# File lib/util/test.rb, line 220 def self.copy_visible_files(src, dest) Dir.glob(File.join(src, "**/*")).each do |file_src| file_path = file_src[src.size+1..-1] if File.file?(file_src) FileUtils.cp(file_src, File.join(dest, file_path)) elsif File.directory?(file_src) FileUtils.mkdir(File.join(dest, file_path)) end end dest end
# File lib/util/test.rb, line 202 def self.final_tester_check catcher = ExceptionCatcher.new $testers.each do |tester| if !tester.clear? puts "Tester#{tester.test_name ? " '#{tester.test_name}'" : ""} has not been cleared! Please add the missing .after() command. Clearing it automatically." catcher.catch do tester.after end end end catcher.check end
# File lib/util/test.rb, line 56 def initialize(test_name = nil) @test_name = test_name @cleaners = [] $testers << self end
Verifies that files exist in target directory. If files or directories are missing, contents are wrong, type is wrong or there are unwanted files raises an exception. @param [String] source_root target directory path @param [List] args containing check_for_extra_files and files map
* check_for_extra_files, by default verify_files does not check if there are other files or directories. If set to true, raises an exception if there are other files * files, a map of file paths and contents. If file name ends with "/" or file contents are nil, path is a directory
@return [Boolean] true if there were no errors @example
@tester.verify_files(tmpdir, "file.txt" => "aa", "dir/my.txt" => "bb", "dir" => nil) # other files are ok @tester.verify_files(tmpdir, true, "file.txt" => "aa", "dir/my.txt" => "bb", "dir" => nil) # other files will fail
# File lib/util/test.rb, line 260 def self.verify_files(source_root, *args) check_for_extra_files, files = args if files.nil? && check_for_extra_files.kind_of?(Hash) files = check_for_extra_files check_for_extra_files = nil end files.each_pair do |file, contents| file_path = File.join(source_root, file) is_dir = file.end_with?("/") || contents.nil? if !File.exists?(file_path) raise "#{ is_dir ? "Directory" : "File"} '#{file_path}' is missing!" end if is_dir != File.directory?(file_path) raise "Existing #{ is_dir ? "file" : "directory"} '#{file_path}' should be a #{ is_dir ? "directory" : "file"}!" end if !is_dir file_contents = IO.read(file_path) [contents].flatten.each do |o| if o.kind_of?(Regexp) if !file_contents.match(o) raise "File '#{file_path}' does not match regexp #{o.inspect}, file contents: '#{file_contents}'" end elsif o.kind_of?(String) if file_contents != o raise "File '#{file_path}' is broken! Expected '#{o}' but was '#{file_contents}'" end elsif o.kind_of?(Proc) if !o.call(file_contents) raise "File '#{file_path}' did not pass test!" end else raise "Unsupported checker! File '#{file_path}' object: #{o.inspect}" end end end end if check_for_extra_files files_and_dirs = {} files.each_pair do |k, v| file_arr=k.split("/") c = file_arr.size while c > 0 c -= 1 files_and_dirs[File.join(source_root, file_arr)]=true file_arr.delete_at(-1) end end Dir.glob(File.join(source_root, "**/*")).each do |file| if !files_and_dirs[file] raise "#{ File.directory?(file) ? "Directory" : "File"} '#{file}' exists, but it should not exist!" end end end end
Writes defined files to target directory
-
note: dest_root and target directories are automatically created
@param [String] dest_root target directory path @param [Hash] files map of files and their contents @return [String] dest_root directory @example
src = Tester.write_files(@tester.tmpdir, "file.txt" => "aa", "dir/my.txt" => "bb")
# File lib/util/test.rb, line 239 def self.write_files(dest_root, files={}) files.each_pair { |file_path, content| dir = File.dirname(file_path) if dir != "." FileUtils.mkdir_p(File.join(dest_root, dir)) end File.safe_write(File.join(dest_root, file_path), content) } dest_root end
Public Instance Methods
Executes all pending cleanup operations
-
cleaners lists all procs that will be executed
-
all exceptions from procs are caught and raised together
-
Tester
helper methods schedule cleanup operations to cleaners if needed
@return [void] @example RSpec test
describe "My tests" do before do @tester = Tester.new end after do @tester.after end it "should export files" do dest = @tester.tmpdir end end
@example Tests can add their own cleanup procs. This reduces the need to write exception management in tests
it "should combine ..." do dao = DAO.new @tester.cleaners << -> { dao.rollback } dao.do_something_that_does_not_cleanup_environment_and_might_raise_exception end
@see ExceptionCatcher
, cleaners
# File lib/util/test.rb, line 187 def after catcher = ExceptionCatcher.new @cleaners.each do |after_block| catcher.catch do after_block.call end end @cleaners.clear catcher.check end
Redirects $stdin, $stdout, $stderr streams to the Tester
instance
-
if called without block, streams are restored when after is called
@param [Proc] block if block is defined, restores streams once the block ends @return self which is useful when catch_stdio
is called with block because stdin, stdout and stderr are available after the block @example
@tester.catch_stdio do puts "foo" end @tester.stdio.join == "foo\n"
@see DummyIO
, stdin, stdout, stderr
# File lib/util/test.rb, line 113 def catch_stdio(&block) original_streams = [$stdin, $stdout, $stderr] cleanup = -> { $stdin, $stdout, $stderr = original_streams } stdin.clear stdout.clear stderr.clear $stdin = stdin $stdout = stdout $stderr = stderr if block begin block.call ensure cleanup.call end else @cleaners << cleanup end self end
Changes working directory to target @param (String
) dest target directory @param (Proc) block if block is defined, restores working directory after block ends @return (Object
) if block is defined returns block's return value @example
@tester.chdir(dest)
# File lib/util/test.rb, line 140 def chdir(dest, &block) if block Dir.chdir(dest, &block) else if !defined? @original_dir @original_dir = Dir.pwd @cleaners << -> { Dir.chdir(@original_dir); @original_dir=nil } end Dir.chdir(dest) end end
# File lib/util/test.rb, line 198 def clear? @cleaners.empty? end
# File lib/util/test.rb, line 152 def env(key, value) current = ENV[key] @cleaners << -> {ENV[key]=current} ENV[key]=value self end
# File lib/util/test.rb, line 326 def restore_extensions original_commands = KiCommand::KiExtensions.dup cleaners << lambda do KiCommand::KiExtensions.clear KiCommand::KiExtensions.register(original_commands) end end
Creates a temporary directory
-
if called without a block removes directory when after is called
@param [String] src tmpdir copies contents of src path if src is defined. note: only visible files are copied. files and directories starting with . are excluded @param [Proc] block if a block is defined, passes the temporary directory path to the block as parameter and removes the directory when the block ends @return [String, Object] If block is not defined, returns the path of the temporary directory. If block is defined, returns the value the block returns. @example
tmp_source = @tester.tmpdir File.touch(File.join(tmp_source, "file.txt")) @tester.tmpdir(tmp_source).each do |dest_2| end
@see copy_visible_files
# File lib/util/test.rb, line 74 def tmpdir(src=nil, &block) dest = Dir.mktmpdir cleanup = -> { FileUtils.remove_entry_secure(dest) } if src catcher = ExceptionCatcher.new catcher.catch do Tester.copy_visible_files(src, dest) end # if there is a problem copying files, cleanup and raise original exception if catcher.exceptions? catcher.catch do cleanup.call end catcher.check end end if block begin block.call(dest) ensure cleanup.call end else @cleaners << cleanup dest end end