# -*- coding: utf-8 -*- # Copyright (c), Sviatoslav Sydorenko 2018 # Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) """Test low-level utility functions from ``module_utils.common.collections``.""" from __future__ import absolute_import, division, print_function __metaclass__ = type import pytest from ansible.module_utils.common._collections_compat import Sequence from ansible.module_utils.common.collections import ImmutableDict, is_iterable, is_sequence class SeqStub: """Stub emulating a sequence type. >>> from collections.abc import Sequence >>> assert issubclass(SeqStub, Sequence) >>> assert isinstance(SeqStub(), Sequence) """ Sequence.register(SeqStub) class IteratorStub: def __next__(self): raise StopIteration class IterableStub: def __iter__(self): return IteratorStub() TEST_STRINGS = u'he', u'Україна', u'Česká republika' TEST_STRINGS = TEST_STRINGS + tuple(s.encode('utf-8') for s in TEST_STRINGS) TEST_ITEMS_NON_SEQUENCES = ( {}, object(), frozenset(), 4, 0., ) + TEST_STRINGS TEST_ITEMS_SEQUENCES = ( [], (), SeqStub(), ) TEST_ITEMS_SEQUENCES = TEST_ITEMS_SEQUENCES + ( # Iterable effectively containing nested random data: TEST_ITEMS_NON_SEQUENCES, ) @pytest.mark.parametrize('sequence_input', TEST_ITEMS_SEQUENCES) def test_sequence_positive(sequence_input): """Test that non-string item sequences are identified correctly.""" assert is_sequence(sequence_input) assert is_sequence(sequence_input, include_strings=False) @pytest.mark.parametrize('non_sequence_input', TEST_ITEMS_NON_SEQUENCES) def test_sequence_negative(non_sequence_input): """Test that non-sequences are identified correctly.""" assert not is_sequence(non_sequence_input) @pytest.mark.parametrize('string_input', TEST_STRINGS) def test_sequence_string_types_with_strings(string_input): """Test that ``is_sequence`` can separate string and non-string.""" assert is_sequence(string_input, include_strings=True) @pytest.mark.parametrize('string_input', TEST_STRINGS) def test_sequence_string_types_without_strings(string_input): """Test that ``is_sequence`` can separate string and non-string.""" assert not is_sequence(string_input, include_strings=False) @pytest.mark.parametrize( 'seq', ([], (), {}, set(), frozenset(), IterableStub()), ) def test_iterable_positive(seq): assert is_iterable(seq) @pytest.mark.parametrize( 'seq', (IteratorStub(), object(), 5, 9.) ) def test_iterable_negative(seq): assert not is_iterable(seq) @pytest.mark.parametrize('string_input', TEST_STRINGS) def test_iterable_including_strings(string_input): assert is_iterable(string_input, include_strings=True) @pytest.mark.parametrize('string_input', TEST_STRINGS) def test_iterable_excluding_strings(string_input): assert not is_iterable(string_input, include_strings=False) class TestImmutableDict: def test_scalar(self): imdict = ImmutableDict({1: 2}) assert imdict[1] == 2 def test_string(self): imdict = ImmutableDict({u'café': u'くらとみ'}) assert imdict[u'café'] == u'くらとみ' def test_container(self): imdict = ImmutableDict({(1, 2): ['1', '2']}) assert imdict[(1, 2)] == ['1', '2'] def test_from_tuples(self): imdict = ImmutableDict((('a', 1), ('b', 2))) assert frozenset(imdict.items()) == frozenset((('a', 1), ('b', 2))) def test_from_kwargs(self): imdict = ImmutableDict(a=1, b=2) assert frozenset(imdict.items()) == frozenset((('a', 1), ('b', 2))) def test_immutable(self): imdict = ImmutableDict({1: 2}) with pytest.raises(TypeError) as exc_info: imdict[1] = 3 assert exc_info.value.args[0] == "'ImmutableDict' object does not support item assignment" with pytest.raises(TypeError) as exc_info: imdict[5] = 3 assert exc_info.value.args[0] == "'ImmutableDict' object does not support item assignment" def test_hashable(self): # ImmutableDict is hashable when all of its values are hashable imdict = ImmutableDict({u'café': u'くらとみ'}) assert hash(imdict) def test_nonhashable(self): # ImmutableDict is unhashable when one of its values is unhashable imdict = ImmutableDict({u'café': u'くらとみ', 1: [1, 2]}) with pytest.raises(TypeError) as exc_info: hash(imdict) assert exc_info.value.args[0] == "unhashable type: 'list'" def test_len(self): imdict = ImmutableDict({1: 2, 'a': 'b'}) assert len(imdict) == 2 def test_repr(self): imdict = ImmutableDict({1: 2, 'a': 'b'}) assert repr(imdict) == "ImmutableDict({1: 2, 'a': 'b'})"