You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

226 lines
7.2 KiB

# pylint: disable=redefined-outer-name, attribute-defined-outside-init
import pytest
import preserve
@pytest.fixture
def primitive_data():
return {'key1': ["item1", {"d2k1": "val1", "d2k2": 2},
{"d3k1": "val1", "d3k2": True}, "test str"]}
def obj_attrs_and_type_equal(obj1, obj2):
# Checks that both the __dict__ on each object and the __class__ on each object
# are equal
return (vars(type(obj1)) == vars(type(obj2))) and (vars(obj1) == vars(obj2))
def test_primitive_data(primitive_data):
# preserve/restore shouldn't modify something that's already all primitives
preserved_data = preserve.preserve(primitive_data)
assert preserved_data == primitive_data
assert (preserve.restore(preserved_data)) == primitive_data
class PlainClass:
def __init__(self):
self.attr_int = 734
self.attr_string = "I'm a test string"
self.attr_float = 42.085
self.attr_dict = {"key1": 1, "key2": "val2"}
self.attr_list = ["item1", 2, 3, 4]
self.attr_bool = True
@preserve.preservable
class PreservableClass(PlainClass):
pass
def test_data_with_preservable(primitive_data):
# Preservable class within a data structure
primitive_data["preservable"] = PreservableClass()
restored_data = preserve.restore(preserve.preserve(primitive_data))
assert restored_data['key1'] == primitive_data['key1']
assert restored_data["preservable"] != primitive_data["preservable"]
assert obj_attrs_and_type_equal(restored_data["preservable"], primitive_data["preservable"])
@preserve.preservable(exclude_attrs=['attr_int'])
class PreservableClass_Args1(PlainClass):
pass
@preserve.preservable(include_attrs=['attr_float'], name='other_name')
class PreservableClass_Args2(PlainClass):
pass
def test_preservable_args():
restored = preserve.restore(preserve.preserve(PreservableClass_Args1()))
assert not hasattr(restored, 'attr_int')
preserved = preserve.preserve(PreservableClass_Args2())
restored = preserve.restore(preserved)
assert preserved[preserve.escape_key] == 'other_name'
assert len(vars(restored)) == 1
assert restored.attr_float == 42.085
@preserve.preservable
class PreservableClass_AttrArgs(PlainClass):
_preserve_include_attrs = ['attr_string']
pass
@preserve.preservable
class PreservableClass_AttrArgs2(PreservableClass_AttrArgs):
pass
def test_preservable_class_attr_args():
restored = preserve.restore(preserve.preserve(PreservableClass_AttrArgs()))
assert len(vars(restored)) == 1
assert restored.attr_string == "I'm a test string"
# The class attribute args should propogate to the subclass
restored = preserve.restore(preserve.preserve(PreservableClass_AttrArgs2()))
assert len(vars(restored)) == 1
assert restored.attr_string == "I'm a test string"
assert type(restored) == PreservableClass_AttrArgs2
@preserve.preservable
class PreservableClass_RestoreInit(PlainClass):
def __restore_init__(self):
self.attr_int2 = 987
def test_restore_init():
restored = preserve.restore(preserve.preserve(PreservableClass_RestoreInit()))
assert restored.attr_int2 == 987
@preserve.preservable
class PreservableClass_PickleProtocol(PlainClass):
def __setstate__(self, state):
self.only_attr = state
def __getstate__(self):
return "abcd"
def test_pickle_protocol():
restored = preserve.restore(preserve.preserve(PreservableClass_PickleProtocol()))
assert len(vars(restored)) == 1
assert restored.only_attr == "abcd"
def test_non_dict_preservable_state():
preserved = preserve.preserve(PreservableClass_PickleProtocol())
assert preserved[preserve.escaped_state_key] == "abcd"
def test_common_preservable_tuple(primitive_data):
primitive_data["tuple"] = (1, 2, 3)
with pytest.raises(Exception, match="is not preservable"):
preserve.restore(preserve.preserve(primitive_data))
preserve.register(preserve.common.tuple)
restored = preserve.restore(preserve.preserve(primitive_data))
assert restored["tuple"] == (1, 2, 3)
def test_subclass_of_preservable():
class PreservableClassSubclass(PreservableClass):
pass
# A subclass _shouldn't_ be preservable by default, as if it was restore() would
# return an instance of the parent class.
with pytest.raises(Exception, match="is not preservable"):
preserve.restore(preserve.preserve(PreservableClassSubclass()))
def test_subclass_of_raw_preservable():
class RawPreservableSubclass(dict):
pass
# A subclass _shouldn't_ be preservable by default, as if it was restore() would
# return an instance of the parent class.
with pytest.raises(Exception, match="is not preservable"):
preserve.restore(preserve.preserve(RawPreservableSubclass()))
def test_plain_class():
# plain class is not preservable
with pytest.raises(Exception, match="is not preservable"):
preserve.preserve(PlainClass())
def test_preservable_class():
# Preservable class should restore to a new instance with same content
obj = PreservableClass()
restored_obj = preserve.restore(preserve.preserve(obj))
assert restored_obj != obj
assert obj_attrs_and_type_equal(restored_obj, obj)
def test_attr_plain_class():
# Class with non-preservable class as an attribute should fail
obj = PreservableClass()
obj.attr_obj = PlainClass()
with pytest.raises(Exception, match="is not preservable"):
preserve.preserve(obj)
def test_escape_key():
# Should be able to change the default class key before decorators
# and have preserve/restore work
old_escape_key = preserve.escape_key
preserve.class_key = "A different key"
@preserve.preservable
class TestClass(PlainClass):
pass
obj = TestClass()
restored_obj = preserve.restore(preserve.preserve(obj))
assert restored_obj != obj
assert obj_attrs_and_type_equal(restored_obj, obj)
preserve.escape_key = old_escape_key
def test_escape_key_in_data():
# Can't use the class key as dict key being preserved
with pytest.raises(Exception, match="reserved as an internal escape key"):
preserve.preserve({preserve.escape_key: 1})
with pytest.raises(Exception, match="reserved as an internal escape key"):
preserve.preserve({preserve.escaped_state_key: 1})
def test_unrestorable(primitive_data):
# Shouldn't be able to restore from data containing non-primitive types
preserved_data = preserve.preserve(primitive_data)
preserved_data["unrestorable"] = (1, 2, 3)
with pytest.raises(Exception, match="is not restorable"):
preserve.restore(preserved_data)
preserved_data["unrestorable"] = PlainClass()
with pytest.raises(Exception, match="is not restorable"):
preserve.restore(preserved_data)
def test_str_dict_keys(primitive_data):
# Non-string dict keys aren't restorable or preservable
preserved_data = preserve.preserve(primitive_data)
primitive_data[2] = "has a non-string key"
with pytest.raises(Exception, match="Non-string dictionary keys are not preservable"):
preserve.preserve(primitive_data)
preserved_data[2] = "has a non-string key"
with pytest.raises(Exception, match="Non-string dictionary keys are not restorable"):
preserve.restore(preserved_data)