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
15from absl import flags
16from absl.flags import _helpers
17from absl.testing import absltest
18
19FLAGS = flags.FLAGS
20
21
22class FlagsUnitTest(absltest.TestCase):
23  """Flags formatting Unit Test."""
24
25  def test_get_help_width(self):
26    """Verify that get_help_width() reflects _help_width."""
27    default_help_width = _helpers._DEFAULT_HELP_WIDTH  # Save.
28    self.assertEqual(80, _helpers._DEFAULT_HELP_WIDTH)
29    self.assertEqual(_helpers._DEFAULT_HELP_WIDTH, flags.get_help_width())
30    _helpers._DEFAULT_HELP_WIDTH = 10
31    self.assertEqual(_helpers._DEFAULT_HELP_WIDTH, flags.get_help_width())
32    _helpers._DEFAULT_HELP_WIDTH = default_help_width  # restore
33
34  def test_text_wrap(self):
35    """Test that wrapping works as expected.
36
37    Also tests that it is using global flags._help_width by default.
38    """
39    default_help_width = _helpers._DEFAULT_HELP_WIDTH
40    _helpers._DEFAULT_HELP_WIDTH = 10
41
42    # Generate a string with length 40, no spaces
43    text = ''
44    expect = []
45    for n in range(4):
46      line = str(n)
47      line += '123456789'
48      text += line
49      expect.append(line)
50
51    # Verify we still break
52    wrapped = flags.text_wrap(text).split('\n')
53    self.assertEqual(4, len(wrapped))
54    self.assertEqual(expect, wrapped)
55
56    wrapped = flags.text_wrap(text, 80).split('\n')
57    self.assertEqual(1, len(wrapped))
58    self.assertEqual([text], wrapped)
59
60    # Normal case, breaking at word boundaries and rewriting new lines
61    input_value = 'a b c d e f g h'
62    expect = {1: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'],
63              2: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'],
64              3: ['a b', 'c d', 'e f', 'g h'],
65              4: ['a b', 'c d', 'e f', 'g h'],
66              5: ['a b c', 'd e f', 'g h'],
67              6: ['a b c', 'd e f', 'g h'],
68              7: ['a b c d', 'e f g h'],
69              8: ['a b c d', 'e f g h'],
70              9: ['a b c d e', 'f g h'],
71              10: ['a b c d e', 'f g h'],
72              11: ['a b c d e f', 'g h'],
73              12: ['a b c d e f', 'g h'],
74              13: ['a b c d e f g', 'h'],
75              14: ['a b c d e f g', 'h'],
76              15: ['a b c d e f g h']}
77    for width, exp in expect.items():
78      self.assertEqual(exp, flags.text_wrap(input_value, width).split('\n'))
79
80    # We turn lines with only whitespace into empty lines
81    # We strip from the right up to the first new line
82    self.assertEqual('', flags.text_wrap('  '))
83    self.assertEqual('\n', flags.text_wrap('  \n  '))
84    self.assertEqual('\n', flags.text_wrap('\n\n'))
85    self.assertEqual('\n\n', flags.text_wrap('\n\n\n'))
86    self.assertEqual('\n', flags.text_wrap('\n '))
87    self.assertEqual('a\n\nb', flags.text_wrap('a\n  \nb'))
88    self.assertEqual('a\n\n\nb', flags.text_wrap('a\n  \n  \nb'))
89    self.assertEqual('a\nb', flags.text_wrap('  a\nb  '))
90    self.assertEqual('\na\nb', flags.text_wrap('\na\nb\n'))
91    self.assertEqual('\na\nb\n', flags.text_wrap('  \na\nb\n  '))
92    self.assertEqual('\na\nb\n', flags.text_wrap('  \na\nb\n\n'))
93
94    # Double newline.
95    self.assertEqual('a\n\nb', flags.text_wrap(' a\n\n b'))
96
97    # We respect prefix
98    self.assertEqual(' a\n b\n c', flags.text_wrap('a\nb\nc', 80, ' '))
99    self.assertEqual('a\n b\n c', flags.text_wrap('a\nb\nc', 80, ' ', ''))
100
101    # tabs
102    self.assertEqual('a\n b   c',
103                     flags.text_wrap('a\nb\tc', 80, ' ', ''))
104    self.assertEqual('a\n bb  c',
105                     flags.text_wrap('a\nbb\tc', 80, ' ', ''))
106    self.assertEqual('a\n bbb c',
107                     flags.text_wrap('a\nbbb\tc', 80, ' ', ''))
108    self.assertEqual('a\n bbbb    c',
109                     flags.text_wrap('a\nbbbb\tc', 80, ' ', ''))
110    self.assertEqual('a\n b\n c\n d',
111                     flags.text_wrap('a\nb\tc\td', 3, ' ', ''))
112    self.assertEqual('a\n b\n c\n d',
113                     flags.text_wrap('a\nb\tc\td', 4, ' ', ''))
114    self.assertEqual('a\n b\n c\n d',
115                     flags.text_wrap('a\nb\tc\td', 5, ' ', ''))
116    self.assertEqual('a\n b   c\n d',
117                     flags.text_wrap('a\nb\tc\td', 6, ' ', ''))
118    self.assertEqual('a\n b   c\n d',
119                     flags.text_wrap('a\nb\tc\td', 7, ' ', ''))
120    self.assertEqual('a\n b   c\n d',
121                     flags.text_wrap('a\nb\tc\td', 8, ' ', ''))
122    self.assertEqual('a\n b   c\n d',
123                     flags.text_wrap('a\nb\tc\td', 9, ' ', ''))
124    self.assertEqual('a\n b   c   d',
125                     flags.text_wrap('a\nb\tc\td', 10, ' ', ''))
126
127    # multiple tabs
128    self.assertEqual('a       c',
129                     flags.text_wrap('a\t\tc', 80, ' ', ''))
130
131    _helpers._DEFAULT_HELP_WIDTH = default_help_width  # restore
132
133  def test_doc_to_help(self):
134    self.assertEqual('', flags.doc_to_help('  '))
135    self.assertEqual('', flags.doc_to_help('  \n  '))
136    self.assertEqual('a\n\nb', flags.doc_to_help('a\n  \nb'))
137    self.assertEqual('a\n\n\nb', flags.doc_to_help('a\n  \n  \nb'))
138    self.assertEqual('a b', flags.doc_to_help('  a\nb  '))
139    self.assertEqual('a b', flags.doc_to_help('\na\nb\n'))
140    self.assertEqual('a\n\nb', flags.doc_to_help('\na\n\nb\n'))
141    self.assertEqual('a b', flags.doc_to_help('  \na\nb\n  '))
142    # Different first line, one line empty - erm double new line.
143    self.assertEqual('a b c\n\nd', flags.doc_to_help('a\n  b\n  c\n\n  d'))
144    self.assertEqual('a b\n      c d', flags.doc_to_help('a\n  b\n  \tc\n  d'))
145    self.assertEqual('a b\n      c\n      d',
146                     flags.doc_to_help('a\n  b\n  \tc\n  \td'))
147
148  def test_doc_to_help_flag_values(self):
149    # !!!!!!!!!!!!!!!!!!!!
150    # The following doc string is taken as is directly from flags.py:FlagValues
151    # The intention of this test is to verify 'live' performance
152    # !!!!!!!!!!!!!!!!!!!!
153    """Used as a registry for 'Flag' objects.
154
155    A 'FlagValues' can then scan command line arguments, passing flag
156    arguments through to the 'Flag' objects that it owns.  It also
157    provides easy access to the flag values.  Typically only one
158    'FlagValues' object is needed by an application:  flags.FLAGS
159
160    This class is heavily overloaded:
161
162    'Flag' objects are registered via __setitem__:
163         FLAGS['longname'] = x   # register a new flag
164
165    The .value member of the registered 'Flag' objects can be accessed as
166    members of this 'FlagValues' object, through __getattr__.  Both the
167    long and short name of the original 'Flag' objects can be used to
168    access its value:
169         FLAGS.longname          # parsed flag value
170         FLAGS.x                 # parsed flag value (short name)
171
172    Command line arguments are scanned and passed to the registered 'Flag'
173    objects through the __call__ method.  Unparsed arguments, including
174    argv[0] (e.g. the program name) are returned.
175         argv = FLAGS(sys.argv)  # scan command line arguments
176
177    The original registered Flag objects can be retrieved through the use
178    """
179    doc = flags.doc_to_help(self.test_doc_to_help_flag_values.__doc__)
180    # Test the general outline of the converted docs
181    lines = doc.splitlines()
182    self.assertEqual(17, len(lines))
183    empty_lines = [index for index in range(len(lines)) if not lines[index]]
184    self.assertEqual([1, 3, 5, 8, 12, 15], empty_lines)
185    # test that some starting prefix is kept
186    flags_lines = [index for index in range(len(lines))
187                   if lines[index].startswith('     FLAGS')]
188    self.assertEqual([7, 10, 11], flags_lines)
189    # but other, especially common space has been removed
190    space_lines = [index for index in range(len(lines))
191                   if lines[index] and lines[index][0].isspace()]
192    self.assertEqual([7, 10, 11, 14], space_lines)
193    # No right space was kept
194    rspace_lines = [index for index in range(len(lines))
195                    if lines[index] != lines[index].rstrip()]
196    self.assertEqual([], rspace_lines)
197    # test double spaces are kept
198    self.assertEqual(True, lines[2].endswith('application:  flags.FLAGS'))
199
200  def test_text_wrap_raises_on_excessive_indent(self):
201    """Ensure an indent longer than line length raises."""
202    self.assertRaises(ValueError,
203                      flags.text_wrap, 'dummy', length=10, indent=' ' * 10)
204
205  def test_text_wrap_raises_on_excessive_first_line(self):
206    """Ensure a first line indent longer than line length raises."""
207    self.assertRaises(
208        ValueError,
209        flags.text_wrap, 'dummy', length=80, firstline_indent=' ' * 80)
210
211
212if __name__ == '__main__':
213  absltest.main()
214