From: martinahansen Date: Mon, 14 Jun 2010 13:57:21 +0000 (+0000) Subject: fixed unit tests for biopieces.rb X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=c10792701ac2bd526240b88e8c47e3a6271e8f71;p=biopieces.git fixed unit tests for biopieces.rb git-svn-id: http://biopieces.googlecode.com/svn/trunk@980 74ccb610-7750-0410-82ae-013aeee3265d --- diff --git a/code_ruby/Maasha/lib/biopieces.rb b/code_ruby/Maasha/lib/biopieces.rb index f1a448e..5f054f1 100644 --- a/code_ruby/Maasha/lib/biopieces.rb +++ b/code_ruby/Maasha/lib/biopieces.rb @@ -12,73 +12,22 @@ require 'pp' # of lines with a key/value pair seperated by a colon and a white space ': '. # Each record is separated by a line with three dashes '---'. class Biopieces - REGEX_LIST = /^(list|files|files!)$/ - REGEX_INT = /^(int|uint)$/ - REGEX_STRING = /^(string|file|file!|dir|dir!|genome)$/ - # Initialize a Biopiece and write the status to file. # Options are for testing purposes only. - def initialize(no_status=nil,input=STDIN,output=STDOUT) - status_set unless no_status + def initialize(test=nil, input=STDIN, output=STDOUT) + @test = test @input = input @output = output + status_set unless @test end # Check the integrity of a list of casts, followed by parsion options from argv - # and finally checking the options according to the casts. Returns nil if - # argv is empty, otherwise an options hash. - def parse(argv,cast_list=[],script_path=$0) - casts = Casts.new(cast_list) - - pp casts - - @script_path = script_path - - @options = {} - - options_template = OptionParser.new do |option| - casts.each do |cast| - if cast[:type] == 'flag' - option.on("-#{cast[:short]}", "--#{cast[:long]}") do |o| - @options[cast[:long]] = o - end - elsif cast[:type] =~ REGEX_LIST - option.on( "-#{cast[:short]}", "--#{cast[:long]} A", Array) do |a| - @options[cast[:long]] = a - end - elsif cast[:type] =~ REGEX_INT - option.on("-#{cast[:short]}", "--#{cast[:long]} I", Integer) do |i| - @options[cast[:long]] = i - end - elsif cast[:type] =~ REGEX_STRING - option.on("-#{cast[:short]}", "--#{cast[:long]} S", String) do |s| - @options[cast[:long]] = s - end - elsif cast[:type] == 'float' - option.on("-#{cast[:short]}", "--#{cast[:long]} F", Float) do |f| - @options[cast[:long]] = f - end - else - raise ArgumentError, "Unknown option type: '#{cast[:type]}'" - end - end - end - - options_template.parse!(argv) - - if print_usage_full? - print_usage_and_exit(true) - return # break for unit testing. - elsif print_usage_short? - print_usage_and_exit - return # break for unit testing. - end - - options_default - options_glob - options_check - - @options + # and finally checking the options according to the casts. Returns nil if argv + # is empty, otherwise an options hash. + def parse(argv, cast_list=[], script_path=$0) + casts = Casts.new(cast_list) + option_handler = OptionHandler.new(argv, casts, script_path, @test) + @options = option_handler.options_parse end # Open Biopiece input stream if not open and iterate over all Biopiece @@ -129,198 +78,21 @@ class Biopieces private - # Given the script name determine the path of the wiki file with the usage info. - def wiki_path - path = ENV["BP_DIR"] + "/bp_usage/" + File.basename(@script_path, ".rb") + ".wiki" - raise "No such wiki file: #{path}" unless File.file? path - - path - end - - # Check if full "usage info" should be printed. - def print_usage_full? - @options["help"] - end - - # Check if short "usage info" should be printed. - def print_usage_short? - if not $stdin.tty? - return false - elsif @options["stream_in"] - return false - elsif @options["data_in"] - return false - elsif wiki_path =~ /^(list_biopieces|list_genomes|list_mysql_databases|biostat)$/ # TODO get rid of this! - return false - else - return true - end - end - - # Print usage info by Calling an external script 'print_wiki' - # using a system() call and exit. An optional 'full' flag - # outputs the full usage info. - def print_usage_and_exit(full=nil) - if full - system("print_wiki --data_in #{wiki_path} --help") - else - system("print_wiki --data_in #{wiki_path}") - end - - raise "Failed printing wiki: #{wiki_path}" unless $?.success? - - exit - end - - # Set default options value from cast unless a value is set. - def options_default - casts.each do |cast| - if cast[:default] - @options[cast[:long]] = cast[:default] unless @options.has_key? cast[:long] - end - end - end - - # Expands glob expressions to a full list of paths. - # Examples: "*.fna" or "foo.fna,*.fna" or "foo.fna,/bar/*.fna" - def options_glob - casts.each do |cast| - if cast[:type] == 'files' or cast[:type] == 'files!' - if @options.has_key? cast[:long] - files = [] - - @options[cast[:long]].each do |path| - if path.include? "*" - Dir.glob(path).each do |file| - files << file if File.file? file - end - else - files << path - end - end - - @options[cast[:long]] = files - end - end - end - end - - # Check all options according to casts. - def options_check - casts.each do |cast| - options_check_mandatory(cast) - options_check_int(cast) - options_check_uint(cast) - options_check_file(cast) - options_check_files(cast) - options_check_dir(cast) - options_check_allowed(cast) - options_check_disallowed(cast) - end - end - - # Check if a mandatory option is set and raise if it isn't. - def options_check_mandatory(cast) - if cast[:mandatory] - raise ArgumentError, "Mandatory argument: --#{cast[:long]}" unless @options.has_key? cast[:long] - end - end - - # Check int type option and raise if not an integer. - def options_check_int(cast) - if cast[:type] == 'int' and @options.has_key? cast[:long] - unless @options[cast[:long]].is_a? Integer - raise ArgumentError, "Argument to --#{cast[:long]} must be an integer, not '#{@options[cast[:long]]}'" - end - end - end - - # Check uint type option and raise if not an unsinged integer. - def options_check_uint(cast) - if cast[:type] == 'uint' and @options.has_key? cast[:long] - unless @options[cast[:long]].is_a? Integer and @options[cast[:long]] >= 0 - raise ArgumentError, "Argument to --#{cast[:long]} must be an unsigned integer, not '#{@options[cast[:long]]}'" - end - end - end - - # Check file! type argument and raise if file don't exists. - def options_check_file(cast) - if cast[:type] == 'file!' and @options.has_key? cast[:long] - raise ArgumentError, "No such file: '#{@options[cast[:long]]}'" unless File.file? @options[cast[:long]] - end - end - - # Check files! type argument and raise if files don't exists. - def options_check_files(cast) - if cast[:type] == 'files!' and @options.has_key? cast[:long] - @options[cast[:long]].each do |path| - raise ArgumentError, "No such file: '#{path}'" unless File.file? path - end - end - end - - # Check dir! type argument and raise if directory don't exist. - def options_check_dir(cast) - if cast[:type] == 'dir!' and @options.has_key? cast[:long] - raise ArgumentError, "No such directory: '#{@options[cast[:long]]}'" unless File.directory? @options[cast[:long]] - end - end - - # Check options and raise unless allowed. - def options_check_allowed(cast) - if cast[:allowed] and @options.has_key? cast[:long] - allowed_hash = {} - cast[:allowed].split(',').each { |a| allowed_hash[a] = 1 } - - raise ArgumentError, "Argument '#{@options[cast[:long]]}' to --#{cast[:long]} not allowed" unless allowed_hash.has_key? @options[cast[:long]] - end - end - - # Check disallowed argument values and raise if disallowed. - def options_check_disallowed(cast) - if cast[:disallowed] and @options.has_key? cast[:long] - cast[:disallowed].split(',').each do |val| - raise ArgumentError, "Argument '#{@options[cast[:long]]}' to --#{cast[:long]} is disallowed" if val == @options[cast[:long]] - end - end - end - # Open Biopieces input data stream for reading from either # stdin or from a list of files specified in options["stream_in"]. def stream_in_open - if not $stdin.tty? - p "IN" - stream = @input - else - stream = read(@options["stream_in"]) - end - - stream + $stdin.tty? ? read(@options["stream_in"]) : @input end # Open Biopieces output data stream for writing to stdout # or a file specified in options["stream_out"]. def stream_out_open - if @options["stream_out"] - stream = write(@options["stream_out"], @options["compress"]) - else - p "OUT" - stream = @output - end - - stream + @options["stream_out"] ? write(@options["stream_out"], @options["compress"]) : @output end # Opens a reads stream to a list of files. def read(files) - if zipped?(files) - stream = zread(files) - else - stream = nread(files) - end - - stream + zipped?(files) ? zread(files) : nread(files) end # Opens a write stream to a file and returns a _io_ object. @@ -452,10 +224,12 @@ class Biopieces end end + # Error class for all exceptions to do with option casts. class CastError < StandardError end + # Class to handle casts of command line options. Each cast prescribes the long and # short name of the option, the type, if it is mandatory, the default value, and # allowed and disallowed values. An optional list of extra casts can be supplied, @@ -468,9 +242,11 @@ class Casts < Array @cast_list = cast_list ubiquitous check - self << @cast_list + self.push *@cast_list end + private + # Add ubiquitous options casts. def ubiquitous @cast_list << {:long => 'help', :short => '?', :type => 'flag', :mandatory => false, :default => nil, :allowed => nil, :disallowed => nil} @@ -577,4 +353,232 @@ class Casts < Array end end + +# Class for parsing argv using OptionParser accordind to given casts. +# Default options are set, file glob expressions expanded, and options are +# checked according to the casts. Usage information is printed and exit called +# if required. +class OptionHandler + REGEX_LIST = /^(list|files|files!)$/ + REGEX_INT = /^(int|uint)$/ + REGEX_STRING = /^(string|file|file!|dir|dir!|genome)$/ + + def initialize(argv, casts, script_path, test=nil) + @argv = argv + @casts = casts + @script_path = script_path + @test = test + end + + # Parse options from argv using OptionParser and casts denoting long and + # short option names. Usage information is printed and exit called. + # A hash with options is returned. + def options_parse + @options = {} + + option_parser = OptionParser.new do |option| + @casts.each do |cast| + if cast[:type] == 'flag' + option.on("-#{cast[:short]}", "--#{cast[:long]}") do |o| + @options[cast[:long]] = o + end + elsif cast[:type] =~ REGEX_LIST + option.on( "-#{cast[:short]}", "--#{cast[:long]} A", Array) do |a| + @options[cast[:long]] = a + end + elsif cast[:type] =~ REGEX_INT + option.on("-#{cast[:short]}", "--#{cast[:long]} I", Integer) do |i| + @options[cast[:long]] = i + end + elsif cast[:type] =~ REGEX_STRING + option.on("-#{cast[:short]}", "--#{cast[:long]} S", String) do |s| + @options[cast[:long]] = s + end + elsif cast[:type] == 'float' + option.on("-#{cast[:short]}", "--#{cast[:long]} F", Float) do |f| + @options[cast[:long]] = f + end + else + raise ArgumentError, "Unknown option type: '#{cast[:type]}'" + end + end + end + + option_parser.parse!(@argv) + + if print_usage_full? + print_usage_and_exit(true) + elsif print_usage_short? + print_usage_and_exit + end + + options_default + options_glob + options_check + + @options + end + + # Given the script name determine the path of the wiki file with the usage info. + def wiki_path + path = ENV["BP_DIR"] + "/bp_usage/" + File.basename(@script_path, ".rb") + ".wiki" + raise "No such wiki file: #{path}" unless File.file? path + path + end + + # Check if full "usage info" should be printed. + def print_usage_full? + @options["help"] + end + + # Check if short "usage info" should be printed. + def print_usage_short? + if not $stdin.tty? + return false + elsif @options["stream_in"] + return false + elsif @options["data_in"] + return false + elsif wiki_path =~ /^(list_biopieces|list_genomes|list_mysql_databases|biostat)$/ # TODO get rid of this! + return false + else + return true + end + end + + # Print usage info by Calling an external script 'print_wiki' + # using a system() call and exit. An optional 'full' flag + # outputs the full usage info. + def print_usage_and_exit(full=nil) + if @test + return + else + if full + system("print_wiki --data_in #{wiki_path} --help") + else + system("print_wiki --data_in #{wiki_path}") + end + + raise "Failed printing wiki: #{wiki_path}" unless $?.success? + + exit + end + end + + # Set default options value from cast unless a value is set. + def options_default + @casts.each do |cast| + if cast[:default] + @options[cast[:long]] = cast[:default] unless @options.has_key? cast[:long] + end + end + end + + # Expands glob expressions to a full list of paths. + # Examples: "*.fna" or "foo.fna,*.fna" or "foo.fna,/bar/*.fna" + def options_glob + @casts.each do |cast| + if cast[:type] == 'files' or cast[:type] == 'files!' + if @options.has_key? cast[:long] + files = [] + + @options[cast[:long]].each do |path| + if path.include? "*" + Dir.glob(path).each do |file| + files << file if File.file? file + end + else + files << path + end + end + + @options[cast[:long]] = files + end + end + end + end + + # Check all options according to casts. + def options_check + @casts.each do |cast| + options_check_mandatory(cast) + options_check_int(cast) + options_check_uint(cast) + options_check_file(cast) + options_check_files(cast) + options_check_dir(cast) + options_check_allowed(cast) + options_check_disallowed(cast) + end + end + + # Check if a mandatory option is set and raise if it isn't. + def options_check_mandatory(cast) + if cast[:mandatory] + raise ArgumentError, "Mandatory argument: --#{cast[:long]}" unless @options.has_key? cast[:long] + end + end + + # Check int type option and raise if not an integer. + def options_check_int(cast) + if cast[:type] == 'int' and @options.has_key? cast[:long] + unless @options[cast[:long]].is_a? Integer + raise ArgumentError, "Argument to --#{cast[:long]} must be an integer, not '#{@options[cast[:long]]}'" + end + end + end + + # Check uint type option and raise if not an unsinged integer. + def options_check_uint(cast) + if cast[:type] == 'uint' and @options.has_key? cast[:long] + unless @options[cast[:long]].is_a? Integer and @options[cast[:long]] >= 0 + raise ArgumentError, "Argument to --#{cast[:long]} must be an unsigned integer, not '#{@options[cast[:long]]}'" + end + end + end + + # Check file! type argument and raise if file don't exists. + def options_check_file(cast) + if cast[:type] == 'file!' and @options.has_key? cast[:long] + raise ArgumentError, "No such file: '#{@options[cast[:long]]}'" unless File.file? @options[cast[:long]] + end + end + + # Check files! type argument and raise if files don't exists. + def options_check_files(cast) + if cast[:type] == 'files!' and @options.has_key? cast[:long] + @options[cast[:long]].each do |path| + raise ArgumentError, "No such file: '#{path}'" unless File.file? path + end + end + end + + # Check dir! type argument and raise if directory don't exist. + def options_check_dir(cast) + if cast[:type] == 'dir!' and @options.has_key? cast[:long] + raise ArgumentError, "No such directory: '#{@options[cast[:long]]}'" unless File.directory? @options[cast[:long]] + end + end + + # Check options and raise unless allowed. + def options_check_allowed(cast) + if cast[:allowed] and @options.has_key? cast[:long] + allowed_hash = {} + cast[:allowed].split(',').each { |a| allowed_hash[a] = 1 } + + raise ArgumentError, "Argument '#{@options[cast[:long]]}' to --#{cast[:long]} not allowed" unless allowed_hash.has_key? @options[cast[:long]] + end + end + + # Check disallowed argument values and raise if disallowed. + def options_check_disallowed(cast) + if cast[:disallowed] and @options.has_key? cast[:long] + cast[:disallowed].split(',').each do |val| + raise ArgumentError, "Argument '#{@options[cast[:long]]}' to --#{cast[:long]} is disallowed" if val == @options[cast[:long]] + end + end + end +end + + __END__ diff --git a/code_ruby/Maasha/test/test_biopieces.rb b/code_ruby/Maasha/test/test_biopieces.rb index d007d35..7871d6b 100755 --- a/code_ruby/Maasha/test/test_biopieces.rb +++ b/code_ruby/Maasha/test/test_biopieces.rb @@ -20,12 +20,11 @@ class OptionTest < Test::Unit::TestCase # >>>>>>>>>>>>>>>>>>>> Testing Options.new <<<<<<<<<<<<<<<<<<<< - test "Biopieces#parse with all cast keys don't raise" do - argv = [] - casts = [{:long=>"foo", :short=>"f", :type=>"int", :mandatory=>false, :default=>nil, :allowed=>nil, :disallowed=>nil}] - @bp.expects(:print_usage_and_exit).with() - assert_nothing_raised(CastError) { @bp.parse(argv, casts, SCRIPT_PATH) } - end + test "Biopieces#parse with all cast keys don't raise" do + argv = [] + casts = [{:long=>"foo", :short=>"f", :type=>"int", :mandatory=>false, :default=>nil, :allowed=>nil, :disallowed=>nil}] + assert_nothing_raised(CastError) { @bp.parse(argv, casts, SCRIPT_PATH) } + end test "Biopieces#parse with illegal long cast values raises" do [nil, true, false, 1, 0, "a"].each do |long| @@ -39,7 +38,6 @@ class OptionTest < Test::Unit::TestCase ["foo", "!!", "0123"].each do |long| argv = [] casts = [{:long=>long, :short=>"f", :type=>"int", :mandatory=>false, :default=>nil, :allowed=>nil, :disallowed=>nil}] - @bp.expects(:print_usage_and_exit).with() assert_nothing_raised(CastError) { @bp.parse(argv, casts, SCRIPT_PATH) } end end @@ -56,7 +54,6 @@ class OptionTest < Test::Unit::TestCase ["!", "1", "a"].each do |short| argv = [] casts = [{:long=>"foo", :short=>short, :type=>"int", :mandatory=>false, :default=>nil, :allowed=>nil, :disallowed=>nil}] - @bp.expects(:print_usage_and_exit).with() assert_nothing_raised(CastError) { @bp.parse(argv, casts, SCRIPT_PATH) } end end @@ -73,7 +70,6 @@ class OptionTest < Test::Unit::TestCase TYPES.each do |type| argv = [] casts = [{:long=>"foo", :short=>"f", :type=>type, :mandatory=>false, :default=>nil, :allowed=>nil, :disallowed=>nil}] - @bp.expects(:print_usage_and_exit).with() assert_nothing_raised(CastError) { @bp.parse(argv, casts, SCRIPT_PATH) } end end @@ -88,9 +84,8 @@ class OptionTest < Test::Unit::TestCase test "Biopieces#parse with legal mandatory cast values don't raise" do [true, false].each do |mandatory| - argv = [] + argv = [ "--foo", "1" ] casts = [{:long=>"foo", :short=>"f", :type=>"int", :mandatory=>mandatory, :default=>nil, :allowed=>nil, :disallowed=>nil}] - @bp.expects(:print_usage_and_exit).with() assert_nothing_raised(CastError) { @bp.parse(argv, casts, SCRIPT_PATH) } end end @@ -104,10 +99,9 @@ class OptionTest < Test::Unit::TestCase end test "Biopieces#parse with legal default cast values don't raise" do - ["foo", nil, 0, 0.0, 1, 1.2].each do |default| + [nil, 0, 1, -1].each do |default| argv = [] casts = [{:long=>"foo", :short=>"f", :type=>"int", :mandatory=>false, :default=>default, :allowed=>nil, :disallowed=>nil}] - @bp.expects(:print_usage_and_exit).with() assert_nothing_raised(CastError) { @bp.parse(argv, casts, SCRIPT_PATH) } end end @@ -124,7 +118,6 @@ class OptionTest < Test::Unit::TestCase ["foo,bar",nil].each do |allowed| argv = [] casts = [{:long=>"foo", :short=>"f", :type=>"int", :mandatory=>false, :default=>nil, :allowed=>allowed, :disallowed=>nil}] - @bp.expects(:print_usage_and_exit).with() assert_nothing_raised(CastError) { @bp.parse(argv, casts, SCRIPT_PATH) } end end @@ -141,7 +134,6 @@ class OptionTest < Test::Unit::TestCase ["foo,bar",nil].each do |disallowed| argv = [] casts = [{:long=>"foo", :short=>"f", :type=>"int", :mandatory=>false, :default=>nil, :allowed=>nil, :disallowed=>disallowed}] - @bp.expects(:print_usage_and_exit).with() assert_nothing_raised(CastError) { @bp.parse(argv, casts, SCRIPT_PATH) } end end @@ -167,7 +159,6 @@ class OptionTest < Test::Unit::TestCase casts = [] casts << {:long=>"foo", :short=>"f", :type=>"int", :mandatory=>false, :default=>nil, :allowed=>nil, :disallowed=>nil} casts << {:long=>"bar", :short=>"b", :type=>"int", :mandatory=>false, :default=>nil, :allowed=>nil, :disallowed=>nil} - @bp.expects(:print_usage_and_exit).with() assert_nothing_raised(CastError) { @bp.parse(argv, casts, SCRIPT_PATH) } end @@ -182,13 +173,11 @@ class OptionTest < Test::Unit::TestCase test "Biopieces#parse with empty argv and existing wiki file don't raise" do argv = [] casts = [] - @bp.expects(:print_usage_and_exit).with() assert_nothing_raised { @bp.parse(argv, casts, SCRIPT_PATH) } end test "Biopieces#parse with --help in argv and existing wiki output long usage" do argv = ["--help"] - @bp.expects(:print_usage_and_exit).with(true) assert_nothing_raised { @bp.parse(argv,[],SCRIPT_PATH) } end