| # 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. |
| |
| """Unittests for helpers module.""" |
| |
| import sys |
| |
| from absl.flags import _helpers |
| from absl.flags.tests import module_bar |
| from absl.flags.tests import module_foo |
| from absl.testing import absltest |
| |
| |
| class FlagSuggestionTest(absltest.TestCase): |
| |
| def setUp(self): |
| self.longopts = [ |
| 'fsplit-ivs-in-unroller=', |
| 'fsplit-wide-types=', |
| 'fstack-protector=', |
| 'fstack-protector-all=', |
| 'fstrict-aliasing=', |
| 'fstrict-overflow=', |
| 'fthread-jumps=', |
| 'ftracer', |
| 'ftree-bit-ccp', |
| 'ftree-builtin-call-dce', |
| 'ftree-ccp', |
| 'ftree-ch'] |
| |
| def test_damerau_levenshtein_id(self): |
| self.assertEqual(0, _helpers._damerau_levenshtein('asdf', 'asdf')) |
| |
| def test_damerau_levenshtein_empty(self): |
| self.assertEqual(5, _helpers._damerau_levenshtein('', 'kites')) |
| self.assertEqual(6, _helpers._damerau_levenshtein('kitten', '')) |
| |
| def test_damerau_levenshtein_commutative(self): |
| self.assertEqual(2, _helpers._damerau_levenshtein('kitten', 'kites')) |
| self.assertEqual(2, _helpers._damerau_levenshtein('kites', 'kitten')) |
| |
| def test_damerau_levenshtein_transposition(self): |
| self.assertEqual(1, _helpers._damerau_levenshtein('kitten', 'ktiten')) |
| |
| def test_mispelled_suggestions(self): |
| suggestions = _helpers.get_flag_suggestions('fstack_protector_all', |
| self.longopts) |
| self.assertEqual(['fstack-protector-all'], suggestions) |
| |
| def test_ambiguous_prefix_suggestion(self): |
| suggestions = _helpers.get_flag_suggestions('fstack', self.longopts) |
| self.assertEqual(['fstack-protector', 'fstack-protector-all'], suggestions) |
| |
| def test_misspelled_ambiguous_prefix_suggestion(self): |
| suggestions = _helpers.get_flag_suggestions('stack', self.longopts) |
| self.assertEqual(['fstack-protector', 'fstack-protector-all'], suggestions) |
| |
| def test_crazy_suggestion(self): |
| suggestions = _helpers.get_flag_suggestions('asdfasdgasdfa', self.longopts) |
| self.assertEqual([], suggestions) |
| |
| def test_suggestions_are_sorted(self): |
| sorted_flags = sorted(['aab', 'aac', 'aad']) |
| misspelt_flag = 'aaa' |
| suggestions = _helpers.get_flag_suggestions(misspelt_flag, |
| reversed(sorted_flags)) |
| self.assertEqual(sorted_flags, suggestions) |
| |
| |
| class GetCallingModuleTest(absltest.TestCase): |
| """Test whether we correctly determine the module which defines the flag.""" |
| |
| def test_get_calling_module(self): |
| self.assertEqual(_helpers.get_calling_module(), sys.argv[0]) |
| self.assertEqual(module_foo.get_module_name(), |
| 'absl.flags.tests.module_foo') |
| self.assertEqual(module_bar.get_module_name(), |
| 'absl.flags.tests.module_bar') |
| |
| # We execute the following exec statements for their side-effect |
| # (i.e., not raising an error). They emphasize the case that not |
| # all code resides in one of the imported modules: Python is a |
| # really dynamic language, where we can dynamically construct some |
| # code and execute it. |
| code = ('from absl.flags import _helpers\n' |
| 'module_name = _helpers.get_calling_module()') |
| exec(code) # pylint: disable=exec-used |
| |
| # Next two exec statements executes code with a global environment |
| # that is different from the global environment of any imported |
| # module. |
| exec(code, {}) # pylint: disable=exec-used |
| # vars(self) returns a dictionary corresponding to the symbol |
| # table of the self object. dict(...) makes a distinct copy of |
| # this dictionary, such that any new symbol definition by the |
| # exec-ed code (e.g., import flags, module_name = ...) does not |
| # affect the symbol table of self. |
| exec(code, dict(vars(self))) # pylint: disable=exec-used |
| |
| # Next test is actually more involved: it checks not only that |
| # get_calling_module does not crash inside exec code, it also checks |
| # that it returns the expected value: the code executed via exec |
| # code is treated as being executed by the current module. We |
| # check it twice: first time by executing exec from the main |
| # module, second time by executing it from module_bar. |
| global_dict = {} |
| exec(code, global_dict) # pylint: disable=exec-used |
| self.assertEqual(global_dict['module_name'], |
| sys.argv[0]) |
| |
| global_dict = {} |
| module_bar.execute_code(code, global_dict) |
| self.assertEqual(global_dict['module_name'], |
| 'absl.flags.tests.module_bar') |
| |
| def test_get_calling_module_with_iteritems_error(self): |
| # This test checks that get_calling_module is using |
| # sys.modules.items(), instead of .iteritems(). |
| orig_sys_modules = sys.modules |
| |
| # Mock sys.modules: simulates error produced by importing a module |
| # in parallel with our iteration over sys.modules.iteritems(). |
| class SysModulesMock(dict): |
| |
| def __init__(self, original_content): |
| dict.__init__(self, original_content) |
| |
| def iteritems(self): |
| # Any dictionary method is fine, but not .iteritems(). |
| raise RuntimeError('dictionary changed size during iteration') |
| |
| sys.modules = SysModulesMock(orig_sys_modules) |
| try: |
| # _get_calling_module should still work as expected: |
| self.assertEqual(_helpers.get_calling_module(), sys.argv[0]) |
| self.assertEqual(module_foo.get_module_name(), |
| 'absl.flags.tests.module_foo') |
| finally: |
| sys.modules = orig_sys_modules |
| |
| |
| if __name__ == '__main__': |
| absltest.main() |