blob: 78b90518c22b5f74d0972f98d5f6bfc894b805c0 [file] [log] [blame]
# 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()