• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2017 The Abseil Authors.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Tests for flags.FlagValues class."""
16
17import collections
18import copy
19import pickle
20import types
21from unittest import mock
22
23from absl import logging
24from absl.flags import _defines
25from absl.flags import _exceptions
26from absl.flags import _flagvalues
27from absl.flags import _helpers
28from absl.flags import _validators
29from absl.flags.tests import module_foo
30from absl.testing import absltest
31from absl.testing import parameterized
32
33
34class FlagValuesTest(absltest.TestCase):
35
36  def test_bool_flags(self):
37    for arg, expected in (('--nothing', True),
38                          ('--nothing=true', True),
39                          ('--nothing=false', False),
40                          ('--nonothing', False)):
41      fv = _flagvalues.FlagValues()
42      _defines.DEFINE_boolean('nothing', None, '', flag_values=fv)
43      fv(('./program', arg))
44      self.assertIs(expected, fv.nothing)
45
46    for arg in ('--nonothing=true', '--nonothing=false'):
47      fv = _flagvalues.FlagValues()
48      _defines.DEFINE_boolean('nothing', None, '', flag_values=fv)
49      with self.assertRaises(ValueError):
50        fv(('./program', arg))
51
52  def test_boolean_flag_parser_gets_string_argument(self):
53    for arg, expected in (('--nothing', 'true'),
54                          ('--nothing=true', 'true'),
55                          ('--nothing=false', 'false'),
56                          ('--nonothing', 'false')):
57      fv = _flagvalues.FlagValues()
58      _defines.DEFINE_boolean('nothing', None, '', flag_values=fv)
59      with mock.patch.object(fv['nothing'].parser, 'parse') as mock_parse:
60        fv(('./program', arg))
61        mock_parse.assert_called_once_with(expected)
62
63  def test_unregistered_flags_are_cleaned_up(self):
64    fv = _flagvalues.FlagValues()
65    module, module_name = _helpers.get_calling_module_object_and_name()
66
67    # Define first flag.
68    _defines.DEFINE_integer('cores', 4, '', flag_values=fv, short_name='c')
69    old_cores_flag = fv['cores']
70    fv.register_key_flag_for_module(module_name, old_cores_flag)
71    self.assertEqual(fv.flags_by_module_dict(),
72                     {module_name: [old_cores_flag]})
73    self.assertEqual(fv.flags_by_module_id_dict(),
74                     {id(module): [old_cores_flag]})
75    self.assertEqual(fv.key_flags_by_module_dict(),
76                     {module_name: [old_cores_flag]})
77
78    # Redefine the same flag.
79    _defines.DEFINE_integer(
80        'cores', 4, '', flag_values=fv, short_name='c', allow_override=True)
81    new_cores_flag = fv['cores']
82    self.assertNotEqual(old_cores_flag, new_cores_flag)
83    self.assertEqual(fv.flags_by_module_dict(),
84                     {module_name: [new_cores_flag]})
85    self.assertEqual(fv.flags_by_module_id_dict(),
86                     {id(module): [new_cores_flag]})
87    # old_cores_flag is removed from key flags, and the new_cores_flag is
88    # not automatically added because it must be registered explicitly.
89    self.assertEqual(fv.key_flags_by_module_dict(), {module_name: []})
90
91    # Define a new flag but with the same short_name.
92    _defines.DEFINE_integer(
93        'changelist',
94        0,
95        '',
96        flag_values=fv,
97        short_name='c',
98        allow_override=True)
99    old_changelist_flag = fv['changelist']
100    fv.register_key_flag_for_module(module_name, old_changelist_flag)
101    # The short named flag -c is overridden to be the old_changelist_flag.
102    self.assertEqual(fv['c'], old_changelist_flag)
103    self.assertNotEqual(fv['c'], new_cores_flag)
104    self.assertEqual(fv.flags_by_module_dict(),
105                     {module_name: [new_cores_flag, old_changelist_flag]})
106    self.assertEqual(fv.flags_by_module_id_dict(),
107                     {id(module): [new_cores_flag, old_changelist_flag]})
108    self.assertEqual(fv.key_flags_by_module_dict(),
109                     {module_name: [old_changelist_flag]})
110
111    # Define a flag only with the same long name.
112    _defines.DEFINE_integer(
113        'changelist',
114        0,
115        '',
116        flag_values=fv,
117        short_name='l',
118        allow_override=True)
119    new_changelist_flag = fv['changelist']
120    self.assertNotEqual(old_changelist_flag, new_changelist_flag)
121    self.assertEqual(fv.flags_by_module_dict(),
122                     {module_name: [new_cores_flag,
123                                    old_changelist_flag,
124                                    new_changelist_flag]})
125    self.assertEqual(fv.flags_by_module_id_dict(),
126                     {id(module): [new_cores_flag,
127                                   old_changelist_flag,
128                                   new_changelist_flag]})
129    self.assertEqual(fv.key_flags_by_module_dict(),
130                     {module_name: [old_changelist_flag]})
131
132    # Delete the new changelist's long name, it should still be registered
133    # because of its short name.
134    del fv.changelist
135    self.assertNotIn('changelist', fv)
136    self.assertEqual(fv.flags_by_module_dict(),
137                     {module_name: [new_cores_flag,
138                                    old_changelist_flag,
139                                    new_changelist_flag]})
140    self.assertEqual(fv.flags_by_module_id_dict(),
141                     {id(module): [new_cores_flag,
142                                   old_changelist_flag,
143                                   new_changelist_flag]})
144    self.assertEqual(fv.key_flags_by_module_dict(),
145                     {module_name: [old_changelist_flag]})
146
147    # Delete the new changelist's short name, it should be removed.
148    del fv.l
149    self.assertNotIn('l', fv)
150    self.assertEqual(fv.flags_by_module_dict(),
151                     {module_name: [new_cores_flag,
152                                    old_changelist_flag]})
153    self.assertEqual(fv.flags_by_module_id_dict(),
154                     {id(module): [new_cores_flag,
155                                   old_changelist_flag]})
156    self.assertEqual(fv.key_flags_by_module_dict(),
157                     {module_name: [old_changelist_flag]})
158
159  def _test_find_module_or_id_defining_flag(self, test_id):
160    """Tests for find_module_defining_flag and find_module_id_defining_flag.
161
162    Args:
163      test_id: True to test find_module_id_defining_flag, False to test
164          find_module_defining_flag.
165    """
166    fv = _flagvalues.FlagValues()
167    current_module, current_module_name = (
168        _helpers.get_calling_module_object_and_name())
169    alt_module_name = _flagvalues.__name__
170
171    if test_id:
172      current_module_or_id = id(current_module)
173      alt_module_or_id = id(_flagvalues)
174      testing_fn = fv.find_module_id_defining_flag
175    else:
176      current_module_or_id = current_module_name
177      alt_module_or_id = alt_module_name
178      testing_fn = fv.find_module_defining_flag
179
180    # Define first flag.
181    _defines.DEFINE_integer('cores', 4, '', flag_values=fv, short_name='c')
182    module_or_id_cores = testing_fn('cores')
183    self.assertEqual(module_or_id_cores, current_module_or_id)
184    module_or_id_c = testing_fn('c')
185    self.assertEqual(module_or_id_c, current_module_or_id)
186
187    # Redefine the same flag in another module.
188    _defines.DEFINE_integer(
189        'cores',
190        4,
191        '',
192        flag_values=fv,
193        module_name=alt_module_name,
194        short_name='c',
195        allow_override=True)
196    module_or_id_cores = testing_fn('cores')
197    self.assertEqual(module_or_id_cores, alt_module_or_id)
198    module_or_id_c = testing_fn('c')
199    self.assertEqual(module_or_id_c, alt_module_or_id)
200
201    # Define a new flag but with the same short_name.
202    _defines.DEFINE_integer(
203        'changelist',
204        0,
205        '',
206        flag_values=fv,
207        short_name='c',
208        allow_override=True)
209    module_or_id_cores = testing_fn('cores')
210    self.assertEqual(module_or_id_cores, alt_module_or_id)
211    module_or_id_changelist = testing_fn('changelist')
212    self.assertEqual(module_or_id_changelist, current_module_or_id)
213    module_or_id_c = testing_fn('c')
214    self.assertEqual(module_or_id_c, current_module_or_id)
215
216    # Define a flag in another module only with the same long name.
217    _defines.DEFINE_integer(
218        'changelist',
219        0,
220        '',
221        flag_values=fv,
222        module_name=alt_module_name,
223        short_name='l',
224        allow_override=True)
225    module_or_id_cores = testing_fn('cores')
226    self.assertEqual(module_or_id_cores, alt_module_or_id)
227    module_or_id_changelist = testing_fn('changelist')
228    self.assertEqual(module_or_id_changelist, alt_module_or_id)
229    module_or_id_c = testing_fn('c')
230    self.assertEqual(module_or_id_c, current_module_or_id)
231    module_or_id_l = testing_fn('l')
232    self.assertEqual(module_or_id_l, alt_module_or_id)
233
234    # Delete the changelist flag, its short name should still be registered.
235    del fv.changelist
236    module_or_id_changelist = testing_fn('changelist')
237    self.assertIsNone(module_or_id_changelist)
238    module_or_id_c = testing_fn('c')
239    self.assertEqual(module_or_id_c, current_module_or_id)
240    module_or_id_l = testing_fn('l')
241    self.assertEqual(module_or_id_l, alt_module_or_id)
242
243  def test_find_module_defining_flag(self):
244    self._test_find_module_or_id_defining_flag(test_id=False)
245
246  def test_find_module_id_defining_flag(self):
247    self._test_find_module_or_id_defining_flag(test_id=True)
248
249  def test_set_default(self):
250    fv = _flagvalues.FlagValues()
251    fv.mark_as_parsed()
252    with self.assertRaises(_exceptions.UnrecognizedFlagError):
253      fv.set_default('changelist', 1)
254    _defines.DEFINE_integer('changelist', 0, 'help', flag_values=fv)
255    self.assertEqual(0, fv.changelist)
256    fv.set_default('changelist', 2)
257    self.assertEqual(2, fv.changelist)
258
259  def test_default_gnu_getopt_value(self):
260    self.assertTrue(_flagvalues.FlagValues().is_gnu_getopt())
261
262  def test_known_only_flags_in_gnustyle(self):
263
264    def run_test(argv, defined_py_flags, expected_argv):
265      fv = _flagvalues.FlagValues()
266      fv.set_gnu_getopt(True)
267      for f in defined_py_flags:
268        if f.startswith('b'):
269          _defines.DEFINE_boolean(f, False, 'help', flag_values=fv)
270        else:
271          _defines.DEFINE_string(f, 'default', 'help', flag_values=fv)
272      output_argv = fv(argv, known_only=True)
273      self.assertEqual(expected_argv, output_argv)
274
275    run_test(
276        argv='0 --f1=v1 cmd --f2 v2 --b1 --f3 v3 --nob2'.split(' '),
277        defined_py_flags=[],
278        expected_argv='0 --f1=v1 cmd --f2 v2 --b1 --f3 v3 --nob2'.split(' '))
279    run_test(
280        argv='0 --f1=v1 cmd --f2 v2 --b1 --f3 v3 --nob2'.split(' '),
281        defined_py_flags=['f1'],
282        expected_argv='0 cmd --f2 v2 --b1 --f3 v3 --nob2'.split(' '))
283    run_test(
284        argv='0 --f1=v1 cmd --f2 v2 --b1 --f3 v3 --nob2'.split(' '),
285        defined_py_flags=['f2'],
286        expected_argv='0 --f1=v1 cmd --b1 --f3 v3 --nob2'.split(' '))
287    run_test(
288        argv='0 --f1=v1 cmd --f2 v2 --b1 --f3 v3 --nob2'.split(' '),
289        defined_py_flags=['b1'],
290        expected_argv='0 --f1=v1 cmd --f2 v2 --f3 v3 --nob2'.split(' '))
291    run_test(
292        argv='0 --f1=v1 cmd --f2 v2 --b1 --f3 v3 --nob2'.split(' '),
293        defined_py_flags=['f3'],
294        expected_argv='0 --f1=v1 cmd --f2 v2 --b1 --nob2'.split(' '))
295    run_test(
296        argv='0 --f1=v1 cmd --f2 v2 --b1 --f3 v3 --nob2'.split(' '),
297        defined_py_flags=['b2'],
298        expected_argv='0 --f1=v1 cmd --f2 v2 --b1 --f3 v3'.split(' '))
299    run_test(
300        argv=('0 --f1=v1 cmd --undefok=f1 --f2 v2 --b1 '
301              '--f3 v3 --nob2').split(' '),
302        defined_py_flags=['b2'],
303        expected_argv='0 cmd --f2 v2 --b1 --f3 v3'.split(' '))
304    run_test(
305        argv=('0 --f1=v1 cmd --undefok f1,f2 --f2 v2 --b1 '
306              '--f3 v3 --nob2').split(' '),
307        defined_py_flags=['b2'],
308        # Note v2 is preserved here, since undefok requires the flag being
309        # specified in the form of --flag=value.
310        expected_argv='0 cmd v2 --b1 --f3 v3'.split(' '))
311
312  def test_invalid_flag_name(self):
313    with self.assertRaises(_exceptions.Error):
314      _defines.DEFINE_boolean('test ', 0, '')
315
316    with self.assertRaises(_exceptions.Error):
317      _defines.DEFINE_boolean(' test', 0, '')
318
319    with self.assertRaises(_exceptions.Error):
320      _defines.DEFINE_boolean('te st', 0, '')
321
322    with self.assertRaises(_exceptions.Error):
323      _defines.DEFINE_boolean('', 0, '')
324
325    with self.assertRaises(_exceptions.Error):
326      _defines.DEFINE_boolean(1, 0, '')
327
328  def test_len(self):
329    fv = _flagvalues.FlagValues()
330    self.assertEmpty(fv)
331    self.assertFalse(fv)
332
333    _defines.DEFINE_boolean('boolean', False, 'help', flag_values=fv)
334    self.assertLen(fv, 1)
335    self.assertTrue(fv)
336
337    _defines.DEFINE_boolean(
338        'bool', False, 'help', short_name='b', flag_values=fv)
339    self.assertLen(fv, 3)
340    self.assertTrue(fv)
341
342  def test_pickle(self):
343    fv = _flagvalues.FlagValues()
344    with self.assertRaisesRegex(TypeError, "can't pickle FlagValues"):
345      pickle.dumps(fv)
346
347  def test_copy(self):
348    fv = _flagvalues.FlagValues()
349    _defines.DEFINE_integer('answer', 0, 'help', flag_values=fv)
350    fv(['', '--answer=1'])
351
352    with self.assertRaisesRegex(TypeError,
353                                'FlagValues does not support shallow copies'):
354      copy.copy(fv)
355
356    fv2 = copy.deepcopy(fv)
357    self.assertEqual(fv2.answer, 1)
358
359    fv2.answer = 42
360    self.assertEqual(fv2.answer, 42)
361    self.assertEqual(fv.answer, 1)
362
363  def test_conflicting_flags(self):
364    fv = _flagvalues.FlagValues()
365    with self.assertRaises(_exceptions.FlagNameConflictsWithMethodError):
366      _defines.DEFINE_boolean('is_gnu_getopt', False, 'help', flag_values=fv)
367    _defines.DEFINE_boolean(
368        'is_gnu_getopt',
369        False,
370        'help',
371        flag_values=fv,
372        allow_using_method_names=True)
373    self.assertFalse(fv['is_gnu_getopt'].value)
374    self.assertIsInstance(fv.is_gnu_getopt, types.MethodType)
375
376  def test_get_flags_for_module(self):
377    fv = _flagvalues.FlagValues()
378    _defines.DEFINE_string('foo', None, 'help', flag_values=fv)
379    module_foo.define_flags(fv)
380    flags = fv.get_flags_for_module('__main__')
381
382    self.assertEqual({'foo'}, {flag.name for flag in flags})
383
384    flags = fv.get_flags_for_module(module_foo)
385    self.assertEqual({'tmod_foo_bool', 'tmod_foo_int', 'tmod_foo_str'},
386                     {flag.name for flag in flags})
387
388  def test_get_help(self):
389    fv = _flagvalues.FlagValues()
390    self.assertMultiLineEqual('''\
391--flagfile: Insert flag definitions from the given file into the command line.
392  (default: '')
393--undefok: comma-separated list of flag names that it is okay to specify on the
394  command line even if the program does not define a flag with that name.
395  IMPORTANT: flags in this list that have arguments MUST use the --flag=value
396  format.
397  (default: '')''', fv.get_help())
398
399    module_foo.define_flags(fv)
400    self.assertMultiLineEqual('''
401absl.flags.tests.module_bar:
402  --tmod_bar_t: Sample int flag.
403    (default: '4')
404    (an integer)
405  --tmod_bar_u: Sample int flag.
406    (default: '5')
407    (an integer)
408  --tmod_bar_v: Sample int flag.
409    (default: '6')
410    (an integer)
411  --[no]tmod_bar_x: Boolean flag.
412    (default: 'true')
413  --tmod_bar_y: String flag.
414    (default: 'default')
415  --[no]tmod_bar_z: Another boolean flag from module bar.
416    (default: 'false')
417
418absl.flags.tests.module_foo:
419  --[no]tmod_foo_bool: Boolean flag from module foo.
420    (default: 'true')
421  --tmod_foo_int: Sample int flag.
422    (default: '3')
423    (an integer)
424  --tmod_foo_str: String flag.
425    (default: 'default')
426
427absl.flags:
428  --flagfile: Insert flag definitions from the given file into the command line.
429    (default: '')
430  --undefok: comma-separated list of flag names that it is okay to specify on
431    the command line even if the program does not define a flag with that name.
432    IMPORTANT: flags in this list that have arguments MUST use the --flag=value
433    format.
434    (default: '')''', fv.get_help())
435
436    self.assertMultiLineEqual('''
437xxxxabsl.flags.tests.module_bar:
438xxxx  --tmod_bar_t: Sample int flag.
439xxxx    (default: '4')
440xxxx    (an integer)
441xxxx  --tmod_bar_u: Sample int flag.
442xxxx    (default: '5')
443xxxx    (an integer)
444xxxx  --tmod_bar_v: Sample int flag.
445xxxx    (default: '6')
446xxxx    (an integer)
447xxxx  --[no]tmod_bar_x: Boolean flag.
448xxxx    (default: 'true')
449xxxx  --tmod_bar_y: String flag.
450xxxx    (default: 'default')
451xxxx  --[no]tmod_bar_z: Another boolean flag from module bar.
452xxxx    (default: 'false')
453
454xxxxabsl.flags.tests.module_foo:
455xxxx  --[no]tmod_foo_bool: Boolean flag from module foo.
456xxxx    (default: 'true')
457xxxx  --tmod_foo_int: Sample int flag.
458xxxx    (default: '3')
459xxxx    (an integer)
460xxxx  --tmod_foo_str: String flag.
461xxxx    (default: 'default')
462
463xxxxabsl.flags:
464xxxx  --flagfile: Insert flag definitions from the given file into the command
465xxxx    line.
466xxxx    (default: '')
467xxxx  --undefok: comma-separated list of flag names that it is okay to specify
468xxxx    on the command line even if the program does not define a flag with that
469xxxx    name.  IMPORTANT: flags in this list that have arguments MUST use the
470xxxx    --flag=value format.
471xxxx    (default: '')''', fv.get_help(prefix='xxxx'))
472
473    self.assertMultiLineEqual('''
474absl.flags.tests.module_bar:
475  --tmod_bar_t: Sample int flag.
476    (default: '4')
477    (an integer)
478  --tmod_bar_u: Sample int flag.
479    (default: '5')
480    (an integer)
481  --tmod_bar_v: Sample int flag.
482    (default: '6')
483    (an integer)
484  --[no]tmod_bar_x: Boolean flag.
485    (default: 'true')
486  --tmod_bar_y: String flag.
487    (default: 'default')
488  --[no]tmod_bar_z: Another boolean flag from module bar.
489    (default: 'false')
490
491absl.flags.tests.module_foo:
492  --[no]tmod_foo_bool: Boolean flag from module foo.
493    (default: 'true')
494  --tmod_foo_int: Sample int flag.
495    (default: '3')
496    (an integer)
497  --tmod_foo_str: String flag.
498    (default: 'default')''', fv.get_help(include_special_flags=False))
499
500  def test_str(self):
501    fv = _flagvalues.FlagValues()
502    self.assertEqual(str(fv), fv.get_help())
503    module_foo.define_flags(fv)
504    self.assertEqual(str(fv), fv.get_help())
505
506  def test_empty_argv(self):
507    fv = _flagvalues.FlagValues()
508    with self.assertRaises(ValueError):
509      fv([])
510
511  def test_invalid_argv(self):
512    fv = _flagvalues.FlagValues()
513    with self.assertRaises(TypeError):
514      fv('./program')
515    with self.assertRaises(TypeError):
516      fv(b'./program')
517    with self.assertRaises(TypeError):
518      fv(u'./program')
519
520  def test_flags_dir(self):
521    flag_values = _flagvalues.FlagValues()
522    flag_name1 = 'bool_flag'
523    flag_name2 = 'string_flag'
524    flag_name3 = 'float_flag'
525    description = 'Description'
526    _defines.DEFINE_boolean(
527        flag_name1, None, description, flag_values=flag_values)
528    _defines.DEFINE_string(
529        flag_name2, None, description, flag_values=flag_values)
530    self.assertEqual(sorted([flag_name1, flag_name2]), dir(flag_values))
531
532    _defines.DEFINE_float(
533        flag_name3, None, description, flag_values=flag_values)
534    self.assertEqual(
535        sorted([flag_name1, flag_name2, flag_name3]), dir(flag_values))
536
537  def test_flags_into_string_deterministic(self):
538    flag_values = _flagvalues.FlagValues()
539    _defines.DEFINE_string(
540        'fa', 'x', '', flag_values=flag_values, module_name='mb')
541    _defines.DEFINE_string(
542        'fb', 'x', '', flag_values=flag_values, module_name='mb')
543    _defines.DEFINE_string(
544        'fc', 'x', '', flag_values=flag_values, module_name='ma')
545    _defines.DEFINE_string(
546        'fd', 'x', '', flag_values=flag_values, module_name='ma')
547
548    expected = ('--fc=x\n'
549                '--fd=x\n'
550                '--fa=x\n'
551                '--fb=x\n')
552
553    flags_by_module_items = sorted(
554        flag_values.flags_by_module_dict().items(), reverse=True)
555    for _, module_flags in flags_by_module_items:
556      module_flags.sort(reverse=True)
557
558    flag_values.__dict__['__flags_by_module'] = collections.OrderedDict(
559        flags_by_module_items)
560
561    actual = flag_values.flags_into_string()
562    self.assertEqual(expected, actual)
563
564  def test_validate_all_flags(self):
565    fv = _flagvalues.FlagValues()
566    _defines.DEFINE_string('name', None, '', flag_values=fv)
567    _validators.mark_flag_as_required('name', flag_values=fv)
568    with self.assertRaises(_exceptions.IllegalFlagValueError):
569      fv.validate_all_flags()
570    fv.name = 'test'
571    fv.validate_all_flags()
572
573
574class FlagValuesLoggingTest(absltest.TestCase):
575  """Test to make sure logging.* functions won't recurse.
576
577  Logging may and does happen before flags initialization. We need to make
578  sure that any warnings trown by flagvalues do not result in unlimited
579  recursion.
580  """
581
582  def test_logging_do_not_recurse(self):
583    logging.info('test info')
584    try:
585      raise ValueError('test exception')
586    except ValueError:
587      logging.exception('test message')
588
589
590class FlagSubstrMatchingTests(parameterized.TestCase):
591  """Tests related to flag substring matching."""
592
593  def _get_test_flag_values(self):
594    """Get a _flagvalues.FlagValues() instance, set up for tests."""
595    flag_values = _flagvalues.FlagValues()
596
597    _defines.DEFINE_string('strf', '', '', flag_values=flag_values)
598    _defines.DEFINE_boolean('boolf', 0, '', flag_values=flag_values)
599
600    return flag_values
601
602  # Test cases that should always make parsing raise an error.
603  # Tuples of strings with the argv to use.
604  FAIL_TEST_CASES = [
605      ('./program', '--boo', '0'),
606      ('./program', '--boo=true', '0'),
607      ('./program', '--boo=0'),
608      ('./program', '--noboo'),
609      ('./program', '--st=blah'),
610      ('./program', '--st=de'),
611      ('./program', '--st=blah', '--boo'),
612      ('./program', '--st=blah', 'unused'),
613      ('./program', '--st=--blah'),
614      ('./program', '--st', '--blah'),
615  ]
616
617  @parameterized.parameters(FAIL_TEST_CASES)
618  def test_raise(self, *argv):
619    """Test that raising works."""
620    fv = self._get_test_flag_values()
621    with self.assertRaises(_exceptions.UnrecognizedFlagError):
622      fv(argv)
623
624  @parameterized.parameters(
625      FAIL_TEST_CASES + [('./program', 'unused', '--st=blah')])
626  def test_gnu_getopt_raise(self, *argv):
627    """Test that raising works when combined with GNU-style getopt."""
628    fv = self._get_test_flag_values()
629    fv.set_gnu_getopt()
630    with self.assertRaises(_exceptions.UnrecognizedFlagError):
631      fv(argv)
632
633
634class SettingUnknownFlagTest(absltest.TestCase):
635
636  def setUp(self):
637    super(SettingUnknownFlagTest, self).setUp()
638    self.setter_called = 0
639
640  def set_undef(self, unused_name, unused_val):
641    self.setter_called += 1
642
643  def test_raise_on_undefined(self):
644    new_flags = _flagvalues.FlagValues()
645    with self.assertRaises(_exceptions.UnrecognizedFlagError):
646      new_flags.undefined_flag = 0
647
648  def test_not_raise(self):
649    new_flags = _flagvalues.FlagValues()
650    new_flags._register_unknown_flag_setter(self.set_undef)
651    new_flags.undefined_flag = 0
652    self.assertEqual(self.setter_called, 1)
653
654  def test_not_raise_on_undefined_if_undefok(self):
655    new_flags = _flagvalues.FlagValues()
656    args = ['0', '--foo', '--bar=1', '--undefok=foo,bar']
657    unparsed = new_flags(args, known_only=True)
658    self.assertEqual(['0'], unparsed)
659
660  def test_re_raise_undefined(self):
661    def setter(unused_name, unused_val):
662      raise NameError()
663    new_flags = _flagvalues.FlagValues()
664    new_flags._register_unknown_flag_setter(setter)
665    with self.assertRaises(_exceptions.UnrecognizedFlagError):
666      new_flags.undefined_flag = 0
667
668  def test_re_raise_invalid(self):
669    def setter(unused_name, unused_val):
670      raise ValueError()
671    new_flags = _flagvalues.FlagValues()
672    new_flags._register_unknown_flag_setter(setter)
673    with self.assertRaises(_exceptions.IllegalFlagValueError):
674      new_flags.undefined_flag = 0
675
676
677class SetAttributesTest(absltest.TestCase):
678
679  def setUp(self):
680    super(SetAttributesTest, self).setUp()
681    self.new_flags = _flagvalues.FlagValues()
682    _defines.DEFINE_boolean(
683        'defined_flag', None, '', flag_values=self.new_flags)
684    _defines.DEFINE_boolean(
685        'another_defined_flag', None, '', flag_values=self.new_flags)
686    self.setter_called = 0
687
688  def set_undef(self, unused_name, unused_val):
689    self.setter_called += 1
690
691  def test_two_defined_flags(self):
692    self.new_flags._set_attributes(
693        defined_flag=False, another_defined_flag=False)
694    self.assertEqual(self.setter_called, 0)
695
696  def test_one_defined_one_undefined_flag(self):
697    with self.assertRaises(_exceptions.UnrecognizedFlagError):
698      self.new_flags._set_attributes(defined_flag=False, undefined_flag=0)
699
700  def test_register_unknown_flag_setter(self):
701    self.new_flags._register_unknown_flag_setter(self.set_undef)
702    self.new_flags._set_attributes(defined_flag=False, undefined_flag=0)
703    self.assertEqual(self.setter_called, 1)
704
705
706class FlagsDashSyntaxTest(absltest.TestCase):
707
708  def setUp(self):
709    super(FlagsDashSyntaxTest, self).setUp()
710    self.fv = _flagvalues.FlagValues()
711    _defines.DEFINE_string(
712        'long_name', 'default', 'help', flag_values=self.fv, short_name='s')
713
714  def test_long_name_one_dash(self):
715    self.fv(['./program', '-long_name=new'])
716    self.assertEqual('new', self.fv.long_name)
717
718  def test_long_name_two_dashes(self):
719    self.fv(['./program', '--long_name=new'])
720    self.assertEqual('new', self.fv.long_name)
721
722  def test_long_name_three_dashes(self):
723    with self.assertRaises(_exceptions.UnrecognizedFlagError):
724      self.fv(['./program', '---long_name=new'])
725
726  def test_short_name_one_dash(self):
727    self.fv(['./program', '-s=new'])
728    self.assertEqual('new', self.fv.s)
729
730  def test_short_name_two_dashes(self):
731    self.fv(['./program', '--s=new'])
732    self.assertEqual('new', self.fv.s)
733
734  def test_short_name_three_dashes(self):
735    with self.assertRaises(_exceptions.UnrecognizedFlagError):
736      self.fv(['./program', '---s=new'])
737
738
739class UnparseFlagsTest(absltest.TestCase):
740
741  def test_using_default_value_none(self):
742    fv = _flagvalues.FlagValues()
743    _defines.DEFINE_string('default_none', None, 'help', flag_values=fv)
744    self.assertTrue(fv['default_none'].using_default_value)
745    fv(['', '--default_none=notNone'])
746    self.assertFalse(fv['default_none'].using_default_value)
747    fv.unparse_flags()
748    self.assertTrue(fv['default_none'].using_default_value)
749    fv(['', '--default_none=alsoNotNone'])
750    self.assertFalse(fv['default_none'].using_default_value)
751    fv.unparse_flags()
752    self.assertTrue(fv['default_none'].using_default_value)
753
754  def test_using_default_value_not_none(self):
755    fv = _flagvalues.FlagValues()
756    _defines.DEFINE_string('default_foo', 'foo', 'help', flag_values=fv)
757
758    fv.mark_as_parsed()
759    self.assertTrue(fv['default_foo'].using_default_value)
760
761    fv(['', '--default_foo=foo'])
762    self.assertFalse(fv['default_foo'].using_default_value)
763
764    fv(['', '--default_foo=notFoo'])
765    self.assertFalse(fv['default_foo'].using_default_value)
766
767    fv.unparse_flags()
768    self.assertTrue(fv['default_foo'].using_default_value)
769
770    fv(['', '--default_foo=alsoNotFoo'])
771    self.assertFalse(fv['default_foo'].using_default_value)
772
773  def test_allow_overwrite_false(self):
774    fv = _flagvalues.FlagValues()
775    _defines.DEFINE_string(
776        'default_none', None, 'help', allow_overwrite=False, flag_values=fv)
777    _defines.DEFINE_string(
778        'default_foo', 'foo', 'help', allow_overwrite=False, flag_values=fv)
779
780    fv.mark_as_parsed()
781    self.assertEqual('foo', fv.default_foo)
782    self.assertIsNone(fv.default_none)
783
784    fv(['', '--default_foo=notFoo', '--default_none=notNone'])
785    self.assertEqual('notFoo', fv.default_foo)
786    self.assertEqual('notNone', fv.default_none)
787
788    fv.unparse_flags()
789    self.assertEqual('foo', fv['default_foo'].value)
790    self.assertIsNone(fv['default_none'].value)
791
792    fv(['', '--default_foo=alsoNotFoo', '--default_none=alsoNotNone'])
793    self.assertEqual('alsoNotFoo', fv.default_foo)
794    self.assertEqual('alsoNotNone', fv.default_none)
795
796  def test_multi_string_default_none(self):
797    fv = _flagvalues.FlagValues()
798    _defines.DEFINE_multi_string('foo', None, 'help', flag_values=fv)
799    fv.mark_as_parsed()
800    self.assertIsNone(fv.foo)
801    fv(['', '--foo=aa'])
802    self.assertEqual(['aa'], fv.foo)
803    fv.unparse_flags()
804    self.assertIsNone(fv['foo'].value)
805    fv(['', '--foo=bb', '--foo=cc'])
806    self.assertEqual(['bb', 'cc'], fv.foo)
807    fv.unparse_flags()
808    self.assertIsNone(fv['foo'].value)
809
810  def test_multi_string_default_string(self):
811    fv = _flagvalues.FlagValues()
812    _defines.DEFINE_multi_string('foo', 'xyz', 'help', flag_values=fv)
813    expected_default = ['xyz']
814    fv.mark_as_parsed()
815    self.assertEqual(expected_default, fv.foo)
816    fv(['', '--foo=aa'])
817    self.assertEqual(['aa'], fv.foo)
818    fv.unparse_flags()
819    self.assertEqual(expected_default, fv['foo'].value)
820    fv(['', '--foo=bb', '--foo=cc'])
821    self.assertEqual(['bb', 'cc'], fv['foo'].value)
822    fv.unparse_flags()
823    self.assertEqual(expected_default, fv['foo'].value)
824
825  def test_multi_string_default_list(self):
826    fv = _flagvalues.FlagValues()
827    _defines.DEFINE_multi_string(
828        'foo', ['xx', 'yy', 'zz'], 'help', flag_values=fv)
829    expected_default = ['xx', 'yy', 'zz']
830    fv.mark_as_parsed()
831    self.assertEqual(expected_default, fv.foo)
832    fv(['', '--foo=aa'])
833    self.assertEqual(['aa'], fv.foo)
834    fv.unparse_flags()
835    self.assertEqual(expected_default, fv['foo'].value)
836    fv(['', '--foo=bb', '--foo=cc'])
837    self.assertEqual(['bb', 'cc'], fv.foo)
838    fv.unparse_flags()
839    self.assertEqual(expected_default, fv['foo'].value)
840
841
842class UnparsedFlagAccessTest(absltest.TestCase):
843
844  def test_unparsed_flag_access(self):
845    fv = _flagvalues.FlagValues()
846    _defines.DEFINE_string('name', 'default', 'help', flag_values=fv)
847    with self.assertRaises(_exceptions.UnparsedFlagAccessError):
848      _ = fv.name
849
850  def test_hasattr_raises_in_py3(self):
851    fv = _flagvalues.FlagValues()
852    _defines.DEFINE_string('name', 'default', 'help', flag_values=fv)
853    with self.assertRaises(_exceptions.UnparsedFlagAccessError):
854      _ = hasattr(fv, 'name')
855
856  def test_unparsed_flags_access_raises_after_unparse_flags(self):
857    fv = _flagvalues.FlagValues()
858    _defines.DEFINE_string('a_str', 'default_value', 'help', flag_values=fv)
859    fv.mark_as_parsed()
860    self.assertEqual(fv.a_str, 'default_value')
861    fv.unparse_flags()
862    with self.assertRaises(_exceptions.UnparsedFlagAccessError):
863      _ = fv.a_str
864
865
866class FlagHolderTest(absltest.TestCase):
867
868  def setUp(self):
869    super(FlagHolderTest, self).setUp()
870    self.fv = _flagvalues.FlagValues()
871    self.name_flag = _defines.DEFINE_string(
872        'name', 'default', 'help', flag_values=self.fv)
873
874  def parse_flags(self, *argv):
875    self.fv.unparse_flags()
876    self.fv(['binary_name'] + list(argv))
877
878  def test_name(self):
879    self.assertEqual('name', self.name_flag.name)
880
881  def test_value_before_flag_parsing(self):
882    with self.assertRaises(_exceptions.UnparsedFlagAccessError):
883      _ = self.name_flag.value
884
885  def test_value_returns_default_value_if_not_explicitly_set(self):
886    self.parse_flags()
887    self.assertEqual('default', self.name_flag.value)
888
889  def test_value_returns_explicitly_set_value(self):
890    self.parse_flags('--name=new_value')
891    self.assertEqual('new_value', self.name_flag.value)
892
893  def test_present_returns_false_before_flag_parsing(self):
894    self.assertFalse(self.name_flag.present)
895
896  def test_present_returns_false_if_not_explicitly_set(self):
897    self.parse_flags()
898    self.assertFalse(self.name_flag.present)
899
900  def test_present_returns_true_if_explicitly_set(self):
901    self.parse_flags('--name=new_value')
902    self.assertTrue(self.name_flag.present)
903
904  def test_allow_override(self):
905    first = _defines.DEFINE_integer(
906        'int_flag', 1, 'help', flag_values=self.fv, allow_override=1)
907    second = _defines.DEFINE_integer(
908        'int_flag', 2, 'help', flag_values=self.fv, allow_override=1)
909    self.parse_flags('--int_flag=3')
910    self.assertEqual(3, first.value)
911    self.assertEqual(3, second.value)
912    self.assertTrue(first.present)
913    self.assertTrue(second.present)
914
915  def test_eq(self):
916    with self.assertRaises(TypeError):
917      self.name_flag == 'value'  # pylint: disable=pointless-statement
918
919  def test_eq_reflection(self):
920    with self.assertRaises(TypeError):
921      'value' == self.name_flag  # pylint: disable=pointless-statement
922
923  def test_bool(self):
924    with self.assertRaises(TypeError):
925      bool(self.name_flag)
926
927
928if __name__ == '__main__':
929  absltest.main()
930