PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://bitbucket.org/redricko/pragprog-scripting
Ruby | 415 lines | 355 code | 45 blank | 15 comment | 2 complexity | ca02a5c382c47854f32f52d8883ae0f5 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. class TestExternallyFilledHash < Test::Unit::TestCase
  13. class SubHash < UserChoices::ExternallyFilledHash
  14. def self.fill(names_to_values); new(names_to_values); end
  15. def source; "the test hash"; end
  16. def initialize(names_to_values)
  17. super()
  18. names_to_values.each do | name, value |
  19. key = name.to_sym
  20. record_name_key_and_value(name, key, value)
  21. end
  22. end
  23. end
  24. # Checking
  25. def test_checking_by_list
  26. SubHash.fill('a' => 'one').check_values(:a => ['one', 'two'])
  27. assert_raises_with_matching_message(StandardError,
  28. /Error in the test hash: 'one' is not a valid value for 'a'/) {
  29. SubHash.fill('a' => 'one').check_values(:a => ['two', 'three'])
  30. }
  31. end
  32. def test_checking_as_integer
  33. SubHash.fill('a' => '1').check_values(:a => :integer)
  34. assert_raises_with_matching_message(StandardError,
  35. /Error in the test hash: 'a' requires an integer value, and '1d' doesn't look like one/) {
  36. SubHash.fill('a' => '1d').check_values(:a => :integer)
  37. }
  38. end
  39. def test_checking_as_boolean
  40. SubHash.fill('a' => 'true').check_values(:a => :boolean)
  41. SubHash.fill('a' => 'false').check_values(:a => :boolean)
  42. # Case insensitive
  43. SubHash.fill('a' => 'False').check_values(:a => :boolean)
  44. SubHash.fill('a' => 'TRUE').check_values(:a => :boolean)
  45. assert_raises_with_matching_message(StandardError,
  46. /Error in the test hash: 'a' requires a boolean value, and 'tru' doesn't look like one/) {
  47. SubHash.fill('a' => 'tru').check_values(:a => :boolean)
  48. }
  49. end
  50. def test_it_is_ok_for_key_not_to_appear
  51. SubHash.fill('b' => 'foo').check_values(:a => :integer)
  52. end
  53. # Changing values
  54. def test_updating_into_integer
  55. h = SubHash.fill('a' => '1')
  56. h.update_values(:a => :integer)
  57. assert_equal(1, h[:a])
  58. end
  59. def test_updating_into_boolean
  60. h = SubHash.fill('a' => 'true')
  61. h.update_values(:a => :boolean)
  62. assert_equal(true, h[:a])
  63. h = SubHash.fill('a' => 'FALSE')
  64. h.update_values(:a => :boolean)
  65. assert_equal(false, h[:a])
  66. end
  67. def test_updating_into_array
  68. h = SubHash.fill('names' => 'brian,dawn,paul,sophie')
  69. h.update_values(:names => [:string])
  70. assert_equal(%w{brian dawn paul sophie}, h[:names])
  71. end
  72. def test_it_is_ok_for_key_not_to_appear
  73. h = SubHash.fill('b' => 'foo')
  74. h.update_values(:a => :integer)
  75. assert_equal('foo', h[:b])
  76. assert_false(h.respond_to?(:a))
  77. end
  78. end
  79. class DefaultChoicesTest < Test::Unit::TestCase
  80. include UserChoices
  81. def test_default_values_are_created_with_key_not_string
  82. # It just seemed for the default values to be created with a
  83. # string transformed into a hash -- too different from creating
  84. # a default hash.
  85. choices = DefaultChoices.fill(:a => 'a')
  86. assert_equal(1, choices.size)
  87. assert_equal('a', choices[:a])
  88. end
  89. def test_value_checking_is_set_up_properly
  90. assert_raises_with_matching_message(StandardError,
  91. /Error in the default values: ':a' requires an integer value, and 'five' doesn't look like one/) {
  92. DefaultChoices.fill(:a => 'five').check_values(:a => :integer)
  93. }
  94. end
  95. def test_value_updating_is_set_up_properly
  96. # Even though this means that the value 1 in the default is
  97. # specified as "1". Too awkward?
  98. choices = DefaultChoices.fill(:a => '5')
  99. choices.update_values(:a => :integer)
  100. assert_equal(5, choices[:a])
  101. choices = DefaultChoices.fill(:a => '5,6')
  102. choices.update_values(:a => [:string])
  103. assert_equal(['5', '6'], choices[:a])
  104. end
  105. def test_nil_is_default
  106. choices = DefaultChoices.fill(:a => '5')
  107. assert_nil(choices[:foo])
  108. end
  109. end
  110. class EnvironmentChoicesTest < Test::Unit::TestCase
  111. include UserChoices
  112. def test_the_environment_args_of_interest_can_be_listed_explicitly
  113. with_environment_vars('amazon_option' => "1",
  114. 'root' => 'ok',
  115. '~' => 'ok, too') do
  116. choices = EnvironmentChoices.fill(:option => 'amazon_option',
  117. :root => 'root',
  118. :home => '~')
  119. assert_equal(3, choices.size)
  120. assert_equal('1', choices[:option])
  121. assert_equal('ok', choices[:root])
  122. assert_equal('ok, too', choices[:home])
  123. end
  124. end
  125. def test_unmentioned_environment_vars_are_ignored
  126. with_environment_vars('unfound' => "1") do
  127. choices = EnvironmentChoices.fill(:option => 'amazon_option')
  128. assert_true(choices.empty?)
  129. end
  130. end
  131. def test_nil_is_default
  132. with_environment_vars('found' => "1") do
  133. choices = EnvironmentChoices.fill(:option => 'f')
  134. assert_nil(choices[:foo])
  135. assert_nil(choices[:option]) # for fun
  136. end
  137. end
  138. def test_value_checking_is_set_up_properly
  139. with_environment_vars('amazon_option' => "1") do
  140. assert_raises_with_matching_message(StandardError,
  141. /Error in the environment: '1' is not a valid value for 'amazon_option'/) {
  142. choices = EnvironmentChoices.fill(:a => 'amazon_option')
  143. choices.check_values(:a => ['one', 'dva'])
  144. }
  145. end
  146. with_environment_vars('amazon_option' => "1ed") do
  147. assert_raises_with_matching_message(StandardError,
  148. /Error in the environment: 'amazon_option' requires an integer value, and '1ed' doesn't look like one/) {
  149. choices = EnvironmentChoices.fill(:a => 'amazon_option')
  150. choices.check_values(:a => :integer)
  151. }
  152. end
  153. end
  154. def test_value_updating_is_set_up_properly
  155. with_environment_vars('a' => "1", 'names' => 'foo,bar') do
  156. choices = EnvironmentChoices.fill(:a => 'a', :names => 'names')
  157. choices.update_values(:a => :integer,
  158. :names => [:string])
  159. assert_equal(1, choices[:a])
  160. assert_equal(['foo', 'bar'], choices[:names])
  161. end
  162. end
  163. def test_the_environment_args_of_interest_can_be_described_by_prefix
  164. with_environment_vars('amazon_option' => "1") do
  165. choices = EnvironmentChoices.with_prefix('amazon_')
  166. assert_true(choices.has_key?(:option))
  167. assert_equal('1', choices[:option])
  168. end
  169. end
  170. def test_can_also_give_explicit_args_of_interest
  171. with_environment_vars('amazon_o' => "1",
  172. 'other_option' => 'still found') do
  173. choices = EnvironmentChoices.with_prefix('amazon_',
  174. :other => 'other_option')
  175. assert_equal(2, choices.size)
  176. assert_equal('1', choices[:o])
  177. assert_equal('still found', choices[:other])
  178. end
  179. end
  180. end
  181. class XmlConfigFileChoicesTestCase < Test::Unit::TestCase
  182. include UserChoices
  183. def setup
  184. builder = Builder::XmlMarkup.new(:indent => 2)
  185. @some_xml = builder.config {
  186. builder.reverse("true")
  187. builder.maximum("53")
  188. builder.host('a.com')
  189. builder.host('b.com')
  190. }
  191. end
  192. def test_config_file_normal_use
  193. with_local_config_file('.amazonrc', @some_xml) {
  194. choices = XmlConfigFileChoices.fill(".amazonrc")
  195. assert_equal(3, choices.size)
  196. assert_equal("true", choices[:reverse])
  197. assert_equal("53", choices[:maximum])
  198. assert_equal(['a.com', 'b.com'], choices[:host])
  199. }
  200. end
  201. def test_config_file_need_not_exist
  202. assert_false(File.exist?(".amazonrc"))
  203. choices = XmlConfigFileChoices.fill(".amazonrc")
  204. assert_true(choices.empty?)
  205. end
  206. def test_config_file_with_bad_xml
  207. with_local_config_file('.amazonrc',"<malformed></xml>") {
  208. assert_raise_with_matching_message(REXML::ParseException,
  209. %r{Badly formatted configuration file ./.amazonrc: .*Missing end tag}) do
  210. XmlConfigFileChoices.fill(".amazonrc")
  211. end
  212. }
  213. end
  214. def test_config_file_value_checking_is_set_up_properly
  215. with_local_config_file(".amazonrc", @some_xml) do
  216. assert_raises_with_matching_message(StandardError,
  217. %r{Error in configuration file ./.amazonrc: '53' is not a valid value for 'maximum'}) {
  218. choices = XmlConfigFileChoices.fill(".amazonrc")
  219. choices.check_values(:maximum => ['low', 'high'])
  220. }
  221. end
  222. with_local_config_file(".amazonrc", @some_xml) do
  223. assert_raises_with_matching_message(StandardError,
  224. /Error in configuration file.* 'reverse' requires an integer value, and 'true' doesn't look like one/) {
  225. choices = XmlConfigFileChoices.fill('.amazonrc')
  226. choices.check_values(:reverse => :integer)
  227. }
  228. end
  229. end
  230. def test_value_updating_is_set_up_properly
  231. with_local_config_file('.amazonrc', @some_xml) do
  232. choices = XmlConfigFileChoices.fill('.amazonrc')
  233. choices.update_values(:maximum => :integer)
  234. assert_equal(53, choices[:maximum])
  235. end
  236. end
  237. def test_unmentioned_values_are_nil
  238. with_local_config_file('.amazonrc', @some_xml) do
  239. choices = XmlConfigFileChoices.fill('.amazonrc')
  240. assert_nil(choices[:unmentioned])
  241. end
  242. end
  243. end
  244. class CompositionChoicesTests < Test::Unit::TestCase
  245. include UserChoices
  246. def test_earlier_has_priority
  247. with_command_args("--option perfectly-fine --only-cmd=oc") {
  248. choices = ComposedChoices.fill(
  249. CommandLineChoices.fill { | c |
  250. c.uses_option(:option, '--option VALUE')
  251. c.uses_option(:only_cmd, '--only-cmd VALUE')
  252. },
  253. DefaultChoices.fill(:option => '0.3',
  254. :only_default => 'od'))
  255. assert_equal("perfectly-fine", choices[:option])
  256. assert_equal("oc", choices[:only_cmd])
  257. assert_equal("od", choices[:only_default])
  258. }
  259. end
  260. def test_checking_is_forwarded
  261. with_command_args("--command-option perfectly-fine") {
  262. assert_raises_with_matching_message(StandardError,
  263. /Error in the default values/) {
  264. ComposedChoices.fill(
  265. CommandLineChoices.fill { | c |
  266. c.uses_option(:command_option,
  267. '--command-option VALUE')
  268. },
  269. DefaultChoices.fill(:broken => '0.3')) { | c |
  270. c.check_values(:broken => :integer)
  271. }
  272. }
  273. }
  274. end
  275. def test_updates_are_forwarded
  276. with_environment_vars("amazon_rc" => "1") {
  277. choices = ComposedChoices.fill(
  278. EnvironmentChoices.with_prefix('amazon'),
  279. DefaultChoices.fill(:_rc => '3')) { | c |
  280. c.check_values(:_rc => :integer)
  281. c.update_values(:_rc => :integer)
  282. }
  283. assert_equal(1, choices[:_rc])
  284. }
  285. end
  286. def test_unmentioned_choices_are_nil
  287. choices = ComposedChoices.fill(
  288. DefaultChoices.fill(:rc => '5'),
  289. DefaultChoices.fill(:rc => '3')) { | c |
  290. c.check_values(:rc => :integer)
  291. }
  292. assert_equal(nil, choices[:unmentioned])
  293. assert_equal('5', choices[:rc]) # for fun
  294. end
  295. # There's an unfortunate side effect of the way command-line arglists
  296. # are handled. Should empty arglists override later choices objects?
  297. # Probably. But right now that only happens for optional arguments.
  298. # An empty command list overrides, though, turning into the empty list.
  299. # Hard to fix. Fix if anyone ever complains.
  300. def test_missing_optional_args_do_not_override_lower_precedence_sources
  301. with_command_args("") {
  302. choices = ComposedChoices.fill(
  303. CommandLineChoices.fill { | c |
  304. c.uses_optional_arg(:name)
  305. },
  306. DefaultChoices.fill(:name => 'default'))
  307. assert_equal('default', choices[:name])
  308. }
  309. end
  310. def test_however_given_optional_args_do
  311. with_command_args("override") {
  312. choices = ComposedChoices.fill(
  313. CommandLineChoices.fill { | c |
  314. c.uses_optional_arg(:name)
  315. },
  316. DefaultChoices.fill(:name => 'default'))
  317. assert_equal('override', choices[:name])
  318. }
  319. end
  320. def test_empty_arglists_DO__surprisingly__override_lower_precedence_sources
  321. xml = "<config><name>1</name><name>2</name></config>"
  322. with_local_config_file("test-config", xml) {
  323. with_command_args("") {
  324. choices = ComposedChoices.fill(
  325. CommandLineChoices.fill { | c |
  326. c.uses_arglist(:name)
  327. },
  328. XmlConfigFileChoices.fill("test-config"))
  329. # assert_equal(['1', '2'], choices[:name])
  330. assert_equal([], choices[:name])
  331. }
  332. }
  333. end
  334. def test_however_non_empty_arglists_do # name suggests what should be expected of the previous test.
  335. with_command_args("1") {
  336. choices = ComposedChoices.fill(
  337. CommandLineChoices.fill { | c |
  338. c.uses_arglist(:name)
  339. },
  340. DefaultChoices.fill(:name => ['default', '1']))
  341. assert_equal(['1'], choices[:name])
  342. }
  343. end
  344. end