CLI.t 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4. ################################################################################
  5. # A sub-class of Fcm::CLI::Invoker for testing
  6. {
  7. package TestInvoker;
  8. use base qw{Fcm::CLI::Invoker};
  9. our $LATEST_INSTANCE;
  10. ############################################################################
  11. # Returns a test attrib
  12. sub get_test_attrib {
  13. my ($self) = @_;
  14. return $self->{test_attrib};
  15. }
  16. ############################################################################
  17. # Invokes the sub-system
  18. sub invoke {
  19. my ($self) = @_;
  20. $LATEST_INSTANCE = $self;
  21. }
  22. }
  23. use Fcm::CLI::Config;
  24. use Fcm::CLI::Subcommand;
  25. use Test::More (tests => 25);
  26. main();
  27. sub main {
  28. use_ok('Fcm::CLI');
  29. test_invalid_subcommand();
  30. test_invoker_not_implemented();
  31. test_normal_invoke();
  32. test_help_invoke();
  33. test_get_invoker_normal();
  34. test_load_invoker_class();
  35. }
  36. ################################################################################
  37. # Tests to ensure that an invalid subcommand results in an exception
  38. sub test_invalid_subcommand {
  39. Fcm::CLI::Config->instance({core_subcommands => [], vc_subcommands => []});
  40. eval {
  41. local(@ARGV) = ('foo');
  42. Fcm::CLI::invoke();
  43. };
  44. like($@, qr{foo: unknown command}, 'invalid subcommand');
  45. }
  46. ################################################################################
  47. # Tests to ensure that an unimplemented invoker results in an exception
  48. sub test_invoker_not_implemented {
  49. Fcm::CLI::Config->instance({
  50. core_subcommands => [
  51. Fcm::CLI::Subcommand->new({names => ['foo']}),
  52. Fcm::CLI::Subcommand->new({
  53. names => ['bar'], invoker_class => 'barley',
  54. }),
  55. ],
  56. vc_subcommands => [],
  57. });
  58. eval {
  59. local(@ARGV) = ('foo');
  60. Fcm::CLI::invoke();
  61. };
  62. like($@, qr{foo: \s command \s not \s implemented}xms, 'not implemented');
  63. eval {
  64. local(@ARGV) = ('bar');
  65. Fcm::CLI::invoke();
  66. };
  67. like($@, qr{barley: \s class \s loading \s failed}xms, 'not implemented');
  68. }
  69. ################################################################################
  70. # Tests normal usage of invoke
  71. sub test_normal_invoke {
  72. my $prefix = "normal invoke";
  73. Fcm::CLI::Config->instance({
  74. core_subcommands => [
  75. Fcm::CLI::Subcommand->new({
  76. names => ['foo'],
  77. invoker_class => 'TestInvoker',
  78. invoker_config => {test_attrib => 'test_attrib value'},
  79. }),
  80. ],
  81. vc_subcommands => [],
  82. });
  83. ok(!$TestInvoker::LATEST_INSTANCE, "$prefix: invoker not called");
  84. local(@ARGV) = ('foo', 'bar', 'baz');
  85. Fcm::CLI::invoke();
  86. my $invoker = $TestInvoker::LATEST_INSTANCE;
  87. if (!$invoker) {
  88. fail($prefix);
  89. }
  90. else {
  91. is($invoker->get_command(), 'foo', "$prefix: invoker command");
  92. is_deeply({$invoker->get_options()}, {}, "$prefix: invoker options");
  93. is_deeply([$invoker->get_arguments()], ['bar', 'baz'],
  94. "$prefix: invoker arguments");
  95. is($invoker->get_test_attrib(), 'test_attrib value',
  96. "$prefix: invoker test attrib");
  97. }
  98. $TestInvoker::LATEST_INSTANCE = undef;
  99. }
  100. ################################################################################
  101. # Tests help usage of invoke
  102. sub test_help_invoke {
  103. my $prefix = "help invoke";
  104. Fcm::CLI::Config->instance({
  105. core_subcommands => [
  106. Fcm::CLI::Subcommand->new({
  107. names => ['foo'],
  108. invoker_class => 'TestInvoker',
  109. invoker_config => {test_attrib => 'test_attrib value normal'},
  110. options => [
  111. Fcm::CLI::Option->new({name => 'foo', is_help => 1}),
  112. ],
  113. }),
  114. Fcm::CLI::Subcommand->new({
  115. names => [q{}],
  116. invoker_class => 'TestInvoker',
  117. }),
  118. ],
  119. vc_subcommands => [],
  120. });
  121. ok(!$TestInvoker::LATEST_INSTANCE, "$prefix: invoker not called");
  122. local(@ARGV) = ('foo', '--foo');
  123. Fcm::CLI::invoke();
  124. my $invoker = $TestInvoker::LATEST_INSTANCE;
  125. if (!$invoker) {
  126. fail($prefix);
  127. }
  128. else {
  129. is_deeply([$invoker->get_arguments()], ['foo'],
  130. "$prefix: invoker argument");
  131. }
  132. $TestInvoker::LATEST_INSTANCE = undef;
  133. }
  134. ################################################################################
  135. # Tests getting an invoker
  136. sub test_get_invoker_normal {
  137. my $prefix = 'get invoker normal';
  138. my @options = (
  139. Fcm::CLI::Option->new({name => 'foo'}),
  140. Fcm::CLI::Option->new({name => 'bar'}),
  141. Fcm::CLI::Option->new({name => 'baz'}),
  142. Fcm::CLI::Option->new({
  143. name => q{pork},
  144. delimiter => q{,},
  145. has_arg => Fcm::CLI::Option->ARRAY_ARG,
  146. }),
  147. );
  148. my $subcommand = Fcm::CLI::Subcommand->new({options => \@options});
  149. my %TEST = (
  150. test1 => {
  151. argv => ['--foo', '--bar', 'egg', 'ham', 'sausage'],
  152. command => 'command',
  153. options => {foo => 1, bar => 1},
  154. arguments => ['egg', 'ham', 'sausage'],
  155. },
  156. test2 => {
  157. argv => ['--baz', '--foo', '--bar'],
  158. command => 'test',
  159. options => {foo => 1, bar => 1, baz => 1},
  160. arguments => [],
  161. },
  162. test3 => {
  163. argv => ['egg', 'ham', 'sausage'],
  164. command => 'meal',
  165. options => {},
  166. arguments => ['egg', 'ham', 'sausage'],
  167. },
  168. test4 => {
  169. argv => ['--pork', 'ham', '--pork', 'sausage'],
  170. command => 'pig',
  171. options => {pork => ['ham', 'sausage']},
  172. arguments => [],
  173. },
  174. test5 => {
  175. argv => ['--pork', 'ham,sausage', '--pork', 'bacon', 'liver'],
  176. command => 'pig',
  177. options => {pork => ['ham', 'sausage', 'bacon']},
  178. arguments => ['liver'],
  179. },
  180. );
  181. for my $key (keys(%TEST)) {
  182. local(@ARGV) = @{$TEST{$key}{argv}};
  183. my ($opts_ref, $args_ref) = Fcm::CLI::_parse_argv_using($subcommand);
  184. is_deeply($opts_ref, $TEST{$key}{options},
  185. "$prefix $key: get options");
  186. is_deeply($args_ref, $TEST{$key}{arguments},
  187. "$prefix $key: get arguments");
  188. }
  189. my %BAD_TEST = (
  190. test1 => {
  191. argv => ['--egg', '--bar', 'foo', 'ham', 'sausage'],
  192. },
  193. test2 => {
  194. argv => ['--foo=egg'],
  195. },
  196. );
  197. for my $key (keys(%BAD_TEST)) {
  198. local(@ARGV) = @{$BAD_TEST{$key}{argv}};
  199. eval {
  200. Fcm::CLI::_parse_argv_using($subcommand);
  201. };
  202. isa_ok($@, 'Fcm::CLI::Exception', "$prefix $key");
  203. }
  204. }
  205. ################################################################################
  206. # Tests loading an invoker with a different class
  207. sub test_load_invoker_class {
  208. my $prefix = 'get invoker class';
  209. eval {
  210. my $subcommand = Fcm::CLI::Subcommand->new({invoker_class => 'foo'});
  211. Fcm::CLI::_load_invoker_class_of($subcommand);
  212. };
  213. isa_ok($@, 'Fcm::Exception', "$prefix");
  214. my $invoker_class = 'Fcm::CLI::Invoker::ConfigSystem';
  215. my $subcommand
  216. = Fcm::CLI::Subcommand->new({invoker_class => $invoker_class});
  217. my $class = Fcm::CLI::_load_invoker_class_of($subcommand);
  218. is($class, $invoker_class, "$prefix: $invoker_class");
  219. }
  220. __END__