aboutsummaryrefslogtreecommitdiffhomepage
path: root/third_party/py/gflags/tests/gflags_helpxml_test.py
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/py/gflags/tests/gflags_helpxml_test.py')
-rwxr-xr-xthird_party/py/gflags/tests/gflags_helpxml_test.py535
1 files changed, 535 insertions, 0 deletions
diff --git a/third_party/py/gflags/tests/gflags_helpxml_test.py b/third_party/py/gflags/tests/gflags_helpxml_test.py
new file mode 100755
index 0000000000..fd78004b73
--- /dev/null
+++ b/third_party/py/gflags/tests/gflags_helpxml_test.py
@@ -0,0 +1,535 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Unit tests for the XML-format help generated by the gflags.py module."""
+
+__author__ = 'salcianu@google.com (Alex Salcianu)'
+
+
+import string
+import StringIO
+import sys
+import xml.dom.minidom
+import xml.sax.saxutils
+import gflags_googletest as googletest
+import gflags
+from flags_modules_for_testing import module_bar
+
+
+class _MakeXMLSafeTest(googletest.TestCase):
+
+ def _Check(self, s, expected_output):
+ self.assertEqual(gflags._MakeXMLSafe(s), expected_output)
+
+ def testMakeXMLSafe(self):
+ self._Check('plain text', 'plain text')
+ self._Check('(x < y) && (a >= b)',
+ '(x &lt; y) &amp;&amp; (a &gt;= b)')
+ # Some characters with ASCII code < 32 are illegal in XML 1.0 and
+ # are removed by us. However, '\n', '\t', and '\r' are legal.
+ self._Check('\x09\x0btext \x02 with\x0dsome \x08 good & bad chars',
+ '\ttext with\rsome good &amp; bad chars')
+
+
+def _ListSeparatorsInXMLFormat(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 WriteFlagHelpInXMLFormatTest(googletest.TestCase):
+ """Test the XML-format help 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 gflags.FLAGS. Each
+ # test registers one flag with this FlagValues.
+ self.fv = gflags.FlagValues()
+
+ def _CheckFlagHelpInXML(self, flag_name, module_name,
+ expected_output, is_key=False):
+ # StringIO.StringIO is a file object that writes into a memory string.
+ sio = StringIO.StringIO()
+ flag_obj = self.fv[flag_name]
+ flag_obj.WriteInfoInXMLFormat(sio, module_name, is_key=is_key, indent=' ')
+ self.assertMultiLineEqual(sio.getvalue(), expected_output)
+ sio.close()
+
+ def testFlagHelpInXML_Int(self):
+ gflags.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._CheckFlagHelpInXML('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._CheckFlagHelpInXML('index', 'module.name',
+ expected_output_pattern % 20)
+
+ def testFlagHelpInXML_IntWithBounds(self):
+ gflags.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._CheckFlagHelpInXML('nb_iters', 'module.name',
+ expected_output, is_key=True)
+
+ def testFlagHelpInXML_String(self):
+ gflags.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._CheckFlagHelpInXML('file_path', 'simple_module',
+ expected_output)
+
+ def testFlagHelpInXML_StringWithXMLIllegalChars(self):
+ gflags.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._CheckFlagHelpInXML('file_path', 'simple_module',
+ expected_output)
+
+ def testFlagHelpInXML_Boolean(self):
+ gflags.DEFINE_boolean('use_hack', False, 'Use performance hack',
+ flag_values=self.fv)
+ expected_output = (
+ ' <flag>\n'
+ ' <key>yes</key>\n'
+ ' <file>a_module</file>\n'
+ ' <name>use_hack</name>\n'
+ ' <meaning>Use performance hack</meaning>\n'
+ ' <default>false</default>\n'
+ ' <current>false</current>\n'
+ ' <type>bool</type>\n'
+ ' </flag>\n')
+ self._CheckFlagHelpInXML('use_hack', 'a_module',
+ expected_output, is_key=True)
+
+ def testFlagHelpInXML_Enum(self):
+ gflags.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>&lt;stable|experimental&gt;: '
+ '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._CheckFlagHelpInXML('cc_version', 'tool', expected_output)
+
+ def testFlagHelpInXML_CommaSeparatedList(self):
+ gflags.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._CheckFlagHelpInXML('files', 'tool', expected_output)
+
+ def testListAsDefaultArgument_CommaSeparatedList(self):
+ gflags.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._CheckFlagHelpInXML('allow_users', 'tool', expected_output)
+
+ def testFlagHelpInXML_SpaceSeparatedList(self):
+ gflags.DEFINE_spaceseplist('dirs', 'src libs bin',
+ 'Directories to search.', flag_values=self.fv)
+ 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',
+ _ListSeparatorsInXMLFormat(string.whitespace,
+ indent=' '))
+ self._CheckFlagHelpInXML('dirs', 'tool', expected_output)
+
+ def testFlagHelpInXML_MultiString(self):
+ gflags.DEFINE_multistring('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._CheckFlagHelpInXML('to_delete', 'tool', expected_output)
+
+ def testFlagHelpInXML_MultiInt(self):
+ gflags.DEFINE_multi_int('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._CheckFlagHelpInXML('cols', '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.
+#
+# NOTE: given the current implementation of _GetMainModule(), we
+# already know the ordering between the main module and module_bar.
+# However, there is no guarantee that _GetMainModule will never be
+# changed in the future (especially since it's far from perfect).
+EXPECTED_HELP_XML_START = """\
+<?xml version="1.0"?>
+<AllFlags>
+ <program>gflags_helpxml_test.py</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>&lt;stable|experimental&gt;: 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>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_hack</name>
+ <meaning>Use performance hack</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(googletest.TestCase):
+ """Big test of FlagValues.WriteHelpInXMLFormat, with several flags."""
+
+ def testWriteHelpInXMLFormat(self):
+ fv = gflags.FlagValues()
+ # Since these flags are defined by the top module, they are all key.
+ gflags.DEFINE_integer('index', 17, 'An integer flag', flag_values=fv)
+ gflags.DEFINE_integer('nb_iters', 17, 'An integer flag',
+ lower_bound=5, upper_bound=27, flag_values=fv)
+ gflags.DEFINE_string('file_path', '/path/to/my/dir', 'A test string flag.',
+ flag_values=fv)
+ gflags.DEFINE_boolean('use_hack', False, 'Use performance hack',
+ flag_values=fv)
+ gflags.DEFINE_enum('cc_version', 'stable', ['stable', 'experimental'],
+ 'Compiler version to use.', flag_values=fv)
+ gflags.DEFINE_list('files', 'a.cc,a.h,archive/old.zip',
+ 'Files to process.', flag_values=fv)
+ gflags.DEFINE_list('allow_users', ['alice', 'bob'],
+ 'Users with access.', flag_values=fv)
+ gflags.DEFINE_spaceseplist('dirs', 'src libs bins',
+ 'Directories to create.', flag_values=fv)
+ gflags.DEFINE_multistring('to_delete', ['a.cc', 'b.h'],
+ 'Files to delete', flag_values=fv)
+ gflags.DEFINE_multi_int('cols', [5, 7, 23],
+ 'Columns to select', flag_values=fv)
+ # Define a few flags in a different module.
+ module_bar.DefineFlags(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.
+ gflags.DECLARE_key_flag('tmod_bar_z', flag_values=fv)
+ gflags.DECLARE_key_flag('tmod_bar_u', flag_values=fv)
+
+ # Generate flag help in XML format in the StringIO sio.
+ sio = StringIO.StringIO()
+ fv.WriteHelpInXMLFormat(sio)
+
+ # Check that we got the expected result.
+ expected_output_template = EXPECTED_HELP_XML_START
+ main_module_name = gflags._GetMainModule()
+ 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 = _ListSeparatorsInXMLFormat(string.whitespace,
+ indent=' ')
+ expected_output = (
+ expected_output_template %
+ {'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(actual_output, expected_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__':
+ googletest.main()