# -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals import re import sys import textwrap import salt.utils.stringutils # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import builtins, range # pylint: disable=redefined-builtin # Import Salt libs from tests.support.mock import patch from tests.support.unit import LOREM_IPSUM, TestCase UNICODE = "中国語 (繁体)" STR = BYTES = UNICODE.encode("utf-8") # This is an example of a unicode string with й constructed using two separate # code points. Do not modify it. EGGS = "\u044f\u0438\u0306\u0446\u0430" LATIN1_UNICODE = "räksmörgås" LATIN1_BYTES = LATIN1_UNICODE.encode("latin-1") DOUBLE_TXT = """\ # set variable identifying the chroot you work in (used in the prompt below) if [ -z "$debian_chroot" ] && [ -r /etc/debian_chroot ]; then debian_chroot=$(cat /etc/debian_chroot) fi """ SINGLE_TXT = """\ # set variable identifying the chroot you work in (used in the prompt below) if [ -z '$debian_chroot' ] && [ -r /etc/debian_chroot ]; then debian_chroot=$(cat /etc/debian_chroot) fi """ SINGLE_DOUBLE_TXT = """\ # set variable identifying the chroot you work in (used in the prompt below) if [ -z '$debian_chroot' ] && [ -r /etc/debian_chroot ]; then debian_chroot=$(cat /etc/debian_chroot) fi # set variable identifying the chroot you work in (used in the prompt below) if [ -z "$debian_chroot" ] && [ -r /etc/debian_chroot ]; then debian_chroot=$(cat /etc/debian_chroot) fi """ SINGLE_DOUBLE_SAME_LINE_TXT = """\ # set variable identifying the chroot you work in (used in the prompt below) if [ -z '$debian_chroot' ] && [ -r "/etc/debian_chroot" ]; then debian_chroot=$(cat /etc/debian_chroot) fi """ MATCH = """\ # set variable identifying the chroot you work in (used in the prompt below) if [ -z '$debian_chroot' ] && [ -r /etc/debian_chroot ]; then debian_chroot=$(cat /etc/debian_chroot) fi # set variable identifying the chroot you work in (used in the prompt below) if [ -z "$debian_chroot" ] && [ -r /etc/debian_chroot ]; then debian_chroot=$(cat /etc/debian_chroot) fi # set variable identifying the chroot you work in (used in the prompt below) if [ -z "$debian_chroot" ] && [ -r /etc/debian_chroot ]; then debian_chroot=$(cat /etc/debian_chroot) fi # set variable identifying the chroot you work in (used in the prompt below) if [ -z '$debian_chroot' ] && [ -r /etc/debian_chroot ]; then debian_chroot=$(cat /etc/debian_chroot) fi # set variable identifying the chroot you work in (used in the prompt below) if [ -z '$debian_chroot' ] && [ -r "/etc/debian_chroot" ]; then debian_chroot=$(cat /etc/debian_chroot) fi """ class TestBuildWhitespaceRegex(TestCase): def test_single_quotes(self): regex = salt.utils.stringutils.build_whitespace_split_regex(SINGLE_TXT) self.assertTrue(re.search(regex, MATCH)) def test_double_quotes(self): regex = salt.utils.stringutils.build_whitespace_split_regex(DOUBLE_TXT) self.assertTrue(re.search(regex, MATCH)) def test_single_and_double_quotes(self): regex = salt.utils.stringutils.build_whitespace_split_regex(SINGLE_DOUBLE_TXT) self.assertTrue(re.search(regex, MATCH)) def test_issue_2227(self): regex = salt.utils.stringutils.build_whitespace_split_regex( SINGLE_DOUBLE_SAME_LINE_TXT ) self.assertTrue(re.search(regex, MATCH)) class StringutilsTestCase(TestCase): def test_contains_whitespace(self): does_contain_whitespace = "A brown fox jumped over the red hen." does_not_contain_whitespace = "Abrownfoxjumpedovertheredhen." self.assertTrue( salt.utils.stringutils.contains_whitespace(does_contain_whitespace) ) self.assertFalse( salt.utils.stringutils.contains_whitespace(does_not_contain_whitespace) ) def test_to_num(self): self.assertEqual(7, salt.utils.stringutils.to_num("7")) self.assertIsInstance(salt.utils.stringutils.to_num("7"), int) self.assertEqual(7, salt.utils.stringutils.to_num("7.0")) self.assertIsInstance(salt.utils.stringutils.to_num("7.0"), float) self.assertEqual(salt.utils.stringutils.to_num("Seven"), "Seven") self.assertIsInstance(salt.utils.stringutils.to_num("Seven"), six.text_type) def test_to_none(self): self.assertIsNone(salt.utils.stringutils.to_none("")) self.assertIsNone(salt.utils.stringutils.to_none(" ")) # Ensure that we do not inadvertently convert certain strings or 0 to None self.assertIsNotNone(salt.utils.stringutils.to_none("None")) self.assertIsNotNone(salt.utils.stringutils.to_none(0)) def test_is_binary(self): self.assertFalse(salt.utils.stringutils.is_binary(LOREM_IPSUM)) # Also test bytestring self.assertFalse( salt.utils.stringutils.is_binary( salt.utils.stringutils.is_binary(LOREM_IPSUM) ) ) zero_str = "{0}{1}".format(LOREM_IPSUM, "\0") self.assertTrue(salt.utils.stringutils.is_binary(zero_str)) # Also test bytestring self.assertTrue( salt.utils.stringutils.is_binary(salt.utils.stringutils.to_bytes(zero_str)) ) # To to ensure safe exit if str passed doesn't evaluate to True self.assertFalse(salt.utils.stringutils.is_binary("")) self.assertFalse(salt.utils.stringutils.is_binary(b"")) nontext = 3 * ( "".join([chr(x) for x in range(1, 32) if x not in (8, 9, 10, 12, 13)]) ) almost_bin_str = "{0}{1}".format(LOREM_IPSUM[:100], nontext[:42]) self.assertFalse(salt.utils.stringutils.is_binary(almost_bin_str)) # Also test bytestring self.assertFalse( salt.utils.stringutils.is_binary( salt.utils.stringutils.to_bytes(almost_bin_str) ) ) bin_str = almost_bin_str + "\x01" self.assertTrue(salt.utils.stringutils.is_binary(bin_str)) # Also test bytestring self.assertTrue( salt.utils.stringutils.is_binary(salt.utils.stringutils.to_bytes(bin_str)) ) def test_to_str(self): for x in (123, (1, 2, 3), [1, 2, 3], {1: 23}, None): self.assertRaises(TypeError, salt.utils.stringutils.to_str, x) if six.PY3: self.assertEqual(salt.utils.stringutils.to_str("plugh"), "plugh") self.assertEqual(salt.utils.stringutils.to_str("áéíóúý", "utf-8"), "áéíóúý") self.assertEqual(salt.utils.stringutils.to_str(BYTES, "utf-8"), UNICODE) self.assertEqual( salt.utils.stringutils.to_str(bytearray(BYTES), "utf-8"), UNICODE ) # Test situation when a minion returns incorrect utf-8 string because of... million reasons ut2 = b"\x9c" self.assertRaises( UnicodeDecodeError, salt.utils.stringutils.to_str, ut2, "utf-8" ) self.assertEqual( salt.utils.stringutils.to_str(ut2, "utf-8", "replace"), "\ufffd" ) self.assertRaises( UnicodeDecodeError, salt.utils.stringutils.to_str, bytearray(ut2), "utf-8", ) self.assertEqual( salt.utils.stringutils.to_str(bytearray(ut2), "utf-8", "replace"), "\ufffd", ) else: self.assertEqual( salt.utils.stringutils.to_str("plugh"), str("plugh") ) # future lint: disable=blacklisted-function self.assertEqual( salt.utils.stringutils.to_str("áéíóúý", "utf-8"), "áéíóúý".encode("utf-8"), ) self.assertEqual(salt.utils.stringutils.to_str(UNICODE, "utf-8"), STR) self.assertEqual( salt.utils.stringutils.to_str(bytearray(STR), "utf-8"), STR ) # Test utf-8 fallback with Windows default codepage with patch.object(builtins, "__salt_system_encoding__", "CP1252"): self.assertEqual( salt.utils.stringutils.to_str("Ψ"), "Ψ".encode("utf-8") ) def test_to_bytes(self): for x in (123, (1, 2, 3), [1, 2, 3], {1: 23}, None): self.assertRaises(TypeError, salt.utils.stringutils.to_bytes, x) if six.PY3: self.assertEqual(salt.utils.stringutils.to_bytes("xyzzy"), b"xyzzy") self.assertEqual(salt.utils.stringutils.to_bytes(BYTES), BYTES) self.assertEqual(salt.utils.stringutils.to_bytes(bytearray(BYTES)), BYTES) self.assertEqual(salt.utils.stringutils.to_bytes(UNICODE, "utf-8"), BYTES) # Test utf-8 fallback with ascii default encoding with patch.object(builtins, "__salt_system_encoding__", "ascii"): self.assertEqual(salt.utils.stringutils.to_bytes("Ψ"), b"\xce\xa8") else: self.assertEqual(salt.utils.stringutils.to_bytes("xyzzy"), "xyzzy") self.assertEqual(salt.utils.stringutils.to_bytes(BYTES), BYTES) self.assertEqual(salt.utils.stringutils.to_bytes(bytearray(BYTES)), BYTES) self.assertEqual(salt.utils.stringutils.to_bytes(UNICODE, "utf-8"), BYTES) def test_to_unicode(self): self.assertEqual( salt.utils.stringutils.to_unicode(EGGS, normalize=True), "яйца" ) self.assertNotEqual( salt.utils.stringutils.to_unicode(EGGS, normalize=False), "яйца" ) self.assertEqual( salt.utils.stringutils.to_unicode(LATIN1_BYTES, encoding="latin-1"), LATIN1_UNICODE, ) if six.PY3: self.assertEqual(salt.utils.stringutils.to_unicode("plugh"), "plugh") self.assertEqual(salt.utils.stringutils.to_unicode("áéíóúý"), "áéíóúý") self.assertEqual(salt.utils.stringutils.to_unicode(BYTES, "utf-8"), UNICODE) self.assertEqual( salt.utils.stringutils.to_unicode(bytearray(BYTES), "utf-8"), UNICODE ) else: self.assertEqual( salt.utils.stringutils.to_unicode(str("xyzzy"), "utf-8"), "xyzzy" ) # future lint: disable=blacklisted-function self.assertEqual(salt.utils.stringutils.to_unicode(BYTES, "utf-8"), UNICODE) # Test that unicode chars are decoded properly even when using # locales which are not UTF-8 compatible with patch.object(builtins, "__salt_system_encoding__", "ascii"): self.assertEqual( salt.utils.stringutils.to_unicode("Ψ".encode("utf-8")), "Ψ" ) with patch.object(builtins, "__salt_system_encoding__", "CP1252"): self.assertEqual( salt.utils.stringutils.to_unicode("Ψ".encode("utf-8")), "Ψ" ) def test_to_unicode_multi_encoding(self): result = salt.utils.stringutils.to_unicode( LATIN1_BYTES, encoding=("utf-8", "latin1") ) assert result == LATIN1_UNICODE def test_build_whitespace_split_regex(self): # With 3.7+, re.escape only escapes special characters, no longer # escaping all characters other than ASCII letters, numbers and # underscores. This includes commas. if sys.version_info >= (3, 7): expected_regex = ( "(?m)^(?:[\\s]+)?Lorem(?:[\\s]+)?ipsum(?:[\\s]+)?dolor(?:[\\s]+)?sit(?:[\\s]+)?amet," "(?:[\\s]+)?$" ) else: expected_regex = ( "(?m)^(?:[\\s]+)?Lorem(?:[\\s]+)?ipsum(?:[\\s]+)?dolor(?:[\\s]+)?sit(?:[\\s]+)?amet\\," "(?:[\\s]+)?$" ) ret = salt.utils.stringutils.build_whitespace_split_regex( " ".join(LOREM_IPSUM.split()[:5]) ) self.assertEqual(ret, expected_regex) def test_get_context(self): expected_context = textwrap.dedent( """\ --- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque eget urna a arcu lacinia sagittis. Sed scelerisque, lacus eget malesuada vestibulum, justo diam facilisis tortor, in sodales dolor [...] ---""" ) ret = salt.utils.stringutils.get_context(LOREM_IPSUM, 1, num_lines=1) self.assertEqual(ret, expected_context) def test_get_context_has_enough_context(self): template = "1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf" context = salt.utils.stringutils.get_context(template, 8) expected = "---\n[...]\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\n[...]\n---" self.assertEqual(expected, context) def test_get_context_at_top_of_file(self): template = "1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf" context = salt.utils.stringutils.get_context(template, 1) expected = "---\n1\n2\n3\n4\n5\n6\n[...]\n---" self.assertEqual(expected, context) def test_get_context_at_bottom_of_file(self): template = "1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf" context = salt.utils.stringutils.get_context(template, 15) expected = "---\n[...]\na\nb\nc\nd\ne\nf\n---" self.assertEqual(expected, context) def test_get_context_2_context_lines(self): template = "1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf" context = salt.utils.stringutils.get_context(template, 8, num_lines=2) expected = "---\n[...]\n6\n7\n8\n9\na\n[...]\n---" self.assertEqual(expected, context) def test_get_context_with_marker(self): template = "1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf" context = salt.utils.stringutils.get_context( template, 8, num_lines=2, marker=" <---" ) expected = "---\n[...]\n6\n7\n8 <---\n9\na\n[...]\n---" self.assertEqual(expected, context) def test_expr_match(self): val = "foo/bar/baz" # Exact match self.assertTrue(salt.utils.stringutils.expr_match(val, val)) # Glob match self.assertTrue(salt.utils.stringutils.expr_match(val, "foo/*/baz")) # Glob non-match self.assertFalse(salt.utils.stringutils.expr_match(val, "foo/*/bar")) # Regex match self.assertTrue(salt.utils.stringutils.expr_match(val, r"foo/\w+/baz")) # Regex non-match self.assertFalse(salt.utils.stringutils.expr_match(val, r"foo/\w/baz")) def test_check_whitelist_blacklist(self): """ Ensure that whitelist matching works on both PY2 and PY3 """ whitelist = ["one/two/three", r"web[0-9]"] blacklist = ["four/five/six", r"web[5-9]"] # Tests with string whitelist/blacklist self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( "web_one", whitelist=whitelist[1], blacklist=None, ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( "web_one", whitelist=whitelist[1], blacklist=[], ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( "web1", whitelist=whitelist[1], blacklist=None, ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( "web1", whitelist=whitelist[1], blacklist=[], ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( "web5", whitelist=None, blacklist=blacklist[1], ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( "web5", whitelist=[], blacklist=blacklist[1], ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( "web_five", whitelist=None, blacklist=blacklist[1], ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( "web_five", whitelist=[], blacklist=blacklist[1], ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( "web5", whitelist=whitelist[1], blacklist=blacklist[1], ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( "web4", whitelist=whitelist[1], blacklist=blacklist[1], ) ) # Tests with list whitelist/blacklist self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( "web_one", whitelist=whitelist, blacklist=None, ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( "web_one", whitelist=whitelist, blacklist=[], ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( "web1", whitelist=whitelist, blacklist=None, ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( "web1", whitelist=whitelist, blacklist=[], ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( "web5", whitelist=None, blacklist=blacklist, ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( "web5", whitelist=[], blacklist=blacklist, ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( "web_five", whitelist=None, blacklist=blacklist, ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( "web_five", whitelist=[], blacklist=blacklist, ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( "web5", whitelist=whitelist, blacklist=blacklist, ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( "web4", whitelist=whitelist, blacklist=blacklist, ) ) # Tests with set whitelist/blacklist self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( "web_one", whitelist=set(whitelist), blacklist=None, ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( "web_one", whitelist=set(whitelist), blacklist=set(), ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( "web1", whitelist=set(whitelist), blacklist=None, ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( "web1", whitelist=set(whitelist), blacklist=set(), ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( "web5", whitelist=None, blacklist=set(blacklist), ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( "web5", whitelist=set(), blacklist=set(blacklist), ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( "web_five", whitelist=None, blacklist=set(blacklist), ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( "web_five", whitelist=set(), blacklist=set(blacklist), ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( "web5", whitelist=set(whitelist), blacklist=set(blacklist), ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( "web4", whitelist=set(whitelist), blacklist=set(blacklist), ) ) # Test with invalid type for whitelist/blacklist self.assertRaises( TypeError, salt.utils.stringutils.check_whitelist_blacklist, "foo", whitelist=123, ) self.assertRaises( TypeError, salt.utils.stringutils.check_whitelist_blacklist, "foo", blacklist=123, ) def test_check_include_exclude_empty(self): self.assertTrue(salt.utils.stringutils.check_include_exclude("/some/test")) def test_check_include_exclude_exclude(self): self.assertFalse( salt.utils.stringutils.check_include_exclude("/some/test", None, "*test*") ) def test_check_include_exclude_exclude_list(self): self.assertFalse( salt.utils.stringutils.check_include_exclude("/some/test", None, ["*test"]) ) def test_check_include_exclude_exclude_include(self): self.assertTrue( salt.utils.stringutils.check_include_exclude( "/some/test", "*test*", "/some/" ) ) def test_check_include_exclude_regex(self): self.assertFalse( salt.utils.stringutils.check_include_exclude( "/some/test", None, "E@/some/(test|other)" ) )