Beispiel #1
0
def test_purge(historian: mincepy.Historian):
    car = Car()
    garage = testing.Garage(mincepy.ObjRef(car))
    car_id, _ = historian.save(car, garage)

    # Now update the car
    car.colour = 'blue'
    car.save()

    records_count = historian.records.find().count()

    # This should not perge anything as v0 of the car has a reference from the garage, while
    # v1 is the current, live, version
    res = historian.purge(dry_run=False)
    assert not res.deleted_purged
    assert not res.unreferenced_purged
    assert records_count == historian.records.find().count()

    # Now mutate the car, save and purge again
    car.colour = 'white'
    car.save()

    # This time, version 1 is unreferenced by anything and so can be safely deleted
    res = historian.purge(dry_run=False)
    assert not res.deleted_purged
    assert res.unreferenced_purged == {mincepy.SnapshotId(car_id, 1)}
    assert records_count == historian.records.find().count()
Beispiel #2
0
def test_storing_internal_object(historian: mincepy.Historian):

    class Person(mincepy.SavableObject):
        TYPE_ID = uuid.UUID('f6f83595-6375-4bc4-89f2-d8f31a1286b0')

        def __init__(self, car):
            super().__init__()
            self.car = car  # This person 'owns' the car

        def __eq__(self, other):
            return self.car == other.car

        def yield_hashables(self, hasher):
            yield from hasher.yield_hashables(self.car)

        def save_instance_state(self, _depositor):
            return {'car': self.car}

        def load_instance_state(self, saved_state, _depositor):
            self.car = saved_state['car']

    ferrari = Car('ferrari')
    mike = Person(ferrari)

    mike_id = historian.save(mike)
    del mike

    loaded_mike = historian.load(mike_id)
    assert loaded_mike.car.make == 'ferrari'

    # Default is to save by value so two cars should not be the same
    assert loaded_mike.car is not ferrari
    assert loaded_mike.car == ferrari  # But values should match
Beispiel #3
0
def test_ref_dict(historian: mincepy.Historian):
    """Test that the ref dict correctly stores entries as references"""
    dict1 = mincepy.builtins.RefDict()
    dict2 = mincepy.builtins.RefDict()
    car = Car()
    dict1['car'] = car
    dict2['car'] = car

    # Test iteration
    for key, value in dict1.items():
        assert key == 'car'
        assert value is car

    # Reference condition satisfied
    assert dict1['car'] is dict2['car']

    # Now delete everything, reload, and make sure the condition is still satisfied
    dict1_id, dict2_id = historian.save(dict1, dict2)
    del dict1, dict2, car

    dict1_loaded = historian.load(dict1_id)
    dict2_loaded = historian.load(dict2_id)
    assert len(dict1_loaded) == 1
    assert len(dict2_loaded) == 1
    assert isinstance(dict1_loaded['car'], Car)
    assert dict1_loaded['car'] is dict2_loaded['car']
Beispiel #4
0
def test_merge_file(historian: mincepy.Historian):
    """Test that merging files works correctly"""
    file = historian.create_file('test.dat')
    file.write_text('bla bla')
    file.save()
    with testing.temporary_historian(
            testing.create_archive_uri(db_name='test_historian')) as remote:
        local = historian
        # Merge the file into the remote
        result = remote.merge(local.find(obj_id=file.obj_id))
        assert len(result.all) == len(result.merged) == local.find().count()
        assert local.find().count() == remote.find().count()

        remote_file = remote.get(file.obj_id)
        assert file.read_text() == remote_file.read_text()

        # Now check that files contained within objects are correctly merged
        file_list = mincepy.List((file, ))
        file_list.save()  # pylint: disable=no-member
        result = remote.merge(local.find(obj_id=file_list.obj_id))  # pylint: disable=no-member
        assert len(result.merged) == 1
        assert historian.get_snapshot_id(file_list) in result.merged

        remote_file_list = remote.get(file_list.obj_id)  # pylint: disable=no-member
        assert file.read_text() == remote_file_list[0].read_text()
