add the code
This commit is contained in:
parent
6b3ed73ae2
commit
8245c819bd
330
bin/cmdtest.rb
Executable file
330
bin/cmdtest.rb
Executable file
@ -0,0 +1,330 @@
|
||||
#!/usr/bin/ruby
|
||||
#----------------------------------------------------------------------
|
||||
# cmdtest.rb
|
||||
#----------------------------------------------------------------------
|
||||
# Copyright 2002-2009 Johan Holmberg.
|
||||
#----------------------------------------------------------------------
|
||||
# This file is part of "cmdtest".
|
||||
#
|
||||
# "cmdtest" is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# "cmdtest" is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with "cmdtest". If not, see <http://www.gnu.org/licenses/>.
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
# This is the main program of "cmdtest".
|
||||
# It reads a number of "CMDTEST_*.rb" files and executes the testcases
|
||||
# found in the files. The result can be reported in different ways.
|
||||
# Most of the testing logic is found in the library files "cmdtest/*.rb".
|
||||
|
||||
top_dir = File.dirname(File.dirname(__FILE__))
|
||||
lib_dir = File.join(File.expand_path(top_dir), "lib")
|
||||
$:.unshift(lib_dir) if File.directory?(File.join(lib_dir, "cmdtest"))
|
||||
|
||||
require "cmdtest/baselogger"
|
||||
require "cmdtest/consolelogger"
|
||||
require "cmdtest/junitlogger"
|
||||
require "cmdtest/testcase"
|
||||
require "cmdtest/workdir"
|
||||
require "cmdtest/util"
|
||||
require "set"
|
||||
require "stringio"
|
||||
require "fileutils"
|
||||
require "find"
|
||||
require "rbconfig"
|
||||
|
||||
module Cmdtest
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class TestMethod
|
||||
|
||||
def initialize(test_method, test_class, runner)
|
||||
@test_method, @test_class, @runner = test_method, test_class, runner
|
||||
end
|
||||
|
||||
def run
|
||||
@runner.notify("testmethod", @test_method) do
|
||||
obj = @test_class.testcase_class.new(self, @runner)
|
||||
obj._work_dir.chdir do
|
||||
obj.setup
|
||||
begin
|
||||
obj.send(@test_method)
|
||||
@runner.assert_success
|
||||
rescue Cmdtest::AssertFailed => e
|
||||
@runner.assert_failure(e.message)
|
||||
rescue => e
|
||||
io = StringIO.new
|
||||
io.puts "CAUGHT EXCEPTION:"
|
||||
io.puts " " + e.message + " (#{e.class})"
|
||||
io.puts "BACKTRACE:"
|
||||
io.puts e.backtrace.map {|line| " " + line }
|
||||
@runner.assert_error(io.string)
|
||||
end
|
||||
obj.teardown
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class TestClass
|
||||
|
||||
attr_reader :testcase_class
|
||||
|
||||
def initialize(testcase_class, file, runner)
|
||||
@testcase_class, @file, @runner = testcase_class, file, runner
|
||||
end
|
||||
|
||||
def run
|
||||
@runner.notify("testclass", @testcase_class) do
|
||||
get_test_methods.each do |method|
|
||||
TestMethod.new(method, self, @runner).run
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def get_test_methods
|
||||
@testcase_class.public_instance_methods(false).sort.select do |method|
|
||||
in_list = @runner.opts.tests.empty? || @runner.opts.tests.include?(method)
|
||||
method =~ /^test_/ && in_list
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class TestFile
|
||||
|
||||
def initialize(file)
|
||||
@file = file
|
||||
end
|
||||
|
||||
def run(runner)
|
||||
@runner = runner
|
||||
@runner.notify("testfile", @file) do
|
||||
testcase_classes = Cmdtest::Testcase.new_subclasses do
|
||||
Kernel.load(@file, true)
|
||||
end
|
||||
for testcase_class in testcase_classes
|
||||
test_class = TestClass.new(testcase_class, self, @runner)
|
||||
if ! test_class.get_test_methods.empty?
|
||||
test_class.run
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class Runner
|
||||
|
||||
attr_reader :opts
|
||||
|
||||
def initialize(project_dir, opts)
|
||||
@project_dir = project_dir
|
||||
@listeners = []
|
||||
@opts = opts
|
||||
end
|
||||
|
||||
def add_listener(listener)
|
||||
@listeners << listener
|
||||
end
|
||||
|
||||
def notify_once(method, *args)
|
||||
for listener in @listeners
|
||||
listener.send(method, *args)
|
||||
end
|
||||
end
|
||||
|
||||
def notify(method, *args)
|
||||
if block_given?
|
||||
notify_once(method + "_begin", *args)
|
||||
yield
|
||||
notify_once(method + "_end", *args)
|
||||
else
|
||||
notify_once(method, *args)
|
||||
end
|
||||
end
|
||||
|
||||
def run
|
||||
ENV["PATH"] = Dir.pwd + Config::CONFIG["PATH_SEPARATOR"] + ENV["PATH"]
|
||||
@n_assert_failures = 0
|
||||
@n_assert_errors = 0
|
||||
@n_assert_successes = 0
|
||||
notify("testsuite") do
|
||||
for test_file in @project_dir.test_files
|
||||
test_file.run(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def assert_success
|
||||
@n_assert_successes += 1
|
||||
end
|
||||
|
||||
def assert_failure(str)
|
||||
@n_assert_failures += 1
|
||||
notify("assert_failure", str)
|
||||
end
|
||||
|
||||
def assert_error(str)
|
||||
@n_assert_errors += 1
|
||||
notify("assert_error", str)
|
||||
end
|
||||
end
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class ProjectDir
|
||||
|
||||
def initialize(argv)
|
||||
@argv = argv
|
||||
end
|
||||
|
||||
def test_files
|
||||
if ! @argv.empty?
|
||||
files = _expand_files_or_dirs(@argv)
|
||||
if files.empty?
|
||||
puts "ERROR: no files given"
|
||||
exit 1
|
||||
end
|
||||
return files
|
||||
end
|
||||
|
||||
try = Dir.glob("t/CMDTEST_*.rb")
|
||||
return _test_files(try) if ! try.empty?
|
||||
|
||||
try = Dir.glob("CMDTEST_*.rb")
|
||||
return _test_files(try) if ! try.empty?
|
||||
|
||||
puts "ERROR: no CMDTEST_*.rb files found"
|
||||
exit 1
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def _test_files(files)
|
||||
files.map {|file| TestFile.new(file) }
|
||||
end
|
||||
|
||||
def _expand_files_or_dirs(argv)
|
||||
files = []
|
||||
for arg in @argv
|
||||
if File.file?(arg)
|
||||
if File.basename(arg) =~ /^.*\.rb$/
|
||||
files << TestFile.new(arg)
|
||||
else
|
||||
puts "ERROR: illegal file: #{arg}"
|
||||
exit(1)
|
||||
end
|
||||
elsif File.directory?(arg)
|
||||
Dir.foreach(arg) do |entry|
|
||||
path = File.join(arg,entry)
|
||||
next unless File.file?(path)
|
||||
next unless entry =~ /^CMDTEST_.*\.rb$/
|
||||
files << TestFile.new(path)
|
||||
end
|
||||
else
|
||||
puts "ERROR: unknown file: #{arg}"
|
||||
end
|
||||
end
|
||||
return files
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class Main
|
||||
|
||||
attr_reader :tests, :quiet, :verbose, :fast, :ruby_s
|
||||
|
||||
def initialize
|
||||
@tests = []
|
||||
@quiet = false
|
||||
@verbose = false
|
||||
@fast = false
|
||||
@xml = nil
|
||||
@ruby_s = false
|
||||
|
||||
_update_cmdtest_level
|
||||
end
|
||||
|
||||
def run
|
||||
while ! ARGV.empty? && ARGV[0] =~ /^-/
|
||||
opt = ARGV.shift
|
||||
case opt
|
||||
when /^--test=(.*)/
|
||||
@tests << $1
|
||||
when /^--quiet$/
|
||||
@quiet = true
|
||||
when /^--verbose$/
|
||||
@verbose = true
|
||||
when /^--fast$/
|
||||
@fast = true
|
||||
when /^--xml=(.+)$/
|
||||
@xml = $1
|
||||
when /^--ruby_s$/
|
||||
@ruby_s = true
|
||||
when /^--help$/, /^-h$/
|
||||
puts
|
||||
_show_options
|
||||
puts
|
||||
exit 0
|
||||
else
|
||||
puts "ERROR: unknown option: #{opt}"
|
||||
puts
|
||||
_show_options
|
||||
puts
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
Util.opts = self
|
||||
@project_dir = ProjectDir.new(ARGV)
|
||||
@runner = Runner.new(@project_dir, self)
|
||||
logger = ConsoleLogger.new(self)
|
||||
@runner.add_listener(logger)
|
||||
if @xml
|
||||
@runner.add_listener(JunitLogger.new(self, @xml))
|
||||
end
|
||||
|
||||
@runner.run
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def _update_cmdtest_level
|
||||
$cmdtest_level = (ENV["CMDTEST_LEVEL"] || "0").to_i + 1
|
||||
ENV["CMDTEST_LEVEL"] = $cmdtest_level.to_s
|
||||
end
|
||||
|
||||
def _show_options
|
||||
puts " --help show this help"
|
||||
puts " --quiet be more quiet"
|
||||
puts " --verbose be more verbose"
|
||||
puts " --fast run fast without waiting for unique mtime:s"
|
||||
puts " --test=NAME only run named test"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
Cmdtest::Main.new.run
|
||||
#----------------------------------------------------------------------
|
94
lib/cmdtest/baselogger.rb
Normal file
94
lib/cmdtest/baselogger.rb
Normal file
@ -0,0 +1,94 @@
|
||||
#----------------------------------------------------------------------
|
||||
# baselogger.rb
|
||||
#----------------------------------------------------------------------
|
||||
# Copyright 2002-2009 Johan Holmberg.
|
||||
#----------------------------------------------------------------------
|
||||
# This file is part of "cmdtest".
|
||||
#
|
||||
# "cmdtest" is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# "cmdtest" is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with "cmdtest". If not, see <http://www.gnu.org/licenses/>.
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
module Cmdtest
|
||||
class BaseLogger
|
||||
|
||||
@@debug = false
|
||||
|
||||
attr_reader :opts
|
||||
|
||||
attr_reader :n_suites, :n_files, :n_classes
|
||||
attr_reader :n_methods, :n_commands, :n_failures, :n_errors
|
||||
|
||||
def initialize(opts)
|
||||
@opts = opts
|
||||
|
||||
@n_suites = 0
|
||||
@n_files = 0
|
||||
@n_classes = 0
|
||||
@n_methods = 0
|
||||
@n_commands = 0
|
||||
@n_failures = 0
|
||||
@n_errors = 0
|
||||
end
|
||||
|
||||
def testsuite_begin
|
||||
p :testsuite_begin if @@debug
|
||||
@n_suites += 1
|
||||
end
|
||||
|
||||
def testsuite_end
|
||||
p :testsuite_end if @@debug
|
||||
end
|
||||
|
||||
def testfile_begin(file)
|
||||
p [:testfile_begin, file] if @@debug
|
||||
@n_files += 1
|
||||
end
|
||||
|
||||
def testfile_end(file)
|
||||
p :testfile_end if @@debug
|
||||
end
|
||||
|
||||
def testclass_begin(testcase_class)
|
||||
p [:testclass_begin, testcase_class] if @@debug
|
||||
@n_classes += 1
|
||||
end
|
||||
|
||||
def testclass_end(testcase_class)
|
||||
p :testclass_end if @@debug
|
||||
end
|
||||
|
||||
def testmethod_begin(method)
|
||||
p [:testmethod_begin, method] if @@debug
|
||||
@n_methods += 1
|
||||
end
|
||||
|
||||
def testmethod_end(method)
|
||||
p :testmethod_end if @@debug
|
||||
end
|
||||
|
||||
def cmdline(method, comment)
|
||||
p :testmethod_end if @@debug
|
||||
@n_commands += 1
|
||||
end
|
||||
|
||||
def assert_failure
|
||||
@n_failures += 1
|
||||
end
|
||||
|
||||
def assert_error
|
||||
@n_errors += 1
|
||||
end
|
||||
|
||||
end
|
||||
end
|
83
lib/cmdtest/cmdeffects.rb
Normal file
83
lib/cmdtest/cmdeffects.rb
Normal file
@ -0,0 +1,83 @@
|
||||
#----------------------------------------------------------------------
|
||||
# cmdeffects.rb
|
||||
#----------------------------------------------------------------------
|
||||
# Copyright 2002-2009 Johan Holmberg.
|
||||
#----------------------------------------------------------------------
|
||||
# This file is part of "cmdtest".
|
||||
#
|
||||
# "cmdtest" is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# "cmdtest" is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with "cmdtest". If not, see <http://www.gnu.org/licenses/>.
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
require "set"
|
||||
|
||||
module Cmdtest
|
||||
class CmdEffects
|
||||
|
||||
attr_reader :stdout, :stderr
|
||||
|
||||
def initialize(process_status, stdout, stderr, snapshot_before, snapshot_after)
|
||||
@process_status = process_status
|
||||
@stdout = stdout
|
||||
@stderr = stderr
|
||||
@before = snapshot_before
|
||||
@after = snapshot_after
|
||||
end
|
||||
|
||||
def exit_status
|
||||
@process_status.exitstatus
|
||||
end
|
||||
|
||||
def _select_files
|
||||
files = @before.files.to_set + @after.files.to_set
|
||||
files.sort.select do |file|
|
||||
before = @before.fileinfo(file)
|
||||
after = @after.fileinfo(file)
|
||||
yield before, after
|
||||
end
|
||||
end
|
||||
|
||||
def affected_files
|
||||
_select_files do |before,after|
|
||||
((!! before ^ !! after) ||
|
||||
(before && after && before != after))
|
||||
end
|
||||
end
|
||||
|
||||
def written_files
|
||||
_select_files do |before,after|
|
||||
((! before && after) ||
|
||||
(before && after && before != after))
|
||||
end
|
||||
end
|
||||
|
||||
def created_files
|
||||
_select_files do |before,after|
|
||||
(! before && after)
|
||||
end
|
||||
end
|
||||
|
||||
def modified_files
|
||||
_select_files do |before,after|
|
||||
(before && after && before != after)
|
||||
end
|
||||
end
|
||||
|
||||
def removed_files
|
||||
_select_files do |before,after|
|
||||
(before && ! after)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
82
lib/cmdtest/consolelogger.rb
Normal file
82
lib/cmdtest/consolelogger.rb
Normal file
@ -0,0 +1,82 @@
|
||||
#----------------------------------------------------------------------
|
||||
# consolelogger.rb
|
||||
#----------------------------------------------------------------------
|
||||
# Copyright 2002-2009 Johan Holmberg.
|
||||
#----------------------------------------------------------------------
|
||||
# This file is part of "cmdtest".
|
||||
#
|
||||
# "cmdtest" is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# "cmdtest" is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with "cmdtest". If not, see <http://www.gnu.org/licenses/>.
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
require "cmdtest/baselogger"
|
||||
|
||||
module Cmdtest
|
||||
class ConsoleLogger < BaseLogger
|
||||
|
||||
def _banner(ch, str)
|
||||
puts "### " + ch * 40 + " " + str
|
||||
end
|
||||
|
||||
def testfile_begin(file)
|
||||
super
|
||||
_banner "=", file if ! opts.quiet
|
||||
end
|
||||
|
||||
def testclass_begin(testcase_class)
|
||||
super
|
||||
_banner "-", testcase_class.display_name if ! opts.quiet
|
||||
end
|
||||
|
||||
def testmethod_begin(method)
|
||||
super
|
||||
_banner ".", method.to_s if ! opts.quiet
|
||||
end
|
||||
|
||||
def cmdline(cmdline_arg, comment)
|
||||
super
|
||||
|
||||
if opts.verbose
|
||||
first = comment || "..."
|
||||
puts "### %s" % [first]
|
||||
puts "### %s" % [cmdline_arg]
|
||||
else
|
||||
first = comment || cmdline_arg
|
||||
puts "### %s" % [first]
|
||||
end
|
||||
end
|
||||
|
||||
def assert_failure(str)
|
||||
super()
|
||||
puts str.gsub(/^/, "--- ")
|
||||
end
|
||||
|
||||
def assert_error(str)
|
||||
super()
|
||||
puts str.gsub(/^/, "--- ")
|
||||
end
|
||||
|
||||
def testsuite_end
|
||||
super
|
||||
if ! opts.quiet
|
||||
puts
|
||||
puts "%s %d test classes, %d test methods, %d commands, %d errors, %d fatals." % [
|
||||
n_failures == 0 && n_errors == 0 ? "###" : "---",
|
||||
n_classes, n_methods, n_commands, n_failures, n_errors
|
||||
]
|
||||
puts
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
67
lib/cmdtest/fileinfo.rb
Normal file
67
lib/cmdtest/fileinfo.rb
Normal file
@ -0,0 +1,67 @@
|
||||
#----------------------------------------------------------------------
|
||||
# fileinfo.rb
|
||||
#----------------------------------------------------------------------
|
||||
# Copyright 2002-2009 Johan Holmberg.
|
||||
#----------------------------------------------------------------------
|
||||
# This file is part of "cmdtest".
|
||||
#
|
||||
# "cmdtest" is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# "cmdtest" is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with "cmdtest". If not, see <http://www.gnu.org/licenses/>.
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
require "digest/md5"
|
||||
|
||||
module Cmdtest
|
||||
class FileInfo
|
||||
|
||||
attr_reader :stat, :digest
|
||||
|
||||
def initialize(path)
|
||||
@path = path
|
||||
@stat = File.lstat(path)
|
||||
|
||||
if @stat.file?
|
||||
md5 = Digest::MD5.new
|
||||
File.open(path) {|f| f.binmode; md5.update(f.read) }
|
||||
@digest = md5.hexdigest
|
||||
else
|
||||
@digest = "a-directory"
|
||||
end
|
||||
end
|
||||
|
||||
FILE_SUFFIXES = {
|
||||
"file" => "",
|
||||
"directory" => "/",
|
||||
"link" => "@",
|
||||
}
|
||||
|
||||
def display_path
|
||||
@path + (FILE_SUFFIXES[@stat.ftype] || "?")
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
stat = other.stat
|
||||
case
|
||||
when @stat.file? && stat.file?
|
||||
(@stat.mtime == stat.mtime &&
|
||||
@stat.ino == stat.ino &&
|
||||
@digest == other.digest)
|
||||
when @stat.directory? && stat.directory?
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
62
lib/cmdtest/fssnapshot.rb
Normal file
62
lib/cmdtest/fssnapshot.rb
Normal file
@ -0,0 +1,62 @@
|
||||
#----------------------------------------------------------------------
|
||||
# fssnapshot.rb
|
||||
#----------------------------------------------------------------------
|
||||
# Copyright 2002-2009 Johan Holmberg.
|
||||
#----------------------------------------------------------------------
|
||||
# This file is part of "cmdtest".
|
||||
#
|
||||
# "cmdtest" is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# "cmdtest" is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with "cmdtest". If not, see <http://www.gnu.org/licenses/>.
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
require "cmdtest/fileinfo"
|
||||
require "cmdtest/util"
|
||||
|
||||
require "find"
|
||||
|
||||
module Cmdtest
|
||||
class FsSnapshot
|
||||
|
||||
def initialize(dir, ignored_files)
|
||||
@dir = dir
|
||||
@ignored_files = ignored_files
|
||||
@fileinfo_by_path = {}
|
||||
Util.chdir(@dir) do
|
||||
Find.find(".") do |path|
|
||||
next if path == "."
|
||||
path.sub!(/^\.\//, "")
|
||||
file_info = FileInfo.new(path)
|
||||
display_path = file_info.display_path
|
||||
Find.prune if _ignore_file?(display_path)
|
||||
@fileinfo_by_path[display_path] = file_info
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def _ignore_file?(path)
|
||||
@ignored_files.any? {|ignored| ignored === path }
|
||||
end
|
||||
|
||||
def files
|
||||
@fileinfo_by_path.keys.sort.select do |path|
|
||||
stat = @fileinfo_by_path[path].stat
|
||||
stat.file? || stat.directory?
|
||||
end
|
||||
end
|
||||
|
||||
def fileinfo(path)
|
||||
@fileinfo_by_path[path]
|
||||
end
|
||||
|
||||
end
|
||||
end
|
173
lib/cmdtest/junitfile.rb
Normal file
173
lib/cmdtest/junitfile.rb
Normal file
@ -0,0 +1,173 @@
|
||||
#----------------------------------------------------------------------
|
||||
# junitfile.rb
|
||||
#----------------------------------------------------------------------
|
||||
# Copyright 2009 Johan Holmberg.
|
||||
#----------------------------------------------------------------------
|
||||
# This file is part of "cmdtest".
|
||||
#
|
||||
# "cmdtest" is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# "cmdtest" is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with "cmdtest". If not, see <http://www.gnu.org/licenses/>.
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
module Cmdtest
|
||||
class JunitFile
|
||||
|
||||
#----------
|
||||
|
||||
class XmlFile
|
||||
|
||||
def initialize(file)
|
||||
@file = file
|
||||
@f = File.open(file, "w")
|
||||
end
|
||||
|
||||
def put(str, args=[])
|
||||
@f.puts str % args.map {|arg| String === arg ? _quote(arg) : arg}
|
||||
end
|
||||
|
||||
def _quote(arg)
|
||||
arg.gsub(/&/, "&").gsub(/</, "<").gsub(/>/, ">")
|
||||
end
|
||||
|
||||
def close
|
||||
@f.close
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#----------
|
||||
|
||||
class Testcase
|
||||
def write(f)
|
||||
f.put ' <testcase classname="%s" name="%s"/>', [
|
||||
@classname,
|
||||
@name,
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
#----------
|
||||
|
||||
class OkTestcase < Testcase
|
||||
|
||||
def initialize(classname, name)
|
||||
@classname = classname
|
||||
@name = name
|
||||
@message = @type = @text = nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#----------
|
||||
|
||||
class ErrTestcase < Testcase
|
||||
|
||||
def initialize(classname, name, message, type, text)
|
||||
@classname = classname
|
||||
@name = name
|
||||
@message = message
|
||||
@type = type
|
||||
@text = text
|
||||
end
|
||||
|
||||
def write(f)
|
||||
f.put ' <testcase classname="%s" name="%s">', [
|
||||
@classname,
|
||||
@name,
|
||||
]
|
||||
f.put ' <failure message="%s" type="%s">%s</failure>', [
|
||||
@message,
|
||||
@type,
|
||||
@text,
|
||||
]
|
||||
f.put ' </testcase>'
|
||||
end
|
||||
end
|
||||
|
||||
#----------
|
||||
|
||||
class Testsuite
|
||||
|
||||
def initialize(package, name)
|
||||
@package = package
|
||||
@name = name
|
||||
@testcases = []
|
||||
end
|
||||
|
||||
def ok_testcase(classname, name)
|
||||
testcase = OkTestcase.new(classname, name)
|
||||
@testcases << testcase
|
||||
testcase
|
||||
end
|
||||
|
||||
def err_testcase(classname, name, message, type, text)
|
||||
testcase = ErrTestcase.new(classname, name, message, type, text)
|
||||
@testcases << testcase
|
||||
testcase
|
||||
end
|
||||
|
||||
def write(f)
|
||||
f.put ' <testsuite errors="%d" failures="%d" name="%s" package="%s">', [
|
||||
0,
|
||||
@testcases.grep(ErrTestcase).size,
|
||||
@name,
|
||||
@package,
|
||||
]
|
||||
for testcase in @testcases
|
||||
testcase.write(f)
|
||||
end
|
||||
f.put ' </testsuite>'
|
||||
end
|
||||
end
|
||||
|
||||
#----------
|
||||
|
||||
def initialize(file)
|
||||
@file = file
|
||||
@testsuites = []
|
||||
end
|
||||
|
||||
def new_testsuite(*args)
|
||||
testsuite = Testsuite.new(*args)
|
||||
@testsuites << testsuite
|
||||
testsuite
|
||||
end
|
||||
|
||||
def write
|
||||
@f = XmlFile.new(@file)
|
||||
@f.put '<?xml version="1.0" encoding="UTF-8" ?>'
|
||||
@f.put '<testsuites>'
|
||||
for testsuite in @testsuites
|
||||
testsuite.write(@f)
|
||||
end
|
||||
@f.put '</testsuites>'
|
||||
@f.close
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
if $0 == __FILE__
|
||||
jf = Cmdtest::JunitFile.new("jh.xml")
|
||||
ts = jf.new_testsuite("foo")
|
||||
ts.ok_testcase("jh.Foo", "test_a")
|
||||
ts.ok_testcase("jh.Foo", "test_b")
|
||||
|
||||
ts.err_testcase("jh.Foo", "test_c", "2 > 1", "assert", "111\n222\n333\n")
|
||||
|
||||
ts = jf.new_testsuite("bar")
|
||||
ts.ok_testcase("jh.Bar", "test_x")
|
||||
|
||||
jf.write
|
||||
end
|
||||
|
93
lib/cmdtest/junitlogger.rb
Normal file
93
lib/cmdtest/junitlogger.rb
Normal file
@ -0,0 +1,93 @@
|
||||
#----------------------------------------------------------------------
|
||||
# junitlogger.rb
|
||||
#----------------------------------------------------------------------
|
||||
# Copyright 2002-2009 Johan Holmberg.
|
||||
#----------------------------------------------------------------------
|
||||
# This file is part of "cmdtest".
|
||||
#
|
||||
# "cmdtest" is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# "cmdtest" is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with "cmdtest". If not, see <http://www.gnu.org/licenses/>.
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
require "cmdtest/baselogger"
|
||||
require "cmdtest/junitfile"
|
||||
|
||||
module Cmdtest
|
||||
class JunitLogger < BaseLogger
|
||||
|
||||
def initialize(opts, file)
|
||||
super(opts)
|
||||
@file = file
|
||||
end
|
||||
|
||||
def testsuite_begin
|
||||
@jf = JunitFile.new(@file)
|
||||
end
|
||||
|
||||
def testfile_begin(file)
|
||||
super
|
||||
end
|
||||
|
||||
def testclass_begin(testcase_class)
|
||||
super
|
||||
@testcase_class = testcase_class
|
||||
@ts = @jf.new_testsuite("CMDTEST", testcase_class.display_name)
|
||||
end
|
||||
|
||||
def testclass_end(testcase_class)
|
||||
super
|
||||
end
|
||||
|
||||
def testmethod_begin(method)
|
||||
super
|
||||
@err_assertions = []
|
||||
end
|
||||
|
||||
def testmethod_end(method)
|
||||
super
|
||||
if @err_assertions.size > 0
|
||||
message = @err_assertions[0].split(/\n/)[0]
|
||||
type = "assert"
|
||||
text = @err_assertions.join
|
||||
@ts.err_testcase(_xml_class, method, message, type, text)
|
||||
else
|
||||
@ts.ok_testcase(_xml_class, method)
|
||||
end
|
||||
end
|
||||
|
||||
def _xml_class
|
||||
"CMDTEST." + @testcase_class.display_name
|
||||
end
|
||||
|
||||
def cmdline(cmdline_arg, comment)
|
||||
super
|
||||
end
|
||||
|
||||
def assert_failure(str)
|
||||
super()
|
||||
@err_assertions << str
|
||||
end
|
||||
|
||||
def assert_error(str)
|
||||
super()
|
||||
@err_assertions << str
|
||||
end
|
||||
|
||||
def testsuite_end
|
||||
super
|
||||
@jf.write
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
515
lib/cmdtest/testcase.rb
Normal file
515
lib/cmdtest/testcase.rb
Normal file
@ -0,0 +1,515 @@
|
||||
#----------------------------------------------------------------------
|
||||
# testcase.rb
|
||||
#----------------------------------------------------------------------
|
||||
# Copyright 2002-2009 Johan Holmberg.
|
||||
#----------------------------------------------------------------------
|
||||
# This file is part of "cmdtest".
|
||||
#
|
||||
# "cmdtest" is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# "cmdtest" is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with "cmdtest". If not, see <http://www.gnu.org/licenses/>.
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
require "set"
|
||||
require "stringio"
|
||||
|
||||
module Cmdtest
|
||||
|
||||
class AssertFailed < RuntimeError ; end
|
||||
|
||||
# Base class for testcases.
|
||||
# Some attributes and methods are prefixed with an "_" to avoid
|
||||
# name collisions with user declared variables/methods.
|
||||
|
||||
class Testcase
|
||||
|
||||
@@_loaded_classes = []
|
||||
|
||||
def self.inherited(klass)
|
||||
@@_loaded_classes << klass
|
||||
end
|
||||
|
||||
def self.new_subclasses
|
||||
@@_loaded_classes = []
|
||||
yield
|
||||
@@_loaded_classes
|
||||
end
|
||||
|
||||
#------------------------------
|
||||
|
||||
def self.display_name
|
||||
to_s.sub(/^.*?::/, "")
|
||||
end
|
||||
|
||||
#------------------------------
|
||||
|
||||
def setup
|
||||
end
|
||||
|
||||
def teardown
|
||||
end
|
||||
|
||||
#------------------------------
|
||||
|
||||
attr_reader :_work_dir
|
||||
|
||||
def initialize(test_method, runner)
|
||||
@_test_method = test_method
|
||||
@_runner = runner
|
||||
@_work_dir = Workdir.new(runner)
|
||||
@_in_cmd = false
|
||||
@_comment_str = nil
|
||||
end
|
||||
|
||||
#------------------------------
|
||||
# Import file into the "workdir" from the outside world.
|
||||
# The source is found relative to the current directory when "cmdtest"
|
||||
# was invoked. The target is created inside the "workdir" relative to
|
||||
# the current directory at the time of the call.
|
||||
|
||||
def import_file(src, tgt)
|
||||
src_path = File.expand_path(src, Workdir::ORIG_CWD)
|
||||
tgt_path = tgt # rely on CWD
|
||||
FileUtils.mkdir_p(File.dirname(tgt_path))
|
||||
FileUtils.cp(src_path, tgt_path)
|
||||
end
|
||||
|
||||
#------------------------------
|
||||
# Create a file inside the "workdir".
|
||||
# The content can be specified either as an Array of lines or as
|
||||
# a string with the content of the whole file.
|
||||
# The filename is evaluated relative to the current directory at the
|
||||
# time of the call.
|
||||
|
||||
def create_file(filename, lines)
|
||||
Util.wait_for_new_second
|
||||
FileUtils.mkdir_p( File.dirname(filename) )
|
||||
File.open(filename, "w") do |f|
|
||||
case lines
|
||||
when Array
|
||||
f.puts lines
|
||||
else
|
||||
f.write lines
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#------------------------------
|
||||
# "touch" a file inside the "workdir".
|
||||
# The filename is evaluated relative to the current directory at the
|
||||
# time of the call.
|
||||
|
||||
def touch_file(filename)
|
||||
Util.wait_for_new_second
|
||||
FileUtils.touch(filename)
|
||||
end
|
||||
|
||||
#------------------------------
|
||||
# Dont count the specified file when calculating the "side effects"
|
||||
# of a command.
|
||||
|
||||
def ignore_file(file)
|
||||
@_work_dir.ignore_file(file)
|
||||
end
|
||||
|
||||
#------------------------------
|
||||
# Dont count the specified file when calculating the "side effects"
|
||||
# of a command.
|
||||
|
||||
def ignore_files(*files)
|
||||
for file in files.flatten
|
||||
@_work_dir.ignore_file(file)
|
||||
end
|
||||
end
|
||||
|
||||
#==============================
|
||||
|
||||
# Used in methods invoked from the "cmd" do-block, in methods that
|
||||
# should be executed *before* the actual command.
|
||||
def _process_before
|
||||
yield
|
||||
end
|
||||
|
||||
# Used in methods invoked from the "cmd" do-block, in methods that
|
||||
# should be executed *after* the actual command.
|
||||
def _process_after
|
||||
_delayed_run_cmd
|
||||
yield
|
||||
end
|
||||
|
||||
|
||||
def comment(str)
|
||||
_process_before do
|
||||
@_comment_str = str
|
||||
end
|
||||
end
|
||||
|
||||
#------------------------------
|
||||
|
||||
def assert(flag, msg=nil)
|
||||
_process_after do
|
||||
_assert flag do
|
||||
msg ? "assertion: #{msg}" : "assertion failed"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#------------------------------
|
||||
|
||||
def exit_zero
|
||||
_process_after do
|
||||
@_checked_status = true
|
||||
status = @_effects.exit_status
|
||||
_assert status == 0 do
|
||||
"expected zero exit status, got #{status}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#------------------------------
|
||||
|
||||
def exit_nonzero
|
||||
_process_after do
|
||||
@_checked_status = true
|
||||
status = @_effects.exit_status
|
||||
_assert status != 0 do
|
||||
"expected nonzero exit status"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#------------------------------
|
||||
|
||||
def exit_status(expected_status)
|
||||
_process_after do
|
||||
@_checked_status = true
|
||||
status = @_effects.exit_status
|
||||
_assert status == expected_status do
|
||||
"expected #{expected_status} exit status, got #{status}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#------------------------------
|
||||
#------------------------------
|
||||
|
||||
def _xxx_files(xxx, files)
|
||||
actual = @_effects.send(xxx)
|
||||
expected = files.flatten.sort
|
||||
#p [:xxx_files, xxx, actual, expected]
|
||||
_assert0 actual == expected do
|
||||
_format_output(xxx.to_s.gsub(/_/, " ").gsub(/modified/, "changed"),
|
||||
actual.inspect + "\n",
|
||||
expected.inspect + "\n")
|
||||
end
|
||||
end
|
||||
|
||||
#------------------------------
|
||||
|
||||
def created_files(*files)
|
||||
_process_after do
|
||||
_xxx_files(:created_files, files)
|
||||
@_checked_files_set << :created
|
||||
end
|
||||
end
|
||||
|
||||
#------------------------------
|
||||
|
||||
def modified_files(*files)
|
||||
_process_after do
|
||||
_xxx_files(:modified_files, files)
|
||||
@_checked_files_set << :modified
|
||||
end
|
||||
end
|
||||
|
||||
alias :changed_files :modified_files
|
||||
|
||||
#------------------------------
|
||||
|
||||
def removed_files(*files)
|
||||
_process_after do
|
||||
_xxx_files(:removed_files, files)
|
||||
@_checked_files_set << :removed
|
||||
end
|
||||
end
|
||||
|
||||
#------------------------------
|
||||
|
||||
def written_files(*files)
|
||||
_process_after do
|
||||
_xxx_files(:written_files, files)
|
||||
@_checked_files_set << :created << :modified
|
||||
end
|
||||
end
|
||||
|
||||
#------------------------------
|
||||
|
||||
def affected_files(*files)
|
||||
_process_after do
|
||||
_xxx_files(:affected_files, files)
|
||||
@_checked_files_set << :created << :modified << :removed
|
||||
end
|
||||
end
|
||||
|
||||
#------------------------------
|
||||
|
||||
def _read_file(file)
|
||||
if File.directory?(file) && RUBY_PLATFORM =~ /mswin32/
|
||||
:is_directory
|
||||
else
|
||||
File.read(file)
|
||||
end
|
||||
rescue Errno::ENOENT
|
||||
:no_such_file
|
||||
rescue Errno::EISDIR
|
||||
:is_directory
|
||||
rescue
|
||||
:other_error
|
||||
end
|
||||
|
||||
# Assert file equal to specific value.
|
||||
def file_equal(file, expected)
|
||||
_file_equal_aux(true, file, expected)
|
||||
end
|
||||
|
||||
def file_not_equal(file, expected)
|
||||
_file_equal_aux(false, file, expected)
|
||||
end
|
||||
|
||||
def _file_equal_aux(positive, file, expected)
|
||||
_process_after do
|
||||
actual = _read_file(file)
|
||||
case actual
|
||||
when :no_such_file
|
||||
_assert false do
|
||||
"no such file: '#{file}'"
|
||||
end
|
||||
when :is_directory
|
||||
_assert false do
|
||||
"is a directory: '#{file}'"
|
||||
end
|
||||
when :other_error
|
||||
_assert false do
|
||||
"error reading file: '#{file}'"
|
||||
end
|
||||
else
|
||||
_xxx_equal("file '#{file}'", positive, actual, expected)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Assert stdout equal to specific value.
|
||||
def stdout_equal(expected)
|
||||
_stdxxx_equal_aux("stdout", true, expected)
|
||||
end
|
||||
|
||||
# Assert stdout not equal to specific value.
|
||||
def stdout_not_equal(expected)
|
||||
_stdxxx_equal_aux("stdout", false, expected)
|
||||
end
|
||||
|
||||
# Assert stderr equal to specific value.
|
||||
def stderr_equal(expected)
|
||||
_stdxxx_equal_aux("stderr", true, expected)
|
||||
end
|
||||
|
||||
# Assert stderr not equal to specific value.
|
||||
def stderr_not_equal(expected)
|
||||
_stdxxx_equal_aux("stderr", false, expected)
|
||||
end
|
||||
|
||||
# helper methods
|
||||
|
||||
def _stdxxx_equal_aux(stdxxx, positive, expected)
|
||||
_process_after do
|
||||
@_checked[stdxxx] = true
|
||||
actual = @_effects.send(stdxxx)
|
||||
_xxx_equal(stdxxx, positive, actual, expected)
|
||||
end
|
||||
end
|
||||
|
||||
def _xxx_equal(xxx, positive, actual, expected)
|
||||
_assert0 _output_match(positive, actual, expected) do
|
||||
_format_output "wrong #{xxx}", actual, expected
|
||||
end
|
||||
end
|
||||
|
||||
def _output_match(positive, actual, expected)
|
||||
! positive ^ _output_match_positive(actual, expected)
|
||||
end
|
||||
|
||||
def _output_match_positive(actual, expected)
|
||||
case expected
|
||||
when String
|
||||
expected == actual
|
||||
when Regexp
|
||||
expected =~ actual
|
||||
when Array
|
||||
actual_lines = _str_as_lines(actual)
|
||||
expected_lines = _str_or_arr_as_lines(expected)
|
||||
if actual_lines.size != expected_lines.size
|
||||
return false
|
||||
end
|
||||
actual_lines.each_index do |i|
|
||||
return false if ! (expected_lines[i] === actual_lines[i])
|
||||
end
|
||||
return true
|
||||
else
|
||||
raise "error"
|
||||
end
|
||||
end
|
||||
|
||||
def _str_as_lines(str)
|
||||
lines = str.split(/\n/, -1)
|
||||
if lines[-1] == ""
|
||||
lines.pop
|
||||
elsif ! lines.empty?
|
||||
lines[-1] << "<<missing newline>>"
|
||||
end
|
||||
return lines
|
||||
end
|
||||
|
||||
def _str_or_arr_as_lines(arg)
|
||||
case arg
|
||||
when Array
|
||||
arg
|
||||
when String
|
||||
_str_as_lines(arg)
|
||||
else
|
||||
raise "unknown arg: #{arg.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
def _indented_lines(prefix, output)
|
||||
case output
|
||||
when Array
|
||||
lines = output
|
||||
when String
|
||||
lines = output.split(/\n/, -1)
|
||||
if lines[-1] == ""
|
||||
lines.pop
|
||||
elsif ! lines.empty?
|
||||
lines[-1] << "<<missing newline>>"
|
||||
end
|
||||
when Regexp
|
||||
lines = [output]
|
||||
else
|
||||
raise "unexpected arg: #{output}"
|
||||
end
|
||||
if lines == []
|
||||
lines = ["[[empty]]"]
|
||||
end
|
||||
first = true
|
||||
lines.map do |line|
|
||||
if first
|
||||
first = false
|
||||
prefix + line.to_s + "\n"
|
||||
else
|
||||
" " * prefix.size + line.to_s + "\n"
|
||||
end
|
||||
end.join("")
|
||||
end
|
||||
|
||||
def _format_output(error, actual, expected)
|
||||
res = ""
|
||||
res << "ERROR: #{error}\n"
|
||||
res << _indented_lines(" actual: ", actual)
|
||||
res << _indented_lines(" expect: ", expected)
|
||||
return res
|
||||
end
|
||||
|
||||
def _assert0(flag)
|
||||
if ! flag
|
||||
msg = yield
|
||||
@_io.puts msg
|
||||
@_nerrors += 1
|
||||
end
|
||||
end
|
||||
|
||||
def _assert(flag)
|
||||
if ! flag
|
||||
msg = yield
|
||||
@_io.puts "ERROR: " + msg
|
||||
@_nerrors += 1
|
||||
end
|
||||
end
|
||||
|
||||
#------------------------------
|
||||
|
||||
def _update_hardlinks
|
||||
return if ! @_runner.opts.fast
|
||||
|
||||
@_work_dir.chdir do
|
||||
FileUtils.mkdir_p("../hardlinks")
|
||||
Find.find(".") do |path|
|
||||
st = File.lstat(path)
|
||||
if st.file?
|
||||
inode_path = "../hardlinks/%d" % [st.ino]
|
||||
if ! File.file?(inode_path)
|
||||
FileUtils.ln(path,inode_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#------------------------------
|
||||
|
||||
def _delayed_run_cmd
|
||||
return if @_cmd_done
|
||||
@_cmd_done = true
|
||||
|
||||
@_runner.notify("cmdline", @_cmdline, @_comment_str)
|
||||
@_comment_str = nil
|
||||
@_effects = @_work_dir.run_cmd(@_cmdline)
|
||||
|
||||
@_checked_status = false
|
||||
|
||||
@_checked = {}
|
||||
@_checked["stdout"] = false
|
||||
@_checked["stderr"] = false
|
||||
|
||||
@_checked_files_set = Set.new
|
||||
|
||||
@_nerrors = 0
|
||||
@_io = StringIO.new
|
||||
end
|
||||
|
||||
#------------------------------
|
||||
|
||||
def cmd(cmdline)
|
||||
Util.wait_for_new_second
|
||||
_update_hardlinks
|
||||
@_cmdline = cmdline
|
||||
@_cmd_done = false
|
||||
|
||||
yield
|
||||
_delayed_run_cmd
|
||||
|
||||
exit_zero if ! @_checked_status
|
||||
stdout_equal "" if ! @_checked["stdout"]
|
||||
stderr_equal "" if ! @_checked["stderr"]
|
||||
|
||||
created_files [] if ! @_checked_files_set.include?( :created )
|
||||
modified_files [] if ! @_checked_files_set.include?( :modified )
|
||||
removed_files [] if ! @_checked_files_set.include?( :removed )
|
||||
|
||||
if @_nerrors > 0
|
||||
str = @_io.string
|
||||
str = str.gsub(/actual: \S+\/tmp-command\.sh/, "actual: COMMAND.sh")
|
||||
raise AssertFailed, str
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
67
lib/cmdtest/util.rb
Normal file
67
lib/cmdtest/util.rb
Normal file
@ -0,0 +1,67 @@
|
||||
#----------------------------------------------------------------------
|
||||
# util.rb
|
||||
#----------------------------------------------------------------------
|
||||
# Copyright 2002-2009 Johan Holmberg.
|
||||
#----------------------------------------------------------------------
|
||||
# This file is part of "cmdtest".
|
||||
#
|
||||
# "cmdtest" is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# "cmdtest" is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with "cmdtest". If not, see <http://www.gnu.org/licenses/>.
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
module Cmdtest
|
||||
class Util
|
||||
|
||||
TRUST_MTIME = (ENV["TRUST_MTIME"] || "1").to_i != 0
|
||||
|
||||
@@opts = nil
|
||||
|
||||
def self.opts=(opts)
|
||||
@@opts = opts
|
||||
end
|
||||
|
||||
def self._timestamp_file
|
||||
File.join(Workdir.tmp_cmdtest_dir, "TIMESTAMP")
|
||||
end
|
||||
|
||||
def self.wait_for_new_second
|
||||
return if ! TRUST_MTIME || @@opts.fast
|
||||
loop do
|
||||
File.open(_timestamp_file, "w") {|f| f.puts Time.now }
|
||||
break if File.mtime(_timestamp_file) != _newest_file_time
|
||||
sleep 0.2
|
||||
end
|
||||
end
|
||||
|
||||
def self._newest_file_time
|
||||
tnew = Time.at(0)
|
||||
Find.find(Workdir.tmp_work_dir) do |path|
|
||||
next if ! File.file?(path)
|
||||
t = File.mtime(path)
|
||||
tnew = t > tnew ? t : tnew
|
||||
end
|
||||
return tnew
|
||||
end
|
||||
|
||||
def self.chdir(dir)
|
||||
old_cwd = Dir.pwd
|
||||
Dir.chdir(dir)
|
||||
yield
|
||||
ensure
|
||||
if Dir.pwd != old_cwd
|
||||
Dir.chdir(old_cwd)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
120
lib/cmdtest/workdir.rb
Normal file
120
lib/cmdtest/workdir.rb
Normal file
@ -0,0 +1,120 @@
|
||||
#----------------------------------------------------------------------
|
||||
# workdir.rb
|
||||
#----------------------------------------------------------------------
|
||||
# Copyright 2002-2009 Johan Holmberg.
|
||||
#----------------------------------------------------------------------
|
||||
# This file is part of "cmdtest".
|
||||
#
|
||||
# "cmdtest" is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# "cmdtest" is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with "cmdtest". If not, see <http://www.gnu.org/licenses/>.
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
require "cmdtest/fssnapshot"
|
||||
require "cmdtest/cmdeffects"
|
||||
|
||||
require "fileutils"
|
||||
|
||||
module Cmdtest
|
||||
class Workdir
|
||||
|
||||
ORIG_CWD = Dir.pwd
|
||||
|
||||
def self.tmp_cmdtest_dir
|
||||
File.join(ORIG_CWD, "tmp-cmdtest-%d" % [$cmdtest_level])
|
||||
end
|
||||
|
||||
def self.tmp_work_dir
|
||||
File.join(tmp_cmdtest_dir, "workdir")
|
||||
end
|
||||
|
||||
def initialize(runner)
|
||||
@runner = runner
|
||||
@dir = Workdir.tmp_work_dir
|
||||
hardlinkdir = File.join(Workdir.tmp_cmdtest_dir, "hardlinks")
|
||||
FileUtils.rm_rf(@dir)
|
||||
FileUtils.rm_rf(hardlinkdir)
|
||||
FileUtils.mkdir_p(@dir)
|
||||
@ignored_files = []
|
||||
end
|
||||
|
||||
#--------------------
|
||||
# called by user (indirectly)
|
||||
|
||||
def ignore_file(file)
|
||||
@ignored_files << file
|
||||
end
|
||||
|
||||
#--------------------
|
||||
|
||||
def chdir(&block)
|
||||
Util.chdir(@dir, &block)
|
||||
end
|
||||
|
||||
def _take_snapshot
|
||||
FsSnapshot.new(@dir, @ignored_files)
|
||||
end
|
||||
|
||||
def _windows
|
||||
RUBY_PLATFORM =~ /mswin32/
|
||||
end
|
||||
|
||||
def _shell
|
||||
_windows ? "cmd /Q /c" : "sh"
|
||||
end
|
||||
|
||||
def _tmp_command_sh
|
||||
File.join(Workdir.tmp_cmdtest_dir,
|
||||
_windows ? "tmp-command.bat" : "tmp-command.sh")
|
||||
end
|
||||
|
||||
def _tmp_stdout_log
|
||||
File.join(Workdir.tmp_cmdtest_dir, "tmp-stdout.log")
|
||||
end
|
||||
|
||||
def _tmp_stderr_log
|
||||
File.join(Workdir.tmp_cmdtest_dir, "tmp-stderr.log")
|
||||
end
|
||||
|
||||
def _ruby_S(cmdline)
|
||||
if @runner.opts.ruby_s
|
||||
if cmdline =~ /ruby/
|
||||
cmdline
|
||||
else
|
||||
cmdline.gsub(/\b(\w+\.rb)\b/, 'ruby -S \1')
|
||||
end
|
||||
else
|
||||
cmdline
|
||||
end
|
||||
end
|
||||
|
||||
def run_cmd(cmdline)
|
||||
File.open(_tmp_command_sh, "w") do |f|
|
||||
f.puts _ruby_S(cmdline)
|
||||
end
|
||||
str = "%s %s > %s 2> %s" % [
|
||||
_shell,
|
||||
_tmp_command_sh,
|
||||
_tmp_stdout_log,
|
||||
_tmp_stderr_log,
|
||||
]
|
||||
before = _take_snapshot
|
||||
ok = system(str)
|
||||
after = _take_snapshot
|
||||
CmdEffects.new($?,
|
||||
File.read(_tmp_stdout_log),
|
||||
File.read(_tmp_stderr_log),
|
||||
before, after)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user