From 6254349aa9114b2deb1d269b543ee48ae8296fc5 Mon Sep 17 00:00:00 2001 From: Denis Knauf Date: Thu, 9 Dec 2021 13:02:12 +0100 Subject: [PATCH] tests implemented. do not overwrite cmds by aliases. `call *a, **o` behaviour differs 2.5 <=> 2.7 --- bin/example.rb | 204 +++++++++++++++++++++++++++++++++++--- lib/dencli.rb | 2 +- lib/dencli/cmd.rb | 8 +- lib/dencli/interactive.rb | 2 +- lib/dencli/sub.rb | 4 +- 5 files changed, 202 insertions(+), 18 deletions(-) diff --git a/bin/example.rb b/bin/example.rb index 557cbff..e98ea76 100755 --- a/bin/example.rb +++ b/bin/example.rb @@ -3,8 +3,188 @@ require 'pathname' $:.unshift Pathname.new(__FILE__).dirname.dirname.join('lib').to_s require 'dencli' +require 'shellwords' +require 'stringio' cli = DenCli.new :example, "This is an example for generate a DenCli-API" + +class Capture + def initialize cli, verbose: + @cli, @counter, @verbose = cli, 0, verbose + end + + def capture + @args = NilClass + @counter += 1 + stdout, stderr = $stdout, $stderr + $stdout = $stderr = StringIO.new + begin + yield stdout, stderr + ensure + $stderr, $stdout = stderr, stdout + end + end + + def args= args + @args = args + end + + def logstart command + STDERR.printf "[% 4d] \e[1;35m? \e[0m %s tests %s\r", @counter, $0.shellescape, command.shelljoin + end + + def logok command + STDERR.printf "[% 4d] \e[1;32mok\e[0m %s tests %s\e[J\n", @counter, $0.shellescape, command.shelljoin + end + + def logfail command + STDERR.printf "[% 4d] \e[1;31mer\e[0m %s tests %s\e[J\n", @counter, $0.shellescape, command.shelljoin + end + + def loginfo text + STDERR.printf " %s\n", text + end + + def should_ok expect, *command + logstart command + $capture.capture { @cli.call 'tests', *command } + if expect === @args + logok command + else + logfail command + loginfo "expected args: #{expect.inspect}" + loginfo "given args: #{@args.inspect}" + STDERR.puts + end + rescue SystemExit + if 0 == $!.status + logok command + else + logfail command + end + rescue Object + logfail command + loginfo "unexpected raise: (#{$!.class.name}) #$!" + STDERR.puts + end + + def should_fail exception, message, *command + logstart command + $capture.capture { @cli.call 'tests', *command } + logfail command + rescue exception + if message === $!.message + logok command + if @verbose + loginfo "raised: (#{$!.class.name}) #$!" + STDERR.puts + end + else + loginfo "unexpected message: (#{$!.class.name}) #$!" + STDERR.puts + end + rescue Object + logfail command + loginfo "unexpected raised: (#{$!.class.name}) #$!" + STDERR.puts + end +end + +cli.sub :tests, "Some tests" do |tcli| + tcli.cmd( :'-', "No arguments no options expected", &lambda {|| $capture.args = [] }) + tcli.cmd( :'arg', "", &lambda {|one| $capture.args = [one] }) + tcli.cmd( :'oar', "", &lambda {|one=nil| $capture.args = [one] }) + tcli.cmd( :'arg-arg', "", &lambda {|one, two| $capture.args = [one, two] }) + tcli.cmd( :'oar-oar', "", &lambda {|one=nil, two=nil| $capture.args = [one, two] }) + tcli.cmd( :'arg-oar', "expected", &lambda {|one, two=nil| $capture.args = [one, two] }) + tcli.cmd( :'oar-arg', "expected", &lambda {|one=nil, two| $capture.args = [one, two] }) + tcli.cmd( :'bool', '', &lambda {|a:| $capture.args = [a] }).opt(:a, '-a', '') + tcli.cmd( :'optbool', '', &lambda {|a: nil| $capture.args = [a] }).opt(:a, '-a', '') + tcli.cmd( :'defbool', '', &lambda {|a:| $capture.args = [a] }).opt(:a, '-a', '', default: 'default') + tcli.cmd( :'str', '', &lambda {|a:| $capture.args = [a] }).opt(:a, '-a=STR', '') + tcli.cmd( :'lstr', '', &lambda {|a:| $capture.args = [a] }).opt(:a, '--astr=STR', '') + tcli.cmd( :'bstr', '', &lambda {|a:| $capture.args = [a] }).opt(:a, '-a', '--astr=STR', '') + + tcli.cmd( :run, "Run all tests") do |verbose:| + $capture = Capture.new cli, verbose: verbose + + $capture.should_fail DenCli::UnknownCommand, //, 'unknown-command' + + $capture.should_ok [], '-' + $capture.should_fail DenCli::UsageError, //, '-', 'unexpected' + + $capture.should_ok %w[first], 'arg', 'first' + $capture.should_fail DenCli::UsageError, //, 'arg' + $capture.should_fail DenCli::UsageError, //, 'arg', 'first', 'unexpected' + + $capture.should_ok %w[first], 'oar', 'first' + $capture.should_ok [nil], 'oar' + $capture.should_fail DenCli::UsageError, //, 'oar', 'first', 'unexpected' + + $capture.should_ok %w[first two], 'oar-oar', 'first', 'two' + $capture.should_ok ['first', nil], 'oar-oar', 'first' + $capture.should_ok [nil,nil], 'oar-oar' + $capture.should_fail DenCli::UsageError, //, 'oar-oar', 'first', 'two', 'unexpected' + + $capture.should_ok %w[first two], 'arg-oar', 'first', 'two' + $capture.should_ok ['first', nil], 'arg-oar', 'first' + $capture.should_fail DenCli::UsageError, //, 'arg-oar' + + $capture.should_ok [nil, 'first'], 'oar-arg', 'first' + $capture.should_ok ['first', 'second'], 'oar-arg', 'first', 'second' + $capture.should_fail DenCli::UsageError, //, 'oar-arg' + $capture.should_fail DenCli::UsageError, //, 'oar-arg', 'first', 'two', 'unexpected' + + $capture.should_ok [true], 'bool', '-a' + $capture.should_fail DenCli::UsageError, //, 'bool' + $capture.should_fail DenCli::UsageError, //, 'bool', '-a', 'unexpected' + $capture.should_fail OptionParser::InvalidOption, //, 'bool', '-b' + $capture.should_fail OptionParser::InvalidOption, //, 'bool', '--unexpected' + $capture.should_fail DenCli::UsageError, //, 'bool', 'unexpected' + + $capture.should_ok [true], 'optbool', '-a' + $capture.should_ok [nil], 'optbool' + $capture.should_fail DenCli::UsageError, //, 'optbool', '-a', 'unexpected' + $capture.should_fail OptionParser::InvalidOption, //, 'optbool', '-b' + $capture.should_fail OptionParser::InvalidOption, //, 'optbool', '--unexpected' + $capture.should_fail DenCli::UsageError, //, 'optbool', 'unexpected' + + $capture.should_ok [true], 'defbool', '-a' + $capture.should_ok ['default'], 'defbool' + $capture.should_fail DenCli::UsageError, //, 'defbool', '-a', 'unexpected' + $capture.should_fail OptionParser::InvalidOption, //, 'defbool', '-b' + $capture.should_fail OptionParser::InvalidOption, //, 'defbool', '--unexpected' + $capture.should_fail DenCli::UsageError, //, 'defbool', 'unexpected' + + $capture.should_ok %w[first], 'str', '-a', 'first' + $capture.should_ok %w[first], 'str', '-afirst' + $capture.should_fail OptionParser::MissingArgument, //, 'str', '-a' + $capture.should_fail DenCli::UsageError, //, 'str' + $capture.should_fail OptionParser::InvalidOption, //, 'str', '-b' + $capture.should_fail OptionParser::InvalidOption, //, 'str', '--unexpected' + $capture.should_fail DenCli::UsageError, //, 'str', 'unexpected' + + $capture.should_ok %w[first], 'lstr', '--astr', 'first' + $capture.should_ok %w[first], 'lstr', '--astr=first' + $capture.should_fail OptionParser::MissingArgument, //, 'lstr', '--astr' + $capture.should_fail DenCli::UsageError, //, 'lstr' + $capture.should_fail OptionParser::InvalidOption, //, 'lstr', '-b' + $capture.should_fail OptionParser::InvalidOption, //, 'lstr', '--unexpected' + $capture.should_fail DenCli::UsageError, //, 'lstr', 'unexpected' + + $capture.should_ok %w[first], 'bstr', '-a', 'first' + $capture.should_ok %w[first], 'bstr', '-afirst' + $capture.should_ok %w[first], 'bstr', '--astr', 'first' + $capture.should_ok %w[first], 'bstr', '--astr=first' + $capture.should_fail OptionParser::MissingArgument, //, 'bstr', '--astr' + $capture.should_fail OptionParser::MissingArgument, //, 'bstr', '-a' + $capture.should_fail DenCli::UsageError, //, 'bstr' + $capture.should_fail OptionParser::InvalidOption, //, 'bstr', '-b' + $capture.should_fail OptionParser::InvalidOption, //, 'bstr', '--unexpected' + $capture.should_fail DenCli::UsageError, //, 'bstr', 'unexpected' + end.opt( :verbose, '-v', 'Prints additional information per test', default: false) +end + cli.cmd( :args, "Expects and prints given arguments", &lambda {|a, b, c:, d:, e:, f:, g:| p a: a, b: b, c: c, d: d, e: e @@ -16,21 +196,21 @@ cli.cmd( :args, "Expects and prints given arguments", opt( :g, '--long-option=sth', "Long option, no short option", default: "nothing"). opt( :h, '-hsth', "No long option, only short option", default: "nothing") -cli.cmd( :example, "I have an example command") { STDERR.puts "This is an example" } +cli.cmd( :example, "I have an example command") { $stderr.puts "This is an example" } cli.cmd( :help, "An example for help", aliases: [nil, '-h', '--help'], &lambda {|*commands, full:| if full - cli.help_full *commands, output: STDERR + cli.help_full *commands, output: $stderr else - cli.help *commands, output: STDERR + cli.help *commands, output: $stderr end }). opt( :full, '-f', '--[no-]full', "Print all commands and sub-commands.", default: false) cli.sub( :more, "Sub-Commands are also possible with a new cli") do |sub| - sub.cmd( :help, "", aliases: [nil, '-h', '--help']) {|*args| STDERR.puts sub.help(*args) } - sub.cmd( :help, "") {|*args| STDERR.puts cli.help( 'more', *args) } - sub.cmd( :example, "Here is an example, too") { STDERR.puts "This is an other example" } - sub.cmd( :foo, "BAR") { STDERR.puts "FOO bar"} + sub.cmd( :help, "", aliases: [nil, '-h', '--help']) {|*args| $stderr.puts sub.help(*args) } + sub.cmd( :help, "") {|*args| $stderr.puts cli.help( 'more', *args) } + sub.cmd( :example, "Here is an example, too") { $stderr.puts "This is an other example" } + sub.cmd( :foo, "BAR") { $stderr.puts "FOO bar"} sub.cmd( :args, "Expects and prints given arguments", &lambda {|a, b=1, c:, d: 5, e:| p a: a, b: b, c: c, d: d, e: e @@ -40,12 +220,12 @@ cli.sub( :more, "Sub-Commands are also possible with a new cli") do |sub| opt( :e, '-e', "Toggle e") sub.sub( :deeper, "You want to have Sub-Sub-Commands?") do |sub2| - sub2.cmd( :help, "", aliases: [nil, '-h', '--help'], &lambda {|*commands| sub2.help( *commands, output: STDERR) }) - sub2.cmd( :last, "The last example", &lambda { STDERR.puts "The last example" }) + sub2.cmd( :help, "", aliases: [nil, '-h', '--help'], &lambda {|*commands| sub2.help( *commands, output: $stderr) }) + sub2.cmd( :last, "The last example", &lambda { $stderr.puts "The last example" }) sub2.sub( :'sub-commands', "Endless Sub-Sub- ...") do |sub3| - sub3.cmd( :help, "") {|*args| STDERR.puts sub3.help( sub3, *args) } - sub3.cmd( :hehe, "The real last example", min: 2) { STDERR.puts "Trust me!" } + sub3.cmd( :help, "") {|*args| $stderr.puts sub3.help( sub3, *args) } + sub3.cmd( :hehe, "The real last example", min: 2) { $stderr.puts "Trust me!" } end end end @@ -53,6 +233,6 @@ end begin cli.call *ARGV rescue DenCli::UsageError - STDERR.puts $! + $stderr.puts $! exit 1 end diff --git a/lib/dencli.rb b/lib/dencli.rb index c9c8264..a4970d6 100755 --- a/lib/dencli.rb +++ b/lib/dencli.rb @@ -96,7 +96,7 @@ class DenCli end def help_full *args, output: nil - output ||= STDOUT + output ||= $stdout x = @subs.goto *args _help_full output, x end diff --git a/lib/dencli/cmd.rb b/lib/dencli/cmd.rb index b60c895..f057b2d 100644 --- a/lib/dencli/cmd.rb +++ b/lib/dencli/cmd.rb @@ -60,7 +60,11 @@ class DenCli::CMD raise DenCli::UsageError, "Missing argument(s): #{kr.map {|_, o| o.long || o.short }.join ', '}" end end - @exe.call *as, **os + if os.empty? + @exe.call *as + else + @exe.call *as, **os + end end def usage output: nil @@ -87,7 +91,7 @@ class DenCli::CMD end end else - output << ' ...' + output << ' [...]' end end diff --git a/lib/dencli/interactive.rb b/lib/dencli/interactive.rb index d03b482..be778c2 100644 --- a/lib/dencli/interactive.rb +++ b/lib/dencli/interactive.rb @@ -121,7 +121,7 @@ class DenCli::Interactive begin cur.call *line rescue ::DenCli::UsageError - STDERR.puts "! #$!" + $stderr.puts "! #$!" end true end diff --git a/lib/dencli/sub.rb b/lib/dencli/sub.rb index 16683d5..3277e50 100644 --- a/lib/dencli/sub.rb +++ b/lib/dencli/sub.rb @@ -118,7 +118,7 @@ class DenCli::Sub #DenCli::assert_type self, __method__, :aliases, aliases, Array, NilClass name = name.to_s unless name.nil? @subs[name] = obj - DenCli.gen_aliases( name, min) {|a| @aliases[a] = obj } + DenCli.gen_aliases( name, min) {|a| @aliases[a] ||= obj } if aliases [*aliases].each {|a| @aliases[a] = obj } end @@ -141,7 +141,7 @@ class DenCli::Sub elsif sub = @subs[pre[0]] sub.complete *pre[1..-1], str else - STDOUT.print "\a" + $stdout.print "\a" end end