Writing A Wrapper Tool For A JAR
April 15, 2013
I am currently working on a project where I had to write a wrapper for a Java JAR. Since I was trying to compare the results of two different automated test generation tools, I needed them to behave in a similar fashion. EvoSuite, despite what the documentation claims, creates tests for each class in a JAR using the specified maximum time as the maximum time for each class. On the other hand Randoop creates tests for the entire JAR stopping when the maximum time is reached. I needed Randoop to behave like EvoSuite and use the maximum amount of time per class. In order to achieve this goal, I used jRuby to write a wrapper around Randoop for the specific call that I would be making to it.The first thing you need to do is set up your project in the proper way.
/bin
--randoop (this is the file that gets user input)
/lib
--all the classes that the bin file uses
This is my bin/randoop file:
#!/home/brett/.rvm/rubies/jruby-1.7.3/bin/jruby
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
require 'Randoop'
require 'trollop'
p = Trollop::Parser.new do
version "Randoop 1.3.3"
banner <<-EOS
Randoop will automatically generate a jUnit test suite for a given jar.
It uses a random algorithm with a unique heuristic to prune the inputs
so that coverage is improved over completely random test input generators.
randoop <jarfile> [options]
where [options] are:
EOS
opt :classlist, "Location of a file that contains the list of Java classes", :short => 'c', :type => :string, :default => nil
opt :maxtime, "Maximum amount of time each class is given to random generate tests", :short => 'm', :type => :integer, :default => 60
opt :nocleanup, "Don't clean up temporary files", :type => :flag
opt :jdk_jar, "Path to Oracle's JDK 'jar' executable", :type => :string
opt :output_dir, "Specify the output directory", :type => :string
end
opts = Trollop::with_standard_exception_handling p do
raise Trollop::HelpNeeded if ARGV.empty?
p.parse ARGV
end
#additional errros
Trollop::with_standard_exception_handling p do
raise Trollop::CommandlineError, "File '#{ARGV[0]}' does not exist" unless File.exist?(ARGV[0])
raise Trollop::CommandlineError, "Directory '#{opts[:output_dir]}' does not exist" unless !opts[:output_dir] || Dir.exist?(opts[:output_dir])
end
randoop = Randoop::Main.new(ARGV[0], opts)
randoop.start
randoop.cleanup unless opts[:nocleanup]
Side note: I really recommend trollop as an easy to use tool to specify usage and get command line parameters. It makes things easy.
After you have everything created and ready to go, you can use the warbler gem to create an executable jar out of your project. In the project root just type
$ warble jarand the tool should figure everything out for you.
So all of that was fairly easy so far. I ran into trouble when I wanted to package the jar that I was wrapping and call it from inside jRuby. In order to run the command on the internal Randoop JAR, I had to first extract it and then run the command. The only way I could figure out how to do it was to use Java. This is the jRuby code I wrote to accomplish extracting the original Randoop JAR.
BUFFER = 2048
def extract_randoop
require 'java'
randoop_path = __FILE__.gsub('jar:file:', '').split('!')[0]
zis = java.util.zip.ZipInputStream.new(java.io.BufferedInputStream.new(java.io.FileInputStream.new(randoop_path)))
while (entry = zis.getNextEntry()) != nil
if entry.to_s =~ /^.+randoop[\.0-9]*\.jar/
File.open('randoop.jar', 'w'){} unless File.exist?('randoop.jar')
dest = java.io.BufferedOutputStream.new(java.io.FileOutputStream.new('randoop.jar'), BUFFER)
data = Java::byte[BUFFER].new
while (count = zis.read(data, 0, BUFFER)) != -1
dest.write(data,0,count)
end
dest.flush()
dest.close()
end
end
zis.close()
return File.join(Dir.pwd, 'randoop.jar')
end
From there everything should work. I'm sure this isn't a common problem that people have but it took me a while to figure out why my program wasn't working after I created the JAR because it worked perfectly when I ran it as a jRuby script. Hopefully this helps someone else out there.
Peace!