bidict/tests/hypothesis/_strategies.py

257 lines
8.0 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2009-2019 Joshua Bronson. All Rights Reserved.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""Strategies for Hypothesis tests."""
import re
from collections import OrderedDict
from hypothesis import assume, strategies as st
from bidict import IGNORE, OVERWRITE, RAISE, OrderedBidictBase, namedbidict
from bidict.compat import izip
import _types as t
# pylint: disable=invalid-name
DATA = st.data()
RAND = st.randoms()
BIDICT_TYPES = st.sampled_from(t.BIDICT_TYPES)
MUTABLE_BIDICT_TYPES = st.sampled_from(t.MUTABLE_BIDICT_TYPES)
FROZEN_BIDICT_TYPES = st.sampled_from(t.FROZEN_BIDICT_TYPES)
ORDERED_BIDICT_TYPES = st.sampled_from(t.ORDERED_BIDICT_TYPES)
MAPPING_TYPES = st.sampled_from(t.MAPPING_TYPES)
NON_BIDICT_MAPPING_TYPES = st.sampled_from(t.NON_BIDICT_MAPPING_TYPES)
ORDERED_MAPPING_TYPES = st.sampled_from(t.ORDERED_MAPPING_TYPES)
HASHABLE_MAPPING_TYPES = st.sampled_from(t.HASHABLE_MAPPING_TYPES)
DUP_POLICIES = st.sampled_from((IGNORE, OVERWRITE, RAISE))
DUP_POLICIES_DICT = st.fixed_dictionaries(dict(
on_dup_key=DUP_POLICIES,
on_dup_val=DUP_POLICIES,
on_dup_kv=DUP_POLICIES,
))
BOOLEANS = st.booleans()
FLOATS = st.floats(allow_nan=False)
INTS = st.integers()
TEXT = st.text()
NONE = st.none()
IMMUTABLES = BOOLEANS | TEXT | NONE | INTS | FLOATS
LISTS_MAX_SIZE = 10
LISTS_PAIRS = st.lists(st.tuples(IMMUTABLES, IMMUTABLES), max_size=LISTS_MAX_SIZE)
NON_MAPPINGS = IMMUTABLES | LISTS_PAIRS
@st.composite
def _lists_pairs_with_duplication(draw):
# pylint: disable=too-many-locals
n = draw(st.integers(min_value=3, max_value=LISTS_MAX_SIZE))
indexes = st.integers(min_value=0, max_value=n - 1)
keys = draw(st.lists(IMMUTABLES, min_size=n, max_size=n))
vals = draw(st.lists(IMMUTABLES, min_size=n, max_size=n))
fwd = OrderedDict(izip(keys, vals))
inv = OrderedDict(izip(vals, keys))
which_to_dup = draw(RAND).choice((1, 2, 3))
should_dup_key = which_to_dup in (1, 3)
should_dup_val = which_to_dup in (2, 3)
should_add_dup_key = should_dup_key and len(fwd) < n
should_add_dup_val = should_dup_val and len(inv) < n
if not should_add_dup_key and not should_add_dup_val:
return list(izip(keys, vals))
if should_add_dup_key:
dup_key_idx = draw(indexes)
added_key = keys[dup_key_idx]
else:
added_key = draw(IMMUTABLES)
assume(added_key not in fwd)
if should_add_dup_val:
dup_val_idx = draw(indexes)
if should_add_dup_key:
assume(dup_val_idx != dup_key_idx)
added_val = vals[dup_val_idx]
else:
added_val = draw(IMMUTABLES)
assume(added_val not in inv)
insert_idx = draw(indexes)
keys.insert(insert_idx, added_key)
vals.insert(insert_idx, added_val)
return list(izip(keys, vals))
# pylint: disable=no-value-for-parameter
LISTS_PAIRS_DUP = _lists_pairs_with_duplication()
@st.composite
def lists_pairs_nodup(draw, elements=IMMUTABLES, min_size=0, max_size=LISTS_MAX_SIZE):
"""Generate a list of pairs from the given elements with no duplication."""
n = draw(st.integers(min_value=min_size, max_value=max_size))
size_n_sets = st.sets(elements, min_size=n, max_size=n)
keys = draw(size_n_sets)
vals = draw(size_n_sets)
return list(izip(keys, vals))
LISTS_PAIRS_NODUP = lists_pairs_nodup()
LISTS_TEXT_PAIRS_NODUP = lists_pairs_nodup(elements=TEXT)
# pylint: disable=dangerous-default-value
@st.composite
def bidicts(draw, bi_types=BIDICT_TYPES, init_items=LISTS_PAIRS_NODUP):
"""Generate bidicts."""
bi_cls = draw(bi_types)
items = draw(init_items)
bi = bi_cls(items)
# Return the inverse bidict with 50% probability to increase coverage.
return bi if draw(RAND).choice((True, False)) else bi.inv
BIDICTS = bidicts()
FROZEN_BIDICTS = bidicts(bi_types=FROZEN_BIDICT_TYPES)
MUTABLE_BIDICTS = bidicts(bi_types=MUTABLE_BIDICT_TYPES)
ORDERED_BIDICTS = bidicts(bi_types=ORDERED_BIDICT_TYPES)
NAMEDBIDICT_VALID_NAME_PAT = re.compile('[A-z][A-z0-9_]*$')
NAMEDBIDICT_NAMES = st.from_regex(NAMEDBIDICT_VALID_NAME_PAT, fullmatch=True)
NAMEDBIDICT_3_NAMES = st.tuples(
NAMEDBIDICT_NAMES,
NAMEDBIDICT_NAMES,
NAMEDBIDICT_NAMES,
)
@st.composite
def _namedbidict_types(draw, names=NAMEDBIDICT_3_NAMES, base_types=BIDICT_TYPES):
typename, keyname, valname = draw(names)
assume(keyname != valname)
base_type = draw(base_types)
return namedbidict(typename, keyname, valname, base_type=base_type)
NAMEDBIDICT_TYPES = _namedbidict_types()
@st.composite
def _namedbidicts(draw, nb_types=NAMEDBIDICT_TYPES, init_items=LISTS_PAIRS_NODUP):
nb_cls = draw(nb_types)
items = draw(init_items)
nb = nb_cls(items)
# Return the inverse namedbidict with 50% probability to increase coverage.
return nb if draw(RAND).choice((True, False)) else nb.inv
NAMEDBIDICTS = _namedbidicts()
_COMPARE_DICT_TYPE = object()
_SAME_AS_BI_ITEMS = object()
_SAME_AS_BI_ITEMS_DIFF_ORDER = object()
@st.composite
def _bidict_and_mapping_from_items(
draw,
bi_types=BIDICT_TYPES,
map_types=MAPPING_TYPES,
bi_items=LISTS_PAIRS_NODUP,
map_items=_SAME_AS_BI_ITEMS,
):
bi_cls = draw(bi_types)
if map_types is _COMPARE_DICT_TYPE:
map_cls = OrderedDict if issubclass(bi_cls, OrderedBidictBase) else dict
else:
map_cls = draw(map_types)
bi_items_ = draw(bi_items)
if map_items in (_SAME_AS_BI_ITEMS, _SAME_AS_BI_ITEMS_DIFF_ORDER):
map_items_ = bi_items_[:]
if map_items is _SAME_AS_BI_ITEMS_DIFF_ORDER:
draw(RAND).shuffle(map_items_)
assume(map_items_ != bi_items_)
else:
map_items_ = draw(map_items)
assume(map_items_ != bi_items_)
return bi_cls(bi_items_), map_cls(map_items_)
BIDICT_AND_MAPPING_FROM_SAME_ITEMS_NODUP = _bidict_and_mapping_from_items()
BIDICT_AND_MAPPING_FROM_DIFFERENT_ITEMS = _bidict_and_mapping_from_items(
map_items=LISTS_PAIRS_NODUP,
)
BIDICT_AND_COMPARE_DICT_FROM_SAME_ITEMS_NODUP = _bidict_and_mapping_from_items(
map_types=_COMPARE_DICT_TYPE,
)
ORDERED_BIDICT_AND_ORDERED_DICT_FROM_SAME_ITEMS_NODUP = _bidict_and_mapping_from_items(
bi_types=ORDERED_BIDICT_TYPES,
map_types=_COMPARE_DICT_TYPE,
)
ORDERED_BIDICT_AND_ORDERED_MAPPING_FROM_SAME_ITEMS_NODUP = _bidict_and_mapping_from_items(
bi_types=ORDERED_BIDICT_TYPES,
map_types=ORDERED_MAPPING_TYPES,
)
HASHABLE_BIDICT_AND_MAPPING_FROM_SAME_ITEMS_NODUP = _bidict_and_mapping_from_items(
bi_types=FROZEN_BIDICT_TYPES,
map_types=HASHABLE_MAPPING_TYPES,
)
ORDERED_BIDICT_AND_ORDERED_MAPPING_FROM_SAME_ITEMS_DIFF_ORDER = _bidict_and_mapping_from_items(
bi_types=ORDERED_BIDICT_TYPES,
map_types=ORDERED_MAPPING_TYPES,
map_items=_SAME_AS_BI_ITEMS_DIFF_ORDER,
)
NO_ARGS = st.just(())
IM_ARG = st.tuples(IMMUTABLES)
LP_ARG = st.tuples(LISTS_PAIRS)
TWO_IM_ARGS = st.tuples(IMMUTABLES, IMMUTABLES)
ARGS_BY_METHOD = st.fixed_dictionaries({
# mutating
# 0-arity
(0, 'clear'): NO_ARGS,
(0, 'popitem'): NO_ARGS,
# 1-arity, an immutable atom
(1, '__delitem__'): IM_ARG,
(1, 'pop'): IM_ARG,
(1, 'setdefault'): IM_ARG,
(1, 'move_to_end'): IM_ARG,
# 1-arity, a list of pairs
(1, 'update'): LP_ARG,
(1, 'forceupdate'): LP_ARG,
# 2-arity
(2, 'pop'): TWO_IM_ARGS,
(2, 'setdefault'): TWO_IM_ARGS,
(2, '__setitem__'): TWO_IM_ARGS,
(2, 'put'): TWO_IM_ARGS,
(2, 'forceput'): TWO_IM_ARGS,
(2, 'move_to_end'): st.tuples(IMMUTABLES, BOOLEANS),
# non-mutating
# 0-arity
(0, '__copy__'): NO_ARGS,
(0, '__iter__'): NO_ARGS,
(0, '__len__'): NO_ARGS,
(0, 'copy'): NO_ARGS,
(0, 'keys'): NO_ARGS,
(0, 'items'): NO_ARGS,
(0, 'values'): NO_ARGS,
(0, 'iterkeys'): NO_ARGS,
(0, 'iteritems'): NO_ARGS,
(0, 'itervalues'): NO_ARGS,
(0, 'viewkeys'): NO_ARGS,
(0, 'viewitems'): NO_ARGS,
(0, 'viewvalues'): NO_ARGS,
# 1-arity
(1, '__contains__'): IM_ARG,
(1, '__getitem__'): IM_ARG,
(1, 'get'): IM_ARG,
})