views:

38

answers:

3

Can I replace an executable (accessed via a system call from ruby) with an executable that expects certain input and supplies the expected output in a consistent amount of time? I'm mainly operating on Mac OSX 10.6 (Snow Leopard), but I also have access to Linux and Windows. I'm using MRI ruby 1.8.7.

Background: I'm looking at doing several DNA sequence alignments, one in each thread. When I try using BioRuby for this, either BioRuby or ruby's standard library's tempfile sometimes raise exceptions (which is better than failing silently!).

I set up a test that reproduces the problem, but only some of the time. I assume the main sources of variability between tests are the threading, the tempfile system, and the executable used for alignment (ClustalW). Since ClustalW probably isn't malfunctioning, but can be a source of variability, I'm thinking that eliminating it may aid reproducibility.

For those thinking select isn't broken - that's what I'm wondering too. However, according to the changelog, there was concern about tempfile's thread safety in August 2009. Also, I've checked on the BioRuby mailing list whether I'm calling the BioRuby code correctly, and that seems to be the case.

+1  A: 

I really don't understand what the problem is or what exactly are you after, can't you just write something like

#!/bin/sh
#Test for input (syntax might be wrong, but you get the idea)
if [ $* ne "expected input" ]; then
    echo "expected output for failure"
    exit -1
fi
#have it work in a consistent amount of time 
$CONSISTENT_AMOUNT_OF_TIME = 20
sleep $CONSISTENT_AMOUNT_OF_TIME
echo "expected output"
Vinko Vrsalovic
I tried something like that, and it looked like the tests were hanging, and therefore I was doing something wrong. But I've had another look, and found that it was just printing stuff even after I'd reached the unix prompt.
Andrew Grimm
+1  A: 

You can. In cases where I'm writing a functional test for program A, I may need to "mock" a program, B, that A runs via system. What I do then is to make program B's pathname configurable, with a default:

class ProgramA

  def initialize(argv)
    @args = ParseArgs(argv)
    @config = Config.new(@args.config_path || default_config_path)
  end

  def run
    command = [
      program_b_path,
      '--verbose',
      '--do_something_wonderful',
    ].join(' ')
    system(command)
    ...
  end

  def program_a_path
    @config.fetch('program_b_path', default_program_b_path)
  end

end

Program A takes a switch, "--config PATH", which can override the default config file path. The test sets up a configuration file in /tmp:

program_b_path: /home/wayne/project/tests/mock_program_b.rb

And passes to program A that configuration file:

program_a.rb --config /tmp/config.yaml

Now program A will run not the real program B, but the mock one.

Wayne Conrad
Thankfully BioRuby allows you to change the name of the executable (you can pass it in as an option to `Bio::ClustalW.new()`). I've even experimented with the executable's name being `false` (the unix command, not the boolean)
Andrew Grimm
A: 

Have you tried the Mocha gem? It's used a lot for testing, and you describe it perfectly. It "fakes" the method call of an object (which includes just about anything in ruby), and returns the result you want without actually running the method. Take this example file:

# test.rb
require 'rubygems'
require 'mocha'

self.stubs(:system).with('ls').returns('monkey')

puts system('ls')

Running this script outputs "monkey" because I stubbed out the system call. You can use this to bypass parts of an application you don't want test, to factor out irrelevant parts.

Jaime Bellmyer