2018-12-29 18:13:04 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
2019-01-01 18:51:32 +00:00
|
|
|
# Copyright 2009-2019 Joshua Bronson. All Rights Reserved.
|
2018-12-29 18:13:04 +00:00
|
|
|
#
|
|
|
|
# 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,
|
|
|
|
})
|