PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/user-choices/test/command-line-tests.rb

https://bitbucket.org/redricko/pragprog-scripting
Ruby | 414 lines | 396 code | 11 blank | 7 comment | 1 complexity | 35ad5125de91c25ef285b65568c8e883 MD5 | raw file
  1. #---
  2. # Excerpted from "Everyday Scripting in Ruby"
  3. # We make no guarantees that this code is fit for any purpose.
  4. # Visit http://www.pragmaticprogrammer.com/titles/bmsft for more book information.
  5. #---
  6. load "set-standalone-test-paths.rb" unless $started_from_rakefile
  7. require 'test/unit'
  8. require 's4t-utils'
  9. require 'builder'
  10. require 'user-choices'
  11. include S4tUtils
  12. ### Handling of options with arguments ###
  13. class OPTIONS_CommandLineTests < Test::Unit::TestCase
  14. include UserChoices
  15. def test_options_can_be_given_in_the_command_line
  16. with_command_args('--given-option value') {
  17. hash = CommandLineChoices.fill do | c |
  18. c.uses_option(:given_option, "--given-option VALUE")
  19. end
  20. assert_true(hash.has_key?(:given_option))
  21. assert_equal("value", hash[:given_option])
  22. assert_false(hash.has_key?(:unspecified_option))
  23. assert_equal(nil, hash[:unspecified_option])
  24. }
  25. end
  26. def test_the_specification_can_describe_options_that_are_not_given
  27. # They're really /optional/.
  28. with_command_args('') {
  29. hash = CommandLineChoices.fill do | c |
  30. c.uses_option(:unused_option, "--unused-option VALUE")
  31. end
  32. assert_false(hash.has_key?(:unused_option))
  33. assert_equal(nil, hash[:unused_option])
  34. }
  35. end
  36. def test_options_can_have_one_letter_abbreviations
  37. with_command_args('-s s-value --option=option-value') {
  38. hash = CommandLineChoices.fill do | c |
  39. c.uses_option(:option, "-o", "--option=VALUE")
  40. c.uses_option(:something, "-s", "--something=VALUE")
  41. end
  42. assert_equal("s-value", hash[:something])
  43. assert_equal("option-value", hash[:option])
  44. }
  45. end
  46. def test_command_line_list_of_possible_values_checking
  47. with_command_args("-n true") do
  48. assert_raises_with_matching_message(StandardError,
  49. %r{Error in the command line: 'true' is not a valid value for '--north-west'}) {
  50. choices = CommandLineChoices.fill do | c |
  51. c.uses_option(:north_west, "-n", "--north-west=VALUE")
  52. end
  53. choices.check_values(:north_west => ['low', 'high'])
  54. }
  55. end
  56. end
  57. def test_command_line_integer_value_checking
  58. with_command_args("--day-count-max=2d3") do
  59. assert_raises_with_matching_message(StandardError,
  60. /Error in the command line: '--day-count-max' requires an integer value, and '2d3' doesn't look like one/) {
  61. choices = CommandLineChoices.fill do | c |
  62. c.uses_option(:day_count_max, "--day-count-max=VALUE")
  63. end
  64. choices.check_values(:day_count_max => :integer)
  65. }
  66. end
  67. end
  68. def test_integer_value_updating
  69. with_command_args("--day-count-max 23") do
  70. choices = CommandLineChoices.fill do | c |
  71. c.uses_option(:day_count_max, "--day-count-max=VALUE")
  72. end
  73. choices.update_values(:day_count_max => :integer)
  74. assert_equal(23, choices[:day_count_max])
  75. end
  76. end
  77. def test_array_value_updating_with_proper_multivalue_declaration
  78. with_command_args("--hosts localhost,foo.com") do
  79. choices = CommandLineChoices.fill do | c |
  80. c.uses_option(:hosts, "--hosts HOST,HOST")
  81. end
  82. choices.update_values(:hosts => [:string])
  83. assert_equal(['localhost', 'foo.com'],
  84. choices[:hosts])
  85. end
  86. end
  87. def test_array_value_updating_without_proper_multivalue_declaration
  88. with_command_args("--hosts localhost,foo.com") do
  89. choices = CommandLineChoices.fill do | c |
  90. c.uses_option(:hosts, "--hosts HOSTS...")
  91. end
  92. choices.update_values(:hosts => [:string])
  93. assert_equal(['localhost', 'foo.com'],
  94. choices[:hosts])
  95. end
  96. end
  97. end
  98. ### Boolean switches ###
  99. # Note that switches are string-valued for consistency with
  100. # other sources (like environment variables).
  101. class SWITCHES_CommandLineTest < Test::Unit::TestCase
  102. include UserChoices
  103. def test_boolean_switches_are_accepted
  104. with_command_args("--c") do
  105. choices = CommandLineChoices.fill do | c |
  106. c.uses_switch(:csv, "-c", "--csv")
  107. end
  108. assert_equal("true", choices[:csv])
  109. end
  110. end
  111. def test_unmentioned_switches_have_no_value
  112. with_command_args("") do
  113. choices = CommandLineChoices.fill do | c |
  114. c.uses_switch(:csv, "-c", "--csv")
  115. end
  116. assert_false(choices.has_key?(:csv))
  117. end
  118. end
  119. def test_switches_can_be_explicitly_false
  120. with_command_args("--no-csv") do
  121. choices = CommandLineChoices.fill do | c |
  122. c.uses_switch(:csv, "-c", "--csv")
  123. end
  124. assert_equal("false", choices[:csv])
  125. end
  126. end
  127. end
  128. ### Argument Lists ###
  129. # Arguments lists are treated as another option.
  130. class ARGLISTS_CommandLineTest < Test::Unit::TestCase
  131. include UserChoices
  132. def test_arglist_after_options_can_turned_into_an_option
  133. with_command_args("--unused unused arg1 arg2") {
  134. hash = CommandLineChoices.fill do | c |
  135. c.uses_option(:unused, "--unused VALUE") # just for grins
  136. c.uses_arglist(:args)
  137. end
  138. assert_true(hash.has_key?(:args))
  139. assert_equal(["arg1", "arg2"], hash[:args])
  140. }
  141. end
  142. def test_arglist_can_describe_allowable_number_of_arguments
  143. with_command_args("--unused unused arg1 arg2") {
  144. hash = CommandLineChoices.fill do | c |
  145. c.uses_option(:unused, "--unused VALUE") # just for grins
  146. c.uses_arglist(:args, 2)
  147. end
  148. assert_true(hash.has_key?(:args))
  149. assert_equal(["arg1", "arg2"], hash[:args])
  150. }
  151. end
  152. def test_error_if_exact_arglist_number_is_wrong
  153. with_command_args("arg1 arg2") {
  154. output = capturing_stderr do
  155. assert_wants_to_exit do
  156. hash = CommandLineChoices.fill do | c |
  157. c.uses_arglist(:args, 3)
  158. end
  159. end
  160. end
  161. assert_match(/Error in the command line:.*2 arguments given, 3 expected./, output)
  162. }
  163. end
  164. def test_arglist_arity_can_be_a_range
  165. with_command_args("arg1 arg2") {
  166. hash = CommandLineChoices.fill do | c |
  167. c.uses_arglist(:args, 1..3)
  168. end
  169. assert_true(hash.has_key?(:args))
  170. assert_equal(["arg1", "arg2"], hash[:args])
  171. }
  172. end
  173. def test_error_if_arglist_does_not_match_range
  174. with_command_args("arg1 arg2") {
  175. output = capturing_stderr do
  176. assert_wants_to_exit do
  177. hash = CommandLineChoices.fill do | c |
  178. c.uses_arglist(:args, 3..6)
  179. end
  180. end
  181. end
  182. assert_match(/Error in the command line:.*2 arguments given, 3 to 6 expected./, output)
  183. }
  184. end
  185. end
  186. ### Option-Handling Style ###
  187. class OPTION_STYLE_CommandLineTest < Test::Unit::TestCase
  188. include UserChoices
  189. Arglist_def = proc { | c |
  190. c.uses_switch(:switch, "--switch")
  191. c.uses_arglist(:args)
  192. }
  193. def test_default_style_is_permutation
  194. with_command_args('3 --switch 5') {
  195. choices = CommandLineChoices.fill(&Arglist_def)
  196. assert_equal('true', choices[:switch])
  197. assert_equal(['3', '5'], choices[:args])
  198. }
  199. end
  200. def test_subclass_allows_all_options_before_arguments
  201. with_command_args('3 --switch 5') {
  202. choices = PosixCommandLineChoices.fill(&Arglist_def)
  203. assert_equal(nil, choices[:switch])
  204. assert_equal(['3', '--switch', '5'], choices[:args])
  205. }
  206. end
  207. def test_choosing_posix_parsing_does_not_override_environment_variable
  208. with_environment_vars('POSIXLY_CORRECT' => 'hello') do
  209. with_command_args('3 --switch 5') {
  210. choices = PosixCommandLineChoices.fill(&Arglist_def)
  211. assert_equal('hello', ENV['POSIXLY_CORRECT'])
  212. }
  213. end
  214. end
  215. end
  216. ### Error Handling ###
  217. # Additional commandline-specific error checking.
  218. class ERROR_CommandLineTest < Test::Unit::TestCase
  219. include UserChoices
  220. def test_invalid_option_produces_error_message_and_exit
  221. with_command_args('--doofus 3') {
  222. output = capturing_stderr do
  223. assert_wants_to_exit do
  224. choices = CommandLineChoices.fill do | c |
  225. c.uses_option(:doofus, "--option VALUE")
  226. end
  227. end
  228. end
  229. assert_match(/invalid option.*doofus/, output)
  230. }
  231. end
  232. def test_error_is_identified_as_coming_from_the_command_line
  233. with_command_args('--doofus') {
  234. output = capturing_stderr do
  235. assert_wants_to_exit do
  236. choices = CommandLineChoices.fill do | c |
  237. c.uses_option(:doofus, "--doofus VALUE")
  238. end
  239. end
  240. end
  241. assert_match(/Error in the command line:.*missing argument.*doofus/, output)
  242. }
  243. end
  244. def test_errors_cause_usage_style_output
  245. with_command_args('wanted --wanted') {
  246. output = capturing_stderr do
  247. assert_wants_to_exit do
  248. choices = CommandLineChoices.fill { | c |
  249. default_isbn = "343"
  250. c.help_banner("Usage: ruby prog [options] [isbn]",
  251. "This and further strings are optional.")
  252. c.uses_option(:option, "-o", "--option=VALUE",
  253. "Message about option",
  254. "More about option")
  255. }
  256. end
  257. end
  258. lines = output.split($/)
  259. # puts output
  260. assert_match(/Error in the command line: /, lines[0])
  261. assert_equal("Usage: ruby prog [options] [isbn]", lines[1])
  262. assert_match(/This and further/, lines[2])
  263. assert_match(/\s*/, lines[3])
  264. assert_match(/Options:/, lines[4])
  265. assert_match(/-o.*--option=VALUE.*Message about option/, lines[5])
  266. assert_match(/More about option/, lines[6])
  267. assert_match(/--help.*Show this message/, lines.last)
  268. }
  269. end
  270. end
  271. # Here are a variety of ways of writing the error messages
  272. class ERROR_FORMATTING_CommandLineTest < Test::Unit::TestCase
  273. include UserChoices
  274. def test_range_violation_descriptions
  275. # Good about plurals.
  276. assert_raises_with_matching_message(StandardError,
  277. /2 arguments given, 3 expected/) {
  278. CommandLineChoices.claim_arglist_arity_OK(2, 3)
  279. }
  280. assert_raises_with_matching_message(StandardError,
  281. /1 argument given, 3 expected/) {
  282. CommandLineChoices.claim_arglist_arity_OK(1, 3)
  283. }
  284. assert_raises_with_matching_message(StandardError,
  285. /0 arguments given, 1 expected/) {
  286. CommandLineChoices.claim_arglist_arity_OK(0, 1)
  287. }
  288. # Handle both types of ranges.
  289. assert_raises_with_matching_message(StandardError,
  290. /2 arguments given, 3 to 5 expected/) {
  291. CommandLineChoices.claim_arglist_arity_OK(2, 3..5)
  292. }
  293. assert_raises_with_matching_message(StandardError,
  294. /1 argument given, 3 to 5 expected/) {
  295. CommandLineChoices.claim_arglist_arity_OK(1, 3...6)
  296. }
  297. end
  298. def test_a_singleton_arg_will_not_be_in_a_list
  299. with_command_args("arg-only") {
  300. hash = CommandLineChoices.fill do | c |
  301. c.uses_option(:unused, "--unused VALUE") # just for grins
  302. c.uses_arg(:arg)
  303. end
  304. assert_true(hash.has_key?(:arg))
  305. assert_equal("arg-only", hash[:arg])
  306. }
  307. end
  308. def test_singleton_args_generate_errors
  309. with_command_args("") {
  310. output = capturing_stderr do
  311. assert_wants_to_exit do
  312. hash = CommandLineChoices.fill do | c |
  313. c.uses_arg(:arg)
  314. end
  315. end
  316. end
  317. assert_match(/0 arguments given, 1 expected./, output)
  318. }
  319. end
  320. def test_singleton_arguments_can_take_ranges_to_make_them_optional
  321. with_command_args("") {
  322. hash = CommandLineChoices.fill do | c |
  323. c.uses_arg(:arg, 0..1)
  324. end
  325. assert_false(hash.has_key?(:arg))
  326. assert_equal(nil, hash[:arg])
  327. }
  328. end
  329. def test_that_optional_singleton_arguments_still_precludes_two
  330. with_command_args("one two") {
  331. output = capturing_stderr do
  332. assert_wants_to_exit do
  333. hash = CommandLineChoices.fill do | c |
  334. c.uses_arg(:arg, 0..1)
  335. end
  336. end
  337. end
  338. assert_match(/2 arguments given, 0 to 1 expected/, output)
  339. }
  340. end
  341. def test_shorthand_for_optional_arg_omits_explicit_range
  342. with_command_args("") {
  343. hash = CommandLineChoices.fill do | c |
  344. c.uses_optional_arg(:arg)
  345. end
  346. assert_false(hash.has_key?(:arg))
  347. assert_equal(nil, hash[:arg])
  348. }
  349. end
  350. end