Beispiel #5
0
def test_lazy_migrating_with_saved(historian: mincepy.Historian):
    """Test migrating an object that has saved references"""
    class V3(mincepy.ConvenientSavable):
        TYPE_ID = uuid.UUID('40377bfc-901c-48bb-a85c-1dd692cddcae')
        ref = mincepy.field(ref=True)
        description = mincepy.field()

        class Migration(mincepy.ObjectMigration):
            VERSION = 2
            PREVIOUS = StoreByRef.ToRefMigration

            @classmethod
            def upgrade(cls, saved_state, loader: 'mincepy.Loader'):
                saved_state['description'] = None

        LATEST_MIGRATION = Migration

        def __init__(self, ref):
            super().__init__()
            self.ref = ref
            self.description = None

    obj = StoreByRef(testing.Car())
    obj_id = obj.save()
    del obj
    gc.collect()

    historian.register_type(V3)
    obj = historian.load(obj_id)

    assert isinstance(obj, V3)
    assert hasattr(obj, 'description')
    assert obj.description is None
Beispiel #6
0
def test_concurrent_modification(historian: mincepy.Historian,
                                 archive_uri: str):
    # Create a second historian connected to the same archive
    historian2 = mincepy.connect(archive_uri, use_globally=False)
    historian2.register_type(Car)

    ferrari = testing.Car(colour='red', make='ferrari')
    ferrari_id = historian.save(ferrari)
    ferrari2 = historian2.load(ferrari_id)

    assert ferrari_id == ferrari2.obj_id
    assert ferrari is not ferrari2, \
        "The archive don't know about each other so the objects instances should not be the same"

    # Repaint
    ferrari.colour = 'yellow'
    historian.save(ferrari)

    # Now change ferrari2 and see what happens
    ferrari2.colour = 'green'
    with pytest.raises(mincepy.ModificationError):
        historian2.save(ferrari2)

    # Now, let's sync up
    assert historian2.sync(ferrari2), "ferrari2 hasn't been updated"
    assert ferrari2.colour == 'yellow'
Beispiel #7
0
def test_find_migratable(historian: mincepy.Historian):
    car = CarV1('white', 'lada')
    car_id = car.save()
    by_val = StoreByValue(car)
    by_val_id = by_val.save()

    # Register a new version of the car
    historian.register_type(CarV2)

    # Now both car and by_val should need migration (because by_val stores a car)
    migratable = tuple(historian.migrations.find_migratable_records())
    assert len(migratable) == 2
    ids = [record.obj_id for record in migratable]
    assert car_id in ids
    assert by_val_id in ids

    # Now register a new version of StoreByVal
    historian.register_type(StoreByRef)

    # There should still be the same to migratables as before
    migratable = tuple(historian.migrations.find_migratable_records())
    assert len(migratable) == 2
    ids = [record.obj_id for record in migratable]
    assert car_id in ids
    assert by_val_id in ids
Beispiel #8
0
def test_encode_nested(historian: mincepy.Historian):

    class CarDelegate(mincepy.SimpleSavable):
        TYPE_ID = uuid.UUID('c0148a43-c0c0-4d2b-9262-ed1c8c6ab2fc')
        ATTRS = ('car',)

        def __init__(self, car):
            super().__init__()
            self.car = car

        def save_instance_state(self, saver):
            return {'car': self.car}

        def load_instance_state(self, saved_state, _loader):
            self.car = saved_state['car']

    historian.register_type(CarDelegate)

    car = Car()
    delegate = CarDelegate(car)
    garage = Garage(delegate)

    garage_id = historian.save(garage)
    del garage

    historian.load(garage_id)
Beispiel #9
0
def test_load_changed_ref(historian: mincepy.Historian, archive_uri):
    """Test what happens when you dereference a reference to an object that was mutated since the
    reference was loaded"""
    historian2 = mincepy.connect(archive_uri)

    car = testing.Car(make='skoda')
    car_id = historian.save(car)
    car_ref = mincepy.ref(car)
    ref_id = historian.save(car_ref)
    ref_sid = historian.get_snapshot_id(car_ref)
    del car, car_ref

    # Now, load the reference but don't dereference it yet
    loaded = historian.load(ref_id)

    # Now, mutate the car that is being referenced
    loaded_car = historian2.load(car_id)
    loaded_car.make = 'honda'
    historian2.save(loaded_car)

    # Finally, dereference and check that it is as expected
    assert loaded().make == 'honda'

    # Now, check the snapshot still points to the original
    loaded_snapshot = historian.load_snapshot(ref_sid)
    assert loaded_snapshot().make == 'skoda'
