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"""Tests for absl.flags used as a package."""
15
16import contextlib
17import enum
18import io
19import os
20import shutil
21import sys
22import tempfile
23import unittest
24
25from absl import flags
26from absl.flags import _exceptions
27from absl.flags import _helpers
28from absl.flags.tests import module_bar
29from absl.flags.tests import module_baz
30from absl.flags.tests import module_foo
31from absl.testing import absltest
32
33FLAGS = flags.FLAGS
34
35
36@contextlib.contextmanager
37def _use_gnu_getopt(flag_values, use_gnu_get_opt):
38  old_use_gnu_get_opt = flag_values.is_gnu_getopt()
39  flag_values.set_gnu_getopt(use_gnu_get_opt)
40  yield
41  flag_values.set_gnu_getopt(old_use_gnu_get_opt)
42
43
44class FlagDictToArgsTest(absltest.TestCase):
45
46  def test_flatten_google_flag_map(self):
47    arg_dict = {
48        'week-end': None,
49        'estudia': False,
50        'trabaja': False,
51        'party': True,
52        'monday': 'party',
53        'score': 42,
54        'loadthatstuff': [42, 'hello', 'goodbye'],
55    }
56    self.assertSameElements(
57        ('--week-end', '--noestudia', '--notrabaja', '--party',
58         '--monday=party', '--score=42', '--loadthatstuff=42,hello,goodbye'),
59        flags.flag_dict_to_args(arg_dict))
60
61  def test_flatten_google_flag_map_with_multi_flag(self):
62    arg_dict = {
63        'some_list': ['value1', 'value2'],
64        'some_multi_string': ['value3', 'value4'],
65    }
66    self.assertSameElements(
67        ('--some_list=value1,value2', '--some_multi_string=value3',
68         '--some_multi_string=value4'),
69        flags.flag_dict_to_args(arg_dict, multi_flags={'some_multi_string'}))
70
71
72class Fruit(enum.Enum):
73  APPLE = object()
74  ORANGE = object()
75
76
77class CaseSensitiveFruit(enum.Enum):
78  apple = 1
79  orange = 2
80  APPLE = 3
81
82
83class EmptyEnum(enum.Enum):
84  pass
85
86
87class AliasFlagsTest(absltest.TestCase):
88
89  def setUp(self):
90    super(AliasFlagsTest, self).setUp()
91    self.flags = flags.FlagValues()
92
93  @property
94  def alias(self):
95    return self.flags['alias']
96
97  @property
98  def aliased(self):
99    return self.flags['aliased']
100
101  def define_alias(self, *args, **kwargs):
102    flags.DEFINE_alias(*args, flag_values=self.flags, **kwargs)
103
104  def define_integer(self, *args, **kwargs):
105    flags.DEFINE_integer(*args, flag_values=self.flags, **kwargs)
106
107  def define_multi_integer(self, *args, **kwargs):
108    flags.DEFINE_multi_integer(*args, flag_values=self.flags, **kwargs)
109
110  def define_string(self, *args, **kwargs):
111    flags.DEFINE_string(*args, flag_values=self.flags, **kwargs)
112
113  def assert_alias_mirrors_aliased(self, alias, aliased, ignore_due_to_bug=()):
114    # A few sanity checks to avoid false success
115    self.assertIn('FlagAlias', alias.__class__.__qualname__)
116    self.assertIsNot(alias, aliased)
117    self.assertNotEqual(aliased.name, alias.name)
118
119    alias_state = {}
120    aliased_state = {}
121    attrs = {
122        'allow_hide_cpp',
123        'allow_override',
124        'allow_override_cpp',
125        'allow_overwrite',
126        'allow_using_method_names',
127        'boolean',
128        'default',
129        'default_as_str',
130        'default_unparsed',
131        # TODO(rlevasseur): This should match, but a bug prevents it from being
132        # in sync.
133        # 'using_default_value',
134        'value',
135    }
136    attrs.difference_update(ignore_due_to_bug)
137
138    for attr in attrs:
139      alias_state[attr] = getattr(alias, attr)
140      aliased_state[attr] = getattr(aliased, attr)
141
142    self.assertEqual(aliased_state, alias_state, 'LHS is aliased; RHS is alias')
143
144  def test_serialize_multi(self):
145    self.define_multi_integer('aliased', [0, 1], '')
146    self.define_alias('alias', 'aliased')
147
148    actual = self.alias.serialize()
149    # TODO(rlevasseur): This should check for --alias=0\n--alias=1, but
150    # a bug causes it to serialize incorrectly.
151    self.assertEqual('--alias=[0, 1]', actual)
152
153  def test_allow_overwrite_false(self):
154    self.define_integer('aliased', None, 'help', allow_overwrite=False)
155    self.define_alias('alias', 'aliased')
156
157    with self.assertRaisesRegex(flags.IllegalFlagValueError, 'already defined'):
158      self.flags(['./program', '--alias=1', '--aliased=2'])
159
160    self.assertEqual(1, self.alias.value)
161    self.assertEqual(1, self.aliased.value)
162
163  def test_aliasing_multi_no_default(self):
164
165    def define_flags():
166      self.flags = flags.FlagValues()
167      self.define_multi_integer('aliased', None, 'help')
168      self.define_alias('alias', 'aliased')
169
170    with self.subTest('after defining'):
171      define_flags()
172      self.assert_alias_mirrors_aliased(self.alias, self.aliased)
173      self.assertIsNone(self.alias.value)
174
175    with self.subTest('set alias'):
176      define_flags()
177      self.flags(['./program', '--alias=1', '--alias=2'])
178      self.assertEqual([1, 2], self.alias.value)
179      self.assert_alias_mirrors_aliased(self.alias, self.aliased)
180
181    with self.subTest('set aliased'):
182      define_flags()
183      self.flags(['./program', '--aliased=1', '--aliased=2'])
184      self.assertEqual([1, 2], self.alias.value)
185      self.assert_alias_mirrors_aliased(self.alias, self.aliased)
186
187    with self.subTest('not setting anything'):
188      define_flags()
189      self.flags(['./program'])
190      self.assertEqual(None, self.alias.value)
191      self.assert_alias_mirrors_aliased(self.alias, self.aliased)
192
193  def test_aliasing_multi_with_default(self):
194
195    def define_flags():
196      self.flags = flags.FlagValues()
197      self.define_multi_integer('aliased', [0], 'help')
198      self.define_alias('alias', 'aliased')
199
200    with self.subTest('after defining'):
201      define_flags()
202      self.assertEqual([0], self.alias.default)
203      self.assert_alias_mirrors_aliased(self.alias, self.aliased)
204
205    with self.subTest('set alias'):
206      define_flags()
207      self.flags(['./program', '--alias=1', '--alias=2'])
208      self.assertEqual([1, 2], self.alias.value)
209      self.assert_alias_mirrors_aliased(self.alias, self.aliased)
210
211      self.assertEqual(2, self.alias.present)
212      # TODO(rlevasseur): This should assert 0, but a bug with aliases and
213      # MultiFlag causes the alias to increment aliased's present counter.
214      self.assertEqual(2, self.aliased.present)
215
216    with self.subTest('set aliased'):
217      define_flags()
218      self.flags(['./program', '--aliased=1', '--aliased=2'])
219      self.assertEqual([1, 2], self.alias.value)
220      self.assert_alias_mirrors_aliased(self.alias, self.aliased)
221      self.assertEqual(0, self.alias.present)
222
223      # TODO(rlevasseur): This should assert 0, but a bug with aliases and
224      # MultiFlag causes the alias to increment aliased present counter.
225      self.assertEqual(2, self.aliased.present)
226
227    with self.subTest('not setting anything'):
228      define_flags()
229      self.flags(['./program'])
230      self.assertEqual([0], self.alias.value)
231      self.assert_alias_mirrors_aliased(self.alias, self.aliased)
232      self.assertEqual(0, self.alias.present)
233      self.assertEqual(0, self.aliased.present)
234
235  def test_aliasing_regular(self):
236
237    def define_flags():
238      self.flags = flags.FlagValues()
239      self.define_string('aliased', '', 'help')
240      self.define_alias('alias', 'aliased')
241
242    define_flags()
243    self.assert_alias_mirrors_aliased(self.alias, self.aliased)
244
245    self.flags(['./program', '--alias=1'])
246    self.assertEqual('1', self.alias.value)
247    self.assert_alias_mirrors_aliased(self.alias, self.aliased)
248    self.assertEqual(1, self.alias.present)
249    self.assertEqual('--alias=1', self.alias.serialize())
250    self.assertEqual(1, self.aliased.present)
251
252    define_flags()
253    self.flags(['./program', '--aliased=2'])
254    self.assertEqual('2', self.alias.value)
255    self.assert_alias_mirrors_aliased(self.alias, self.aliased)
256    self.assertEqual(0, self.alias.present)
257    self.assertEqual('--alias=2', self.alias.serialize())
258    self.assertEqual(1, self.aliased.present)
259
260  def test_defining_alias_doesnt_affect_aliased_state_regular(self):
261    self.define_string('aliased', 'default', 'help')
262    self.define_alias('alias', 'aliased')
263
264    self.assertEqual(0, self.aliased.present)
265    self.assertEqual(0, self.alias.present)
266
267  def test_defining_alias_doesnt_affect_aliased_state_multi(self):
268    self.define_multi_integer('aliased', [0], 'help')
269    self.define_alias('alias', 'aliased')
270
271    self.assertEqual([0], self.aliased.value)
272    self.assertEqual([0], self.aliased.default)
273    self.assertEqual(0, self.aliased.present)
274
275    self.assertEqual([0], self.aliased.value)
276    self.assertEqual([0], self.aliased.default)
277    self.assertEqual(0, self.alias.present)
278
279
280class FlagsUnitTest(absltest.TestCase):
281  """Flags Unit Test."""
282
283  maxDiff = None
284
285  def test_flags(self):
286    """Test normal usage with no (expected) errors."""
287    # Define flags
288    number_test_framework_flags = len(FLAGS)
289    repeat_help = 'how many times to repeat (0-5)'
290    flags.DEFINE_integer(
291        'repeat', 4, repeat_help, lower_bound=0, short_name='r')
292    flags.DEFINE_string('name', 'Bob', 'namehelp')
293    flags.DEFINE_boolean('debug', 0, 'debughelp')
294    flags.DEFINE_boolean('q', 1, 'quiet mode')
295    flags.DEFINE_boolean('quack', 0, "superstring of 'q'")
296    flags.DEFINE_boolean('noexec', 1, 'boolean flag with no as prefix')
297    flags.DEFINE_float('float', 3.14, 'using floats')
298    flags.DEFINE_integer('octal', '0o666', 'using octals')
299    flags.DEFINE_integer('decimal', '666', 'using decimals')
300    flags.DEFINE_integer('hexadecimal', '0x666', 'using hexadecimals')
301    flags.DEFINE_integer('x', 3, 'how eXtreme to be')
302    flags.DEFINE_integer('l', 0x7fffffff00000000, 'how long to be')
303    flags.DEFINE_list('args', 'v=1,"vmodule=a=0,b=2"', 'a list of arguments')
304    flags.DEFINE_list('letters', 'a,b,c', 'a list of letters')
305    flags.DEFINE_list(
306        'list_default_list',
307        ['a', 'b', 'c'],
308        'with default being a list of strings',
309    )
310    flags.DEFINE_enum('kwery', None, ['who', 'what', 'Why', 'where', 'when'],
311                      '?')
312    flags.DEFINE_enum(
313        'sense', None, ['Case', 'case', 'CASE'], '?', case_sensitive=True)
314    flags.DEFINE_enum(
315        'cases',
316        None, ['UPPER', 'lower', 'Initial', 'Ot_HeR'],
317        '?',
318        case_sensitive=False)
319    flags.DEFINE_enum(
320        'funny',
321        None, ['Joke', 'ha', 'ha', 'ha', 'ha'],
322        '?',
323        case_sensitive=True)
324    flags.DEFINE_enum(
325        'blah',
326        None, ['bla', 'Blah', 'BLAH', 'blah'],
327        '?',
328        case_sensitive=False)
329    flags.DEFINE_string(
330        'only_once', None, 'test only sets this once', allow_overwrite=False)
331    flags.DEFINE_string(
332        'universe',
333        None,
334        'test tries to set this three times',
335        allow_overwrite=False)
336
337    # Specify number of flags defined above.  The short_name defined
338    # for 'repeat' counts as an extra flag.
339    number_defined_flags = 22 + 1
340    self.assertLen(FLAGS, number_defined_flags + number_test_framework_flags)
341
342    self.assertEqual(FLAGS.repeat, 4)
343    self.assertEqual(FLAGS.name, 'Bob')
344    self.assertEqual(FLAGS.debug, 0)
345    self.assertEqual(FLAGS.q, 1)
346    self.assertEqual(FLAGS.octal, 0o666)
347    self.assertEqual(FLAGS.decimal, 666)
348    self.assertEqual(FLAGS.hexadecimal, 0x666)
349    self.assertEqual(FLAGS.x, 3)
350    self.assertEqual(FLAGS.l, 0x7fffffff00000000)
351    self.assertEqual(FLAGS.args, ['v=1', 'vmodule=a=0,b=2'])
352    self.assertEqual(FLAGS.letters, ['a', 'b', 'c'])
353    self.assertEqual(FLAGS.list_default_list, ['a', 'b', 'c'])
354    self.assertIsNone(FLAGS.kwery)
355    self.assertIsNone(FLAGS.sense)
356    self.assertIsNone(FLAGS.cases)
357    self.assertIsNone(FLAGS.funny)
358    self.assertIsNone(FLAGS.blah)
359
360    flag_values = FLAGS.flag_values_dict()
361    self.assertEqual(flag_values['repeat'], 4)
362    self.assertEqual(flag_values['name'], 'Bob')
363    self.assertEqual(flag_values['debug'], 0)
364    self.assertEqual(flag_values['r'], 4)  # Short for repeat.
365    self.assertEqual(flag_values['q'], 1)
366    self.assertEqual(flag_values['quack'], 0)
367    self.assertEqual(flag_values['x'], 3)
368    self.assertEqual(flag_values['l'], 0x7fffffff00000000)
369    self.assertEqual(flag_values['args'], ['v=1', 'vmodule=a=0,b=2'])
370    self.assertEqual(flag_values['letters'], ['a', 'b', 'c'])
371    self.assertEqual(flag_values['list_default_list'], ['a', 'b', 'c'])
372    self.assertIsNone(flag_values['kwery'])
373    self.assertIsNone(flag_values['sense'])
374    self.assertIsNone(flag_values['cases'])
375    self.assertIsNone(flag_values['funny'])
376    self.assertIsNone(flag_values['blah'])
377
378    # Verify string form of defaults
379    self.assertEqual(FLAGS['repeat'].default_as_str, "'4'")
380    self.assertEqual(FLAGS['name'].default_as_str, "'Bob'")
381    self.assertEqual(FLAGS['debug'].default_as_str, "'false'")
382    self.assertEqual(FLAGS['q'].default_as_str, "'true'")
383    self.assertEqual(FLAGS['quack'].default_as_str, "'false'")
384    self.assertEqual(FLAGS['noexec'].default_as_str, "'true'")
385    self.assertEqual(FLAGS['x'].default_as_str, "'3'")
386    self.assertEqual(FLAGS['l'].default_as_str, "'9223372032559808512'")
387    self.assertEqual(FLAGS['args'].default_as_str, '\'v=1,"vmodule=a=0,b=2"\'')
388    self.assertEqual(FLAGS['letters'].default_as_str, "'a,b,c'")
389    self.assertEqual(FLAGS['list_default_list'].default_as_str, "'a,b,c'")
390
391    # Verify that the iterator for flags yields all the keys
392    keys = list(FLAGS)
393    keys.sort()
394    reg_flags = list(FLAGS._flags())
395    reg_flags.sort()
396    self.assertEqual(keys, reg_flags)
397
398    # Parse flags
399    # .. empty command line
400    argv = ('./program',)
401    argv = FLAGS(argv)
402    self.assertLen(argv, 1, 'wrong number of arguments pulled')
403    self.assertEqual(argv[0], './program', 'program name not preserved')
404
405    # .. non-empty command line
406    argv = ('./program', '--debug', '--name=Bob', '-q', '--x=8')
407    argv = FLAGS(argv)
408    self.assertLen(argv, 1, 'wrong number of arguments pulled')
409    self.assertEqual(argv[0], './program', 'program name not preserved')
410    self.assertEqual(FLAGS['debug'].present, 1)
411    FLAGS['debug'].present = 0  # Reset
412    self.assertEqual(FLAGS['name'].present, 1)
413    FLAGS['name'].present = 0  # Reset
414    self.assertEqual(FLAGS['q'].present, 1)
415    FLAGS['q'].present = 0  # Reset
416    self.assertEqual(FLAGS['x'].present, 1)
417    FLAGS['x'].present = 0  # Reset
418
419    # Flags list.
420    self.assertLen(FLAGS, number_defined_flags + number_test_framework_flags)
421    self.assertIn('name', FLAGS)
422    self.assertIn('debug', FLAGS)
423    self.assertIn('repeat', FLAGS)
424    self.assertIn('r', FLAGS)
425    self.assertIn('q', FLAGS)
426    self.assertIn('quack', FLAGS)
427    self.assertIn('x', FLAGS)
428    self.assertIn('l', FLAGS)
429    self.assertIn('args', FLAGS)
430    self.assertIn('letters', FLAGS)
431    self.assertIn('list_default_list', FLAGS)
432
433    # __contains__
434    self.assertIn('name', FLAGS)
435    self.assertNotIn('name2', FLAGS)
436
437    # try deleting a flag
438    del FLAGS.r
439    self.assertLen(FLAGS,
440                   number_defined_flags - 1 + number_test_framework_flags)
441    self.assertNotIn('r', FLAGS)
442
443    # .. command line with extra stuff
444    argv = ('./program', '--debug', '--name=Bob', 'extra')
445    argv = FLAGS(argv)
446    self.assertLen(argv, 2, 'wrong number of arguments pulled')
447    self.assertEqual(argv[0], './program', 'program name not preserved')
448    self.assertEqual(argv[1], 'extra', 'extra argument not preserved')
449    self.assertEqual(FLAGS['debug'].present, 1)
450    FLAGS['debug'].present = 0  # Reset
451    self.assertEqual(FLAGS['name'].present, 1)
452    FLAGS['name'].present = 0  # Reset
453
454    # Test reset
455    argv = ('./program', '--debug')
456    argv = FLAGS(argv)
457    self.assertLen(argv, 1, 'wrong number of arguments pulled')
458    self.assertEqual(argv[0], './program', 'program name not preserved')
459    self.assertEqual(FLAGS['debug'].present, 1)
460    self.assertTrue(FLAGS['debug'].value)
461    FLAGS.unparse_flags()
462    self.assertEqual(FLAGS['debug'].present, 0)
463    self.assertFalse(FLAGS['debug'].value)
464
465    # Test that reset restores default value when default value is None.
466    argv = ('./program', '--kwery=who')
467    argv = FLAGS(argv)
468    self.assertLen(argv, 1, 'wrong number of arguments pulled')
469    self.assertEqual(argv[0], './program', 'program name not preserved')
470    self.assertEqual(FLAGS['kwery'].present, 1)
471    self.assertEqual(FLAGS['kwery'].value, 'who')
472    FLAGS.unparse_flags()
473    argv = ('./program', '--kwery=Why')
474    argv = FLAGS(argv)
475    self.assertLen(argv, 1, 'wrong number of arguments pulled')
476    self.assertEqual(argv[0], './program', 'program name not preserved')
477    self.assertEqual(FLAGS['kwery'].present, 1)
478    self.assertEqual(FLAGS['kwery'].value, 'Why')
479    FLAGS.unparse_flags()
480    self.assertEqual(FLAGS['kwery'].present, 0)
481    self.assertIsNone(FLAGS['kwery'].value)
482
483    # Test case sensitive enum.
484    argv = ('./program', '--sense=CASE')
485    argv = FLAGS(argv)
486    self.assertLen(argv, 1, 'wrong number of arguments pulled')
487    self.assertEqual(argv[0], './program', 'program name not preserved')
488    self.assertEqual(FLAGS['sense'].present, 1)
489    self.assertEqual(FLAGS['sense'].value, 'CASE')
490    FLAGS.unparse_flags()
491    argv = ('./program', '--sense=Case')
492    argv = FLAGS(argv)
493    self.assertLen(argv, 1, 'wrong number of arguments pulled')
494    self.assertEqual(argv[0], './program', 'program name not preserved')
495    self.assertEqual(FLAGS['sense'].present, 1)
496    self.assertEqual(FLAGS['sense'].value, 'Case')
497    FLAGS.unparse_flags()
498
499    # Test case insensitive enum.
500    argv = ('./program', '--cases=upper')
501    argv = FLAGS(argv)
502    self.assertLen(argv, 1, 'wrong number of arguments pulled')
503    self.assertEqual(argv[0], './program', 'program name not preserved')
504    self.assertEqual(FLAGS['cases'].present, 1)
505    self.assertEqual(FLAGS['cases'].value, 'UPPER')
506    FLAGS.unparse_flags()
507
508    # Test case sensitive enum with duplicates.
509    argv = ('./program', '--funny=ha')
510    argv = FLAGS(argv)
511    self.assertLen(argv, 1, 'wrong number of arguments pulled')
512    self.assertEqual(argv[0], './program', 'program name not preserved')
513    self.assertEqual(FLAGS['funny'].present, 1)
514    self.assertEqual(FLAGS['funny'].value, 'ha')
515    FLAGS.unparse_flags()
516
517    # Test case insensitive enum with duplicates.
518    argv = ('./program', '--blah=bLah')
519    argv = FLAGS(argv)
520    self.assertLen(argv, 1, 'wrong number of arguments pulled')
521    self.assertEqual(argv[0], './program', 'program name not preserved')
522    self.assertEqual(FLAGS['blah'].present, 1)
523    self.assertEqual(FLAGS['blah'].value, 'Blah')
524    FLAGS.unparse_flags()
525    argv = ('./program', '--blah=BLAH')
526    argv = FLAGS(argv)
527    self.assertLen(argv, 1, 'wrong number of arguments pulled')
528    self.assertEqual(argv[0], './program', 'program name not preserved')
529    self.assertEqual(FLAGS['blah'].present, 1)
530    self.assertEqual(FLAGS['blah'].value, 'Blah')
531    FLAGS.unparse_flags()
532
533    # Test integer argument passing
534    argv = ('./program', '--x', '0x12345')
535    argv = FLAGS(argv)
536    self.assertEqual(FLAGS.x, 0x12345)
537    self.assertEqual(type(FLAGS.x), int)
538
539    argv = ('./program', '--x', '0x1234567890ABCDEF1234567890ABCDEF')
540    argv = FLAGS(argv)
541    self.assertEqual(FLAGS.x, 0x1234567890ABCDEF1234567890ABCDEF)
542    self.assertIsInstance(FLAGS.x, int)
543
544    argv = ('./program', '--x', '0o12345')
545    argv = FLAGS(argv)
546    self.assertEqual(FLAGS.x, 0o12345)
547    self.assertEqual(type(FLAGS.x), int)
548
549    # Treat 0-prefixed parameters as base-10, not base-8
550    argv = ('./program', '--x', '012345')
551    argv = FLAGS(argv)
552    self.assertEqual(FLAGS.x, 12345)
553    self.assertEqual(type(FLAGS.x), int)
554
555    argv = ('./program', '--x', '0123459')
556    argv = FLAGS(argv)
557    self.assertEqual(FLAGS.x, 123459)
558    self.assertEqual(type(FLAGS.x), int)
559
560    argv = ('./program', '--x', '0x123efg')
561    with self.assertRaises(flags.IllegalFlagValueError):
562      argv = FLAGS(argv)
563
564    # Test boolean argument parsing
565    flags.DEFINE_boolean('test0', None, 'test boolean parsing')
566    argv = ('./program', '--notest0')
567    argv = FLAGS(argv)
568    self.assertEqual(FLAGS.test0, 0)
569
570    flags.DEFINE_boolean('test1', None, 'test boolean parsing')
571    argv = ('./program', '--test1')
572    argv = FLAGS(argv)
573    self.assertEqual(FLAGS.test1, 1)
574
575    FLAGS.test0 = None
576    argv = ('./program', '--test0=false')
577    argv = FLAGS(argv)
578    self.assertEqual(FLAGS.test0, 0)
579
580    FLAGS.test1 = None
581    argv = ('./program', '--test1=true')
582    argv = FLAGS(argv)
583    self.assertEqual(FLAGS.test1, 1)
584
585    FLAGS.test0 = None
586    argv = ('./program', '--test0=0')
587    argv = FLAGS(argv)
588    self.assertEqual(FLAGS.test0, 0)
589
590    FLAGS.test1 = None
591    argv = ('./program', '--test1=1')
592    argv = FLAGS(argv)
593    self.assertEqual(FLAGS.test1, 1)
594
595    # Test booleans that already have 'no' as a prefix
596    FLAGS.noexec = None
597    argv = ('./program', '--nonoexec', '--name', 'Bob')
598    argv = FLAGS(argv)
599    self.assertEqual(FLAGS.noexec, 0)
600
601    FLAGS.noexec = None
602    argv = ('./program', '--name', 'Bob', '--noexec')
603    argv = FLAGS(argv)
604    self.assertEqual(FLAGS.noexec, 1)
605
606    # Test unassigned booleans
607    flags.DEFINE_boolean('testnone', None, 'test boolean parsing')
608    argv = ('./program',)
609    argv = FLAGS(argv)
610    self.assertIsNone(FLAGS.testnone)
611
612    # Test get with default
613    flags.DEFINE_boolean('testget1', None, 'test parsing with defaults')
614    flags.DEFINE_boolean('testget2', None, 'test parsing with defaults')
615    flags.DEFINE_boolean('testget3', None, 'test parsing with defaults')
616    flags.DEFINE_integer('testget4', None, 'test parsing with defaults')
617    argv = ('./program', '--testget1', '--notestget2')
618    argv = FLAGS(argv)
619    self.assertEqual(FLAGS.get_flag_value('testget1', 'foo'), 1)
620    self.assertEqual(FLAGS.get_flag_value('testget2', 'foo'), 0)
621    self.assertEqual(FLAGS.get_flag_value('testget3', 'foo'), 'foo')
622    self.assertEqual(FLAGS.get_flag_value('testget4', 'foo'), 'foo')
623
624    # test list code
625    lists = [['hello', 'moo', 'boo', '1'], []]
626
627    flags.DEFINE_list('testcomma_list', '', 'test comma list parsing')
628    flags.DEFINE_spaceseplist('testspace_list', '', 'tests space list parsing')
629    flags.DEFINE_spaceseplist(
630        'testspace_or_comma_list',
631        '',
632        'tests space list parsing with comma compatibility',
633        comma_compat=True)
634
635    for name, sep in (('testcomma_list', ','), ('testspace_list',
636                                                ' '), ('testspace_list', '\n'),
637                      ('testspace_or_comma_list',
638                       ' '), ('testspace_or_comma_list',
639                              '\n'), ('testspace_or_comma_list', ',')):
640      for lst in lists:
641        argv = ('./program', '--%s=%s' % (name, sep.join(lst)))
642        argv = FLAGS(argv)
643        self.assertEqual(getattr(FLAGS, name), lst)
644
645    # Test help text
646    flags_help = str(FLAGS)
647    self.assertNotEqual(
648        flags_help.find('repeat'), -1, 'cannot find flag in help')
649    self.assertNotEqual(
650        flags_help.find(repeat_help), -1, 'cannot find help string in help')
651
652    # Test flag specified twice
653    argv = ('./program', '--repeat=4', '--repeat=2', '--debug', '--nodebug')
654    argv = FLAGS(argv)
655    self.assertEqual(FLAGS.get_flag_value('repeat', None), 2)
656    self.assertEqual(FLAGS.get_flag_value('debug', None), 0)
657
658    # Test MultiFlag with single default value
659    flags.DEFINE_multi_string(
660        's_str',
661        'sing1',
662        'string option that can occur multiple times',
663        short_name='s')
664    self.assertEqual(FLAGS.get_flag_value('s_str', None), ['sing1'])
665
666    # Test MultiFlag with list of default values
667    multi_string_defs = ['def1', 'def2']
668    flags.DEFINE_multi_string(
669        'm_str',
670        multi_string_defs,
671        'string option that can occur multiple times',
672        short_name='m')
673    self.assertEqual(FLAGS.get_flag_value('m_str', None), multi_string_defs)
674
675    # Test flag specified multiple times with a MultiFlag
676    argv = ('./program', '--m_str=str1', '-m', 'str2')
677    argv = FLAGS(argv)
678    self.assertEqual(FLAGS.get_flag_value('m_str', None), ['str1', 'str2'])
679
680    # A flag with allow_overwrite set to False should behave normally when it
681    # is only specified once
682    argv = ('./program', '--only_once=singlevalue')
683    argv = FLAGS(argv)
684    self.assertEqual(FLAGS.get_flag_value('only_once', None), 'singlevalue')
685
686    # A flag with allow_overwrite set to False should complain when it is
687    # specified more than once
688    argv = ('./program', '--universe=ptolemaic', '--universe=copernicean',
689            '--universe=euclidean')
690    self.assertRaisesWithLiteralMatch(
691        flags.IllegalFlagValueError,
692        'flag --universe=copernicean: already defined as ptolemaic', FLAGS,
693        argv)
694
695    # A flag value error shouldn't modify the value:
696    flags.DEFINE_integer('smol', 1, 'smol flag', upper_bound=5)
697    with self.assertRaises(flags.IllegalFlagValueError):
698      FLAGS.smol = 6
699    self.assertEqual(FLAGS.smol, 1)
700    self.assertTrue(FLAGS['smol'].using_default_value)
701
702    # Test single-letter flags; should support both single and double dash
703    argv = ('./program', '-q')
704    argv = FLAGS(argv)
705    self.assertEqual(FLAGS.get_flag_value('q', None), 1)
706
707    argv = ('./program', '--q', '--x', '9', '--noquack')
708    argv = FLAGS(argv)
709    self.assertEqual(FLAGS.get_flag_value('q', None), 1)
710    self.assertEqual(FLAGS.get_flag_value('x', None), 9)
711    self.assertEqual(FLAGS.get_flag_value('quack', None), 0)
712
713    argv = ('./program', '--noq', '--x=10', '--quack')
714    argv = FLAGS(argv)
715    self.assertEqual(FLAGS.get_flag_value('q', None), 0)
716    self.assertEqual(FLAGS.get_flag_value('x', None), 10)
717    self.assertEqual(FLAGS.get_flag_value('quack', None), 1)
718
719    ####################################
720    # Test flag serialization code:
721
722    old_testcomma_list = FLAGS.testcomma_list
723    old_testspace_list = FLAGS.testspace_list
724    old_testspace_or_comma_list = FLAGS.testspace_or_comma_list
725
726    argv = ('./program', FLAGS['test0'].serialize(), FLAGS['test1'].serialize(),
727            FLAGS['s_str'].serialize())
728
729    argv = FLAGS(argv)
730    self.assertEqual(FLAGS['test0'].serialize(), '--notest0')
731    self.assertEqual(FLAGS['test1'].serialize(), '--test1')
732    self.assertEqual(FLAGS['s_str'].serialize(), '--s_str=sing1')
733
734    self.assertEqual(FLAGS['testnone'].serialize(), '')
735
736    testcomma_list1 = ['aa', 'bb']
737    testspace_list1 = ['aa', 'bb', 'cc']
738    testspace_or_comma_list1 = ['aa', 'bb', 'cc', 'dd']
739    FLAGS.testcomma_list = list(testcomma_list1)
740    FLAGS.testspace_list = list(testspace_list1)
741    FLAGS.testspace_or_comma_list = list(testspace_or_comma_list1)
742    argv = ('./program', FLAGS['testcomma_list'].serialize(),
743            FLAGS['testspace_list'].serialize(),
744            FLAGS['testspace_or_comma_list'].serialize())
745    argv = FLAGS(argv)
746    self.assertEqual(FLAGS.testcomma_list, testcomma_list1)
747    self.assertEqual(FLAGS.testspace_list, testspace_list1)
748    self.assertEqual(FLAGS.testspace_or_comma_list, testspace_or_comma_list1)
749
750    testcomma_list1 = ['aa some spaces', 'bb']
751    testspace_list1 = ['aa', 'bb,some,commas,', 'cc']
752    testspace_or_comma_list1 = ['aa', 'bb,some,commas,', 'cc']
753    FLAGS.testcomma_list = list(testcomma_list1)
754    FLAGS.testspace_list = list(testspace_list1)
755    FLAGS.testspace_or_comma_list = list(testspace_or_comma_list1)
756    argv = ('./program', FLAGS['testcomma_list'].serialize(),
757            FLAGS['testspace_list'].serialize(),
758            FLAGS['testspace_or_comma_list'].serialize())
759    argv = FLAGS(argv)
760    self.assertEqual(FLAGS.testcomma_list, testcomma_list1)
761    self.assertEqual(FLAGS.testspace_list, testspace_list1)
762    # We don't expect idempotency when commas are placed in an item value and
763    # comma_compat is enabled.
764    self.assertEqual(FLAGS.testspace_or_comma_list,
765                     ['aa', 'bb', 'some', 'commas', 'cc'])
766
767    FLAGS.testcomma_list = old_testcomma_list
768    FLAGS.testspace_list = old_testspace_list
769    FLAGS.testspace_or_comma_list = old_testspace_or_comma_list
770
771    ####################################
772    # Test flag-update:
773
774    def args_list():
775      # Exclude flags that have different default values based on the
776      # environment.
777      flags_to_exclude = {'log_dir', 'test_srcdir', 'test_tmpdir'}
778      flagnames = set(FLAGS) - flags_to_exclude
779
780      nonbool_flags = []
781      truebool_flags = []
782      falsebool_flags = []
783      for name in flagnames:
784        flag_value = FLAGS.get_flag_value(name, None)
785        if not isinstance(FLAGS[name], flags.BooleanFlag):
786          nonbool_flags.append('--%s %s' % (name, flag_value))
787        elif flag_value:
788          truebool_flags.append('--%s' % name)
789        else:
790          falsebool_flags.append('--no%s' % name)
791      all_flags = nonbool_flags + truebool_flags + falsebool_flags
792      all_flags.sort()
793      return all_flags
794
795    argv = ('./program', '--repeat=3', '--name=giants', '--nodebug')
796
797    FLAGS(argv)
798    self.assertEqual(FLAGS.get_flag_value('repeat', None), 3)
799    self.assertEqual(FLAGS.get_flag_value('name', None), 'giants')
800    self.assertEqual(FLAGS.get_flag_value('debug', None), 0)
801    self.assertListEqual(
802        [
803            '--alsologtostderr',
804            "--args ['v=1', 'vmodule=a=0,b=2']",
805            '--blah None',
806            '--cases None',
807            '--decimal 666',
808            '--float 3.14',
809            '--funny None',
810            '--hexadecimal 1638',
811            '--kwery None',
812            '--l 9223372032559808512',
813            "--letters ['a', 'b', 'c']",
814            "--list_default_list ['a', 'b', 'c']",
815            '--logger_levels {}',
816            "--m ['str1', 'str2']",
817            "--m_str ['str1', 'str2']",
818            '--name giants',
819            '--no?',
820            '--nodebug',
821            '--noexec',
822            '--nohelp',
823            '--nohelpfull',
824            '--nohelpshort',
825            '--nohelpxml',
826            '--nologtostderr',
827            '--noonly_check_args',
828            '--nopdb_post_mortem',
829            '--noq',
830            '--norun_with_pdb',
831            '--norun_with_profiling',
832            '--notest0',
833            '--notestget2',
834            '--notestget3',
835            '--notestnone',
836            '--octal 438',
837            '--only_once singlevalue',
838            '--pdb False',
839            '--profile_file None',
840            '--quack',
841            '--repeat 3',
842            "--s ['sing1']",
843            "--s_str ['sing1']",
844            '--sense None',
845            '--showprefixforinfo',
846            '--smol 1',
847            '--stderrthreshold fatal',
848            '--test1',
849            '--test_random_seed 301',
850            '--test_randomize_ordering_seed ',
851            '--testcomma_list []',
852            '--testget1',
853            '--testget4 None',
854            '--testspace_list []',
855            '--testspace_or_comma_list []',
856            '--tmod_baz_x',
857            '--universe ptolemaic',
858            '--use_cprofile_for_profiling',
859            '--v -1',
860            '--verbosity -1',
861            '--x 10',
862            '--xml_output_file ',
863        ],
864        args_list(),
865    )
866
867    argv = ('./program', '--debug', '--m_str=upd1', '-s', 'upd2')
868    FLAGS(argv)
869    self.assertEqual(FLAGS.get_flag_value('repeat', None), 3)
870    self.assertEqual(FLAGS.get_flag_value('name', None), 'giants')
871    self.assertEqual(FLAGS.get_flag_value('debug', None), 1)
872
873    # items appended to existing non-default value lists for --m/--m_str
874    # new value overwrites default value (not appended to it) for --s/--s_str
875    self.assertListEqual(
876        [
877            '--alsologtostderr',
878            "--args ['v=1', 'vmodule=a=0,b=2']",
879            '--blah None',
880            '--cases None',
881            '--debug',
882            '--decimal 666',
883            '--float 3.14',
884            '--funny None',
885            '--hexadecimal 1638',
886            '--kwery None',
887            '--l 9223372032559808512',
888            "--letters ['a', 'b', 'c']",
889            "--list_default_list ['a', 'b', 'c']",
890            '--logger_levels {}',
891            "--m ['str1', 'str2', 'upd1']",
892            "--m_str ['str1', 'str2', 'upd1']",
893            '--name giants',
894            '--no?',
895            '--noexec',
896            '--nohelp',
897            '--nohelpfull',
898            '--nohelpshort',
899            '--nohelpxml',
900            '--nologtostderr',
901            '--noonly_check_args',
902            '--nopdb_post_mortem',
903            '--noq',
904            '--norun_with_pdb',
905            '--norun_with_profiling',
906            '--notest0',
907            '--notestget2',
908            '--notestget3',
909            '--notestnone',
910            '--octal 438',
911            '--only_once singlevalue',
912            '--pdb False',
913            '--profile_file None',
914            '--quack',
915            '--repeat 3',
916            "--s ['sing1', 'upd2']",
917            "--s_str ['sing1', 'upd2']",
918            '--sense None',
919            '--showprefixforinfo',
920            '--smol 1',
921            '--stderrthreshold fatal',
922            '--test1',
923            '--test_random_seed 301',
924            '--test_randomize_ordering_seed ',
925            '--testcomma_list []',
926            '--testget1',
927            '--testget4 None',
928            '--testspace_list []',
929            '--testspace_or_comma_list []',
930            '--tmod_baz_x',
931            '--universe ptolemaic',
932            '--use_cprofile_for_profiling',
933            '--v -1',
934            '--verbosity -1',
935            '--x 10',
936            '--xml_output_file ',
937        ],
938        args_list(),
939    )
940
941    ####################################
942    # Test all kind of error conditions.
943
944    # Argument not in enum exception
945    argv = ('./program', '--kwery=WHEN')
946    self.assertRaises(flags.IllegalFlagValueError, FLAGS, argv)
947    argv = ('./program', '--kwery=why')
948    self.assertRaises(flags.IllegalFlagValueError, FLAGS, argv)
949
950    # Duplicate flag detection
951    with self.assertRaises(flags.DuplicateFlagError):
952      flags.DEFINE_boolean('run', 0, 'runhelp', short_name='q')
953
954    # Duplicate short flag detection
955    with self.assertRaisesRegex(
956        flags.DuplicateFlagError,
957        r"The flag 'z' is defined twice\. .*First from.*, Second from"):
958      flags.DEFINE_boolean('zoom1', 0, 'runhelp z1', short_name='z')
959      flags.DEFINE_boolean('zoom2', 0, 'runhelp z2', short_name='z')
960      raise AssertionError('duplicate short flag detection failed')
961
962    # Duplicate mixed flag detection
963    with self.assertRaisesRegex(
964        flags.DuplicateFlagError,
965        r"The flag 's' is defined twice\. .*First from.*, Second from"):
966      flags.DEFINE_boolean('short1', 0, 'runhelp s1', short_name='s')
967      flags.DEFINE_boolean('s', 0, 'runhelp s2')
968
969    # Check that duplicate flag detection detects definition sites
970    # correctly.
971    flagnames = ['repeated']
972    original_flags = flags.FlagValues()
973    flags.DEFINE_boolean(
974        flagnames[0],
975        False,
976        'Flag about to be repeated.',
977        flag_values=original_flags)
978    duplicate_flags = module_foo.duplicate_flags(flagnames)
979    with self.assertRaisesRegex(flags.DuplicateFlagError,
980                                'flags_test.*module_foo'):
981      original_flags.append_flag_values(duplicate_flags)
982
983    # Make sure allow_override works
984    try:
985      flags.DEFINE_boolean(
986          'dup1', 0, 'runhelp d11', short_name='u', allow_override=0)
987      flag = FLAGS._flags()['dup1']
988      self.assertEqual(flag.default, 0)
989
990      flags.DEFINE_boolean(
991          'dup1', 1, 'runhelp d12', short_name='u', allow_override=1)
992      flag = FLAGS._flags()['dup1']
993      self.assertEqual(flag.default, 1)
994    except flags.DuplicateFlagError:
995      raise AssertionError('allow_override did not permit a flag duplication')
996
997    # Make sure allow_override works
998    try:
999      flags.DEFINE_boolean(
1000          'dup2', 0, 'runhelp d21', short_name='u', allow_override=1)
1001      flag = FLAGS._flags()['dup2']
1002      self.assertEqual(flag.default, 0)
1003
1004      flags.DEFINE_boolean(
1005          'dup2', 1, 'runhelp d22', short_name='u', allow_override=0)
1006      flag = FLAGS._flags()['dup2']
1007      self.assertEqual(flag.default, 1)
1008    except flags.DuplicateFlagError:
1009      raise AssertionError('allow_override did not permit a flag duplication')
1010
1011    # Make sure that re-importing a module does not cause a DuplicateFlagError
1012    # to be raised.
1013    try:
1014      sys.modules.pop('absl.flags.tests.module_baz')
1015      import absl.flags.tests.module_baz  # pylint: disable=g-import-not-at-top
1016      del absl
1017    except flags.DuplicateFlagError:
1018      raise AssertionError('Module reimport caused flag duplication error')
1019
1020    # Make sure that when we override, the help string gets updated correctly
1021    flags.DEFINE_boolean(
1022        'dup3', 0, 'runhelp d31', short_name='u', allow_override=1)
1023    flags.DEFINE_boolean(
1024        'dup3', 1, 'runhelp d32', short_name='u', allow_override=1)
1025    self.assertEqual(str(FLAGS).find('runhelp d31'), -1)
1026    self.assertNotEqual(str(FLAGS).find('runhelp d32'), -1)
1027
1028    # Make sure append_flag_values works
1029    new_flags = flags.FlagValues()
1030    flags.DEFINE_boolean('new1', 0, 'runhelp n1', flag_values=new_flags)
1031    flags.DEFINE_boolean('new2', 0, 'runhelp n2', flag_values=new_flags)
1032    self.assertEqual(len(new_flags._flags()), 2)
1033    old_len = len(FLAGS._flags())
1034    FLAGS.append_flag_values(new_flags)
1035    self.assertEqual(len(FLAGS._flags()) - old_len, 2)
1036    self.assertEqual('new1' in FLAGS._flags(), True)
1037    self.assertEqual('new2' in FLAGS._flags(), True)
1038
1039    # Then test that removing those flags works
1040    FLAGS.remove_flag_values(new_flags)
1041    self.assertEqual(len(FLAGS._flags()), old_len)
1042    self.assertFalse('new1' in FLAGS._flags())
1043    self.assertFalse('new2' in FLAGS._flags())
1044
1045    # Make sure append_flag_values works with flags with shortnames.
1046    new_flags = flags.FlagValues()
1047    flags.DEFINE_boolean('new3', 0, 'runhelp n3', flag_values=new_flags)
1048    flags.DEFINE_boolean(
1049        'new4', 0, 'runhelp n4', flag_values=new_flags, short_name='n4')
1050    self.assertEqual(len(new_flags._flags()), 3)
1051    old_len = len(FLAGS._flags())
1052    FLAGS.append_flag_values(new_flags)
1053    self.assertEqual(len(FLAGS._flags()) - old_len, 3)
1054    self.assertIn('new3', FLAGS._flags())
1055    self.assertIn('new4', FLAGS._flags())
1056    self.assertIn('n4', FLAGS._flags())
1057    self.assertEqual(FLAGS._flags()['n4'], FLAGS._flags()['new4'])
1058
1059    # Then test removing them
1060    FLAGS.remove_flag_values(new_flags)
1061    self.assertEqual(len(FLAGS._flags()), old_len)
1062    self.assertFalse('new3' in FLAGS._flags())
1063    self.assertFalse('new4' in FLAGS._flags())
1064    self.assertFalse('n4' in FLAGS._flags())
1065
1066    # Make sure append_flag_values fails on duplicates
1067    flags.DEFINE_boolean('dup4', 0, 'runhelp d41')
1068    new_flags = flags.FlagValues()
1069    flags.DEFINE_boolean('dup4', 0, 'runhelp d42', flag_values=new_flags)
1070    with self.assertRaises(flags.DuplicateFlagError):
1071      FLAGS.append_flag_values(new_flags)
1072
1073    # Integer out of bounds
1074    with self.assertRaises(flags.IllegalFlagValueError):
1075      argv = ('./program', '--repeat=-4')
1076      FLAGS(argv)
1077
1078    # Non-integer
1079    with self.assertRaises(flags.IllegalFlagValueError):
1080      argv = ('./program', '--repeat=2.5')
1081      FLAGS(argv)
1082
1083    # Missing required argument
1084    with self.assertRaises(flags.Error):
1085      argv = ('./program', '--name')
1086      FLAGS(argv)
1087
1088    # Non-boolean arguments for boolean
1089    with self.assertRaises(flags.IllegalFlagValueError):
1090      argv = ('./program', '--debug=goofup')
1091      FLAGS(argv)
1092
1093    with self.assertRaises(flags.IllegalFlagValueError):
1094      argv = ('./program', '--debug=42')
1095      FLAGS(argv)
1096
1097    # Non-numeric argument for integer flag --repeat
1098    with self.assertRaises(flags.IllegalFlagValueError):
1099      argv = ('./program', '--repeat', 'Bob', 'extra')
1100      FLAGS(argv)
1101
1102    # Aliases of existing flags
1103    with self.assertRaises(flags.UnrecognizedFlagError):
1104      flags.DEFINE_alias('alias_not_a_flag', 'not_a_flag')
1105
1106    # Programmtically modify alias and aliased flag
1107    flags.DEFINE_alias('alias_octal', 'octal')
1108    FLAGS.octal = 0o2222
1109    self.assertEqual(0o2222, FLAGS.octal)
1110    self.assertEqual(0o2222, FLAGS.alias_octal)
1111    FLAGS.alias_octal = 0o4444
1112    self.assertEqual(0o4444, FLAGS.octal)
1113    self.assertEqual(0o4444, FLAGS.alias_octal)
1114
1115    # Setting alias preserves the default of the original
1116    flags.DEFINE_alias('alias_name', 'name')
1117    flags.DEFINE_alias('alias_debug', 'debug')
1118    flags.DEFINE_alias('alias_decimal', 'decimal')
1119    flags.DEFINE_alias('alias_float', 'float')
1120    flags.DEFINE_alias('alias_letters', 'letters')
1121    self.assertEqual(FLAGS['name'].default, FLAGS.alias_name)
1122    self.assertEqual(FLAGS['debug'].default, FLAGS.alias_debug)
1123    self.assertEqual(int(FLAGS['decimal'].default), FLAGS.alias_decimal)
1124    self.assertEqual(float(FLAGS['float'].default), FLAGS.alias_float)
1125    self.assertSameElements(FLAGS['letters'].default, FLAGS.alias_letters)
1126
1127    # Original flags set on command line
1128    argv = ('./program', '--name=Martin', '--debug=True', '--decimal=777',
1129            '--letters=x,y,z')
1130    FLAGS(argv)
1131    self.assertEqual('Martin', FLAGS.name)
1132    self.assertEqual('Martin', FLAGS.alias_name)
1133    self.assertTrue(FLAGS.debug)
1134    self.assertTrue(FLAGS.alias_debug)
1135    self.assertEqual(777, FLAGS.decimal)
1136    self.assertEqual(777, FLAGS.alias_decimal)
1137    self.assertSameElements(['x', 'y', 'z'], FLAGS.letters)
1138    self.assertSameElements(['x', 'y', 'z'], FLAGS.alias_letters)
1139
1140    # Alias flags set on command line
1141    argv = ('./program', '--alias_name=Auston', '--alias_debug=False',
1142            '--alias_decimal=888', '--alias_letters=l,m,n')
1143    FLAGS(argv)
1144    self.assertEqual('Auston', FLAGS.name)
1145    self.assertEqual('Auston', FLAGS.alias_name)
1146    self.assertFalse(FLAGS.debug)
1147    self.assertFalse(FLAGS.alias_debug)
1148    self.assertEqual(888, FLAGS.decimal)
1149    self.assertEqual(888, FLAGS.alias_decimal)
1150    self.assertSameElements(['l', 'm', 'n'], FLAGS.letters)
1151    self.assertSameElements(['l', 'm', 'n'], FLAGS.alias_letters)
1152
1153    # Make sure importing a module does not change flag value parsed
1154    # from commandline.
1155    flags.DEFINE_integer(
1156        'dup5', 1, 'runhelp d51', short_name='u5', allow_override=0)
1157    self.assertEqual(FLAGS.dup5, 1)
1158    self.assertEqual(FLAGS.dup5, 1)
1159    argv = ('./program', '--dup5=3')
1160    FLAGS(argv)
1161    self.assertEqual(FLAGS.dup5, 3)
1162    flags.DEFINE_integer(
1163        'dup5', 2, 'runhelp d52', short_name='u5', allow_override=1)
1164    self.assertEqual(FLAGS.dup5, 3)
1165
1166    # Make sure importing a module does not change user defined flag value.
1167    flags.DEFINE_integer(
1168        'dup6', 1, 'runhelp d61', short_name='u6', allow_override=0)
1169    self.assertEqual(FLAGS.dup6, 1)
1170    FLAGS.dup6 = 3
1171    self.assertEqual(FLAGS.dup6, 3)
1172    flags.DEFINE_integer(
1173        'dup6', 2, 'runhelp d62', short_name='u6', allow_override=1)
1174    self.assertEqual(FLAGS.dup6, 3)
1175
1176    # Make sure importing a module does not change user defined flag value
1177    # even if it is the 'default' value.
1178    flags.DEFINE_integer(
1179        'dup7', 1, 'runhelp d71', short_name='u7', allow_override=0)
1180    self.assertEqual(FLAGS.dup7, 1)
1181    FLAGS.dup7 = 1
1182    self.assertEqual(FLAGS.dup7, 1)
1183    flags.DEFINE_integer(
1184        'dup7', 2, 'runhelp d72', short_name='u7', allow_override=1)
1185    self.assertEqual(FLAGS.dup7, 1)
1186
1187    # Test module_help().
1188    helpstr = FLAGS.module_help(module_baz)
1189
1190    expected_help = '\n' + module_baz.__name__ + ':' + """
1191  --[no]tmod_baz_x: Boolean flag.
1192    (default: 'true')"""
1193
1194    self.assertMultiLineEqual(expected_help, helpstr)
1195
1196    # Test main_module_help().  This must be part of test_flags because
1197    # it depends on dup1/2/3/etc being introduced first.
1198    helpstr = FLAGS.main_module_help()
1199
1200    expected_help = '\n' + sys.argv[0] + ':' + """
1201  --[no]alias_debug: Alias for --debug.
1202    (default: 'false')
1203  --alias_decimal: Alias for --decimal.
1204    (default: '666')
1205    (an integer)
1206  --alias_float: Alias for --float.
1207    (default: '3.14')
1208    (a number)
1209  --alias_letters: Alias for --letters.
1210    (default: 'a,b,c')
1211    (a comma separated list)
1212  --alias_name: Alias for --name.
1213    (default: 'Bob')
1214  --alias_octal: Alias for --octal.
1215    (default: '438')
1216    (an integer)
1217  --args: a list of arguments
1218    (default: 'v=1,"vmodule=a=0,b=2"')
1219    (a comma separated list)
1220  --blah: <bla|Blah|BLAH|blah>: ?
1221  --cases: <UPPER|lower|Initial|Ot_HeR>: ?
1222  --[no]debug: debughelp
1223    (default: 'false')
1224  --decimal: using decimals
1225    (default: '666')
1226    (an integer)
1227  -u,--[no]dup1: runhelp d12
1228    (default: 'true')
1229  -u,--[no]dup2: runhelp d22
1230    (default: 'true')
1231  -u,--[no]dup3: runhelp d32
1232    (default: 'true')
1233  --[no]dup4: runhelp d41
1234    (default: 'false')
1235  -u5,--dup5: runhelp d51
1236    (default: '1')
1237    (an integer)
1238  -u6,--dup6: runhelp d61
1239    (default: '1')
1240    (an integer)
1241  -u7,--dup7: runhelp d71
1242    (default: '1')
1243    (an integer)
1244  --float: using floats
1245    (default: '3.14')
1246    (a number)
1247  --funny: <Joke|ha|ha|ha|ha>: ?
1248  --hexadecimal: using hexadecimals
1249    (default: '1638')
1250    (an integer)
1251  --kwery: <who|what|Why|where|when>: ?
1252  --l: how long to be
1253    (default: '9223372032559808512')
1254    (an integer)
1255  --letters: a list of letters
1256    (default: 'a,b,c')
1257    (a comma separated list)
1258  --list_default_list: with default being a list of strings
1259    (default: 'a,b,c')
1260    (a comma separated list)
1261  -m,--m_str: string option that can occur multiple times;
1262    repeat this option to specify a list of values
1263    (default: "['def1', 'def2']")
1264  --name: namehelp
1265    (default: 'Bob')
1266  --[no]noexec: boolean flag with no as prefix
1267    (default: 'true')
1268  --octal: using octals
1269    (default: '438')
1270    (an integer)
1271  --only_once: test only sets this once
1272  --[no]q: quiet mode
1273    (default: 'true')
1274  --[no]quack: superstring of 'q'
1275    (default: 'false')
1276  -r,--repeat: how many times to repeat (0-5)
1277    (default: '4')
1278    (a non-negative integer)
1279  -s,--s_str: string option that can occur multiple times;
1280    repeat this option to specify a list of values
1281    (default: "['sing1']")
1282  --sense: <Case|case|CASE>: ?
1283  --smol: smol flag
1284    (default: '1')
1285    (integer <= 5)
1286  --[no]test0: test boolean parsing
1287  --[no]test1: test boolean parsing
1288  --testcomma_list: test comma list parsing
1289    (default: '')
1290    (a comma separated list)
1291  --[no]testget1: test parsing with defaults
1292  --[no]testget2: test parsing with defaults
1293  --[no]testget3: test parsing with defaults
1294  --testget4: test parsing with defaults
1295    (an integer)
1296  --[no]testnone: test boolean parsing
1297  --testspace_list: tests space list parsing
1298    (default: '')
1299    (a whitespace separated list)
1300  --testspace_or_comma_list: tests space list parsing with comma compatibility
1301    (default: '')
1302    (a whitespace or comma separated list)
1303  --universe: test tries to set this three times
1304  --x: how eXtreme to be
1305    (default: '3')
1306    (an integer)
1307  -z,--[no]zoom1: runhelp z1
1308    (default: 'false')"""
1309
1310    self.assertMultiLineEqual(expected_help, helpstr)
1311
1312  def test_string_flag_with_wrong_type(self):
1313    fv = flags.FlagValues()
1314    with self.assertRaises(flags.IllegalFlagValueError):
1315      flags.DEFINE_string('name', False, 'help', flag_values=fv)  # type: ignore
1316    with self.assertRaises(flags.IllegalFlagValueError):
1317      flags.DEFINE_string('name2', 0, 'help', flag_values=fv)  # type: ignore
1318
1319  def test_integer_flag_with_wrong_type(self):
1320    fv = flags.FlagValues()
1321    with self.assertRaises(flags.IllegalFlagValueError):
1322      flags.DEFINE_integer('name', 1e2, 'help', flag_values=fv)  # type: ignore
1323    with self.assertRaises(flags.IllegalFlagValueError):
1324      flags.DEFINE_integer('name', [], 'help', flag_values=fv)  # type: ignore
1325    with self.assertRaises(flags.IllegalFlagValueError):
1326      flags.DEFINE_integer('name', False, 'help', flag_values=fv)
1327
1328  def test_float_flag_with_wrong_type(self):
1329    fv = flags.FlagValues()
1330    with self.assertRaises(flags.IllegalFlagValueError):
1331      flags.DEFINE_float('name', False, 'help', flag_values=fv)
1332
1333  def test_enum_flag_with_empty_values(self):
1334    fv = flags.FlagValues()
1335    with self.assertRaises(ValueError):
1336      flags.DEFINE_enum('fruit', None, [], 'help', flag_values=fv)
1337
1338  def test_enum_flag_with_str_values(self):
1339    fv = flags.FlagValues()
1340    with self.assertRaises(ValueError):
1341      flags.DEFINE_enum('fruit', None, 'option', 'help', flag_values=fv)  # type: ignore
1342
1343  def test_multi_enum_flag_with_str_values(self):
1344    fv = flags.FlagValues()
1345    with self.assertRaises(ValueError):
1346      flags.DEFINE_multi_enum('fruit', None, 'option', 'help', flag_values=fv)  # type: ignore
1347
1348  def test_define_enum_class_flag(self):
1349    fv = flags.FlagValues()
1350    flags.DEFINE_enum_class('fruit', None, Fruit, '?', flag_values=fv)
1351    fv.mark_as_parsed()
1352
1353    self.assertIsNone(fv.fruit)
1354
1355  def test_parse_enum_class_flag(self):
1356    fv = flags.FlagValues()
1357    flags.DEFINE_enum_class('fruit', None, Fruit, '?', flag_values=fv)
1358
1359    argv = ('./program', '--fruit=orange')
1360    argv = fv(argv)
1361    self.assertEqual(len(argv), 1, 'wrong number of arguments pulled')
1362    self.assertEqual(argv[0], './program', 'program name not preserved')
1363    self.assertEqual(fv['fruit'].present, 1)
1364    self.assertEqual(fv['fruit'].value, Fruit.ORANGE)
1365    fv.unparse_flags()
1366    argv = ('./program', '--fruit=APPLE')
1367    argv = fv(argv)
1368    self.assertEqual(len(argv), 1, 'wrong number of arguments pulled')
1369    self.assertEqual(argv[0], './program', 'program name not preserved')
1370    self.assertEqual(fv['fruit'].present, 1)
1371    self.assertEqual(fv['fruit'].value, Fruit.APPLE)
1372    fv.unparse_flags()
1373
1374  def test_enum_class_flag_help_message(self):
1375    fv = flags.FlagValues()
1376    flags.DEFINE_enum_class('fruit', None, Fruit, '?', flag_values=fv)
1377
1378    helpstr = fv.main_module_help()
1379    expected_help = '\n%s:\n  --fruit: <apple|orange>: ?' % sys.argv[0]
1380
1381    self.assertEqual(helpstr, expected_help)
1382
1383  def test_enum_class_flag_with_wrong_default_value_type(self):
1384    fv = flags.FlagValues()
1385    with self.assertRaises(_exceptions.IllegalFlagValueError):
1386      flags.DEFINE_enum_class('fruit', 1, Fruit, 'help', flag_values=fv)  # type: ignore
1387
1388  def test_enum_class_flag_requires_enum_class(self):
1389    fv = flags.FlagValues()
1390    with self.assertRaises(TypeError):
1391      flags.DEFINE_enum_class(  # type: ignore
1392          'fruit', None, ['apple', 'orange'], 'help', flag_values=fv
1393      )
1394
1395  def test_enum_class_flag_requires_non_empty_enum_class(self):
1396    fv = flags.FlagValues()
1397    with self.assertRaises(ValueError):
1398      flags.DEFINE_enum_class('empty', None, EmptyEnum, 'help', flag_values=fv)
1399
1400  def test_required_flag(self):
1401    fv = flags.FlagValues()
1402    fl = flags.DEFINE_integer(
1403        name='int_flag',
1404        default=None,
1405        help='help',
1406        required=True,
1407        flag_values=fv)
1408    # Since the flag is required, the FlagHolder should ensure value returned
1409    # is not None.
1410    self.assertTrue(fl._ensure_non_none_value)
1411
1412  def test_illegal_required_flag(self):
1413    fv = flags.FlagValues()
1414    with self.assertRaises(ValueError):
1415      flags.DEFINE_integer(
1416          name='int_flag',
1417          default=3,
1418          help='help',
1419          required=True,
1420          flag_values=fv)
1421
1422
1423class MultiNumericalFlagsTest(absltest.TestCase):
1424
1425  def test_multi_numerical_flags(self):
1426    """Test multi_int and multi_float flags."""
1427    fv = flags.FlagValues()
1428    int_defaults = [77, 88]
1429    flags.DEFINE_multi_integer(
1430        'm_int',
1431        int_defaults,
1432        'integer option that can occur multiple times',
1433        short_name='mi',
1434        flag_values=fv)
1435    self.assertListEqual(fv['m_int'].default, int_defaults)
1436    argv = ('./program', '--m_int=-99', '--mi=101')
1437    fv(argv)
1438    self.assertListEqual(fv.get_flag_value('m_int', None), [-99, 101])
1439
1440    float_defaults = [2.2, 3]
1441    flags.DEFINE_multi_float(
1442        'm_float',
1443        float_defaults,
1444        'float option that can occur multiple times',
1445        short_name='mf',
1446        flag_values=fv)
1447    for (expected, actual) in zip(float_defaults,
1448                                  fv.get_flag_value('m_float', None)):
1449      self.assertAlmostEqual(expected, actual)
1450    argv = ('./program', '--m_float=-17', '--mf=2.78e9')
1451    fv(argv)
1452    expected_floats = [-17.0, 2.78e9]
1453    for (expected, actual) in zip(expected_floats,
1454                                  fv.get_flag_value('m_float', None)):
1455      self.assertAlmostEqual(expected, actual)
1456
1457  def test_multi_numerical_with_tuples(self):
1458    """Verify multi_int/float accept tuples as default values."""
1459    flags.DEFINE_multi_integer(
1460        'm_int_tuple', (77, 88),
1461        'integer option that can occur multiple times',
1462        short_name='mi_tuple')
1463    self.assertListEqual(FLAGS.get_flag_value('m_int_tuple', None), [77, 88])
1464
1465    dict_with_float_keys = {2.2: 'hello', 3: 'happy'}
1466    float_defaults = dict_with_float_keys.keys()
1467    flags.DEFINE_multi_float(
1468        'm_float_tuple',
1469        float_defaults,
1470        'float option that can occur multiple times',
1471        short_name='mf_tuple')
1472    for (expected, actual) in zip(float_defaults,
1473                                  FLAGS.get_flag_value('m_float_tuple', None)):
1474      self.assertAlmostEqual(expected, actual)
1475
1476  def test_single_value_default(self):
1477    """Test multi_int and multi_float flags with a single default value."""
1478    int_default = 77
1479    flags.DEFINE_multi_integer('m_int1', int_default,
1480                               'integer option that can occur multiple times')
1481    self.assertListEqual(FLAGS.get_flag_value('m_int1', None), [int_default])
1482
1483    float_default = 2.2
1484    flags.DEFINE_multi_float('m_float1', float_default,
1485                             'float option that can occur multiple times')
1486    actual = FLAGS.get_flag_value('m_float1', None)
1487    self.assertEqual(1, len(actual))
1488    self.assertAlmostEqual(actual[0], float_default)
1489
1490  def test_bad_multi_numerical_flags(self):
1491    """Test multi_int and multi_float flags with non-parseable values."""
1492
1493    # Test non-parseable defaults.
1494    self.assertRaisesRegex(
1495        flags.IllegalFlagValueError,
1496        r"flag --m_int2=abc: invalid literal for int\(\) with base 10: 'abc'",
1497        flags.DEFINE_multi_integer, 'm_int2', ['abc'], 'desc')
1498
1499    self.assertRaisesRegex(
1500        flags.IllegalFlagValueError, r'flag --m_float2=abc: '
1501        r'(invalid literal for float\(\)|could not convert string to float): '
1502        r"'?abc'?", flags.DEFINE_multi_float, 'm_float2', ['abc'], 'desc')
1503
1504    # Test non-parseable command line values.
1505    fv = flags.FlagValues()
1506    flags.DEFINE_multi_integer(
1507        'm_int2',
1508        '77',
1509        'integer option that can occur multiple times',
1510        flag_values=fv)
1511    argv = ('./program', '--m_int2=def')
1512    self.assertRaisesRegex(
1513        flags.IllegalFlagValueError,
1514        r"flag --m_int2=def: invalid literal for int\(\) with base 10: 'def'",
1515        fv, argv)
1516
1517    flags.DEFINE_multi_float(
1518        'm_float2',
1519        2.2,
1520        'float option that can occur multiple times',
1521        flag_values=fv)
1522    argv = ('./program', '--m_float2=def')
1523    self.assertRaisesRegex(
1524        flags.IllegalFlagValueError, r'flag --m_float2=def: '
1525        r'(invalid literal for float\(\)|could not convert string to float): '
1526        r"'?def'?", fv, argv)
1527
1528
1529class MultiEnumFlagsTest(absltest.TestCase):
1530
1531  def test_multi_enum_flags(self):
1532    """Test multi_enum flags."""
1533    fv = flags.FlagValues()
1534
1535    enum_defaults = ['FOO', 'BAZ']
1536    flags.DEFINE_multi_enum(
1537        'm_enum',
1538        enum_defaults, ['FOO', 'BAR', 'BAZ', 'WHOOSH'],
1539        'Enum option that can occur multiple times',
1540        short_name='me',
1541        flag_values=fv)
1542    self.assertListEqual(fv['m_enum'].default, enum_defaults)
1543    argv = ('./program', '--m_enum=WHOOSH', '--me=FOO')
1544    fv(argv)
1545    self.assertListEqual(fv.get_flag_value('m_enum', None), ['WHOOSH', 'FOO'])
1546
1547  def test_help_text(self):
1548    """Test multi_enum flag's help text."""
1549    fv = flags.FlagValues()
1550
1551    flags.DEFINE_multi_enum(
1552        'm_enum',
1553        None, ['FOO', 'BAR'],
1554        'Enum option that can occur multiple times',
1555        flag_values=fv)
1556    self.assertRegex(
1557        fv['m_enum'].help,
1558        r'<FOO\|BAR>: Enum option that can occur multiple times;\s+'
1559        'repeat this option to specify a list of values')
1560
1561  def test_single_value_default(self):
1562    """Test multi_enum flags with a single default value."""
1563    fv = flags.FlagValues()
1564    enum_default = 'FOO'
1565    flags.DEFINE_multi_enum(
1566        'm_enum1',
1567        enum_default, ['FOO', 'BAR', 'BAZ', 'WHOOSH'],
1568        'enum option that can occur multiple times',
1569        flag_values=fv)
1570    self.assertListEqual(fv['m_enum1'].default, [enum_default])
1571
1572  def test_case_sensitivity(self):
1573    """Test case sensitivity of multi_enum flag."""
1574    fv = flags.FlagValues()
1575    # Test case insensitive enum.
1576    flags.DEFINE_multi_enum(
1577        'm_enum2', ['whoosh'], ['FOO', 'BAR', 'BAZ', 'WHOOSH'],
1578        'Enum option that can occur multiple times',
1579        short_name='me2',
1580        case_sensitive=False,
1581        flag_values=fv)
1582    argv = ('./program', '--m_enum2=bar', '--me2=fOo')
1583    fv(argv)
1584    self.assertListEqual(fv.get_flag_value('m_enum2', None), ['BAR', 'FOO'])
1585
1586    # Test case sensitive enum.
1587    flags.DEFINE_multi_enum(
1588        'm_enum3', ['BAR'], ['FOO', 'BAR', 'BAZ', 'WHOOSH'],
1589        'Enum option that can occur multiple times',
1590        short_name='me3',
1591        case_sensitive=True,
1592        flag_values=fv)
1593    argv = ('./program', '--m_enum3=bar', '--me3=fOo')
1594    self.assertRaisesRegex(
1595        flags.IllegalFlagValueError,
1596        r'flag --m_enum3=invalid: value should be one of <FOO|BAR|BAZ|WHOOSH>',
1597        fv, argv)
1598
1599  def test_bad_multi_enum_flags(self):
1600    """Test multi_enum with invalid values."""
1601
1602    # Test defaults that are not in the permitted list of enums.
1603    self.assertRaisesRegex(
1604        flags.IllegalFlagValueError,
1605        r'flag --m_enum=INVALID: value should be one of <FOO|BAR|BAZ>',
1606        flags.DEFINE_multi_enum, 'm_enum', ['INVALID'], ['FOO', 'BAR', 'BAZ'],
1607        'desc')
1608
1609    self.assertRaisesRegex(
1610        flags.IllegalFlagValueError,
1611        r'flag --m_enum=1234: value should be one of <FOO|BAR|BAZ>',
1612        flags.DEFINE_multi_enum, 'm_enum2', [1234], ['FOO', 'BAR', 'BAZ'],
1613        'desc')
1614
1615    # Test command-line values that are not in the permitted list of enums.
1616    flags.DEFINE_multi_enum('m_enum4', 'FOO', ['FOO', 'BAR', 'BAZ'],
1617                            'enum option that can occur multiple times')
1618    argv = ('./program', '--m_enum4=INVALID')
1619    self.assertRaisesRegex(
1620        flags.IllegalFlagValueError,
1621        r'flag --m_enum4=invalid: value should be one of <FOO|BAR|BAZ>', FLAGS,
1622        argv)
1623
1624
1625class MultiEnumClassFlagsTest(absltest.TestCase):
1626
1627  def test_short_name(self):
1628    fv = flags.FlagValues()
1629    flags.DEFINE_multi_enum_class(
1630        'fruit',
1631        None,
1632        Fruit,
1633        'Enum option that can occur multiple times',
1634        flag_values=fv,
1635        short_name='me')
1636    self.assertEqual(fv['fruit'].short_name, 'me')
1637
1638  def test_define_results_in_registered_flag_with_none(self):
1639    fv = flags.FlagValues()
1640    enum_defaults = None
1641    flags.DEFINE_multi_enum_class(
1642        'fruit',
1643        enum_defaults,
1644        Fruit,
1645        'Enum option that can occur multiple times',
1646        flag_values=fv)
1647    fv.mark_as_parsed()
1648
1649    self.assertIsNone(fv.fruit)
1650
1651  def test_help_text(self):
1652    fv = flags.FlagValues()
1653    enum_defaults = None
1654    flags.DEFINE_multi_enum_class(
1655        'fruit',
1656        enum_defaults,
1657        Fruit,
1658        'Enum option that can occur multiple times',
1659        flag_values=fv)
1660
1661    self.assertRegex(
1662        fv['fruit'].help,
1663        r'<apple\|orange>: Enum option that can occur multiple times;\s+'
1664        'repeat this option to specify a list of values')
1665
1666  def test_define_results_in_registered_flag_with_string(self):
1667    fv = flags.FlagValues()
1668    enum_defaults = 'apple'
1669    flags.DEFINE_multi_enum_class(
1670        'fruit',
1671        enum_defaults,
1672        Fruit,
1673        'Enum option that can occur multiple times',
1674        flag_values=fv)
1675    fv.mark_as_parsed()
1676
1677    self.assertListEqual(fv.fruit, [Fruit.APPLE])
1678
1679  def test_define_results_in_registered_flag_with_enum(self):
1680    fv = flags.FlagValues()
1681    enum_defaults = Fruit.APPLE
1682    flags.DEFINE_multi_enum_class(
1683        'fruit',
1684        enum_defaults,
1685        Fruit,
1686        'Enum option that can occur multiple times',
1687        flag_values=fv)
1688    fv.mark_as_parsed()
1689
1690    self.assertListEqual(fv.fruit, [Fruit.APPLE])
1691
1692  def test_define_results_in_registered_flag_with_string_list(self):
1693    fv = flags.FlagValues()
1694    enum_defaults = ['apple', 'APPLE']
1695    flags.DEFINE_multi_enum_class(
1696        'fruit',
1697        enum_defaults,
1698        CaseSensitiveFruit,
1699        'Enum option that can occur multiple times',
1700        flag_values=fv,
1701        case_sensitive=True)
1702    fv.mark_as_parsed()
1703
1704    self.assertListEqual(fv.fruit,
1705                         [CaseSensitiveFruit.apple, CaseSensitiveFruit.APPLE])
1706
1707  def test_define_results_in_registered_flag_with_enum_list(self):
1708    fv = flags.FlagValues()
1709    enum_defaults = [Fruit.APPLE, Fruit.ORANGE]
1710    flags.DEFINE_multi_enum_class(
1711        'fruit',
1712        enum_defaults,
1713        Fruit,
1714        'Enum option that can occur multiple times',
1715        flag_values=fv)
1716    fv.mark_as_parsed()
1717
1718    self.assertListEqual(fv.fruit, [Fruit.APPLE, Fruit.ORANGE])
1719
1720  def test_from_command_line_returns_multiple(self):
1721    fv = flags.FlagValues()
1722    enum_defaults = [Fruit.APPLE]
1723    flags.DEFINE_multi_enum_class(
1724        'fruit',
1725        enum_defaults,
1726        Fruit,
1727        'Enum option that can occur multiple times',
1728        flag_values=fv)
1729    argv = ('./program', '--fruit=Apple', '--fruit=orange')
1730    fv(argv)
1731    self.assertListEqual(fv.fruit, [Fruit.APPLE, Fruit.ORANGE])
1732
1733  def test_bad_multi_enum_class_flags_from_definition(self):
1734    with self.assertRaisesRegex(
1735        flags.IllegalFlagValueError,
1736        'flag --fruit=INVALID: value should be one of <apple|orange|APPLE>'):
1737      flags.DEFINE_multi_enum_class('fruit', ['INVALID'], Fruit, 'desc')
1738
1739  def test_bad_multi_enum_class_flags_from_commandline(self):
1740    fv = flags.FlagValues()
1741    enum_defaults = [Fruit.APPLE]
1742    flags.DEFINE_multi_enum_class(
1743        'fruit', enum_defaults, Fruit, 'desc', flag_values=fv)
1744    argv = ('./program', '--fruit=INVALID')
1745    with self.assertRaisesRegex(
1746        flags.IllegalFlagValueError,
1747        'flag --fruit=INVALID: value should be one of <apple|orange|APPLE>'):
1748      fv(argv)
1749
1750
1751class UnicodeFlagsTest(absltest.TestCase):
1752  """Testing proper unicode support for flags."""
1753
1754  def test_unicode_default_and_helpstring(self):
1755    fv = flags.FlagValues()
1756    flags.DEFINE_string(
1757        'unicode_str',
1758        b'\xC3\x80\xC3\xBD'.decode('utf-8'),
1759        b'help:\xC3\xAA'.decode('utf-8'),
1760        flag_values=fv)
1761    argv = ('./program',)
1762    fv(argv)  # should not raise any exceptions
1763
1764    argv = ('./program', '--unicode_str=foo')
1765    fv(argv)  # should not raise any exceptions
1766
1767  def test_unicode_in_list(self):
1768    fv = flags.FlagValues()
1769    flags.DEFINE_list(
1770        'unicode_list',
1771        ['abc', b'\xC3\x80'.decode('utf-8'), b'\xC3\xBD'.decode('utf-8')],
1772        b'help:\xC3\xAB'.decode('utf-8'),
1773        flag_values=fv)
1774    argv = ('./program',)
1775    fv(argv)  # should not raise any exceptions
1776
1777    argv = ('./program', '--unicode_list=hello,there')
1778    fv(argv)  # should not raise any exceptions
1779
1780  def test_xmloutput(self):
1781    fv = flags.FlagValues()
1782    flags.DEFINE_string(
1783        'unicode1',
1784        b'\xC3\x80\xC3\xBD'.decode('utf-8'),
1785        b'help:\xC3\xAC'.decode('utf-8'),
1786        flag_values=fv)
1787    flags.DEFINE_list(
1788        'unicode2',
1789        ['abc', b'\xC3\x80'.decode('utf-8'), b'\xC3\xBD'.decode('utf-8')],
1790        b'help:\xC3\xAD'.decode('utf-8'),
1791        flag_values=fv)
1792    flags.DEFINE_list(
1793        'non_unicode', ['abc', 'def', 'ghi'],
1794        b'help:\xC3\xAD'.decode('utf-8'),
1795        flag_values=fv)
1796
1797    outfile = io.StringIO()
1798    fv.write_help_in_xml_format(outfile)
1799    actual_output = outfile.getvalue()
1800
1801    # The xml output is large, so we just check parts of it.
1802    self.assertIn(
1803        b'<name>unicode1</name>\n'
1804        b'    <meaning>help:\xc3\xac</meaning>\n'
1805        b'    <default>\xc3\x80\xc3\xbd</default>\n'
1806        b'    <current>\xc3\x80\xc3\xbd</current>'.decode('utf-8'),
1807        actual_output)
1808    self.assertIn(
1809        b'<name>unicode2</name>\n'
1810        b'    <meaning>help:\xc3\xad</meaning>\n'
1811        b'    <default>abc,\xc3\x80,\xc3\xbd</default>\n'
1812        b"    <current>['abc', '\xc3\x80', '\xc3\xbd']"
1813        b'</current>'.decode('utf-8'), actual_output)
1814    self.assertIn(
1815        b'<name>non_unicode</name>\n'
1816        b'    <meaning>help:\xc3\xad</meaning>\n'
1817        b'    <default>abc,def,ghi</default>\n'
1818        b"    <current>['abc', 'def', 'ghi']"
1819        b'</current>'.decode('utf-8'), actual_output)
1820
1821
1822class LoadFromFlagFileTest(absltest.TestCase):
1823  """Testing loading flags from a file and parsing them."""
1824
1825  def setUp(self):
1826    self.flag_values = flags.FlagValues()
1827    flags.DEFINE_string(
1828        'unittest_message1',
1829        'Foo!',
1830        'You Add Here.',
1831        flag_values=self.flag_values)
1832    flags.DEFINE_string(
1833        'unittest_message2',
1834        'Bar!',
1835        'Hello, Sailor!',
1836        flag_values=self.flag_values)
1837    flags.DEFINE_boolean(
1838        'unittest_boolflag',
1839        0,
1840        'Some Boolean thing',
1841        flag_values=self.flag_values)
1842    flags.DEFINE_integer(
1843        'unittest_number',
1844        12345,
1845        'Some integer',
1846        lower_bound=0,
1847        flag_values=self.flag_values)
1848    flags.DEFINE_list(
1849        'UnitTestList', '1,2,3', 'Some list', flag_values=self.flag_values)
1850    self.tmp_path = None
1851    self.flag_values.mark_as_parsed()
1852
1853  def tearDown(self):
1854    self._remove_test_files()
1855
1856  def _setup_test_files(self):
1857    """Creates and sets up some dummy flagfile files with bogus flags."""
1858
1859    # Figure out where to create temporary files
1860    self.assertFalse(self.tmp_path)
1861    self.tmp_path = tempfile.mkdtemp()
1862
1863    tmp_flag_file_1 = open(self.tmp_path + '/UnitTestFile1.tst', 'w')
1864    tmp_flag_file_2 = open(self.tmp_path + '/UnitTestFile2.tst', 'w')
1865    tmp_flag_file_3 = open(self.tmp_path + '/UnitTestFile3.tst', 'w')
1866    tmp_flag_file_4 = open(self.tmp_path + '/UnitTestFile4.tst', 'w')
1867
1868    # put some dummy flags in our test files
1869    tmp_flag_file_1.write('#A Fake Comment\n')
1870    tmp_flag_file_1.write('--unittest_message1=tempFile1!\n')
1871    tmp_flag_file_1.write('\n')
1872    tmp_flag_file_1.write('--unittest_number=54321\n')
1873    tmp_flag_file_1.write('--nounittest_boolflag\n')
1874    file_list = [tmp_flag_file_1.name]
1875    # this one includes test file 1
1876    tmp_flag_file_2.write('//A Different Fake Comment\n')
1877    tmp_flag_file_2.write('--flagfile=%s\n' % tmp_flag_file_1.name)
1878    tmp_flag_file_2.write('--unittest_message2=setFromTempFile2\n')
1879    tmp_flag_file_2.write('\t\t\n')
1880    tmp_flag_file_2.write('--unittest_number=6789a\n')
1881    file_list.append(tmp_flag_file_2.name)
1882    # this file points to itself
1883    tmp_flag_file_3.write('--flagfile=%s\n' % tmp_flag_file_3.name)
1884    tmp_flag_file_3.write('--unittest_message1=setFromTempFile3\n')
1885    tmp_flag_file_3.write('#YAFC\n')
1886    tmp_flag_file_3.write('--unittest_boolflag\n')
1887    file_list.append(tmp_flag_file_3.name)
1888    # this file is unreadable
1889    tmp_flag_file_4.write('--flagfile=%s\n' % tmp_flag_file_3.name)
1890    tmp_flag_file_4.write('--unittest_message1=setFromTempFile4\n')
1891    tmp_flag_file_4.write('--unittest_message1=setFromTempFile4\n')
1892    os.chmod(self.tmp_path + '/UnitTestFile4.tst', 0)
1893    file_list.append(tmp_flag_file_4.name)
1894
1895    tmp_flag_file_1.close()
1896    tmp_flag_file_2.close()
1897    tmp_flag_file_3.close()
1898    tmp_flag_file_4.close()
1899
1900    return file_list  # these are just the file names
1901
1902  def _remove_test_files(self):
1903    """Removes the files we just created."""
1904    if self.tmp_path:
1905      shutil.rmtree(self.tmp_path, ignore_errors=True)
1906      self.tmp_path = None
1907
1908  def _read_flags_from_files(self, argv, force_gnu):
1909    return argv[:1] + self.flag_values.read_flags_from_files(
1910        argv[1:], force_gnu=force_gnu)
1911
1912  #### Flagfile Unit Tests ####
1913  def test_method_flagfiles_1(self):
1914    """Test trivial case with no flagfile based options."""
1915    fake_cmd_line = 'fooScript --unittest_boolflag'
1916    fake_argv = fake_cmd_line.split(' ')
1917    self.flag_values(fake_argv)
1918    self.assertEqual(self.flag_values.unittest_boolflag, 1)
1919    self.assertListEqual(fake_argv,
1920                         self._read_flags_from_files(fake_argv, False))
1921
1922  def test_method_flagfiles_2(self):
1923    """Tests parsing one file + arguments off simulated argv."""
1924    tmp_files = self._setup_test_files()
1925    # specify our temp file on the fake cmd line
1926    fake_cmd_line = 'fooScript --q --flagfile=%s' % tmp_files[0]
1927    fake_argv = fake_cmd_line.split(' ')
1928
1929    # We should see the original cmd line with the file's contents spliced in.
1930    # Flags from the file will appear in the order order they are specified
1931    # in the file, in the same position as the flagfile argument.
1932    expected_results = [
1933        'fooScript', '--q', '--unittest_message1=tempFile1!',
1934        '--unittest_number=54321', '--nounittest_boolflag'
1935    ]
1936    test_results = self._read_flags_from_files(fake_argv, False)
1937    self.assertListEqual(expected_results, test_results)
1938
1939  # end testTwo def
1940
1941  def test_method_flagfiles_3(self):
1942    """Tests parsing nested files + arguments of simulated argv."""
1943    tmp_files = self._setup_test_files()
1944    # specify our temp file on the fake cmd line
1945    fake_cmd_line = ('fooScript --unittest_number=77 --flagfile=%s' %
1946                     tmp_files[1])
1947    fake_argv = fake_cmd_line.split(' ')
1948
1949    expected_results = [
1950        'fooScript', '--unittest_number=77', '--unittest_message1=tempFile1!',
1951        '--unittest_number=54321', '--nounittest_boolflag',
1952        '--unittest_message2=setFromTempFile2', '--unittest_number=6789a'
1953    ]
1954    test_results = self._read_flags_from_files(fake_argv, False)
1955    self.assertListEqual(expected_results, test_results)
1956
1957  # end testThree def
1958
1959  def test_method_flagfiles_3_spaces(self):
1960    """Tests parsing nested files + arguments of simulated argv.
1961
1962    The arguments include a pair that is actually an arg with a value, so it
1963    doesn't stop processing.
1964    """
1965    tmp_files = self._setup_test_files()
1966    # specify our temp file on the fake cmd line
1967    fake_cmd_line = ('fooScript --unittest_number 77 --flagfile=%s' %
1968                     tmp_files[1])
1969    fake_argv = fake_cmd_line.split(' ')
1970
1971    expected_results = [
1972        'fooScript', '--unittest_number', '77',
1973        '--unittest_message1=tempFile1!', '--unittest_number=54321',
1974        '--nounittest_boolflag', '--unittest_message2=setFromTempFile2',
1975        '--unittest_number=6789a'
1976    ]
1977    test_results = self._read_flags_from_files(fake_argv, False)
1978    self.assertListEqual(expected_results, test_results)
1979
1980  def test_method_flagfiles_3_spaces_boolean(self):
1981    """Tests parsing nested files + arguments of simulated argv.
1982
1983    The arguments include a pair that looks like a --x y arg with value, but
1984    since the flag is a boolean it's actually not.
1985    """
1986    tmp_files = self._setup_test_files()
1987    # specify our temp file on the fake cmd line
1988    fake_cmd_line = ('fooScript --unittest_boolflag 77 --flagfile=%s' %
1989                     tmp_files[1])
1990    fake_argv = fake_cmd_line.split(' ')
1991
1992    expected_results = [
1993        'fooScript', '--unittest_boolflag', '77',
1994        '--flagfile=%s' % tmp_files[1]
1995    ]
1996    with _use_gnu_getopt(self.flag_values, False):
1997      test_results = self._read_flags_from_files(fake_argv, False)
1998      self.assertListEqual(expected_results, test_results)
1999
2000  def test_method_flagfiles_4(self):
2001    """Tests parsing self-referential files + arguments of simulated argv.
2002
2003    This test should print a warning to stderr of some sort.
2004    """
2005    tmp_files = self._setup_test_files()
2006    # specify our temp file on the fake cmd line
2007    fake_cmd_line = ('fooScript --flagfile=%s --nounittest_boolflag' %
2008                     tmp_files[2])
2009    fake_argv = fake_cmd_line.split(' ')
2010    expected_results = [
2011        'fooScript', '--unittest_message1=setFromTempFile3',
2012        '--unittest_boolflag', '--nounittest_boolflag'
2013    ]
2014
2015    test_results = self._read_flags_from_files(fake_argv, False)
2016    self.assertListEqual(expected_results, test_results)
2017
2018  def test_method_flagfiles_5(self):
2019    """Test that --flagfile parsing respects the '--' end-of-options marker."""
2020    tmp_files = self._setup_test_files()
2021    # specify our temp file on the fake cmd line
2022    fake_cmd_line = 'fooScript --some_flag -- --flagfile=%s' % tmp_files[0]
2023    fake_argv = fake_cmd_line.split(' ')
2024    expected_results = [
2025        'fooScript', '--some_flag', '--',
2026        '--flagfile=%s' % tmp_files[0]
2027    ]
2028
2029    test_results = self._read_flags_from_files(fake_argv, False)
2030    self.assertListEqual(expected_results, test_results)
2031
2032  def test_method_flagfiles_6(self):
2033    """Test that --flagfile parsing stops at non-options (non-GNU behavior)."""
2034    tmp_files = self._setup_test_files()
2035    # specify our temp file on the fake cmd line
2036    fake_cmd_line = ('fooScript --some_flag some_arg --flagfile=%s' %
2037                     tmp_files[0])
2038    fake_argv = fake_cmd_line.split(' ')
2039    expected_results = [
2040        'fooScript', '--some_flag', 'some_arg',
2041        '--flagfile=%s' % tmp_files[0]
2042    ]
2043
2044    with _use_gnu_getopt(self.flag_values, False):
2045      test_results = self._read_flags_from_files(fake_argv, False)
2046      self.assertListEqual(expected_results, test_results)
2047
2048  def test_method_flagfiles_7(self):
2049    """Test that --flagfile parsing skips over a non-option (GNU behavior)."""
2050    self.flag_values.set_gnu_getopt()
2051    tmp_files = self._setup_test_files()
2052    # specify our temp file on the fake cmd line
2053    fake_cmd_line = ('fooScript --some_flag some_arg --flagfile=%s' %
2054                     tmp_files[0])
2055    fake_argv = fake_cmd_line.split(' ')
2056    expected_results = [
2057        'fooScript', '--some_flag', 'some_arg',
2058        '--unittest_message1=tempFile1!', '--unittest_number=54321',
2059        '--nounittest_boolflag'
2060    ]
2061
2062    test_results = self._read_flags_from_files(fake_argv, False)
2063    self.assertListEqual(expected_results, test_results)
2064
2065  def test_method_flagfiles_8(self):
2066    """Test that --flagfile parsing respects force_gnu=True."""
2067    tmp_files = self._setup_test_files()
2068    # specify our temp file on the fake cmd line
2069    fake_cmd_line = ('fooScript --some_flag some_arg --flagfile=%s' %
2070                     tmp_files[0])
2071    fake_argv = fake_cmd_line.split(' ')
2072    expected_results = [
2073        'fooScript', '--some_flag', 'some_arg',
2074        '--unittest_message1=tempFile1!', '--unittest_number=54321',
2075        '--nounittest_boolflag'
2076    ]
2077
2078    test_results = self._read_flags_from_files(fake_argv, True)
2079    self.assertListEqual(expected_results, test_results)
2080
2081  def test_method_flagfiles_repeated_non_circular(self):
2082    """Tests that parsing repeated non-circular flagfiles works."""
2083    tmp_files = self._setup_test_files()
2084    # specify our temp files on the fake cmd line
2085    fake_cmd_line = ('fooScript --flagfile=%s --flagfile=%s' %
2086                     (tmp_files[1], tmp_files[0]))
2087    fake_argv = fake_cmd_line.split(' ')
2088    expected_results = [
2089        'fooScript', '--unittest_message1=tempFile1!',
2090        '--unittest_number=54321', '--nounittest_boolflag',
2091        '--unittest_message2=setFromTempFile2', '--unittest_number=6789a',
2092        '--unittest_message1=tempFile1!', '--unittest_number=54321',
2093        '--nounittest_boolflag'
2094    ]
2095
2096    test_results = self._read_flags_from_files(fake_argv, False)
2097    self.assertListEqual(expected_results, test_results)
2098
2099  @unittest.skipIf(
2100      os.name == 'nt',
2101      'There is no good way to create an unreadable file on Windows.')
2102  def test_method_flagfiles_no_permissions(self):
2103    """Test that --flagfile raises except on file that is unreadable."""
2104    tmp_files = self._setup_test_files()
2105    # specify our temp file on the fake cmd line
2106    fake_cmd_line = ('fooScript --some_flag some_arg --flagfile=%s' %
2107                     tmp_files[3])
2108    fake_argv = fake_cmd_line.split(' ')
2109    self.assertRaises(flags.CantOpenFlagFileError, self._read_flags_from_files,
2110                      fake_argv, True)
2111
2112  def test_method_flagfiles_not_found(self):
2113    """Test that --flagfile raises except on file that does not exist."""
2114    tmp_files = self._setup_test_files()
2115    # specify our temp file on the fake cmd line
2116    fake_cmd_line = ('fooScript --some_flag some_arg --flagfile=%sNOTEXIST' %
2117                     tmp_files[3])
2118    fake_argv = fake_cmd_line.split(' ')
2119    self.assertRaises(flags.CantOpenFlagFileError, self._read_flags_from_files,
2120                      fake_argv, True)
2121
2122  def test_flagfiles_user_path_expansion(self):
2123    """Test that user directory referenced paths are correctly expanded.
2124
2125    Test paths like ~/foo. This test depends on whatever account's running
2126    the unit test to have read/write access to their own home directory,
2127    otherwise it'll FAIL.
2128    """
2129    fake_flagfile_item_style_1 = '--flagfile=~/foo.file'
2130    fake_flagfile_item_style_2 = '-flagfile=~/foo.file'
2131
2132    expected_results = os.path.expanduser('~/foo.file')
2133
2134    test_results = self.flag_values._extract_filename(
2135        fake_flagfile_item_style_1)
2136    self.assertEqual(expected_results, test_results)
2137
2138    test_results = self.flag_values._extract_filename(
2139        fake_flagfile_item_style_2)
2140    self.assertEqual(expected_results, test_results)
2141
2142  def test_no_touchy_non_flags(self):
2143    """Test that the flags parser does not mutilate arguments.
2144
2145    The arguments are not supposed to be flags
2146    """
2147    fake_argv = [
2148        'fooScript', '--unittest_boolflag', 'command', '--command_arg1',
2149        '--UnitTestBoom', '--UnitTestB'
2150    ]
2151    with _use_gnu_getopt(self.flag_values, False):
2152      argv = self.flag_values(fake_argv)
2153      self.assertListEqual(argv, fake_argv[:1] + fake_argv[2:])
2154
2155  def test_parse_flags_after_args_if_using_gnugetopt(self):
2156    """Test that flags given after arguments are parsed if using gnu_getopt."""
2157    self.flag_values.set_gnu_getopt()
2158    fake_argv = [
2159        'fooScript', '--unittest_boolflag', 'command', '--unittest_number=54321'
2160    ]
2161    argv = self.flag_values(fake_argv)
2162    self.assertListEqual(argv, ['fooScript', 'command'])
2163
2164  def test_set_default(self):
2165    """Test changing flag defaults."""
2166    # Test that set_default changes both the default and the value,
2167    # and that the value is changed when one is given as an option.
2168    self.flag_values.set_default('unittest_message1', 'New value')
2169    self.assertEqual(self.flag_values.unittest_message1, 'New value')
2170    self.assertEqual(self.flag_values['unittest_message1'].default_as_str,
2171                     "'New value'")
2172    self.flag_values(['dummyscript', '--unittest_message1=Newer value'])
2173    self.assertEqual(self.flag_values.unittest_message1, 'Newer value')
2174
2175    # Test that setting the default to None works correctly.
2176    self.flag_values.set_default('unittest_number', None)
2177    self.assertEqual(self.flag_values.unittest_number, None)
2178    self.assertEqual(self.flag_values['unittest_number'].default_as_str, None)
2179    self.flag_values(['dummyscript', '--unittest_number=56'])
2180    self.assertEqual(self.flag_values.unittest_number, 56)
2181
2182    # Test that setting the default to zero works correctly.
2183    self.flag_values.set_default('unittest_number', 0)
2184    self.assertEqual(self.flag_values['unittest_number'].default, 0)
2185    self.assertEqual(self.flag_values.unittest_number, 56)
2186    self.assertEqual(self.flag_values['unittest_number'].default_as_str, "'0'")
2187    self.flag_values(['dummyscript', '--unittest_number=56'])
2188    self.assertEqual(self.flag_values.unittest_number, 56)
2189
2190    # Test that setting the default to '' works correctly.
2191    self.flag_values.set_default('unittest_message1', '')
2192    self.assertEqual(self.flag_values['unittest_message1'].default, '')
2193    self.assertEqual(self.flag_values.unittest_message1, 'Newer value')
2194    self.assertEqual(self.flag_values['unittest_message1'].default_as_str, "''")
2195    self.flag_values(['dummyscript', '--unittest_message1=fifty-six'])
2196    self.assertEqual(self.flag_values.unittest_message1, 'fifty-six')
2197
2198    # Test that setting the default to false works correctly.
2199    self.flag_values.set_default('unittest_boolflag', False)
2200    self.assertEqual(self.flag_values.unittest_boolflag, False)
2201    self.assertEqual(self.flag_values['unittest_boolflag'].default_as_str,
2202                     "'false'")
2203    self.flag_values(['dummyscript', '--unittest_boolflag=true'])
2204    self.assertEqual(self.flag_values.unittest_boolflag, True)
2205
2206    # Test that setting a list default works correctly.
2207    self.flag_values.set_default('UnitTestList', '4,5,6')
2208    self.assertListEqual(self.flag_values.UnitTestList, ['4', '5', '6'])
2209    self.assertEqual(self.flag_values['UnitTestList'].default_as_str, "'4,5,6'")
2210    self.flag_values(['dummyscript', '--UnitTestList=7,8,9'])
2211    self.assertListEqual(self.flag_values.UnitTestList, ['7', '8', '9'])
2212
2213    # Test that setting invalid defaults raises exceptions
2214    with self.assertRaises(flags.IllegalFlagValueError):
2215      self.flag_values.set_default('unittest_number', 'oops')
2216    with self.assertRaises(flags.IllegalFlagValueError):
2217      self.flag_values.set_default('unittest_number', -1)
2218
2219
2220class FlagsParsingTest(absltest.TestCase):
2221  """Testing different aspects of parsing: '-f' vs '--flag', etc."""
2222
2223  def setUp(self):
2224    self.flag_values = flags.FlagValues()
2225
2226  def test_two_dash_arg_first(self):
2227    flags.DEFINE_string(
2228        'twodash_name', 'Bob', 'namehelp', flag_values=self.flag_values)
2229    flags.DEFINE_string(
2230        'twodash_blame', 'Rob', 'blamehelp', flag_values=self.flag_values)
2231    argv = ('./program', '--', '--twodash_name=Harry')
2232    argv = self.flag_values(argv)
2233    self.assertEqual('Bob', self.flag_values.twodash_name)
2234    self.assertEqual(argv[1], '--twodash_name=Harry')
2235
2236  def test_two_dash_arg_middle(self):
2237    flags.DEFINE_string(
2238        'twodash2_name', 'Bob', 'namehelp', flag_values=self.flag_values)
2239    flags.DEFINE_string(
2240        'twodash2_blame', 'Rob', 'blamehelp', flag_values=self.flag_values)
2241    argv = ('./program', '--twodash2_blame=Larry', '--',
2242            '--twodash2_name=Harry')
2243    argv = self.flag_values(argv)
2244    self.assertEqual('Bob', self.flag_values.twodash2_name)
2245    self.assertEqual('Larry', self.flag_values.twodash2_blame)
2246    self.assertEqual(argv[1], '--twodash2_name=Harry')
2247
2248  def test_one_dash_arg_first(self):
2249    flags.DEFINE_string(
2250        'onedash_name', 'Bob', 'namehelp', flag_values=self.flag_values)
2251    flags.DEFINE_string(
2252        'onedash_blame', 'Rob', 'blamehelp', flag_values=self.flag_values)
2253    argv = ('./program', '-', '--onedash_name=Harry')
2254    with _use_gnu_getopt(self.flag_values, False):
2255      argv = self.flag_values(argv)
2256      self.assertEqual(len(argv), 3)
2257      self.assertEqual(argv[1], '-')
2258      self.assertEqual(argv[2], '--onedash_name=Harry')
2259
2260  def test_required_flag_not_specified(self):
2261    flags.DEFINE_string(
2262        'str_flag',
2263        default=None,
2264        help='help',
2265        required=True,
2266        flag_values=self.flag_values)
2267    argv = ('./program',)
2268    with _use_gnu_getopt(self.flag_values, False):
2269      with self.assertRaises(flags.IllegalFlagValueError):
2270        self.flag_values(argv)
2271
2272  def test_required_arg_works_with_other_validators(self):
2273    flags.DEFINE_integer(
2274        'int_flag',
2275        default=None,
2276        help='help',
2277        required=True,
2278        lower_bound=4,
2279        flag_values=self.flag_values)
2280    argv = ('./program', '--int_flag=2')
2281    with _use_gnu_getopt(self.flag_values, False):
2282      with self.assertRaises(flags.IllegalFlagValueError):
2283        self.flag_values(argv)
2284
2285  def test_unrecognized_flags(self):
2286    flags.DEFINE_string('name', 'Bob', 'namehelp', flag_values=self.flag_values)
2287    # Unknown flag --nosuchflag
2288    try:
2289      argv = ('./program', '--nosuchflag', '--name=Bob', 'extra')
2290      self.flag_values(argv)
2291      raise AssertionError('Unknown flag exception not raised')
2292    except flags.UnrecognizedFlagError as e:
2293      self.assertEqual(e.flagname, 'nosuchflag')
2294      self.assertEqual(e.flagvalue, '--nosuchflag')
2295
2296    # Unknown flag -w (short option)
2297    try:
2298      argv = ('./program', '-w', '--name=Bob', 'extra')
2299      self.flag_values(argv)
2300      raise AssertionError('Unknown flag exception not raised')
2301    except flags.UnrecognizedFlagError as e:
2302      self.assertEqual(e.flagname, 'w')
2303      self.assertEqual(e.flagvalue, '-w')
2304
2305    # Unknown flag --nosuchflagwithparam=foo
2306    try:
2307      argv = ('./program', '--nosuchflagwithparam=foo', '--name=Bob', 'extra')
2308      self.flag_values(argv)
2309      raise AssertionError('Unknown flag exception not raised')
2310    except flags.UnrecognizedFlagError as e:
2311      self.assertEqual(e.flagname, 'nosuchflagwithparam')
2312      self.assertEqual(e.flagvalue, '--nosuchflagwithparam=foo')
2313
2314    # Allow unknown flag --nosuchflag if specified with undefok
2315    argv = ('./program', '--nosuchflag', '--name=Bob', '--undefok=nosuchflag',
2316            'extra')
2317    argv = self.flag_values(argv)
2318    self.assertEqual(len(argv), 2, 'wrong number of arguments pulled')
2319    self.assertEqual(argv[0], './program', 'program name not preserved')
2320    self.assertEqual(argv[1], 'extra', 'extra argument not preserved')
2321
2322    # Allow unknown flag --noboolflag if undefok=boolflag is specified
2323    argv = ('./program', '--noboolflag', '--name=Bob', '--undefok=boolflag',
2324            'extra')
2325    argv = self.flag_values(argv)
2326    self.assertEqual(len(argv), 2, 'wrong number of arguments pulled')
2327    self.assertEqual(argv[0], './program', 'program name not preserved')
2328    self.assertEqual(argv[1], 'extra', 'extra argument not preserved')
2329
2330    # But not if the flagname is misspelled:
2331    try:
2332      argv = ('./program', '--nosuchflag', '--name=Bob', '--undefok=nosuchfla',
2333              'extra')
2334      self.flag_values(argv)
2335      raise AssertionError('Unknown flag exception not raised')
2336    except flags.UnrecognizedFlagError as e:
2337      self.assertEqual(e.flagname, 'nosuchflag')
2338
2339    try:
2340      argv = ('./program', '--nosuchflag', '--name=Bob',
2341              '--undefok=nosuchflagg', 'extra')
2342      self.flag_values(argv)
2343      raise AssertionError('Unknown flag exception not raised')
2344    except flags.UnrecognizedFlagError as e:
2345      self.assertEqual(e.flagname, 'nosuchflag')
2346
2347    # Allow unknown short flag -w if specified with undefok
2348    argv = ('./program', '-w', '--name=Bob', '--undefok=w', 'extra')
2349    argv = self.flag_values(argv)
2350    self.assertEqual(len(argv), 2, 'wrong number of arguments pulled')
2351    self.assertEqual(argv[0], './program', 'program name not preserved')
2352    self.assertEqual(argv[1], 'extra', 'extra argument not preserved')
2353
2354    # Allow unknown flag --nosuchflagwithparam=foo if specified
2355    # with undefok
2356    argv = ('./program', '--nosuchflagwithparam=foo', '--name=Bob',
2357            '--undefok=nosuchflagwithparam', 'extra')
2358    argv = self.flag_values(argv)
2359    self.assertEqual(len(argv), 2, 'wrong number of arguments pulled')
2360    self.assertEqual(argv[0], './program', 'program name not preserved')
2361    self.assertEqual(argv[1], 'extra', 'extra argument not preserved')
2362
2363    # Even if undefok specifies multiple flags
2364    argv = ('./program', '--nosuchflag', '-w', '--nosuchflagwithparam=foo',
2365            '--name=Bob', '--undefok=nosuchflag,w,nosuchflagwithparam', 'extra')
2366    argv = self.flag_values(argv)
2367    self.assertEqual(len(argv), 2, 'wrong number of arguments pulled')
2368    self.assertEqual(argv[0], './program', 'program name not preserved')
2369    self.assertEqual(argv[1], 'extra', 'extra argument not preserved')
2370
2371    # However, not if undefok doesn't specify the flag
2372    try:
2373      argv = ('./program', '--nosuchflag', '--name=Bob',
2374              '--undefok=another_such', 'extra')
2375      self.flag_values(argv)
2376      raise AssertionError('Unknown flag exception not raised')
2377    except flags.UnrecognizedFlagError as e:
2378      self.assertEqual(e.flagname, 'nosuchflag')
2379
2380    # Make sure --undefok doesn't mask other option errors.
2381    try:
2382      # Provide an option requiring a parameter but not giving it one.
2383      argv = ('./program', '--undefok=name', '--name')
2384      self.flag_values(argv)
2385      raise AssertionError('Missing option parameter exception not raised')
2386    except flags.UnrecognizedFlagError:
2387      raise AssertionError('Wrong kind of error exception raised')
2388    except flags.Error:
2389      pass
2390
2391    # Test --undefok <list>
2392    argv = ('./program', '--nosuchflag', '-w', '--nosuchflagwithparam=foo',
2393            '--name=Bob', '--undefok', 'nosuchflag,w,nosuchflagwithparam',
2394            'extra')
2395    argv = self.flag_values(argv)
2396    self.assertEqual(len(argv), 2, 'wrong number of arguments pulled')
2397    self.assertEqual(argv[0], './program', 'program name not preserved')
2398    self.assertEqual(argv[1], 'extra', 'extra argument not preserved')
2399
2400    # Test incorrect --undefok with no value.
2401    argv = ('./program', '--name=Bob', '--undefok')
2402    with self.assertRaises(flags.Error):
2403      self.flag_values(argv)
2404
2405
2406class NonGlobalFlagsTest(absltest.TestCase):
2407
2408  def test_nonglobal_flags(self):
2409    """Test use of non-global FlagValues."""
2410    nonglobal_flags = flags.FlagValues()
2411    flags.DEFINE_string('nonglobal_flag', 'Bob', 'flaghelp', nonglobal_flags)
2412    argv = ('./program', '--nonglobal_flag=Mary', 'extra')
2413    argv = nonglobal_flags(argv)
2414    self.assertEqual(len(argv), 2, 'wrong number of arguments pulled')
2415    self.assertEqual(argv[0], './program', 'program name not preserved')
2416    self.assertEqual(argv[1], 'extra', 'extra argument not preserved')
2417    self.assertEqual(nonglobal_flags['nonglobal_flag'].value, 'Mary')
2418
2419  def test_unrecognized_nonglobal_flags(self):
2420    """Test unrecognized non-global flags."""
2421    nonglobal_flags = flags.FlagValues()
2422    argv = ('./program', '--nosuchflag')
2423    try:
2424      argv = nonglobal_flags(argv)
2425      raise AssertionError('Unknown flag exception not raised')
2426    except flags.UnrecognizedFlagError as e:
2427      self.assertEqual(e.flagname, 'nosuchflag')
2428
2429    argv = ('./program', '--nosuchflag', '--undefok=nosuchflag')
2430
2431    argv = nonglobal_flags(argv)
2432    self.assertEqual(len(argv), 1, 'wrong number of arguments pulled')
2433    self.assertEqual(argv[0], './program', 'program name not preserved')
2434
2435  def test_create_flag_errors(self):
2436    # Since the exception classes are exposed, nothing stops users
2437    # from creating their own instances. This test makes sure that
2438    # people modifying the flags module understand that the external
2439    # mechanisms for creating the exceptions should continue to work.
2440    _ = flags.Error()
2441    _ = flags.Error('message')
2442    _ = flags.DuplicateFlagError()
2443    _ = flags.DuplicateFlagError('message')
2444    _ = flags.IllegalFlagValueError()
2445    _ = flags.IllegalFlagValueError('message')
2446
2447  def test_flag_values_del_attr(self):
2448    """Checks that del self.flag_values.flag_id works."""
2449    default_value = 'default value for test_flag_values_del_attr'
2450    # 1. Declare and delete a flag with no short name.
2451    flag_values = flags.FlagValues()
2452    flags.DEFINE_string(
2453        'delattr_foo', default_value, 'A simple flag.', flag_values=flag_values)
2454
2455    flag_values.mark_as_parsed()
2456    self.assertEqual(flag_values.delattr_foo, default_value)
2457    flag_obj = flag_values['delattr_foo']
2458    # We also check that _FlagIsRegistered works as expected :)
2459    self.assertTrue(flag_values._flag_is_registered(flag_obj))
2460    del flag_values.delattr_foo
2461    self.assertFalse('delattr_foo' in flag_values._flags())
2462    self.assertFalse(flag_values._flag_is_registered(flag_obj))
2463    # If the previous del FLAGS.delattr_foo did not work properly, the
2464    # next definition will trigger a redefinition error.
2465    flags.DEFINE_integer(
2466        'delattr_foo', 3, 'A simple flag.', flag_values=flag_values)
2467    del flag_values.delattr_foo
2468
2469    self.assertFalse('delattr_foo' in flag_values)
2470
2471    # 2. Declare and delete a flag with a short name.
2472    flags.DEFINE_string(
2473        'delattr_bar',
2474        default_value,
2475        'flag with short name',
2476        short_name='x5',
2477        flag_values=flag_values)
2478    flag_obj = flag_values['delattr_bar']
2479    self.assertTrue(flag_values._flag_is_registered(flag_obj))
2480    del flag_values.x5
2481    self.assertTrue(flag_values._flag_is_registered(flag_obj))
2482    del flag_values.delattr_bar
2483    self.assertFalse(flag_values._flag_is_registered(flag_obj))
2484
2485    # 3. Just like 2, but del flag_values.name last
2486    flags.DEFINE_string(
2487        'delattr_bar',
2488        default_value,
2489        'flag with short name',
2490        short_name='x5',
2491        flag_values=flag_values)
2492    flag_obj = flag_values['delattr_bar']
2493    self.assertTrue(flag_values._flag_is_registered(flag_obj))
2494    del flag_values.delattr_bar
2495    self.assertTrue(flag_values._flag_is_registered(flag_obj))
2496    del flag_values.x5
2497    self.assertFalse(flag_values._flag_is_registered(flag_obj))
2498
2499    self.assertFalse('delattr_bar' in flag_values)
2500    self.assertFalse('x5' in flag_values)
2501
2502  def test_list_flag_format(self):
2503    """Tests for correctly-formatted list flags."""
2504    fv = flags.FlagValues()
2505    flags.DEFINE_list('listflag', '', 'A list of arguments', flag_values=fv)
2506
2507    def _check_parsing(listval):
2508      """Parse a particular value for our test flag, --listflag."""
2509      argv = fv(['./program', '--listflag=' + listval, 'plain-arg'])
2510      self.assertEqual(['./program', 'plain-arg'], argv)
2511      return fv.listflag
2512
2513    # Basic success case
2514    self.assertEqual(_check_parsing('foo,bar'), ['foo', 'bar'])
2515    # Success case: newline in argument is quoted.
2516    self.assertEqual(_check_parsing('"foo","bar\nbar"'), ['foo', 'bar\nbar'])
2517    # Failure case: newline in argument is unquoted.
2518    self.assertRaises(flags.IllegalFlagValueError, _check_parsing,
2519                      '"foo",bar\nbar')
2520    # Failure case: unmatched ".
2521    self.assertRaises(flags.IllegalFlagValueError, _check_parsing,
2522                      '"foo,barbar')
2523
2524  def test_flag_definition_via_setitem(self):
2525    with self.assertRaises(flags.IllegalFlagValueError):
2526      flag_values = flags.FlagValues()
2527      flag_values['flag_name'] = 'flag_value'  # type: ignore
2528
2529
2530class SetDefaultTest(absltest.TestCase):
2531
2532  def setUp(self):
2533    super().setUp()
2534    self.flag_values = flags.FlagValues()
2535
2536  def test_success(self):
2537    int_holder = flags.DEFINE_integer(
2538        'an_int', 1, 'an int', flag_values=self.flag_values)
2539
2540    flags.set_default(int_holder, 2)
2541    self.flag_values.mark_as_parsed()
2542
2543    self.assertEqual(int_holder.value, 2)
2544
2545  def test_update_after_parse(self):
2546    int_holder = flags.DEFINE_integer(
2547        'an_int', 1, 'an int', flag_values=self.flag_values)
2548
2549    self.flag_values.mark_as_parsed()
2550    flags.set_default(int_holder, 2)
2551
2552    self.assertEqual(int_holder.value, 2)
2553
2554  def test_overridden_by_explicit_assignment(self):
2555    int_holder = flags.DEFINE_integer(
2556        'an_int', 1, 'an int', flag_values=self.flag_values)
2557
2558    self.flag_values.mark_as_parsed()
2559    self.flag_values.an_int = 3
2560    flags.set_default(int_holder, 2)
2561
2562    self.assertEqual(int_holder.value, 3)
2563
2564  def test_restores_back_to_none(self):
2565    int_holder = flags.DEFINE_integer(
2566        'an_int', None, 'an int', flag_values=self.flag_values)
2567
2568    self.flag_values.mark_as_parsed()
2569    flags.set_default(int_holder, 3)
2570    flags.set_default(int_holder, None)
2571
2572    self.assertIsNone(int_holder.value)
2573
2574  def test_failure_on_invalid_type(self):
2575    int_holder = flags.DEFINE_integer(
2576        'an_int', 1, 'an int', flag_values=self.flag_values)
2577
2578    self.flag_values.mark_as_parsed()
2579
2580    with self.assertRaises(flags.IllegalFlagValueError):
2581      flags.set_default(int_holder, 'a')  # type: ignore
2582
2583  def test_failure_on_type_protected_none_default(self):
2584    int_holder = flags.DEFINE_integer(
2585        'an_int', 1, 'an int', flag_values=self.flag_values)
2586
2587    self.flag_values.mark_as_parsed()
2588
2589    flags.set_default(int_holder, None)  # type: ignore
2590
2591    with self.assertRaises(flags.IllegalFlagValueError):
2592      _ = int_holder.value  # Will also fail on later access.
2593
2594
2595class OverrideValueTest(absltest.TestCase):
2596
2597  def setUp(self):
2598    super().setUp()
2599    self.flag_values = flags.FlagValues()
2600
2601  def test_success(self):
2602    int_holder = flags.DEFINE_integer(
2603        'an_int', 1, 'an int', flag_values=self.flag_values
2604    )
2605
2606    flags.override_value(int_holder, 2)
2607    self.flag_values.mark_as_parsed()
2608
2609    self.assertEqual(int_holder.value, 2)
2610
2611  def test_update_after_parse(self):
2612    int_holder = flags.DEFINE_integer(
2613        'an_int', 1, 'an int', flag_values=self.flag_values
2614    )
2615
2616    self.flag_values.mark_as_parsed()
2617    flags.override_value(int_holder, 2)
2618
2619    self.assertEqual(int_holder.value, 2)
2620
2621  def test_overrides_explicit_assignment(self):
2622    int_holder = flags.DEFINE_integer(
2623        'an_int', 1, 'an int', flag_values=self.flag_values
2624    )
2625
2626    self.flag_values.mark_as_parsed()
2627    self.flag_values.an_int = 3
2628    flags.override_value(int_holder, 2)
2629
2630    self.assertEqual(int_holder.value, 2)
2631
2632  def test_overriden_by_explicit_assignment(self):
2633    int_holder = flags.DEFINE_integer(
2634        'an_int', 1, 'an int', flag_values=self.flag_values
2635    )
2636
2637    self.flag_values.mark_as_parsed()
2638    flags.override_value(int_holder, 2)
2639    self.flag_values.an_int = 3
2640
2641    self.assertEqual(int_holder.value, 3)
2642
2643  def test_multi_flag(self):
2644    multi_holder = flags.DEFINE_multi_string(
2645        'strs', [], 'some strs', flag_values=self.flag_values
2646    )
2647
2648    flags.override_value(multi_holder, ['a', 'b'])
2649    self.flag_values.mark_as_parsed()
2650
2651    self.assertEqual(multi_holder.value, ['a', 'b'])
2652
2653  def test_failure_on_invalid_type(self):
2654    int_holder = flags.DEFINE_integer(
2655        'an_int', 1, 'an int', flag_values=self.flag_values
2656    )
2657
2658    self.flag_values.mark_as_parsed()
2659
2660    with self.assertRaises(flags.IllegalFlagValueError):
2661      flags.override_value(int_holder, 'a')  # pytype: disable=wrong-arg-types
2662
2663    self.assertEqual(int_holder.value, 1)
2664
2665  def test_failure_on_unparsed_value(self):
2666    int_holder = flags.DEFINE_integer(
2667        'an_int', 1, 'an int', flag_values=self.flag_values
2668    )
2669
2670    self.flag_values.mark_as_parsed()
2671
2672    with self.assertRaises(flags.IllegalFlagValueError):
2673      flags.override_value(int_holder, '2')  # pytype: disable=wrong-arg-types
2674
2675  def test_failure_on_parser_rejection(self):
2676    int_holder = flags.DEFINE_integer(
2677        'an_int', 1, 'an int', flag_values=self.flag_values, upper_bound=5
2678    )
2679
2680    self.flag_values.mark_as_parsed()
2681
2682    with self.assertRaises(flags.IllegalFlagValueError):
2683      flags.override_value(int_holder, 6)
2684
2685    self.assertEqual(int_holder.value, 1)
2686
2687  def test_failure_on_validator_rejection(self):
2688    int_holder = flags.DEFINE_integer(
2689        'an_int', 1, 'an int', flag_values=self.flag_values
2690    )
2691    flags.register_validator(
2692        int_holder.name, lambda x: x < 5, flag_values=self.flag_values
2693    )
2694
2695    self.flag_values.mark_as_parsed()
2696
2697    with self.assertRaises(flags.IllegalFlagValueError):
2698      flags.override_value(int_holder, 6)
2699
2700    self.assertEqual(int_holder.value, 1)
2701
2702
2703class KeyFlagsTest(absltest.TestCase):
2704
2705  def setUp(self):
2706    self.flag_values = flags.FlagValues()
2707
2708  def _get_names_of_defined_flags(self, module, flag_values):
2709    """Returns the list of names of flags defined by a module.
2710
2711    Auxiliary for the test_key_flags* methods.
2712
2713    Args:
2714      module: A module object or a string module name.
2715      flag_values: A FlagValues object.
2716
2717    Returns:
2718      A list of strings.
2719    """
2720    return [f.name for f in flag_values.get_flags_for_module(module)]
2721
2722  def _get_names_of_key_flags(self, module, flag_values):
2723    """Returns the list of names of key flags for a module.
2724
2725    Auxiliary for the test_key_flags* methods.
2726
2727    Args:
2728      module: A module object or a string module name.
2729      flag_values: A FlagValues object.
2730
2731    Returns:
2732      A list of strings.
2733    """
2734    return [f.name for f in flag_values.get_key_flags_for_module(module)]
2735
2736  def _assert_lists_have_same_elements(self, list_1, list_2):
2737    # Checks that two lists have the same elements with the same
2738    # multiplicity, in possibly different order.
2739    list_1 = list(list_1)
2740    list_1.sort()
2741    list_2 = list(list_2)
2742    list_2.sort()
2743    self.assertListEqual(list_1, list_2)
2744
2745  def test_key_flags(self):
2746    flag_values = flags.FlagValues()
2747    # Before starting any testing, make sure no flags are already
2748    # defined for module_foo and module_bar.
2749    self.assertListEqual(
2750        self._get_names_of_key_flags(module_foo, flag_values), [])
2751    self.assertListEqual(
2752        self._get_names_of_key_flags(module_bar, flag_values), [])
2753    self.assertListEqual(
2754        self._get_names_of_defined_flags(module_foo, flag_values), [])
2755    self.assertListEqual(
2756        self._get_names_of_defined_flags(module_bar, flag_values), [])
2757
2758    # Defines a few flags in module_foo and module_bar.
2759    module_foo.define_flags(flag_values=flag_values)
2760
2761    try:
2762      # Part 1. Check that all flags defined by module_foo are key for
2763      # that module, and similarly for module_bar.
2764      for module in [module_foo, module_bar]:
2765        self._assert_lists_have_same_elements(
2766            flag_values.get_flags_for_module(module),
2767            flag_values.get_key_flags_for_module(module))
2768        # Also check that each module defined the expected flags.
2769        self._assert_lists_have_same_elements(
2770            self._get_names_of_defined_flags(module, flag_values),
2771            module.names_of_defined_flags())
2772
2773      # Part 2. Check that flags.declare_key_flag works fine.
2774      # Declare that some flags from module_bar are key for
2775      # module_foo.
2776      module_foo.declare_key_flags(flag_values=flag_values)
2777
2778      # Check that module_foo has the expected list of defined flags.
2779      self._assert_lists_have_same_elements(
2780          self._get_names_of_defined_flags(module_foo, flag_values),
2781          module_foo.names_of_defined_flags())
2782
2783      # Check that module_foo has the expected list of key flags.
2784      self._assert_lists_have_same_elements(
2785          self._get_names_of_key_flags(module_foo, flag_values),
2786          module_foo.names_of_declared_key_flags())
2787
2788      # Part 3. Check that flags.adopt_module_key_flags works fine.
2789      # Trigger a call to flags.adopt_module_key_flags(module_bar)
2790      # inside module_foo.  This should declare a few more key
2791      # flags in module_foo.
2792      module_foo.declare_extra_key_flags(flag_values=flag_values)
2793
2794      # Check that module_foo has the expected list of key flags.
2795      self._assert_lists_have_same_elements(
2796          self._get_names_of_key_flags(module_foo, flag_values),
2797          module_foo.names_of_declared_key_flags() +
2798          module_foo.names_of_declared_extra_key_flags())
2799    finally:
2800      module_foo.remove_flags(flag_values=flag_values)
2801
2802  def test_key_flags_with_non_default_flag_values_object(self):
2803    # Check that key flags work even when we use a FlagValues object
2804    # that is not the default flags.self.flag_values object.  Otherwise, this
2805    # test is similar to test_key_flags, but it uses only module_bar.
2806    # The other test module (module_foo) uses only the default values
2807    # for the flag_values keyword arguments.  This way, test_key_flags
2808    # and this method test both the default FlagValues, the explicitly
2809    # specified one, and a mixed usage of the two.
2810
2811    # A brand-new FlagValues object, to use instead of flags.self.flag_values.
2812    fv = flags.FlagValues()
2813
2814    # Before starting any testing, make sure no flags are already
2815    # defined for module_foo and module_bar.
2816    self.assertListEqual(self._get_names_of_key_flags(module_bar, fv), [])
2817    self.assertListEqual(self._get_names_of_defined_flags(module_bar, fv), [])
2818
2819    module_bar.define_flags(flag_values=fv)
2820
2821    # Check that all flags defined by module_bar are key for that
2822    # module, and that module_bar defined the expected flags.
2823    self._assert_lists_have_same_elements(
2824        fv.get_flags_for_module(module_bar),
2825        fv.get_key_flags_for_module(module_bar))
2826    self._assert_lists_have_same_elements(
2827        self._get_names_of_defined_flags(module_bar, fv),
2828        module_bar.names_of_defined_flags())
2829
2830    # Pick two flags from module_bar, declare them as key for the
2831    # current (i.e., main) module (via flags.declare_key_flag), and
2832    # check that we get the expected effect.  The important thing is
2833    # that we always use flags_values=fv (instead of the default
2834    # self.flag_values).
2835    main_module = sys.argv[0]
2836    names_of_flags_defined_by_bar = module_bar.names_of_defined_flags()
2837    flag_name_0 = names_of_flags_defined_by_bar[0]
2838    flag_name_2 = names_of_flags_defined_by_bar[2]
2839
2840    flags.declare_key_flag(flag_name_0, flag_values=fv)
2841    self._assert_lists_have_same_elements(
2842        self._get_names_of_key_flags(main_module, fv), [flag_name_0])
2843
2844    flags.declare_key_flag(flag_name_2, flag_values=fv)
2845    self._assert_lists_have_same_elements(
2846        self._get_names_of_key_flags(main_module, fv),
2847        [flag_name_0, flag_name_2])
2848
2849    # Try with a special (not user-defined) flag too:
2850    flags.declare_key_flag('undefok', flag_values=fv)
2851    self._assert_lists_have_same_elements(
2852        self._get_names_of_key_flags(main_module, fv),
2853        [flag_name_0, flag_name_2, 'undefok'])
2854
2855    flags.adopt_module_key_flags(module_bar, fv)
2856    self._assert_lists_have_same_elements(
2857        self._get_names_of_key_flags(main_module, fv),
2858        names_of_flags_defined_by_bar + ['undefok'])
2859
2860    # Adopt key flags from the flags module itself.
2861    flags.adopt_module_key_flags(flags, flag_values=fv)
2862    self._assert_lists_have_same_elements(
2863        self._get_names_of_key_flags(main_module, fv),
2864        names_of_flags_defined_by_bar + ['flagfile', 'undefok'])
2865
2866  def test_key_flags_with_flagholders(self):
2867    main_module = sys.argv[0]
2868
2869    self.assertListEqual(
2870        self._get_names_of_key_flags(main_module, self.flag_values), [])
2871    self.assertListEqual(
2872        self._get_names_of_defined_flags(main_module, self.flag_values), [])
2873
2874    int_holder = flags.DEFINE_integer(
2875        'main_module_int_fg',
2876        1,
2877        'Integer flag in the main module.',
2878        flag_values=self.flag_values)
2879
2880    flags.declare_key_flag(int_holder, self.flag_values)
2881
2882    self.assertCountEqual(
2883        self.flag_values.get_flags_for_module(main_module),
2884        self.flag_values.get_key_flags_for_module(main_module))
2885
2886    bool_holder = flags.DEFINE_boolean(
2887        'main_module_bool_fg',
2888        False,
2889        'Boolean flag in the main module.',
2890        flag_values=self.flag_values)
2891
2892    flags.declare_key_flag(bool_holder)  # omitted flag_values
2893
2894    self.assertCountEqual(
2895        self.flag_values.get_flags_for_module(main_module),
2896        self.flag_values.get_key_flags_for_module(main_module))
2897
2898    self.assertLen(self.flag_values.get_flags_for_module(main_module), 2)
2899
2900  def test_main_module_help_with_key_flags(self):
2901    # Similar to test_main_module_help, but this time we make sure to
2902    # declare some key flags.
2903
2904    # Safety check that the main module does not declare any flags
2905    # at the beginning of this test.
2906    expected_help = ''
2907    self.assertMultiLineEqual(expected_help,
2908                              self.flag_values.main_module_help())
2909
2910    # Define one flag in this main module and some flags in modules
2911    # a and b.  Also declare one flag from module a and one flag
2912    # from module b as key flags for the main module.
2913    flags.DEFINE_integer(
2914        'main_module_int_fg',
2915        1,
2916        'Integer flag in the main module.',
2917        flag_values=self.flag_values)
2918
2919    try:
2920      main_module_int_fg_help = (
2921          '  --main_module_int_fg: Integer flag in the main module.\n'
2922          "    (default: '1')\n"
2923          '    (an integer)')
2924
2925      expected_help += '\n%s:\n%s' % (sys.argv[0], main_module_int_fg_help)
2926      self.assertMultiLineEqual(expected_help,
2927                                self.flag_values.main_module_help())
2928
2929      # The following call should be a no-op: any flag declared by a
2930      # module is automatically key for that module.
2931      flags.declare_key_flag('main_module_int_fg', flag_values=self.flag_values)
2932      self.assertMultiLineEqual(expected_help,
2933                                self.flag_values.main_module_help())
2934
2935      # The definition of a few flags in an imported module should not
2936      # change the main module help.
2937      module_foo.define_flags(flag_values=self.flag_values)
2938      self.assertMultiLineEqual(expected_help,
2939                                self.flag_values.main_module_help())
2940
2941      flags.declare_key_flag('tmod_foo_bool', flag_values=self.flag_values)
2942      tmod_foo_bool_help = (
2943          '  --[no]tmod_foo_bool: Boolean flag from module foo.\n'
2944          "    (default: 'true')")
2945      expected_help += '\n' + tmod_foo_bool_help
2946      self.assertMultiLineEqual(expected_help,
2947                                self.flag_values.main_module_help())
2948
2949      flags.declare_key_flag('tmod_bar_z', flag_values=self.flag_values)
2950      tmod_bar_z_help = (
2951          '  --[no]tmod_bar_z: Another boolean flag from module bar.\n'
2952          "    (default: 'false')")
2953      # Unfortunately, there is some flag sorting inside
2954      # main_module_help, so we can't keep incrementally extending
2955      # the expected_help string ...
2956      expected_help = ('\n%s:\n%s\n%s\n%s' %
2957                       (sys.argv[0], main_module_int_fg_help, tmod_bar_z_help,
2958                        tmod_foo_bool_help))
2959      self.assertMultiLineEqual(self.flag_values.main_module_help(),
2960                                expected_help)
2961
2962    finally:
2963      # At the end, delete all the flag information we created.
2964      self.flag_values.__delattr__('main_module_int_fg')
2965      module_foo.remove_flags(flag_values=self.flag_values)
2966
2967  def test_adoptmodule_key_flags(self):
2968    # Check that adopt_module_key_flags raises an exception when
2969    # called with a module name (as opposed to a module object).
2970    self.assertRaises(flags.Error, flags.adopt_module_key_flags, 'pyglib.app')
2971
2972  def test_disclaimkey_flags(self):
2973    original_disclaim_module_ids = _helpers.disclaim_module_ids
2974    _helpers.disclaim_module_ids = set(_helpers.disclaim_module_ids)
2975    try:
2976      module_bar.disclaim_key_flags()
2977      module_foo.define_bar_flags(flag_values=self.flag_values)
2978      module_name = self.flag_values.find_module_defining_flag('tmod_bar_x')
2979      self.assertEqual(module_foo.__name__, module_name)
2980    finally:
2981      _helpers.disclaim_module_ids = original_disclaim_module_ids
2982
2983
2984class FindModuleTest(absltest.TestCase):
2985  """Testing methods that find a module that defines a given flag."""
2986
2987  def test_find_module_defining_flag(self):
2988    self.assertEqual(
2989        'default',
2990        FLAGS.find_module_defining_flag('__NON_EXISTENT_FLAG__', 'default'))
2991    self.assertEqual(module_baz.__name__,
2992                     FLAGS.find_module_defining_flag('tmod_baz_x'))
2993
2994  def test_find_module_id_defining_flag(self):
2995    self.assertEqual(
2996        'default',
2997        FLAGS.find_module_id_defining_flag('__NON_EXISTENT_FLAG__', 'default'))
2998    self.assertEqual(
2999        id(module_baz), FLAGS.find_module_id_defining_flag('tmod_baz_x'))
3000
3001  def test_find_module_defining_flag_passing_module_name(self):
3002    my_flags = flags.FlagValues()
3003    module_name = sys.__name__  # Must use an existing module.
3004    flags.DEFINE_boolean(
3005        'flag_name',
3006        True,
3007        'Flag with a different module name.',
3008        flag_values=my_flags,
3009        module_name=module_name)
3010    self.assertEqual(module_name,
3011                     my_flags.find_module_defining_flag('flag_name'))
3012
3013  def test_find_module_id_defining_flag_passing_module_name(self):
3014    my_flags = flags.FlagValues()
3015    module_name = sys.__name__  # Must use an existing module.
3016    flags.DEFINE_boolean(
3017        'flag_name',
3018        True,
3019        'Flag with a different module name.',
3020        flag_values=my_flags,
3021        module_name=module_name)
3022    self.assertEqual(
3023        id(sys), my_flags.find_module_id_defining_flag('flag_name'))
3024
3025
3026class FlagsErrorMessagesTest(absltest.TestCase):
3027  """Testing special cases for integer and float flags error messages."""
3028
3029  def setUp(self):
3030    self.flag_values = flags.FlagValues()
3031
3032  def test_integer_error_text(self):
3033    # Make sure we get proper error text
3034    flags.DEFINE_integer(
3035        'positive',
3036        4,
3037        'non-negative flag',
3038        lower_bound=1,
3039        flag_values=self.flag_values)
3040    flags.DEFINE_integer(
3041        'non_negative',
3042        4,
3043        'positive flag',
3044        lower_bound=0,
3045        flag_values=self.flag_values)
3046    flags.DEFINE_integer(
3047        'negative',
3048        -4,
3049        'negative flag',
3050        upper_bound=-1,
3051        flag_values=self.flag_values)
3052    flags.DEFINE_integer(
3053        'non_positive',
3054        -4,
3055        'non-positive flag',
3056        upper_bound=0,
3057        flag_values=self.flag_values)
3058    flags.DEFINE_integer(
3059        'greater',
3060        19,
3061        'greater-than flag',
3062        lower_bound=4,
3063        flag_values=self.flag_values)
3064    flags.DEFINE_integer(
3065        'smaller',
3066        -19,
3067        'smaller-than flag',
3068        upper_bound=4,
3069        flag_values=self.flag_values)
3070    flags.DEFINE_integer(
3071        'usual',
3072        4,
3073        'usual flag',
3074        lower_bound=0,
3075        upper_bound=10000,
3076        flag_values=self.flag_values)
3077    flags.DEFINE_integer(
3078        'another_usual',
3079        0,
3080        'usual flag',
3081        lower_bound=-1,
3082        upper_bound=1,
3083        flag_values=self.flag_values)
3084
3085    self._check_error_message('positive', -4, 'a positive integer')
3086    self._check_error_message('non_negative', -4, 'a non-negative integer')
3087    self._check_error_message('negative', 0, 'a negative integer')
3088    self._check_error_message('non_positive', 4, 'a non-positive integer')
3089    self._check_error_message('usual', -4, 'an integer in the range [0, 10000]')
3090    self._check_error_message('another_usual', 4,
3091                              'an integer in the range [-1, 1]')
3092    self._check_error_message('greater', -5, 'integer >= 4')
3093    self._check_error_message('smaller', 5, 'integer <= 4')
3094
3095  def test_float_error_text(self):
3096    flags.DEFINE_float(
3097        'positive',
3098        4,
3099        'non-negative flag',
3100        lower_bound=1,
3101        flag_values=self.flag_values)
3102    flags.DEFINE_float(
3103        'non_negative',
3104        4,
3105        'positive flag',
3106        lower_bound=0,
3107        flag_values=self.flag_values)
3108    flags.DEFINE_float(
3109        'negative',
3110        -4,
3111        'negative flag',
3112        upper_bound=-1,
3113        flag_values=self.flag_values)
3114    flags.DEFINE_float(
3115        'non_positive',
3116        -4,
3117        'non-positive flag',
3118        upper_bound=0,
3119        flag_values=self.flag_values)
3120    flags.DEFINE_float(
3121        'greater',
3122        19,
3123        'greater-than flag',
3124        lower_bound=4,
3125        flag_values=self.flag_values)
3126    flags.DEFINE_float(
3127        'smaller',
3128        -19,
3129        'smaller-than flag',
3130        upper_bound=4,
3131        flag_values=self.flag_values)
3132    flags.DEFINE_float(
3133        'usual',
3134        4,
3135        'usual flag',
3136        lower_bound=0,
3137        upper_bound=10000,
3138        flag_values=self.flag_values)
3139    flags.DEFINE_float(
3140        'another_usual',
3141        0,
3142        'usual flag',
3143        lower_bound=-1,
3144        upper_bound=1,
3145        flag_values=self.flag_values)
3146
3147    self._check_error_message('positive', 0.5, 'number >= 1')
3148    self._check_error_message('non_negative', -4.0, 'a non-negative number')
3149    self._check_error_message('negative', 0.5, 'number <= -1')
3150    self._check_error_message('non_positive', 4.0, 'a non-positive number')
3151    self._check_error_message('usual', -4.0, 'a number in the range [0, 10000]')
3152    self._check_error_message('another_usual', 4.0,
3153                              'a number in the range [-1, 1]')
3154    self._check_error_message('smaller', 5.0, 'number <= 4')
3155
3156  def _check_error_message(self, flag_name, flag_value,
3157                           expected_message_suffix):
3158    """Set a flag to a given value and make sure we get expected message."""
3159
3160    try:
3161      self.flag_values.__setattr__(flag_name, flag_value)
3162      raise AssertionError('Bounds exception not raised!')
3163    except flags.IllegalFlagValueError as e:
3164      expected = ('flag --%(name)s=%(value)s: %(value)s is not %(suffix)s' % {
3165          'name': flag_name,
3166          'value': flag_value,
3167          'suffix': expected_message_suffix
3168      })
3169      self.assertEqual(str(e), expected)
3170
3171
3172if __name__ == '__main__':
3173  absltest.main()
3174