From: Bil Kleb Date: 2002-03-28T13:26:48+09:00 Subject: Test::Unit GUI's fail for IO.popen? I have come across a test case which breaks both GUI TestRunners, but not the Console TestRunner. The trouble appears to be the use of an IO.pipe to capture output for testing. The Fox runner will come up, but as soon as I hit the "run" button it dies and I am left with, X Fatal error. Aborted The Gtk runner begins running immediately(?!) and fails with Gdk-ERROR **: X connection to :0.0 broken (explicit kill or server shutdown). I checked .xsession-errors and tried running in debug mode, but neither provided any further diagnostics. The following is a sample mobility test which demonstrates the failure -- at least for my ruby-1.6.7/testunit-CVS/gtk-0.27/fxruby-1.0.3/i686-linux combination. The test file will invoke the Console runner (via unit.rb's at_exit trick) unless you pass it a command line argument, e.g., "f" or "g", require 'test/unit' require 'CmdLineOpts' class CmdLineOptsMT < Test::Unit::TestCase def testUsage optionParser = CmdLineOpts.instance optionParser.addOption('unchangedParm', nil, -5, true){ |arg| arg.to_f } optionParser.processOptions inPipe,outPipe = IO.pipe if fork # parent process catches stdout from child outPipe.close usage = inPipe.read inPipe.close Process.wait else # child process throws usage to parent inPipe.close $stdout = outPipe optionParser.usage outPipe.close exit 0 end assert(usage.include? " -h|--help\n") assert(usage.include? "--unchangedParm [-5]\n") end end if $0 == __FILE__ then case ARGV[0] when /^G/i require 'test/unit/ui/gtk/testrunner' Test::Unit::UI::GTK::TestRunner.run CmdLineOptsMT when /^F/i require 'test/unit/ui/fox/testrunner' Test::Unit::UI::Fox::TestRunner.run CmdLineOptsMT end end The code under test is merely a convenient wrapper for getoptlong inspired by the Pragmatic Programmer book, viz,  # In the spirit of "once and only once" we make a wrapper # to GetoptLong so that we only need to define options # in one spot. The wrapper takes care of creating accessors, # setting default values, passing the options array to GetoptLong, # and creates a usage method. -- Bill Wood and Bil Kleb require 'singleton' require 'getoptlong' class CmdLineOpts < Hash include Singleton def initialize super addOption('help', 'h'){CmdLineOpts.instance.usage; exit 1} end def addOption(longName, shortName=nil, defaultValue=nil, needsArgument=false, &action) store longName, Option.new(longName, shortName, defaultValue, needsArgument, action) end def processOptions optionArray = Array.new each_value { |opt| optionArray.push opt.to_a } GetoptLong.new(*optionArray).each do |foundOption, arg| each{ |name, opt| opt.execute(arg) if foundOption.include? name } end end def usage puts "Usage: #{File.basename $0}" sort.each{ |key,opt| puts opt } end def [] (key) super(key).value end def []= (key, object) fetch(key).value = object end private class Option attr_reader :shortName attr_accessor :value def initialize( longName, shortName, defaultValue, needsArgument, action ) @longName, @shortName = longName, shortName @needsArgument, @action = needsArgument, action @value = defaultValue end def execute(arg=nil) @value = @action.call arg end def to_s string = " " string += (@shortName ? "-#@shortName|" : " ") + "--#@longName" string += " [#{@value}]" if @value string end def to_a a = ['--'+@longName] a.push '-'+@shortName if @shortName a.push @needsArgument ? GetoptLong::REQUIRED_ARGUMENT : GetoptLong::NO_ARGUMENT end end end -- Bil Kleb NASA Langley Research Center Hampton, Virginia, USA