Beispiel #10
0
def test_nested_files_in_list_mutating(tmp_path, historian: mincepy.Historian):  # pylint: disable=unused-argument
    encoding = 'utf-8'
    INITIAL_DATA = 'First string'.encode(encoding)
    my_file = historian.create_file()
    with my_file.open('wb') as file:
        file.write(INITIAL_DATA)

    my_list = mincepy.builtins.List()
    my_list.append(my_file)

    list_id = historian.save(my_list)

    # Now let's append to the file
    NEW_DATA = 'Second string'.encode(encoding)
    with my_file.open('ab') as file:
        file.write(NEW_DATA)

    # Save the list again
    historian.save(my_list)
    del my_list

    loaded = historian.load(list_id)
    with loaded[0].open('rb') as contents:
        buffer = io.BytesIO()
        shutil.copyfileobj(contents, buffer)
        assert buffer.getvalue() == INITIAL_DATA + NEW_DATA
Beispiel #11
0
def test_ref_list(historian: mincepy.Historian):
    """Test references list"""
    list1 = mincepy.RefList()
    list2 = mincepy.RefList()
    car = Car()
    list1.append(car)
    list2.append(car)

    # Test iteration
    for entry in list1:
        assert entry is car

    # Reference condition satisfied
    assert list1[0] is list2[0]

    # Now delete everything, reload, and make sure the condition is still satisfied
    list1_id, list2_id = historian.save(list1, list2)
    assert car.is_saved(
    ), "The container should automatically save all it's entries saved"
    del list1, list2, car

    list1_loaded = historian.load(list1_id)
    list2_loaded = historian.load(list2_id)
    assert len(list1_loaded) == 1
    assert len(list2_loaded) == 1
    assert isinstance(list1_loaded[0], Car)
    assert list1_loaded[0] is list2_loaded[0]
Beispiel #12
0
def test_meta_on_delete(historian: mincepy.Historian):
    """Test that metadata gets deleted when the object does"""
    car = Car()
    car_id = car.save(meta={'reg': '1234'})

    assert historian.meta.get(car_id) == {'reg': '1234'}
    historian.delete(car_id)
    assert historian.meta.get(car_id) is None
Beispiel #13
0
def test_create_delete_load(historian: mincepy.Historian):
    car = Car('honda', 'red')
    car_id = historian.save(car)
    del car

    loaded_car = historian.load(car_id)
    assert loaded_car.make == 'honda'
    assert loaded_car.colour == 'red'
Beispiel #14
0
def test_metadata_find_objects(historian: mincepy.Historian):
    honda = Car('honda', 'white')
    honda2 = Car('honda', 'white')
    historian.save_one(honda, meta={'reg': 'H123', 'vin': 1234})
    historian.save(honda2)

    results = list(historian.find(Car, meta={'reg': 'H123'}))
    assert len(results) == 1
Beispiel #15
0
def test_metadata_multiple(historian: mincepy.Historian):
    honda = Car('honda', 'white')
    zonda = Car('zonda', 'yellow')

    historian.save((honda, {'reg': 'H123'}), (zonda, {'reg': 'Z456'}))

    assert historian.meta.get(honda) == {'reg': 'H123'}
    assert historian.meta.get(zonda) == {'reg': 'Z456'}
Beispiel #16
0
def test_obj_ref_simple(historian: mincepy.Historian):
    a = testing.Cycle()
    a.ref = mincepy.ObjRef(a)
    aid = historian.save(a)
    del a

    loaded = historian.load(aid)
    assert loaded.ref() is loaded
Beispiel #17
0
def test_delete_multiple_versions(historian: mincepy.Historian):
    car = testing.Car('skoda', 'green')
    car.save()
    car.colour = 'red'
    car.save()

    with historian.transaction():
        historian.delete(car)
Beispiel #18
0
def test_find_many_cars(historian: mincepy.Historian, benchmark, num):
    """Test finding a car as a function of the number of entries in the database"""
    # Put in the correct number of random other entries
    for _ in range(num):
        historian.save(Car(utils.random_str(10), utils.random_str(5)))

    result = benchmark(find, historian)
    assert len(result) == num
Beispiel #19
0
def test_get_snapshot_graph_simple(historian: mincepy.Historian):
    car = Car()
    garage = Garage(mincepy.ObjRef(car))
    garage.save()
    garage_sid = historian.get_snapshot_id(garage)

    garage_graph = historian.archive.get_snapshot_ref_graph(garage_sid)
    assert len(garage_graph.edges) == 1
    assert (garage_sid, historian.get_snapshot_id(car)) in garage_graph.edges
Beispiel #20
0
def test_basic_save_load(historian: mincepy.Historian):
    car = Car('nissan', 'white')

    car_id = historian.save(car)
    del car
    loaded_car = historian.load(car_id)

    assert loaded_car.make == 'nissan'
    assert loaded_car.colour == 'white'
