| # Copyright 2017 The Abseil Authors. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| """Unit tests for the XML-format help generated by the flags.py module.""" |
| |
| from __future__ import absolute_import |
| from __future__ import division |
| from __future__ import print_function |
| |
| import enum |
| import io |
| import os |
| import string |
| import sys |
| import xml.dom.minidom |
| import xml.sax.saxutils |
| |
| from absl import flags |
| from absl.flags import _helpers |
| from absl.flags.tests import module_bar |
| from absl.testing import absltest |
| import six |
| |
| |
| class CreateXMLDOMElement(absltest.TestCase): |
| |
| def _check(self, name, value, expected_output): |
| doc = xml.dom.minidom.Document() |
| node = _helpers.create_xml_dom_element(doc, name, value) |
| output = node.toprettyxml(' ', encoding='utf-8') |
| self.assertEqual(expected_output, output) |
| |
| def test_create_xml_dom_element(self): |
| self._check('tag', '', b'<tag></tag>\n') |
| self._check('tag', 'plain text', b'<tag>plain text</tag>\n') |
| self._check('tag', '(x < y) && (a >= b)', |
| b'<tag>(x < y) && (a >= b)</tag>\n') |
| |
| # If the value is bytes with invalid unicode: |
| bytes_with_invalid_unicodes = b'\x81\xff' |
| if six.PY2: |
| # In python 2 the string representation is invalid unicode so they are |
| # stripped. |
| self._check('tag', bytes_with_invalid_unicodes, b'<tag></tag>\n') |
| else: |
| # In python 3 the string representation is "b'\x81\xff'" so they are kept |
| # as "b'\x81\xff'". |
| self._check('tag', bytes_with_invalid_unicodes, |
| b"<tag>b'\\x81\\xff'</tag>\n") |
| |
| # Some unicode chars are illegal in xml |
| # (http://www.w3.org/TR/REC-xml/#charsets): |
| self._check('tag', u'\x0b\x02\x08\ufffe', b'<tag></tag>\n') |
| |
| # Valid unicode will be encoded: |
| self._check('tag', u'\xff', b'<tag>\xc3\xbf</tag>\n') |
| |
| |
| def _list_separators_in_xmlformat(separators, indent=''): |
| """Generates XML encoding of a list of list separators. |
| |
| Args: |
| separators: A list of list separators. Usually, this should be a |
| string whose characters are the valid list separators, e.g., ',' |
| means that both comma (',') and space (' ') are valid list |
| separators. |
| indent: A string that is added at the beginning of each generated |
| XML element. |
| |
| Returns: |
| A string. |
| """ |
| result = '' |
| separators = list(separators) |
| separators.sort() |
| for sep_char in separators: |
| result += ('%s<list_separator>%s</list_separator>\n' % |
| (indent, repr(sep_char))) |
| return result |
| |
| |
| class FlagCreateXMLDOMElement(absltest.TestCase): |
| """Test the create_xml_dom_element method for a single flag at a time. |
| |
| There is one test* method for each kind of DEFINE_* declaration. |
| """ |
| |
| def setUp(self): |
| # self.fv is a FlagValues object, just like flags.FLAGS. Each |
| # test registers one flag with this FlagValues. |
| self.fv = flags.FlagValues() |
| |
| def _check_flag_help_in_xml(self, flag_name, module_name, |
| expected_output, is_key=False): |
| flag_obj = self.fv[flag_name] |
| doc = xml.dom.minidom.Document() |
| element = flag_obj._create_xml_dom_element(doc, module_name, is_key=is_key) |
| output = element.toprettyxml(indent=' ') |
| self.assertMultiLineEqual(expected_output, output) |
| |
| def test_flag_help_in_xml_int(self): |
| flags.DEFINE_integer('index', 17, 'An integer flag', flag_values=self.fv) |
| expected_output_pattern = ( |
| '<flag>\n' |
| ' <file>module.name</file>\n' |
| ' <name>index</name>\n' |
| ' <meaning>An integer flag</meaning>\n' |
| ' <default>17</default>\n' |
| ' <current>%d</current>\n' |
| ' <type>int</type>\n' |
| '</flag>\n') |
| self._check_flag_help_in_xml('index', 'module.name', |
| expected_output_pattern % 17) |
| # Check that the output is correct even when the current value of |
| # a flag is different from the default one. |
| self.fv['index'].value = 20 |
| self._check_flag_help_in_xml('index', 'module.name', |
| expected_output_pattern % 20) |
| |
| def test_flag_help_in_xml_int_with_bounds(self): |
| flags.DEFINE_integer('nb_iters', 17, 'An integer flag', |
| lower_bound=5, upper_bound=27, |
| flag_values=self.fv) |
| expected_output = ( |
| '<flag>\n' |
| ' <key>yes</key>\n' |
| ' <file>module.name</file>\n' |
| ' <name>nb_iters</name>\n' |
| ' <meaning>An integer flag</meaning>\n' |
| ' <default>17</default>\n' |
| ' <current>17</current>\n' |
| ' <type>int</type>\n' |
| ' <lower_bound>5</lower_bound>\n' |
| ' <upper_bound>27</upper_bound>\n' |
| '</flag>\n') |
| self._check_flag_help_in_xml('nb_iters', 'module.name', expected_output, |
| is_key=True) |
| |
| def test_flag_help_in_xml_string(self): |
| flags.DEFINE_string('file_path', '/path/to/my/dir', 'A test string flag.', |
| flag_values=self.fv) |
| expected_output = ( |
| '<flag>\n' |
| ' <file>simple_module</file>\n' |
| ' <name>file_path</name>\n' |
| ' <meaning>A test string flag.</meaning>\n' |
| ' <default>/path/to/my/dir</default>\n' |
| ' <current>/path/to/my/dir</current>\n' |
| ' <type>string</type>\n' |
| '</flag>\n') |
| self._check_flag_help_in_xml('file_path', 'simple_module', expected_output) |
| |
| def test_flag_help_in_xml_string_with_xmlillegal_chars(self): |
| flags.DEFINE_string('file_path', '/path/to/\x08my/dir', |
| 'A test string flag.', flag_values=self.fv) |
| # '\x08' is not a legal character in XML 1.0 documents. Our |
| # current code purges such characters from the generated XML. |
| expected_output = ( |
| '<flag>\n' |
| ' <file>simple_module</file>\n' |
| ' <name>file_path</name>\n' |
| ' <meaning>A test string flag.</meaning>\n' |
| ' <default>/path/to/my/dir</default>\n' |
| ' <current>/path/to/my/dir</current>\n' |
| ' <type>string</type>\n' |
| '</flag>\n') |
| self._check_flag_help_in_xml('file_path', 'simple_module', expected_output) |
| |
| def test_flag_help_in_xml_boolean(self): |
| flags.DEFINE_boolean('use_gpu', False, 'Use gpu for performance.', |
| flag_values=self.fv) |
| expected_output = ( |
| '<flag>\n' |
| ' <key>yes</key>\n' |
| ' <file>a_module</file>\n' |
| ' <name>use_gpu</name>\n' |
| ' <meaning>Use gpu for performance.</meaning>\n' |
| ' <default>false</default>\n' |
| ' <current>false</current>\n' |
| ' <type>bool</type>\n' |
| '</flag>\n') |
| self._check_flag_help_in_xml('use_gpu', 'a_module', expected_output, |
| is_key=True) |
| |
| def test_flag_help_in_xml_enum(self): |
| flags.DEFINE_enum('cc_version', 'stable', ['stable', 'experimental'], |
| 'Compiler version to use.', flag_values=self.fv) |
| expected_output = ( |
| '<flag>\n' |
| ' <file>tool</file>\n' |
| ' <name>cc_version</name>\n' |
| ' <meaning><stable|experimental>: ' |
| 'Compiler version to use.</meaning>\n' |
| ' <default>stable</default>\n' |
| ' <current>stable</current>\n' |
| ' <type>string enum</type>\n' |
| ' <enum_value>stable</enum_value>\n' |
| ' <enum_value>experimental</enum_value>\n' |
| '</flag>\n') |
| self._check_flag_help_in_xml('cc_version', 'tool', expected_output) |
| |
| def test_flag_help_in_xml_enum_class(self): |
| class Version(enum.Enum): |
| STABLE = 0 |
| EXPERIMENTAL = 1 |
| |
| flags.DEFINE_enum_class('cc_version', 'STABLE', Version, |
| 'Compiler version to use.', flag_values=self.fv) |
| expected_output = ('<flag>\n' |
| ' <file>tool</file>\n' |
| ' <name>cc_version</name>\n' |
| ' <meaning><stable|experimental>: ' |
| 'Compiler version to use.</meaning>\n' |
| ' <default>stable</default>\n' |
| ' <current>Version.STABLE</current>\n' |
| ' <type>enum class</type>\n' |
| ' <enum_value>STABLE</enum_value>\n' |
| ' <enum_value>EXPERIMENTAL</enum_value>\n' |
| '</flag>\n') |
| self._check_flag_help_in_xml('cc_version', 'tool', expected_output) |
| |
| def test_flag_help_in_xml_comma_separated_list(self): |
| flags.DEFINE_list('files', 'a.cc,a.h,archive/old.zip', |
| 'Files to process.', flag_values=self.fv) |
| expected_output = ( |
| '<flag>\n' |
| ' <file>tool</file>\n' |
| ' <name>files</name>\n' |
| ' <meaning>Files to process.</meaning>\n' |
| ' <default>a.cc,a.h,archive/old.zip</default>\n' |
| ' <current>[\'a.cc\', \'a.h\', \'archive/old.zip\']</current>\n' |
| ' <type>comma separated list of strings</type>\n' |
| ' <list_separator>\',\'</list_separator>\n' |
| '</flag>\n') |
| self._check_flag_help_in_xml('files', 'tool', expected_output) |
| |
| def test_list_as_default_argument_comma_separated_list(self): |
| flags.DEFINE_list('allow_users', ['alice', 'bob'], |
| 'Users with access.', flag_values=self.fv) |
| expected_output = ( |
| '<flag>\n' |
| ' <file>tool</file>\n' |
| ' <name>allow_users</name>\n' |
| ' <meaning>Users with access.</meaning>\n' |
| ' <default>alice,bob</default>\n' |
| ' <current>[\'alice\', \'bob\']</current>\n' |
| ' <type>comma separated list of strings</type>\n' |
| ' <list_separator>\',\'</list_separator>\n' |
| '</flag>\n') |
| self._check_flag_help_in_xml('allow_users', 'tool', expected_output) |
| |
| def test_none_as_default_arguments_comma_separated_list(self): |
| flags.DEFINE_list('allow_users', None, |
| 'Users with access.', flag_values=self.fv) |
| expected_output = ( |
| '<flag>\n' |
| ' <file>tool</file>\n' |
| ' <name>allow_users</name>\n' |
| ' <meaning>Users with access.</meaning>\n' |
| ' <default></default>\n' |
| ' <current>None</current>\n' |
| ' <type>comma separated list of strings</type>\n' |
| ' <list_separator>\',\'</list_separator>\n' |
| '</flag>\n') |
| self._check_flag_help_in_xml('allow_users', 'tool', expected_output) |
| |
| def test_flag_help_in_xml_space_separated_list(self): |
| flags.DEFINE_spaceseplist('dirs', 'src libs bin', |
| 'Directories to search.', flag_values=self.fv) |
| expected_separators = sorted(string.whitespace) |
| expected_output = ( |
| '<flag>\n' |
| ' <file>tool</file>\n' |
| ' <name>dirs</name>\n' |
| ' <meaning>Directories to search.</meaning>\n' |
| ' <default>src libs bin</default>\n' |
| ' <current>[\'src\', \'libs\', \'bin\']</current>\n' |
| ' <type>whitespace separated list of strings</type>\n' |
| 'LIST_SEPARATORS' |
| '</flag>\n').replace('LIST_SEPARATORS', |
| _list_separators_in_xmlformat(expected_separators, |
| indent=' ')) |
| self._check_flag_help_in_xml('dirs', 'tool', expected_output) |
| |
| def test_flag_help_in_xml_space_separated_list_with_comma_compat(self): |
| flags.DEFINE_spaceseplist('dirs', 'src libs,bin', |
| 'Directories to search.', comma_compat=True, |
| flag_values=self.fv) |
| expected_separators = sorted(string.whitespace + ',') |
| expected_output = ( |
| '<flag>\n' |
| ' <file>tool</file>\n' |
| ' <name>dirs</name>\n' |
| ' <meaning>Directories to search.</meaning>\n' |
| ' <default>src libs bin</default>\n' |
| ' <current>[\'src\', \'libs\', \'bin\']</current>\n' |
| ' <type>whitespace or comma separated list of strings</type>\n' |
| 'LIST_SEPARATORS' |
| '</flag>\n').replace('LIST_SEPARATORS', |
| _list_separators_in_xmlformat(expected_separators, |
| indent=' ')) |
| self._check_flag_help_in_xml('dirs', 'tool', expected_output) |
| |
| def test_flag_help_in_xml_multi_string(self): |
| flags.DEFINE_multi_string('to_delete', ['a.cc', 'b.h'], |
| 'Files to delete', flag_values=self.fv) |
| expected_output = ( |
| '<flag>\n' |
| ' <file>tool</file>\n' |
| ' <name>to_delete</name>\n' |
| ' <meaning>Files to delete;\n' |
| ' repeat this option to specify a list of values</meaning>\n' |
| ' <default>[\'a.cc\', \'b.h\']</default>\n' |
| ' <current>[\'a.cc\', \'b.h\']</current>\n' |
| ' <type>multi string</type>\n' |
| '</flag>\n') |
| self._check_flag_help_in_xml('to_delete', 'tool', expected_output) |
| |
| def test_flag_help_in_xml_multi_int(self): |
| flags.DEFINE_multi_integer('cols', [5, 7, 23], |
| 'Columns to select', flag_values=self.fv) |
| expected_output = ( |
| '<flag>\n' |
| ' <file>tool</file>\n' |
| ' <name>cols</name>\n' |
| ' <meaning>Columns to select;\n ' |
| 'repeat this option to specify a list of values</meaning>\n' |
| ' <default>[5, 7, 23]</default>\n' |
| ' <current>[5, 7, 23]</current>\n' |
| ' <type>multi int</type>\n' |
| '</flag>\n') |
| self._check_flag_help_in_xml('cols', 'tool', expected_output) |
| |
| def test_flag_help_in_xml_multi_enum(self): |
| flags.DEFINE_multi_enum('flavours', ['APPLE', 'BANANA'], |
| ['APPLE', 'BANANA', 'CHERRY'], |
| 'Compilation flavour.', flag_values=self.fv) |
| expected_output = ( |
| '<flag>\n' |
| ' <file>tool</file>\n' |
| ' <name>flavours</name>\n' |
| ' <meaning>Compilation flavour.;\n' |
| ' repeat this option to specify a list of values</meaning>\n' |
| ' <default>[\'APPLE\', \'BANANA\']</default>\n' |
| ' <current>[\'APPLE\', \'BANANA\']</current>\n' |
| ' <type>multi string enum</type>\n' |
| ' <enum_value>APPLE</enum_value>\n' |
| ' <enum_value>BANANA</enum_value>\n' |
| ' <enum_value>CHERRY</enum_value>\n' |
| '</flag>\n') |
| self._check_flag_help_in_xml('flavours', 'tool', expected_output) |
| |
| def test_flag_help_in_xml_multi_enum_class_singleton_default(self): |
| class Fruit(enum.Enum): |
| ORANGE = 0 |
| BANANA = 1 |
| |
| flags.DEFINE_multi_enum_class('fruit', ['ORANGE'], |
| Fruit, |
| 'The fruit flag.', flag_values=self.fv) |
| expected_output = ( |
| '<flag>\n' |
| ' <file>tool</file>\n' |
| ' <name>fruit</name>\n' |
| ' <meaning><orange|banana>: The fruit flag.;\n' |
| ' repeat this option to specify a list of values</meaning>\n' |
| ' <default>orange</default>\n' |
| ' <current>orange</current>\n' |
| ' <type>multi enum class</type>\n' |
| ' <enum_value>ORANGE</enum_value>\n' |
| ' <enum_value>BANANA</enum_value>\n' |
| '</flag>\n') |
| self._check_flag_help_in_xml('fruit', 'tool', expected_output) |
| |
| def test_flag_help_in_xml_multi_enum_class_list_default(self): |
| class Fruit(enum.Enum): |
| ORANGE = 0 |
| BANANA = 1 |
| |
| flags.DEFINE_multi_enum_class('fruit', ['ORANGE', 'BANANA'], |
| Fruit, |
| 'The fruit flag.', flag_values=self.fv) |
| expected_output = ( |
| '<flag>\n' |
| ' <file>tool</file>\n' |
| ' <name>fruit</name>\n' |
| ' <meaning><orange|banana>: The fruit flag.;\n' |
| ' repeat this option to specify a list of values</meaning>\n' |
| ' <default>orange,banana</default>\n' |
| ' <current>orange,banana</current>\n' |
| ' <type>multi enum class</type>\n' |
| ' <enum_value>ORANGE</enum_value>\n' |
| ' <enum_value>BANANA</enum_value>\n' |
| '</flag>\n') |
| self._check_flag_help_in_xml('fruit', 'tool', expected_output) |
| |
| # The next EXPECTED_HELP_XML_* constants are parts of a template for |
| # the expected XML output from WriteHelpInXMLFormatTest below. When |
| # we assemble these parts into a single big string, we'll take into |
| # account the ordering between the name of the main module and the |
| # name of module_bar. Next, we'll fill in the docstring for this |
| # module (%(usage_doc)s), the name of the main module |
| # (%(main_module_name)s) and the name of the module module_bar |
| # (%(module_bar_name)s). See WriteHelpInXMLFormatTest below. |
| EXPECTED_HELP_XML_START = """\ |
| <?xml version="1.0" encoding="utf-8"?> |
| <AllFlags> |
| <program>%(basename_of_argv0)s</program> |
| <usage>%(usage_doc)s</usage> |
| """ |
| |
| EXPECTED_HELP_XML_FOR_FLAGS_FROM_MAIN_MODULE = """\ |
| <flag> |
| <key>yes</key> |
| <file>%(main_module_name)s</file> |
| <name>allow_users</name> |
| <meaning>Users with access.</meaning> |
| <default>alice,bob</default> |
| <current>['alice', 'bob']</current> |
| <type>comma separated list of strings</type> |
| <list_separator>','</list_separator> |
| </flag> |
| <flag> |
| <key>yes</key> |
| <file>%(main_module_name)s</file> |
| <name>cc_version</name> |
| <meaning><stable|experimental>: Compiler version to use.</meaning> |
| <default>stable</default> |
| <current>stable</current> |
| <type>string enum</type> |
| <enum_value>stable</enum_value> |
| <enum_value>experimental</enum_value> |
| </flag> |
| <flag> |
| <key>yes</key> |
| <file>%(main_module_name)s</file> |
| <name>cols</name> |
| <meaning>Columns to select; |
| repeat this option to specify a list of values</meaning> |
| <default>[5, 7, 23]</default> |
| <current>[5, 7, 23]</current> |
| <type>multi int</type> |
| </flag> |
| <flag> |
| <key>yes</key> |
| <file>%(main_module_name)s</file> |
| <name>dirs</name> |
| <meaning>Directories to create.</meaning> |
| <default>src libs bins</default> |
| <current>['src', 'libs', 'bins']</current> |
| <type>whitespace separated list of strings</type> |
| %(whitespace_separators)s </flag> |
| <flag> |
| <key>yes</key> |
| <file>%(main_module_name)s</file> |
| <name>file_path</name> |
| <meaning>A test string flag.</meaning> |
| <default>/path/to/my/dir</default> |
| <current>/path/to/my/dir</current> |
| <type>string</type> |
| </flag> |
| <flag> |
| <key>yes</key> |
| <file>%(main_module_name)s</file> |
| <name>files</name> |
| <meaning>Files to process.</meaning> |
| <default>a.cc,a.h,archive/old.zip</default> |
| <current>['a.cc', 'a.h', 'archive/old.zip']</current> |
| <type>comma separated list of strings</type> |
| <list_separator>\',\'</list_separator> |
| </flag> |
| <flag> |
| <key>yes</key> |
| <file>%(main_module_name)s</file> |
| <name>flavours</name> |
| <meaning>Compilation flavour.; |
| repeat this option to specify a list of values</meaning> |
| <default>['APPLE', 'BANANA']</default> |
| <current>['APPLE', 'BANANA']</current> |
| <type>multi string enum</type> |
| <enum_value>APPLE</enum_value> |
| <enum_value>BANANA</enum_value> |
| <enum_value>CHERRY</enum_value> |
| </flag> |
| <flag> |
| <key>yes</key> |
| <file>%(main_module_name)s</file> |
| <name>index</name> |
| <meaning>An integer flag</meaning> |
| <default>17</default> |
| <current>17</current> |
| <type>int</type> |
| </flag> |
| <flag> |
| <key>yes</key> |
| <file>%(main_module_name)s</file> |
| <name>nb_iters</name> |
| <meaning>An integer flag</meaning> |
| <default>17</default> |
| <current>17</current> |
| <type>int</type> |
| <lower_bound>5</lower_bound> |
| <upper_bound>27</upper_bound> |
| </flag> |
| <flag> |
| <key>yes</key> |
| <file>%(main_module_name)s</file> |
| <name>to_delete</name> |
| <meaning>Files to delete; |
| repeat this option to specify a list of values</meaning> |
| <default>['a.cc', 'b.h']</default> |
| <current>['a.cc', 'b.h']</current> |
| <type>multi string</type> |
| </flag> |
| <flag> |
| <key>yes</key> |
| <file>%(main_module_name)s</file> |
| <name>use_gpu</name> |
| <meaning>Use gpu for performance.</meaning> |
| <default>false</default> |
| <current>false</current> |
| <type>bool</type> |
| </flag> |
| """ |
| |
| EXPECTED_HELP_XML_FOR_FLAGS_FROM_MODULE_BAR = """\ |
| <flag> |
| <file>%(module_bar_name)s</file> |
| <name>tmod_bar_t</name> |
| <meaning>Sample int flag.</meaning> |
| <default>4</default> |
| <current>4</current> |
| <type>int</type> |
| </flag> |
| <flag> |
| <key>yes</key> |
| <file>%(module_bar_name)s</file> |
| <name>tmod_bar_u</name> |
| <meaning>Sample int flag.</meaning> |
| <default>5</default> |
| <current>5</current> |
| <type>int</type> |
| </flag> |
| <flag> |
| <file>%(module_bar_name)s</file> |
| <name>tmod_bar_v</name> |
| <meaning>Sample int flag.</meaning> |
| <default>6</default> |
| <current>6</current> |
| <type>int</type> |
| </flag> |
| <flag> |
| <file>%(module_bar_name)s</file> |
| <name>tmod_bar_x</name> |
| <meaning>Boolean flag.</meaning> |
| <default>true</default> |
| <current>true</current> |
| <type>bool</type> |
| </flag> |
| <flag> |
| <file>%(module_bar_name)s</file> |
| <name>tmod_bar_y</name> |
| <meaning>String flag.</meaning> |
| <default>default</default> |
| <current>default</current> |
| <type>string</type> |
| </flag> |
| <flag> |
| <key>yes</key> |
| <file>%(module_bar_name)s</file> |
| <name>tmod_bar_z</name> |
| <meaning>Another boolean flag from module bar.</meaning> |
| <default>false</default> |
| <current>false</current> |
| <type>bool</type> |
| </flag> |
| """ |
| |
| EXPECTED_HELP_XML_END = """\ |
| </AllFlags> |
| """ |
| |
| |
| class WriteHelpInXMLFormatTest(absltest.TestCase): |
| """Big test of FlagValues.write_help_in_xml_format, with several flags.""" |
| |
| def test_write_help_in_xmlformat(self): |
| fv = flags.FlagValues() |
| # Since these flags are defined by the top module, they are all key. |
| flags.DEFINE_integer('index', 17, 'An integer flag', flag_values=fv) |
| flags.DEFINE_integer('nb_iters', 17, 'An integer flag', |
| lower_bound=5, upper_bound=27, flag_values=fv) |
| flags.DEFINE_string('file_path', '/path/to/my/dir', 'A test string flag.', |
| flag_values=fv) |
| flags.DEFINE_boolean('use_gpu', False, 'Use gpu for performance.', |
| flag_values=fv) |
| flags.DEFINE_enum('cc_version', 'stable', ['stable', 'experimental'], |
| 'Compiler version to use.', flag_values=fv) |
| flags.DEFINE_list('files', 'a.cc,a.h,archive/old.zip', |
| 'Files to process.', flag_values=fv) |
| flags.DEFINE_list('allow_users', ['alice', 'bob'], |
| 'Users with access.', flag_values=fv) |
| flags.DEFINE_spaceseplist('dirs', 'src libs bins', |
| 'Directories to create.', flag_values=fv) |
| flags.DEFINE_multi_string('to_delete', ['a.cc', 'b.h'], |
| 'Files to delete', flag_values=fv) |
| flags.DEFINE_multi_integer('cols', [5, 7, 23], |
| 'Columns to select', flag_values=fv) |
| flags.DEFINE_multi_enum('flavours', ['APPLE', 'BANANA'], |
| ['APPLE', 'BANANA', 'CHERRY'], |
| 'Compilation flavour.', flag_values=fv) |
| # Define a few flags in a different module. |
| module_bar.define_flags(flag_values=fv) |
| # And declare only a few of them to be key. This way, we have |
| # different kinds of flags, defined in different modules, and not |
| # all of them are key flags. |
| flags.declare_key_flag('tmod_bar_z', flag_values=fv) |
| flags.declare_key_flag('tmod_bar_u', flag_values=fv) |
| |
| # Generate flag help in XML format in the StringIO sio. |
| sio = io.StringIO() if six.PY3 else io.BytesIO() |
| fv.write_help_in_xml_format(sio) |
| |
| # Check that we got the expected result. |
| expected_output_template = EXPECTED_HELP_XML_START |
| main_module_name = sys.argv[0] |
| module_bar_name = module_bar.__name__ |
| |
| if main_module_name < module_bar_name: |
| expected_output_template += EXPECTED_HELP_XML_FOR_FLAGS_FROM_MAIN_MODULE |
| expected_output_template += EXPECTED_HELP_XML_FOR_FLAGS_FROM_MODULE_BAR |
| else: |
| expected_output_template += EXPECTED_HELP_XML_FOR_FLAGS_FROM_MODULE_BAR |
| expected_output_template += EXPECTED_HELP_XML_FOR_FLAGS_FROM_MAIN_MODULE |
| |
| expected_output_template += EXPECTED_HELP_XML_END |
| |
| # XML representation of the whitespace list separators. |
| whitespace_separators = _list_separators_in_xmlformat(string.whitespace, |
| indent=' ') |
| expected_output = ( |
| expected_output_template % |
| {'basename_of_argv0': os.path.basename(sys.argv[0]), |
| 'usage_doc': sys.modules['__main__'].__doc__, |
| 'main_module_name': main_module_name, |
| 'module_bar_name': module_bar_name, |
| 'whitespace_separators': whitespace_separators}) |
| |
| actual_output = sio.getvalue() |
| self.assertMultiLineEqual(expected_output, actual_output) |
| |
| # Also check that our result is valid XML. minidom.parseString |
| # throws an xml.parsers.expat.ExpatError in case of an error. |
| xml.dom.minidom.parseString(actual_output) |
| |
| |
| if __name__ == '__main__': |
| absltest.main() |