Path: blob/main/tools/regression/usr.bin/env/regress-sb.rb
105686 views
#!/usr/local/bin/ruby1# -------+---------+---------+-------- + --------+---------+---------+---------+2# Copyright (c) 2005 - Garance Alistair Drosehn <[email protected]>.3# All rights reserved.4#5# Redistribution and use in source and binary forms, with or without6# modification, are permitted provided that the following conditions7# are met:8# 1. Redistributions of source code must retain the above copyright9# notice, this list of conditions and the following disclaimer.10# 2. Redistributions in binary form must reproduce the above copyright11# notice, this list of conditions and the following disclaimer in the12# documentation and/or other materials provided with the distribution.13#14# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND15# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE16# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE17# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE18# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL19# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS20# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)21# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT22# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY23# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF24# SUCH DAMAGE.25# -------+---------+---------+-------- + --------+---------+---------+---------+26# -------+---------+---------+-------- + --------+---------+---------+---------+27# This script was written to provide a battery of regression-tests for some28# changes I am making to the `env' command. I wrote a new script for this29# for several reasons. 1) I needed to test all kinds of special-character30# combinations, and I wanted to be able to type those in exactly as they would31# would be in real-life situations. 2) I wanted to set environment variables32# before executing a test, 3) I had many different details to test, so I wanted33# to write up dozens of tests, without needing to create a hundred separate34# little tiny files, 4) I wanted to test *failure* conditions, where I expected35# the test would fail but I wanted to be sure that it failed the way I intended36# it to fail.37# This script was written for the special "shebang-line" testing that I38# wanted for my changes to `env', but I expect it could be turned into a39# general-purpose test-suite with a little more work.40# Garance/June 12/200541# -------+---------+---------+-------- + --------+---------+---------+---------+424344# -------+---------+---------+-------- + --------+---------+---------+---------+45class ExpectedResult46attr_writer :cmdvalue, :shebang_args, :user_args47@@gbl_envs = Hash.new4849def ExpectedResult.add_gblenv(avar, avalue)50@@gbl_envs[avar] = avalue51end5253def initialize54@shebang_args = ""55@cmdvalue = 056@clear_envs = Hash.new57@new_envs = Hash.new58@old_envs = Hash.new59@script_lines = ""60@expect_err = Array.new61@expect_out = Array.new62@symlinks = Array.new63@user_args = nil64end6566def add_expecterr(aline)67@expect_err << aline68end6970def add_expectout(aline)71@expect_out << aline72end7374def add_script(aline)75@script_lines += aline76@script_lines += "\n" if aline[-1] != "\n"77end7879def add_clearenv(avar)80@clear_envs[avar] = true81end8283def add_setenv(avar, avalue)84@new_envs[avar] = avalue85end8687def add_symlink(srcf, newf)88@symlinks << Array.[](srcf, newf)89end9091def check_out(name, fname, expect_arr)92idx = -193all_matched = true94extra_lines = 095rdata = File.open(fname)96rdata.each_line { |rline|97rline.chomp!98idx += 199if idx > expect_arr.length - 1100if extra_lines == 0 and $verbose >= 1101printf "-- Extra line(s) on %s:\n", name102end103printf "-- [%d] > %s\n", idx, rline if $verbose >= 1104extra_lines += 1105elsif rline != expect_arr[idx]106if all_matched and $verbose >= 1107printf "-- Mismatched line(s) on %s:\n", name108end109printf "-- [%d] < %s\n", idx, expect_arr[idx] if $verbose >= 2110printf "-- > %s\n", rline if $verbose >= 1111all_matched = false112else113printf "-- %s[%d] = %s\n", name, idx, rline if $verbose >= 5114end115}116rdata.close117if extra_lines > 0118printf "-- %d extra line(s) found on %s\n", extra_lines,119name if $verbose == 0120return false121end122if not all_matched123printf "-- Mismatched line(s) found on %s\n",124name if $verbose == 0125return false126end127return true128end129130def create_links131@symlinks.each { |fnames|132if $verbose >= 2133printf "-- Creating: symlink %s %s\n", fnames[0], fnames[1]134end135symres = File.symlink(fnames[0], fnames[1])136return false if symres == nil137return false unless File.symlink?(fnames[1])138}139return true140end141142def destroy_links143@symlinks.each { |fnames|144if $verbose >= 2145printf "-- Removing: %s (symlink)\n", fnames[1]146end147if File.symlink?(fnames[1])148if File.delete(fnames[1]) != 1149$stderr.printf "Warning: problem removing symlink '%s'\n",150fnames[1]151end152else153$stderr.printf "Warning: Symlink '%s' does not exist?!?\n",154fnames[1]155end156}157return true158end159160def init_io_files161@stderr = $scriptfile + ".stderr"162@stdout = $scriptfile + ".stdout"163File.delete(@stderr) if File.exists?(@stderr)164File.delete(@stdout) if File.exists?(@stdout)165@stdin = "/dev/null"166167@redirs = " <" + @stdin168@redirs += " >" + @stdout169@redirs += " 2>" + @stderr170171end172173def pop_envs174@new_envs.each_key { |evar|175if @old_envs.has_key?(evar)176ENV[evar] = @old_envs[evar]177else178ENV.delete(evar)179end180}181end182183def push_envs184@@gbl_envs.each_pair { |evar, eval|185ENV[evar] = eval186}187@new_envs.each_pair { |evar, eval|188if ENV.has_key?(evar)189@old_envs[evar] = ENV[evar]190end191ENV[evar] = eval192}193end194195def run_test196tscript = File.open($scriptfile, "w")197tscript.printf "#!%s", $testpgm198tscript.printf " %s", @shebang_args if @shebang_args != ""199tscript.printf "\n"200tscript.printf "%s", @script_lines if @script_lines != ""201tscript.close202File.chmod(0755, $scriptfile)203204usercmd = $scriptfile205usercmd += " " + @user_args if @user_args != nil206init_io_files207208push_envs209return 0 unless create_links210printf "- Executing: %s\n", usercmd if $verbose >= 1211printf "----- with: %s\n", @redirs if $verbose >= 6212sys_ok = system(usercmd + @redirs)213if sys_ok214@sav_cmdvalue = 0215elsif $?.exited?216@sav_cmdvalue = $?.exitstatus217else218@sav_cmdvalue = 125219end220destroy_links221pop_envs222sys_ok = true223if @sav_cmdvalue != @cmdvalue224printf "-- Expecting cmdvalue of %d, but $? == %d\n", @cmdvalue,225@sav_cmdvalue226sys_ok = false227end228sys_ok = false unless check_out("stdout", @stdout, @expect_out)229sys_ok = false unless check_out("stderr", @stderr, @expect_err)230return 1 if sys_ok231return 0232end233end234235# -------+---------+---------+-------- + --------+---------+---------+---------+236# Processing of the command-line options given to the regress-sb.rb script.237#238class CommandOptions239def CommandOptions.parse(command_args)240parse_ok = true241command_args.each { |userarg|242case userarg243when /^--rgdata=(\S+)$/244parse_ok = false unless set_rgdatafile($1)245when /^--testpgm=(\S+)$/246parse_ok = false unless set_testpgm($1)247$cmdopt_testpgm = $testpgm248when "--stop-on-error", "--stop_on_error"249$stop_on_error = true250when /^--/251$stderr.printf "Error: Invalid long option: %s\n", userarg252parse_ok = false253when /^-/254userarg = userarg[1...userarg.length]255userarg.each_byte { |byte|256char = byte.chr257case char258when "v"259$verbose += 1260else261$stderr.printf "Error: Invalid short option: -%s\n", char262parse_ok = false263end264}265else266$stderr.printf "Error: Invalid request: %s\n", userarg267parse_ok = false268end269}270if $rgdatafile == nil271rgmatch = Dir.glob("regress*.rgdata")272if rgmatch.length == 1273$rgdatafile = rgmatch[0]274printf "Assuming --rgdata=%s\n", $rgdatafile275else276$stderr.printf "Error: The --rgdata file was not specified\n"277parse_ok = false278end279end280return parse_ok281end282283def CommandOptions.set_rgdatafile(fname)284if not File.exists?(fname)285$stderr.printf "Error: Rgdata file '%s' does not exist\n", fname286return false287elsif not File.readable?(fname)288$stderr.printf "Error: Rgdata file '%s' is not readable\n", fname289return false290end291$rgdatafile = File.expand_path(fname)292return true293end294295def CommandOptions.set_testpgm(fname)296if not File.exists?(fname)297$stderr.printf "Error: Testpgm file '%s' does not exist\n", fname298return false299elsif not File.executable?(fname)300$stderr.printf "Error: Testpgm file '%s' is not executable\n", fname301return false302end303$testpgm = File.expand_path(fname)304return true305end306end307308# -------+---------+---------+-------- + --------+---------+---------+---------+309# Processing of the test-specific options specifed in each [test]/[run]310# section of the regression-data file. This will set values in the311# global $testdata object.312#313class RGTestOptions314@@rgtest_opts = nil;315316def RGTestOptions.init_rgtopts317@@rgtest_opts = Hash.new318@@rgtest_opts["$?"] = true319@@rgtest_opts["clearenv"] = true320@@rgtest_opts["sb_args"] = true321@@rgtest_opts["script"] = true322@@rgtest_opts["setenv"] = true323@@rgtest_opts["stderr"] = true324@@rgtest_opts["stdout"] = true325@@rgtest_opts["symlink"] = true326@@rgtest_opts["user_args"] = true327end328329def RGTestOptions.parse(optname, optval)330init_rgtopts unless @@rgtest_opts331332if not @@rgtest_opts.has_key?(optname)333$stderr.printf "Error: Invalid test-option in rgdata file: %s\n",334optname335return false336end337338# Support a few very specific substitutions in values specified339# for test data. Format of all recognized values should be:340# [%-object.value-%]341# which is hopefully distinctive-enough that they will never342# conflict with any naturally-occurring string. Also note that343# we only match the specific values that we recognize, and not344# "just anything" that matches the general pattern. There are345# no blanks in the recognized values, but I use an x-tended346# regexp and then add blanks to make it more readable.347optval.gsub!(/\[%- testpgm\.pathname -%\]/x, $testpgm)348optval.gsub!(/\[%- testpgm\.basename -%\]/x, File.basename($testpgm))349optval.gsub!(/\[%- script\.pathname -%\]/x, $scriptfile)350351invalid_value = false352case optname353when "$?"354if optval =~ /^\d+$/355$testdata.cmdvalue = optval.to_i356else357invalid_value = true358end359when "clearenv"360if optval =~ /^\s*([A-Za-z]\w*)\s*$/361$testdata.add_clearenv($1)362else363invalid_value = true364end365when "sb_args"366$testdata.shebang_args = optval367when "script"368$testdata.add_script(optval)369when "setenv"370if optval =~ /^\s*([A-Za-z]\w*)=(.*)$/371$testdata.add_setenv($1, $2)372else373invalid_value = true374end375when "stderr"376$testdata.add_expecterr(optval)377when "stdout"378$testdata.add_expectout(optval)379when "symlink"380if optval =~ /^\s*(\S+)\s+(\S+)\s*$/381srcfile = $1382newfile = $2383if not File.exists?(srcfile)384$stderr.printf "Error: source file '%s' does not exist.\n",385srcfile386invalid_value = true387elsif File.exists?(newfile)388$stderr.printf "Error: new file '%s' already exists.\n",389newfile390invalid_value = true391else392$testdata.add_symlink(srcfile, newfile)393end394else395invalid_value = true396end397when "user_args"398$testdata.user_args = optval399else400$stderr.printf "InternalError: Invalid test-option in rgdata file: %s\n",401optname402return false403end404405if invalid_value406$stderr.printf "Error: Invalid value(s) for %s: %s\n",407optname, optval408return false409end410return true411end412end413414# -------+---------+---------+-------- + --------+---------+---------+---------+415# Here's where the "main" routine begins...416#417418$cmdopt_testpgm = nil419$testpgm = nil420$rgdatafile = nil421$scriptfile = "/tmp/env-regress"422$stop_on_error = false423$verbose = 0424425exit 1 unless CommandOptions.parse(ARGV)426427errline = nil428test_count = 0429testok_count = 0430test_lineno = -1431max_test = -1432regress_data = File.open($rgdatafile)433regress_data.each_line { |dline|434case dline435when /^\s*#/, /^\s*$/436# Just a comment line, ignore it.437when /^\s*gblenv=\s*(.+)$/438if test_lineno > 0439$stderr.printf "Error: Cannot define a global-value in the middle of a test (#5d)\n", test_lineno440errline = regress_data.lineno441break;442end443tempval = $1444if tempval !~ /^([A-Za-z]\w*)=(.*)$/445$stderr.printf "Error: Invalid value for 'gblenv=' request: %s\n",446tempval447errline = regress_data.lineno448break;449end450ExpectedResult.add_gblenv($1, $2)451when /^testpgm=\s*(\S+)\s*/452# Set the location of the program to be tested, if it wasn't set453# on the command-line processing.454if $cmdopt_testpgm == nil455if not CommandOptions.set_testpgm($1)456errline = regress_data.lineno457break;458end459end460when /^\[test\]$/461if test_lineno > 0462$stderr.printf "Error: Request to define a [test], but we are still defining\n"463$stderr.printf " the [test] at line #%s\n", test_lineno464errline = regress_data.lineno465break;466end467test_lineno = regress_data.lineno468max_test = test_lineno469printf "- Defining test at line #%s\n", test_lineno if $verbose >= 6470$testdata = ExpectedResult.new471when /^\[end\]$/472# User wants us to ignore the remainder of the rgdata file...473break;474when /^\[run\]$/475if test_lineno < 0476$stderr.printf "Error: Request to [run] a test, but no test is presently defined\n"477errline = regress_data.lineno478break;479end480printf "- Running test at line #%s\n", test_lineno if $verbose >= 1481run_result = $testdata.run_test482test_count += 1483printf "[Test #%3d: ", test_count484case run_result485when 0486# Test failed487printf "Failed! (line %4d)]\n", test_lineno488break if $stop_on_error489when 1490# Test ran as expected491testok_count += 1492printf "OK]\n"493else494# Internal error of some sort495printf "InternalError! (line %4d)]\n", test_lineno496errline = regress_data.lineno497break;498end499test_lineno = -1500501when /^(\s*)([^\s:]+)\s*:(.+)$/502blankpfx = $1503test_lhs = $2504test_rhs = $3505if test_lineno < 0506$stderr.printf "Error: No test is presently being defined\n"507errline = regress_data.lineno508break;509end510# All the real work happens in RGTestOptions.parse511if not RGTestOptions.parse(test_lhs, test_rhs)512errline = regress_data.lineno513break;514end515if blankpfx.length == 0516$stderr.printf "Note: You should at least one blank before:%s\n",517dline.chomp518$stderr.printf " at line %d of rgdata file %s\n",519regress_data.lineno, $rgdatafile520end521522else523$stderr.printf "Error: Invalid line: %s\n", dline.chomp524errline = regress_data.lineno525break;526end527}528regress_data.close529if errline != nil530$stderr.printf " at line %d of rgdata file %s\n", errline, $rgdatafile531exit 2532end533if testok_count != test_count534printf "%d of %d tests were successful.\n", testok_count, test_count535exit 1536end537538printf "All %d tests were successful!\n", testok_count539exit 0540541542