Beispiel #21
0
def test_cyclic_ref_simple(historian: mincepy.Historian):
    a = Cycle()
    a.ref = a  # Cycle complete
    a_id = historian.save(a)
    del a
    gc.collect()  # Force a garbage collection

    loaded_a = historian.load(a_id)
    assert loaded_a.ref is loaded_a
Beispiel #22
0
def test_get_snapshot_self_cycle(historian: mincepy.Historian):
    node = Cycle()
    node.ref = node  # Cycle complete
    historian.save_one(node)

    node_ref = historian.get_snapshot_id(node)
    node_graph = historian.archive.get_snapshot_ref_graph(node_ref)
    assert len(node_graph.edges) == 1
    assert (node_ref, node_ref) in node_graph.edges
Beispiel #23
0
def test_loading_snapshot_cycle(historian: mincepy.Historian):
    a = Cycle()
    a.ref = a  # Close the cycle
    historian.save(a)
    a_sid = historian.get_snapshot_id(a)
    del a

    loaded = historian.load_snapshot(a_sid)
    assert loaded.ref is loaded
Beispiel #24
0
def test_saving_numpy_arrays(historian: mincepy.Historian):
    array = numpy.ones(10)

    array_id = historian.save(array)
    del array
    loaded_array = historian.load(array_id)
    assert all(loaded_array == numpy.ones(10))

    loaded_array[0] = 5.
    historian.save(loaded_array)
Beispiel #25
0
def test_delete_from_obj_id(historian: mincepy.Historian):
    """Test deleting an object using it's object id"""
    car = testing.Car('skoda')
    car_id = car.save()
    del car

    historian.delete(car_id)

    with pytest.raises(mincepy.NotFound):
        historian.load(car_id)
Beispiel #26
0
def test_null_ref(historian: mincepy.Historian):
    null = mincepy.ObjRef()
    null2 = mincepy.ObjRef()

    assert null == null2

    nid1, _nid2 = historian.save(null, null2)
    del null
    loaded = historian.load(nid1)
    assert loaded == null2
Beispiel #27
0
def test_copy_unsaved(historian: mincepy.Historian):
    car = Car('porsche', 'silver')
    car_copy = mincepy.copy(car)

    assert car_copy is not car
    assert car == car_copy

    # The cars should not be saved
    assert not historian.is_known(car)
    assert not historian.is_known(car_copy)
Beispiel #28
0
def test_simple_migration(historian: mincepy.Historian):
    car = CarV0('red', 'ferrari')
    car_id = historian.save(car)
    del car

    # Now change to version 2
    historian.register_type(CarV1)
    loaded_car = historian.load(car_id)
    assert loaded_car.colour == 'red'
    assert loaded_car.make == 'ferrari'
Beispiel #29
0
def test_tuple_helper(historian: mincepy.Historian):
    historian.register_type(mincepy.common_helpers.TupleHelper())

    container = mincepy.builtins.List()
    container.append((Car('ferrari'), ))

    container_id = historian.save(container)
    del container
    loaded = historian.load(container_id)
    assert loaded[0][0] == Car('ferrari')
Beispiel #30
0
def test_find_arg_types(historian: mincepy.Historian):
    """Test the argument types accepted by the historian find() method"""
    red_ferrari = testing.Car(colour='red', make='ferrari')
    green_ferrari = testing.Car(colour='green', make='ferrari')
    red_honda = testing.Car(colour='red', make='honda')
    martin = testing.Person(name='martin', age=35, car=red_honda)

    red_ferrari_id, green_ferrari_id, red_honda_id = \
        historian.save(red_ferrari, green_ferrari, red_honda)
    martin_id = martin.save()

    # Test different possibilities for object ids being passed
    list(historian.find(obj_id=red_ferrari_id))
    list(
        historian.find(
            obj_id=[red_ferrari_id, green_ferrari_id, martin_id, red_honda_id
                    ]))
    list(
        historian.find(obj_id=(red_ferrari_id, green_ferrari_id, martin_id,
                               red_honda_id)))
    list(historian.find(obj_id=str(red_ferrari_id)))

    # Test object types
    list(historian.find(obj_type=testing.Person))
    list(historian.find(obj_type=[testing.Person, testing.Car]))
    list(historian.find(obj_type=(testing.Person, testing.Car)))
    list(historian.find(obj_type=testing.Person.TYPE_ID))
    list(
        historian.find(obj_type=[testing.Person.TYPE_ID, testing.Car.TYPE_ID]))