#!/usr/local/bin/ruby #---------------------------------------------------------------------- # run-regression.rb #---------------------------------------------------------------------- # Copyright (c) 2007-2012 by Johan Holmberg. All rights reserved. #---------------------------------------------------------------------- # @(#) $Id$ #---------------------------------------------------------------------- require "rbconfig" require "dbm" require "digest/md5" #---------------------------------------------------------------------- TMPFILES = [ "tmp-cmdtest-2", "tmp-cmdtest-2/tmp-command.sh", "tmp-cmdtest-2/tmp-stderr.log", "tmp-cmdtest-2/tmp-stdout.log", "tmp-cmdtest-2/workdir", "tmp-cmdtest-2/workdir/TIMESTAMP" ] class ActualTcRegression def initialize @count = 0 @f = File.open("TC_actual-regression.rb", "w") @f.puts [ "class TC_cmdtest < Cmdtest::Testcase", "", " def setup", TMPFILES.map {|file| " ignore_file #{file.inspect}" }, " end", "", ] end def _list_elements(level, lines) lines.map do |line| " " * level + line.inspect + "," end end def _indent(level, lines) lines.map do |line| " " * level + line end end def _gen_count @count += 1 end def _comment_str(lines) lines.each do |line| if line =~ /^#/ # .. res = line.sub(/^#\s*/, "") return res end end return "no comment given" end def add(prefix, code, stdout) @f.puts [ " def test_#{_gen_count}", " create_file \"TC_tmp.rb\", [", " 'class TC_cmdtest < Cmdtest::Testcase',", " ' def test_foo',", _list_elements(3, _indent(2, prefix)), _list_elements(3, _indent(2, code)), " ' end',", " 'end',", " ]", "", " cmd 'cmdtest.rb --quiet' do", " comment #{_comment_str(code).inspect}", " stdout_equal [", _list_elements(4, stdout), " ]", " end", " end", "", ].flatten end def write @f.puts [ "", "end", "", ] @f.close end end #---------------------------------------------------------------------- class ActualRegression def initialize @o2 = ActualTcRegression.new @f = File.open("actual-regression.rb", "w") end def add(prefix, code, stdout) prefix = prefix.map {|line| line.chomp } code = code .map {|line| line.chomp } stdout = stdout.map {|line| line.to_s.chomp } @o2.add(prefix,code,stdout) @f.puts prefix @f.puts "#" + "-" * 35 @f.puts code @f.puts "# stdout begin" @f.puts stdout.map {|line| "# %s" % [line] } @f.puts "# stdout end" end def write @o2.write @f.close end end #---------------------------------------------------------------------- class RegressionData def initialize(files) @lines = files.map {|file| File.readlines(file) }.flatten @i = 0 end def eof? j = @i while j < @lines.size && @lines[j].strip.empty? j += 1 end return j >= @lines.size end def skip_to(pattern) # ignore return value get_to(pattern) end def _show_line(i) puts "%d: %s" % [ i, @lines[i], ] end def get_to(pattern) res = [] i1 = @i while ! eof? && @lines[@i] !~ pattern _show_line(@i) if $opt_verbose res << @lines[@i] @i += 1 end if eof? puts "Error: looking at:" _show_line(i1) raise "eof looking for #{pattern}" end # get past matching line @i += 1 return res end end #---------------------------------------------------------------------- SystemResult = Struct.new(:status, :stdout, :stderr) def my_system(cmd) full_cmd = "#{cmd} > tmp-stdout 2> tmp-stderr" #puts "+ #{full_cmd}" ok = system full_cmd status = $?.exitstatus stdout = File.readlines("tmp-stdout") stderr = File.readlines("tmp-stderr") if status != 0 puts "INTERNAL ERROR:" puts "STDOUT:" puts stdout puts "STDERR:" puts stderr exit 1 end #p [:my_system, cmd, status, stdout, stderr] return SystemResult.new(status, stdout, stderr) end #---------------------------------------------------------------------- def indented(lines) lines.map {|line| " " + line.to_s } end #---------------------------------------------------------------------- def indent_lines(level, lines) lines.map do |line| " " * level + line end end #---------------------------------------------------------------------- def matching_arrays(a,b) return false if a.size != b.size a.each_index do |i| return false unless a[i] === b[i] end return true end #---------------------------------------------------------------------- $opt_verbose = false $opt_only = nil $opt_remember = false while /^-/ =~ ARGV[0] arg = ARGV.shift case arg when "-v" $opt_verbose = true when "-r" $opt_remember = true when /^--only=(\d+)$/ $opt_only = $1.to_i else puts "Error: unknown option: #{arg}" exit 1 end end path_dirs = [ File.expand_path("t/bin") ] if RUBY_PLATFORM =~ /mswin/ path_dirs.unshift File.expand_path("t/bin/windows") else path_dirs.unshift File.expand_path("t/bin/unix") end ENV["PATH"] = [ path_dirs, ENV["PATH"] ].join(Config::CONFIG["PATH_SEPARATOR"]) files = ARGV.empty? ? Dir.glob("t/*.rb") : ARGV rd = RegressionData.new(files) tests = [] while ! rd.eof? prefix = rd.get_to( /^#----------/ ) code = rd.get_to( /^# stdout begin/ ) stdout = rd.get_to( /^# stdout end/ ).map do |line| if line =~ /^# (.*\n)/ #.. $1 elsif line =~ /^#\/(.*)/ #.. Regexp.new($1) else line # should not occur end end tests << [prefix, code, stdout] end puts "###" puts "### found %d tests in file." % [tests.size] puts "###" act = ActualRegression.new already_ok = DBM.open("already_ok") if ! $opt_remember already_ok.clear end errors = 0 iii = -1 for prefix, code, stdout in tests iii += 1 next if $opt_only && iii != $opt_only digest = Digest::MD5.hexdigest([prefix,code,stdout].flatten.join) if already_ok.has_key?(digest) #puts "### #{iii}: %s ... [[cahced]]" % [code[0].chomp] next end code_str = code.join("\n") if code_str =~ /REQUIRE: \s+ (.*)/x expr = $1 if ! eval(expr) puts "### #{iii}: SKIP: %s: %s ..." % [expr, code[0].chomp] act.add(prefix, code, stdout) next end end puts "### #{iii}: %s ..." % [code[0].chomp] lines = [] #lines << "require 'Test/Cmd'" lines << "" lines << "class TC_foo < Cmdtest::Testcase" lines << "" lines << " def test_foo" lines << indent_lines(2, code) lines << " end" lines << "" lines << "end" lines << "" lines.flatten! File.open("CMDTEST_tmp_regression.rb", "w") do |f| f.puts lines end res = my_system "ruby -w bin/cmdtest.rb --no-exit-code --quiet --ruby_s CMDTEST_tmp_regression.rb" if res.status != 0 puts "ERROR: non-zero exit from:" puts lines exit 1 end if matching_arrays(stdout, res.stdout) already_ok[digest] = true else puts "ERROR: unexpected STDOUT:" puts "ACTUAL:" puts indented(res.stdout) puts "EXPECTED:" puts indented(stdout) errors += 1 end act.add(prefix, code, res.stdout) end act.write puts if errors == 0 puts "### all tests OK" else puts "--- error in #{errors} tests" end puts