1# This Source Code Form is subject to the terms of the Mozilla Public 2# License, v. 2.0. If a copy of the MPL was not distributed with this 3# file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5from __future__ import absolute_import, print_function, unicode_literals 6 7import unittest 8 9from mozunit import main 10 11from mozbuild.configure.options import ( 12 CommandLineHelper, 13 ConflictingOptionError, 14 InvalidOptionError, 15 NegativeOptionValue, 16 Option, 17 OptionValue, 18 PositiveOptionValue, 19) 20 21 22class Option(Option): 23 def __init__(self, *args, **kwargs): 24 kwargs['help'] = 'Dummy help' 25 super(Option, self).__init__(*args, **kwargs) 26 27 28class TestOption(unittest.TestCase): 29 def test_option(self): 30 option = Option('--option') 31 self.assertEquals(option.prefix, '') 32 self.assertEquals(option.name, 'option') 33 self.assertEquals(option.env, None) 34 self.assertFalse(option.default) 35 36 option = Option('--enable-option') 37 self.assertEquals(option.prefix, 'enable') 38 self.assertEquals(option.name, 'option') 39 self.assertEquals(option.env, None) 40 self.assertFalse(option.default) 41 42 option = Option('--disable-option') 43 self.assertEquals(option.prefix, 'disable') 44 self.assertEquals(option.name, 'option') 45 self.assertEquals(option.env, None) 46 self.assertTrue(option.default) 47 48 option = Option('--with-option') 49 self.assertEquals(option.prefix, 'with') 50 self.assertEquals(option.name, 'option') 51 self.assertEquals(option.env, None) 52 self.assertFalse(option.default) 53 54 option = Option('--without-option') 55 self.assertEquals(option.prefix, 'without') 56 self.assertEquals(option.name, 'option') 57 self.assertEquals(option.env, None) 58 self.assertTrue(option.default) 59 60 option = Option('--without-option-foo', env='MOZ_OPTION') 61 self.assertEquals(option.env, 'MOZ_OPTION') 62 63 option = Option(env='MOZ_OPTION') 64 self.assertEquals(option.prefix, '') 65 self.assertEquals(option.name, None) 66 self.assertEquals(option.env, 'MOZ_OPTION') 67 self.assertFalse(option.default) 68 69 with self.assertRaises(InvalidOptionError) as e: 70 Option('--option', nargs=0, default=('a',)) 71 self.assertEquals(e.exception.message, 72 "The given `default` doesn't satisfy `nargs`") 73 74 with self.assertRaises(InvalidOptionError) as e: 75 Option('--option', nargs=1, default=()) 76 self.assertEquals( 77 e.exception.message, 78 'default must be a bool, a string or a tuple of strings') 79 80 with self.assertRaises(InvalidOptionError) as e: 81 Option('--option', nargs=1, default=True) 82 self.assertEquals(e.exception.message, 83 "The given `default` doesn't satisfy `nargs`") 84 85 with self.assertRaises(InvalidOptionError) as e: 86 Option('--option', nargs=1, default=('a', 'b')) 87 self.assertEquals(e.exception.message, 88 "The given `default` doesn't satisfy `nargs`") 89 90 with self.assertRaises(InvalidOptionError) as e: 91 Option('--option', nargs=2, default=()) 92 self.assertEquals( 93 e.exception.message, 94 'default must be a bool, a string or a tuple of strings') 95 96 with self.assertRaises(InvalidOptionError) as e: 97 Option('--option', nargs=2, default=True) 98 self.assertEquals(e.exception.message, 99 "The given `default` doesn't satisfy `nargs`") 100 101 with self.assertRaises(InvalidOptionError) as e: 102 Option('--option', nargs=2, default=('a',)) 103 self.assertEquals(e.exception.message, 104 "The given `default` doesn't satisfy `nargs`") 105 106 with self.assertRaises(InvalidOptionError) as e: 107 Option('--option', nargs='?', default=('a', 'b')) 108 self.assertEquals(e.exception.message, 109 "The given `default` doesn't satisfy `nargs`") 110 111 with self.assertRaises(InvalidOptionError) as e: 112 Option('--option', nargs='+', default=()) 113 self.assertEquals( 114 e.exception.message, 115 'default must be a bool, a string or a tuple of strings') 116 117 with self.assertRaises(InvalidOptionError) as e: 118 Option('--option', nargs='+', default=True) 119 self.assertEquals(e.exception.message, 120 "The given `default` doesn't satisfy `nargs`") 121 122 # --disable options with a nargs value that requires at least one 123 # argument need to be given a default. 124 with self.assertRaises(InvalidOptionError) as e: 125 Option('--disable-option', nargs=1) 126 self.assertEquals(e.exception.message, 127 "The given `default` doesn't satisfy `nargs`") 128 129 with self.assertRaises(InvalidOptionError) as e: 130 Option('--disable-option', nargs='+') 131 self.assertEquals(e.exception.message, 132 "The given `default` doesn't satisfy `nargs`") 133 134 # Test nargs inference from default value 135 option = Option('--with-foo', default=True) 136 self.assertEquals(option.nargs, 0) 137 138 option = Option('--with-foo', default=False) 139 self.assertEquals(option.nargs, 0) 140 141 option = Option('--with-foo', default='a') 142 self.assertEquals(option.nargs, '?') 143 144 option = Option('--with-foo', default=('a',)) 145 self.assertEquals(option.nargs, '?') 146 147 option = Option('--with-foo', default=('a', 'b')) 148 self.assertEquals(option.nargs, '*') 149 150 option = Option(env='FOO', default=True) 151 self.assertEquals(option.nargs, 0) 152 153 option = Option(env='FOO', default=False) 154 self.assertEquals(option.nargs, 0) 155 156 option = Option(env='FOO', default='a') 157 self.assertEquals(option.nargs, '?') 158 159 option = Option(env='FOO', default=('a',)) 160 self.assertEquals(option.nargs, '?') 161 162 option = Option(env='FOO', default=('a', 'b')) 163 self.assertEquals(option.nargs, '*') 164 165 def test_option_option(self): 166 for option in ( 167 '--option', 168 '--enable-option', 169 '--disable-option', 170 '--with-option', 171 '--without-option', 172 ): 173 self.assertEquals(Option(option).option, option) 174 self.assertEquals(Option(option, env='FOO').option, option) 175 176 opt = Option(option, default=False) 177 self.assertEquals(opt.option, 178 option.replace('-disable-', '-enable-') 179 .replace('-without-', '-with-')) 180 181 opt = Option(option, default=True) 182 self.assertEquals(opt.option, 183 option.replace('-enable-', '-disable-') 184 .replace('-with-', '-without-')) 185 186 self.assertEquals(Option(env='FOO').option, 'FOO') 187 188 def test_option_choices(self): 189 with self.assertRaises(InvalidOptionError) as e: 190 Option('--option', nargs=3, choices=('a', 'b')) 191 self.assertEquals(e.exception.message, 192 'Not enough `choices` for `nargs`') 193 194 with self.assertRaises(InvalidOptionError) as e: 195 Option('--without-option', nargs=1, choices=('a', 'b')) 196 self.assertEquals(e.exception.message, 197 'A `default` must be given along with `choices`') 198 199 with self.assertRaises(InvalidOptionError) as e: 200 Option('--without-option', nargs='+', choices=('a', 'b')) 201 self.assertEquals(e.exception.message, 202 'A `default` must be given along with `choices`') 203 204 with self.assertRaises(InvalidOptionError) as e: 205 Option('--without-option', default='c', choices=('a', 'b')) 206 self.assertEquals(e.exception.message, 207 "The `default` value must be one of 'a', 'b'") 208 209 with self.assertRaises(InvalidOptionError) as e: 210 Option('--without-option', default=('a', 'c',), choices=('a', 'b')) 211 self.assertEquals(e.exception.message, 212 "The `default` value must be one of 'a', 'b'") 213 214 with self.assertRaises(InvalidOptionError) as e: 215 Option('--without-option', default=('c',), choices=('a', 'b')) 216 self.assertEquals(e.exception.message, 217 "The `default` value must be one of 'a', 'b'") 218 219 option = Option('--with-option', nargs='+', choices=('a', 'b')) 220 with self.assertRaises(InvalidOptionError) as e: 221 option.get_value('--with-option=c') 222 self.assertEquals(e.exception.message, "'c' is not one of 'a', 'b'") 223 224 value = option.get_value('--with-option=b,a') 225 self.assertTrue(value) 226 self.assertEquals(PositiveOptionValue(('b', 'a')), value) 227 228 option = Option('--without-option', nargs='*', default='a', 229 choices=('a', 'b')) 230 with self.assertRaises(InvalidOptionError) as e: 231 option.get_value('--with-option=c') 232 self.assertEquals(e.exception.message, "'c' is not one of 'a', 'b'") 233 234 value = option.get_value('--with-option=b,a') 235 self.assertTrue(value) 236 self.assertEquals(PositiveOptionValue(('b', 'a')), value) 237 238 # Test nargs inference from choices 239 option = Option('--with-option', choices=('a', 'b')) 240 self.assertEqual(option.nargs, 1) 241 242 # Test "relative" values 243 option = Option('--with-option', nargs='*', default=('b', 'c'), 244 choices=('a', 'b', 'c', 'd')) 245 246 value = option.get_value('--with-option=+d') 247 self.assertEquals(PositiveOptionValue(('b', 'c', 'd')), value) 248 249 value = option.get_value('--with-option=-b') 250 self.assertEquals(PositiveOptionValue(('c',)), value) 251 252 value = option.get_value('--with-option=-b,+d') 253 self.assertEquals(PositiveOptionValue(('c','d')), value) 254 255 # Adding something that is in the default is fine 256 value = option.get_value('--with-option=+b') 257 self.assertEquals(PositiveOptionValue(('b', 'c')), value) 258 259 # Removing something that is not in the default is fine, as long as it 260 # is one of the choices 261 value = option.get_value('--with-option=-a') 262 self.assertEquals(PositiveOptionValue(('b', 'c')), value) 263 264 with self.assertRaises(InvalidOptionError) as e: 265 option.get_value('--with-option=-e') 266 self.assertEquals(e.exception.message, 267 "'e' is not one of 'a', 'b', 'c', 'd'") 268 269 # Other "not a choice" errors. 270 with self.assertRaises(InvalidOptionError) as e: 271 option.get_value('--with-option=+e') 272 self.assertEquals(e.exception.message, 273 "'e' is not one of 'a', 'b', 'c', 'd'") 274 275 with self.assertRaises(InvalidOptionError) as e: 276 option.get_value('--with-option=e') 277 self.assertEquals(e.exception.message, 278 "'e' is not one of 'a', 'b', 'c', 'd'") 279 280 def test_option_value_compare(self): 281 # OptionValue are tuple and equivalence should compare as tuples. 282 val = PositiveOptionValue(('foo',)) 283 284 self.assertEqual(val[0], 'foo') 285 self.assertEqual(val, PositiveOptionValue(('foo',))) 286 self.assertNotEqual(val, PositiveOptionValue(('foo', 'bar'))) 287 288 # Can compare a tuple to an OptionValue. 289 self.assertEqual(val, ('foo',)) 290 self.assertNotEqual(val, ('foo', 'bar')) 291 292 # Different OptionValue types are never equal. 293 self.assertNotEqual(val, OptionValue(('foo',))) 294 295 # For usability reasons, we raise TypeError when attempting to compare 296 # against a non-tuple. 297 with self.assertRaisesRegexp(TypeError, 'cannot compare a'): 298 val == 'foo' 299 300 # But we allow empty option values to compare otherwise we can't 301 # easily compare value-less types like PositiveOptionValue and 302 # NegativeOptionValue. 303 empty_positive = PositiveOptionValue() 304 empty_negative = NegativeOptionValue() 305 self.assertEqual(empty_positive, ()) 306 self.assertEqual(empty_positive, PositiveOptionValue()) 307 self.assertEqual(empty_negative, ()) 308 self.assertEqual(empty_negative, NegativeOptionValue()) 309 self.assertNotEqual(empty_positive, 'foo') 310 self.assertNotEqual(empty_positive, ('foo',)) 311 self.assertNotEqual(empty_negative, 'foo') 312 self.assertNotEqual(empty_negative, ('foo',)) 313 314 def test_option_value_format(self): 315 val = PositiveOptionValue() 316 self.assertEquals('--with-value', val.format('--with-value')) 317 self.assertEquals('--with-value', val.format('--without-value')) 318 self.assertEquals('--enable-value', val.format('--enable-value')) 319 self.assertEquals('--enable-value', val.format('--disable-value')) 320 self.assertEquals('--value', val.format('--value')) 321 self.assertEquals('VALUE=1', val.format('VALUE')) 322 323 val = PositiveOptionValue(('a',)) 324 self.assertEquals('--with-value=a', val.format('--with-value')) 325 self.assertEquals('--with-value=a', val.format('--without-value')) 326 self.assertEquals('--enable-value=a', val.format('--enable-value')) 327 self.assertEquals('--enable-value=a', val.format('--disable-value')) 328 self.assertEquals('--value=a', val.format('--value')) 329 self.assertEquals('VALUE=a', val.format('VALUE')) 330 331 val = PositiveOptionValue(('a', 'b')) 332 self.assertEquals('--with-value=a,b', val.format('--with-value')) 333 self.assertEquals('--with-value=a,b', val.format('--without-value')) 334 self.assertEquals('--enable-value=a,b', val.format('--enable-value')) 335 self.assertEquals('--enable-value=a,b', val.format('--disable-value')) 336 self.assertEquals('--value=a,b', val.format('--value')) 337 self.assertEquals('VALUE=a,b', val.format('VALUE')) 338 339 val = NegativeOptionValue() 340 self.assertEquals('--without-value', val.format('--with-value')) 341 self.assertEquals('--without-value', val.format('--without-value')) 342 self.assertEquals('--disable-value', val.format('--enable-value')) 343 self.assertEquals('--disable-value', val.format('--disable-value')) 344 self.assertEquals('', val.format('--value')) 345 self.assertEquals('VALUE=', val.format('VALUE')) 346 347 def test_option_value(self, name='option', nargs=0, default=None): 348 disabled = name.startswith(('disable-', 'without-')) 349 if disabled: 350 negOptionValue = PositiveOptionValue 351 posOptionValue = NegativeOptionValue 352 else: 353 posOptionValue = PositiveOptionValue 354 negOptionValue = NegativeOptionValue 355 defaultValue = (PositiveOptionValue(default) 356 if default else negOptionValue()) 357 358 option = Option('--%s' % name, nargs=nargs, default=default) 359 360 if nargs in (0, '?', '*') or disabled: 361 value = option.get_value('--%s' % name, 'option') 362 self.assertEquals(value, posOptionValue()) 363 self.assertEquals(value.origin, 'option') 364 else: 365 with self.assertRaises(InvalidOptionError) as e: 366 option.get_value('--%s' % name) 367 if nargs == 1: 368 self.assertEquals(e.exception.message, 369 '--%s takes 1 value' % name) 370 elif nargs == '+': 371 self.assertEquals(e.exception.message, 372 '--%s takes 1 or more values' % name) 373 else: 374 self.assertEquals(e.exception.message, 375 '--%s takes 2 values' % name) 376 377 value = option.get_value('') 378 self.assertEquals(value, defaultValue) 379 self.assertEquals(value.origin, 'default') 380 381 value = option.get_value(None) 382 self.assertEquals(value, defaultValue) 383 self.assertEquals(value.origin, 'default') 384 385 with self.assertRaises(AssertionError): 386 value = option.get_value('MOZ_OPTION=', 'environment') 387 388 with self.assertRaises(AssertionError): 389 value = option.get_value('MOZ_OPTION=1', 'environment') 390 391 with self.assertRaises(AssertionError): 392 value = option.get_value('--foo') 393 394 if nargs in (1, '?', '*', '+') and not disabled: 395 value = option.get_value('--%s=' % name, 'option') 396 self.assertEquals(value, PositiveOptionValue(('',))) 397 self.assertEquals(value.origin, 'option') 398 else: 399 with self.assertRaises(InvalidOptionError) as e: 400 option.get_value('--%s=' % name) 401 if disabled: 402 self.assertEquals(e.exception.message, 403 'Cannot pass a value to --%s' % name) 404 else: 405 self.assertEquals(e.exception.message, 406 '--%s takes %d values' % (name, nargs)) 407 408 if nargs in (1, '?', '*', '+') and not disabled: 409 value = option.get_value('--%s=foo' % name, 'option') 410 self.assertEquals(value, PositiveOptionValue(('foo',))) 411 self.assertEquals(value.origin, 'option') 412 else: 413 with self.assertRaises(InvalidOptionError) as e: 414 option.get_value('--%s=foo' % name) 415 if disabled: 416 self.assertEquals(e.exception.message, 417 'Cannot pass a value to --%s' % name) 418 else: 419 self.assertEquals(e.exception.message, 420 '--%s takes %d values' % (name, nargs)) 421 422 if nargs in (2, '*', '+') and not disabled: 423 value = option.get_value('--%s=foo,bar' % name, 'option') 424 self.assertEquals(value, PositiveOptionValue(('foo', 'bar'))) 425 self.assertEquals(value.origin, 'option') 426 else: 427 with self.assertRaises(InvalidOptionError) as e: 428 option.get_value('--%s=foo,bar' % name, 'option') 429 if disabled: 430 self.assertEquals(e.exception.message, 431 'Cannot pass a value to --%s' % name) 432 elif nargs == '?': 433 self.assertEquals(e.exception.message, 434 '--%s takes 0 or 1 values' % name) 435 else: 436 self.assertEquals(e.exception.message, 437 '--%s takes %d value%s' 438 % (name, nargs, 's' if nargs != 1 else '')) 439 440 option = Option('--%s' % name, env='MOZ_OPTION', nargs=nargs, 441 default=default) 442 if nargs in (0, '?', '*') or disabled: 443 value = option.get_value('--%s' % name, 'option') 444 self.assertEquals(value, posOptionValue()) 445 self.assertEquals(value.origin, 'option') 446 else: 447 with self.assertRaises(InvalidOptionError) as e: 448 option.get_value('--%s' % name) 449 if disabled: 450 self.assertEquals(e.exception.message, 451 'Cannot pass a value to --%s' % name) 452 elif nargs == '+': 453 self.assertEquals(e.exception.message, 454 '--%s takes 1 or more values' % name) 455 else: 456 self.assertEquals(e.exception.message, 457 '--%s takes %d value%s' 458 % (name, nargs, 's' if nargs != 1 else '')) 459 460 value = option.get_value('') 461 self.assertEquals(value, defaultValue) 462 self.assertEquals(value.origin, 'default') 463 464 value = option.get_value(None) 465 self.assertEquals(value, defaultValue) 466 self.assertEquals(value.origin, 'default') 467 468 value = option.get_value('MOZ_OPTION=', 'environment') 469 self.assertEquals(value, NegativeOptionValue()) 470 self.assertEquals(value.origin, 'environment') 471 472 if nargs in (0, '?', '*'): 473 value = option.get_value('MOZ_OPTION=1', 'environment') 474 self.assertEquals(value, PositiveOptionValue()) 475 self.assertEquals(value.origin, 'environment') 476 elif nargs in (1, '+'): 477 value = option.get_value('MOZ_OPTION=1', 'environment') 478 self.assertEquals(value, PositiveOptionValue(('1',))) 479 self.assertEquals(value.origin, 'environment') 480 else: 481 with self.assertRaises(InvalidOptionError) as e: 482 option.get_value('MOZ_OPTION=1', 'environment') 483 self.assertEquals(e.exception.message, 'MOZ_OPTION takes 2 values') 484 485 if nargs in (1, '?', '*', '+') and not disabled: 486 value = option.get_value('--%s=' % name, 'option') 487 self.assertEquals(value, PositiveOptionValue(('',))) 488 self.assertEquals(value.origin, 'option') 489 else: 490 with self.assertRaises(InvalidOptionError) as e: 491 option.get_value('--%s=' % name, 'option') 492 if disabled: 493 self.assertEquals(e.exception.message, 494 'Cannot pass a value to --%s' % name) 495 else: 496 self.assertEquals(e.exception.message, 497 '--%s takes %d values' % (name, nargs)) 498 499 with self.assertRaises(AssertionError): 500 value = option.get_value('--foo', 'option') 501 502 if nargs in (1, '?', '*', '+'): 503 value = option.get_value('MOZ_OPTION=foo', 'environment') 504 self.assertEquals(value, PositiveOptionValue(('foo',))) 505 self.assertEquals(value.origin, 'environment') 506 else: 507 with self.assertRaises(InvalidOptionError) as e: 508 option.get_value('MOZ_OPTION=foo', 'environment') 509 self.assertEquals(e.exception.message, 510 'MOZ_OPTION takes %d values' % nargs) 511 512 if nargs in (2, '*', '+'): 513 value = option.get_value('MOZ_OPTION=foo,bar', 'environment') 514 self.assertEquals(value, PositiveOptionValue(('foo', 'bar'))) 515 self.assertEquals(value.origin, 'environment') 516 else: 517 with self.assertRaises(InvalidOptionError) as e: 518 option.get_value('MOZ_OPTION=foo,bar', 'environment') 519 if nargs == '?': 520 self.assertEquals(e.exception.message, 521 'MOZ_OPTION takes 0 or 1 values') 522 else: 523 self.assertEquals(e.exception.message, 524 'MOZ_OPTION takes %d value%s' 525 % (nargs, 's' if nargs != 1 else '')) 526 527 if disabled: 528 return option 529 530 env_option = Option(env='MOZ_OPTION', nargs=nargs, default=default) 531 with self.assertRaises(AssertionError): 532 env_option.get_value('--%s' % name) 533 534 value = env_option.get_value('') 535 self.assertEquals(value, defaultValue) 536 self.assertEquals(value.origin, 'default') 537 538 value = env_option.get_value('MOZ_OPTION=', 'environment') 539 self.assertEquals(value, negOptionValue()) 540 self.assertEquals(value.origin, 'environment') 541 542 if nargs in (0, '?', '*'): 543 value = env_option.get_value('MOZ_OPTION=1', 'environment') 544 self.assertEquals(value, posOptionValue()) 545 self.assertTrue(value) 546 self.assertEquals(value.origin, 'environment') 547 elif nargs in (1, '+'): 548 value = env_option.get_value('MOZ_OPTION=1', 'environment') 549 self.assertEquals(value, PositiveOptionValue(('1',))) 550 self.assertEquals(value.origin, 'environment') 551 else: 552 with self.assertRaises(InvalidOptionError) as e: 553 env_option.get_value('MOZ_OPTION=1', 'environment') 554 self.assertEquals(e.exception.message, 'MOZ_OPTION takes 2 values') 555 556 with self.assertRaises(AssertionError) as e: 557 env_option.get_value('--%s' % name) 558 559 with self.assertRaises(AssertionError) as e: 560 env_option.get_value('--foo') 561 562 if nargs in (1, '?', '*', '+'): 563 value = env_option.get_value('MOZ_OPTION=foo', 'environment') 564 self.assertEquals(value, PositiveOptionValue(('foo',))) 565 self.assertEquals(value.origin, 'environment') 566 else: 567 with self.assertRaises(InvalidOptionError) as e: 568 env_option.get_value('MOZ_OPTION=foo', 'environment') 569 self.assertEquals(e.exception.message, 570 'MOZ_OPTION takes %d values' % nargs) 571 572 if nargs in (2, '*', '+'): 573 value = env_option.get_value('MOZ_OPTION=foo,bar', 'environment') 574 self.assertEquals(value, PositiveOptionValue(('foo', 'bar'))) 575 self.assertEquals(value.origin, 'environment') 576 else: 577 with self.assertRaises(InvalidOptionError) as e: 578 env_option.get_value('MOZ_OPTION=foo,bar', 'environment') 579 if nargs == '?': 580 self.assertEquals(e.exception.message, 581 'MOZ_OPTION takes 0 or 1 values') 582 else: 583 self.assertEquals(e.exception.message, 584 'MOZ_OPTION takes %d value%s' 585 % (nargs, 's' if nargs != 1 else '')) 586 587 return option 588 589 def test_option_value_enable(self, enable='enable', disable='disable', 590 nargs=0, default=None): 591 option = self.test_option_value('%s-option' % enable, nargs=nargs, 592 default=default) 593 594 value = option.get_value('--%s-option' % disable, 'option') 595 self.assertEquals(value, NegativeOptionValue()) 596 self.assertEquals(value.origin, 'option') 597 598 option = self.test_option_value('%s-option' % disable, nargs=nargs, 599 default=default) 600 601 if nargs in (0, '?', '*'): 602 value = option.get_value('--%s-option' % enable, 'option') 603 self.assertEquals(value, PositiveOptionValue()) 604 self.assertEquals(value.origin, 'option') 605 else: 606 with self.assertRaises(InvalidOptionError) as e: 607 option.get_value('--%s-option' % enable, 'option') 608 if nargs == 1: 609 self.assertEquals(e.exception.message, 610 '--%s-option takes 1 value' % enable) 611 elif nargs == '+': 612 self.assertEquals(e.exception.message, 613 '--%s-option takes 1 or more values' 614 % enable) 615 else: 616 self.assertEquals(e.exception.message, 617 '--%s-option takes 2 values' % enable) 618 619 def test_option_value_with(self): 620 self.test_option_value_enable('with', 'without') 621 622 def test_option_value_invalid_nargs(self): 623 with self.assertRaises(InvalidOptionError) as e: 624 Option('--option', nargs='foo') 625 self.assertEquals(e.exception.message, 626 "nargs must be a positive integer, '?', '*' or '+'") 627 628 with self.assertRaises(InvalidOptionError) as e: 629 Option('--option', nargs=-2) 630 self.assertEquals(e.exception.message, 631 "nargs must be a positive integer, '?', '*' or '+'") 632 633 def test_option_value_nargs_1(self): 634 self.test_option_value(nargs=1) 635 self.test_option_value(nargs=1, default=('a',)) 636 self.test_option_value_enable(nargs=1, default=('a',)) 637 638 # A default is required 639 with self.assertRaises(InvalidOptionError) as e: 640 Option('--disable-option', nargs=1) 641 self.assertEquals(e.exception.message, 642 "The given `default` doesn't satisfy `nargs`") 643 644 def test_option_value_nargs_2(self): 645 self.test_option_value(nargs=2) 646 self.test_option_value(nargs=2, default=('a', 'b')) 647 self.test_option_value_enable(nargs=2, default=('a', 'b')) 648 649 # A default is required 650 with self.assertRaises(InvalidOptionError) as e: 651 Option('--disable-option', nargs=2) 652 self.assertEquals(e.exception.message, 653 "The given `default` doesn't satisfy `nargs`") 654 655 def test_option_value_nargs_0_or_1(self): 656 self.test_option_value(nargs='?') 657 self.test_option_value(nargs='?', default=('a',)) 658 self.test_option_value_enable(nargs='?') 659 self.test_option_value_enable(nargs='?', default=('a',)) 660 661 def test_option_value_nargs_0_or_more(self): 662 self.test_option_value(nargs='*') 663 self.test_option_value(nargs='*', default=('a',)) 664 self.test_option_value(nargs='*', default=('a', 'b')) 665 self.test_option_value_enable(nargs='*') 666 self.test_option_value_enable(nargs='*', default=('a',)) 667 self.test_option_value_enable(nargs='*', default=('a', 'b')) 668 669 def test_option_value_nargs_1_or_more(self): 670 self.test_option_value(nargs='+') 671 self.test_option_value(nargs='+', default=('a',)) 672 self.test_option_value(nargs='+', default=('a', 'b')) 673 self.test_option_value_enable(nargs='+', default=('a',)) 674 self.test_option_value_enable(nargs='+', default=('a', 'b')) 675 676 # A default is required 677 with self.assertRaises(InvalidOptionError) as e: 678 Option('--disable-option', nargs='+') 679 self.assertEquals(e.exception.message, 680 "The given `default` doesn't satisfy `nargs`") 681 682 683class TestCommandLineHelper(unittest.TestCase): 684 def test_basic(self): 685 helper = CommandLineHelper({}, ['cmd', '--foo', '--bar']) 686 687 self.assertEquals(['--foo', '--bar'], list(helper)) 688 689 helper.add('--enable-qux') 690 691 self.assertEquals(['--foo', '--bar', '--enable-qux'], list(helper)) 692 693 value, option = helper.handle(Option('--bar')) 694 self.assertEquals(['--foo', '--enable-qux'], list(helper)) 695 self.assertEquals(PositiveOptionValue(), value) 696 self.assertEquals('--bar', option) 697 698 value, option = helper.handle(Option('--baz')) 699 self.assertEquals(['--foo', '--enable-qux'], list(helper)) 700 self.assertEquals(NegativeOptionValue(), value) 701 self.assertEquals(None, option) 702 703 with self.assertRaises(AssertionError): 704 CommandLineHelper({}, ['--foo', '--bar']) 705 706 def test_precedence(self): 707 foo = Option('--with-foo', nargs='*') 708 helper = CommandLineHelper({}, ['cmd', '--with-foo=a,b']) 709 value, option = helper.handle(foo) 710 self.assertEquals(PositiveOptionValue(('a', 'b')), value) 711 self.assertEquals('command-line', value.origin) 712 self.assertEquals('--with-foo=a,b', option) 713 714 helper = CommandLineHelper({}, ['cmd', '--with-foo=a,b', 715 '--without-foo']) 716 value, option = helper.handle(foo) 717 self.assertEquals(NegativeOptionValue(), value) 718 self.assertEquals('command-line', value.origin) 719 self.assertEquals('--without-foo', option) 720 721 helper = CommandLineHelper({}, ['cmd', '--without-foo', 722 '--with-foo=a,b']) 723 value, option = helper.handle(foo) 724 self.assertEquals(PositiveOptionValue(('a', 'b')), value) 725 self.assertEquals('command-line', value.origin) 726 self.assertEquals('--with-foo=a,b', option) 727 728 foo = Option('--with-foo', env='FOO', nargs='*') 729 helper = CommandLineHelper({'FOO': ''}, ['cmd', '--with-foo=a,b']) 730 value, option = helper.handle(foo) 731 self.assertEquals(PositiveOptionValue(('a', 'b')), value) 732 self.assertEquals('command-line', value.origin) 733 self.assertEquals('--with-foo=a,b', option) 734 735 helper = CommandLineHelper({'FOO': 'a,b'}, ['cmd', '--without-foo']) 736 value, option = helper.handle(foo) 737 self.assertEquals(NegativeOptionValue(), value) 738 self.assertEquals('command-line', value.origin) 739 self.assertEquals('--without-foo', option) 740 741 helper = CommandLineHelper({'FOO': ''}, ['cmd', '--with-bar=a,b']) 742 value, option = helper.handle(foo) 743 self.assertEquals(NegativeOptionValue(), value) 744 self.assertEquals('environment', value.origin) 745 self.assertEquals('FOO=', option) 746 747 helper = CommandLineHelper({'FOO': 'a,b'}, ['cmd', '--without-bar']) 748 value, option = helper.handle(foo) 749 self.assertEquals(PositiveOptionValue(('a', 'b')), value) 750 self.assertEquals('environment', value.origin) 751 self.assertEquals('FOO=a,b', option) 752 753 helper = CommandLineHelper({}, ['cmd', '--with-foo=a,b', 'FOO=']) 754 value, option = helper.handle(foo) 755 self.assertEquals(NegativeOptionValue(), value) 756 self.assertEquals('command-line', value.origin) 757 self.assertEquals('FOO=', option) 758 759 helper = CommandLineHelper({}, ['cmd', '--without-foo', 'FOO=a,b']) 760 value, option = helper.handle(foo) 761 self.assertEquals(PositiveOptionValue(('a', 'b')), value) 762 self.assertEquals('command-line', value.origin) 763 self.assertEquals('FOO=a,b', option) 764 765 helper = CommandLineHelper({}, ['cmd', 'FOO=', '--with-foo=a,b']) 766 value, option = helper.handle(foo) 767 self.assertEquals(PositiveOptionValue(('a', 'b')), value) 768 self.assertEquals('command-line', value.origin) 769 self.assertEquals('--with-foo=a,b', option) 770 771 helper = CommandLineHelper({}, ['cmd', 'FOO=a,b', '--without-foo']) 772 value, option = helper.handle(foo) 773 self.assertEquals(NegativeOptionValue(), value) 774 self.assertEquals('command-line', value.origin) 775 self.assertEquals('--without-foo', option) 776 777 def test_extra_args(self): 778 foo = Option('--with-foo', env='FOO', nargs='*') 779 helper = CommandLineHelper({}, ['cmd']) 780 helper.add('FOO=a,b,c', 'other-origin') 781 value, option = helper.handle(foo) 782 self.assertEquals(PositiveOptionValue(('a', 'b', 'c')), value) 783 self.assertEquals('other-origin', value.origin) 784 self.assertEquals('FOO=a,b,c', option) 785 786 helper = CommandLineHelper({}, ['cmd']) 787 helper.add('FOO=a,b,c', 'other-origin') 788 helper.add('--with-foo=a,b,c', 'other-origin') 789 value, option = helper.handle(foo) 790 self.assertEquals(PositiveOptionValue(('a', 'b', 'c')), value) 791 self.assertEquals('other-origin', value.origin) 792 self.assertEquals('--with-foo=a,b,c', option) 793 794 # Adding conflicting options is not allowed. 795 helper = CommandLineHelper({}, ['cmd']) 796 helper.add('FOO=a,b,c', 'other-origin') 797 with self.assertRaises(ConflictingOptionError) as cm: 798 helper.add('FOO=', 'other-origin') 799 self.assertEqual('FOO=', cm.exception.arg) 800 self.assertEqual('other-origin', cm.exception.origin) 801 self.assertEqual('FOO=a,b,c', cm.exception.old_arg) 802 self.assertEqual('other-origin', cm.exception.old_origin) 803 with self.assertRaises(ConflictingOptionError) as cm: 804 helper.add('FOO=a,b', 'other-origin') 805 self.assertEqual('FOO=a,b', cm.exception.arg) 806 self.assertEqual('other-origin', cm.exception.origin) 807 self.assertEqual('FOO=a,b,c', cm.exception.old_arg) 808 self.assertEqual('other-origin', cm.exception.old_origin) 809 # But adding the same is allowed. 810 helper.add('FOO=a,b,c', 'other-origin') 811 value, option = helper.handle(foo) 812 self.assertEquals(PositiveOptionValue(('a', 'b', 'c')), value) 813 self.assertEquals('other-origin', value.origin) 814 self.assertEquals('FOO=a,b,c', option) 815 816 # The same rule as above applies when using the option form vs. the 817 # variable form. But we can't detect it when .add is called. 818 helper = CommandLineHelper({}, ['cmd']) 819 helper.add('FOO=a,b,c', 'other-origin') 820 helper.add('--without-foo', 'other-origin') 821 with self.assertRaises(ConflictingOptionError) as cm: 822 helper.handle(foo) 823 self.assertEqual('--without-foo', cm.exception.arg) 824 self.assertEqual('other-origin', cm.exception.origin) 825 self.assertEqual('FOO=a,b,c', cm.exception.old_arg) 826 self.assertEqual('other-origin', cm.exception.old_origin) 827 helper = CommandLineHelper({}, ['cmd']) 828 helper.add('FOO=a,b,c', 'other-origin') 829 helper.add('--with-foo=a,b', 'other-origin') 830 with self.assertRaises(ConflictingOptionError) as cm: 831 helper.handle(foo) 832 self.assertEqual('--with-foo=a,b', cm.exception.arg) 833 self.assertEqual('other-origin', cm.exception.origin) 834 self.assertEqual('FOO=a,b,c', cm.exception.old_arg) 835 self.assertEqual('other-origin', cm.exception.old_origin) 836 helper = CommandLineHelper({}, ['cmd']) 837 helper.add('FOO=a,b,c', 'other-origin') 838 helper.add('--with-foo=a,b,c', 'other-origin') 839 value, option = helper.handle(foo) 840 self.assertEquals(PositiveOptionValue(('a', 'b', 'c')), value) 841 self.assertEquals('other-origin', value.origin) 842 self.assertEquals('--with-foo=a,b,c', option) 843 844 # Conflicts are also not allowed against what is in the 845 # environment/on the command line. 846 helper = CommandLineHelper({}, ['cmd', '--with-foo=a,b']) 847 helper.add('FOO=a,b,c', 'other-origin') 848 with self.assertRaises(ConflictingOptionError) as cm: 849 helper.handle(foo) 850 self.assertEqual('FOO=a,b,c', cm.exception.arg) 851 self.assertEqual('other-origin', cm.exception.origin) 852 self.assertEqual('--with-foo=a,b', cm.exception.old_arg) 853 self.assertEqual('command-line', cm.exception.old_origin) 854 855 helper = CommandLineHelper({}, ['cmd', '--with-foo=a,b']) 856 helper.add('--without-foo', 'other-origin') 857 with self.assertRaises(ConflictingOptionError) as cm: 858 helper.handle(foo) 859 self.assertEqual('--without-foo', cm.exception.arg) 860 self.assertEqual('other-origin', cm.exception.origin) 861 self.assertEqual('--with-foo=a,b', cm.exception.old_arg) 862 self.assertEqual('command-line', cm.exception.old_origin) 863 864 def test_possible_origins(self): 865 with self.assertRaises(InvalidOptionError): 866 Option('--foo', possible_origins='command-line') 867 868 helper = CommandLineHelper({'BAZ': '1'}, ['cmd', '--foo', '--bar']) 869 foo = Option('--foo', 870 possible_origins=('command-line',)) 871 value, option = helper.handle(foo) 872 self.assertEquals(PositiveOptionValue(), value) 873 self.assertEquals('command-line', value.origin) 874 self.assertEquals('--foo', option) 875 876 bar = Option('--bar', 877 possible_origins=('mozconfig',)) 878 with self.assertRaisesRegexp(InvalidOptionError, 879 "--bar can not be set by command-line. Values are accepted from: mozconfig"): 880 helper.handle(bar) 881 882 baz = Option(env='BAZ', 883 possible_origins=('implied',)) 884 with self.assertRaisesRegexp(InvalidOptionError, 885 "BAZ=1 can not be set by environment. Values are accepted from: implied"): 886 helper.handle(baz) 887 888 889if __name__ == '__main__': 890 main() 891