def test_iter_and_sort(): db = SqliteDb(":memory:") mgr = MyOmen(db, cars=Cars) mgr.cars = mgr[Cars] mgr.cars.add(Car(gas_level=2, color="green")) car = mgr.cars.add(Car(gas_level=3, color="green")) with car: car.doors.add(gen_objs.doors_row(type="z")) car.doors.add(gen_objs.doors_row(type="x")) # tables, caches and relations are all sortable, and iterable for door in car.doors: assert door sorted(car.doors) sorted(mgr.cars) cars = ObjCache(mgr.cars) mgr.cars = cars sorted(mgr.cars)
def test_cache(): db = SqliteDb(":memory:") mgr = MyOmen(db, cars=Cars) mgr.cars = Cars(mgr) orig = mgr.cars cars = ObjCache(mgr.cars) # replace the table with a cache mgr.cars = cars mgr.cars.add(Car(gas_level=2, color="green")) mgr.cars.add(Car(gas_level=3, color="green")) assert cars.select_one(id=1) assert cars.count() == 2 assert orig._cache mgr.db.insert("cars", id=99, gas_level=99, color="green") mgr.db.insert("cars", id=98, gas_level=98, color="green") # this won't hit the db # noinspection PyUnresolvedReferences assert not any(car.id == 99 for car in mgr.cars.select()) # this won't hit the db assert not cars.select_one(id=99) # this will assert cars.table.select(id=99) # now the cache is full assert cars.select(id=99) # but still missing others assert not cars.select_one(id=98) assert cars.reload() == 4 log.debug(orig._cache) # until now assert cars.select_one(id=98)
def test_nested_with(): db = SqliteDb(":memory:") mgr = MyOmen(db, cars=Cars) mgr.cars = mgr[Cars] car = mgr.cars.add(Car(id=4, gas_level=2)) with car: car.gas_level = 3 with pytest.raises(OmenLockingError): with car: car.color = "blx" assert db.select_one("cars", id=4).gas_level == 3
def test_underlying_delete(): db = SqliteDb(":memory:") mgr = MyOmen(db) mgr.cars = Cars(mgr) car = Car(id=44, gas_level=0, color="green") mgr.cars.add(car) assert db.select_one("cars", id=car.id) db.delete("cars", id=car.id) car2 = Car(id=44, gas_level=0, color="green") mgr.cars.add(car2) assert car2 is not car assert mgr.cars.get(id=44) is car2
def test_unbound_add(): db = SqliteDb(":memory:") mgr = MyOmen(db) mgr.cars = Cars(mgr) car = Car(id=44, gas_level=0, color="green") door = gen_objs.doors_row(type="a") car.doors.add(door) assert door.carid == car.id assert door.carid assert door in car.doors assert car.doors.select_one(type="a") mgr.cars.add(car) assert db.select_one("doors", carid=car.id, type="a")
def test_reload_from_disk(): db = SqliteDb(":memory:") mgr = MyOmen(db) mgr.cars = Cars(mgr) car = mgr.cars.add(Car(gas_level=0, color="green")) assert db.select_one("cars", id=1).color == "green" db.update("cars", id=1, color="blue") # doesn't notice db change because we have a weakref-cache assert car.color == "green" car2 = mgr.cars.select_one(id=1) # weakref cache assert car2 is car # db fills to cache on select assert car.color == "blue"
def test_update_only(): db = SqliteDb(":memory:") mgr = MyOmen(db, cars=Cars) mgr.cars = mgr[Cars] car = mgr.cars.add(Car(id=4, gas_level=2)) with car: car.gas_level = 3 # hack in the color.... car.__dict__["color"] = "blue" assert "gas_level" in car._changes assert "color" not in car._changes # color doesn't change in db, because we only update "normally-changed" attributes assert db.select_one("cars", id=4).gas_level == 3 assert db.select_one("cars", id=4).color == "black"
def test_remove_driver(): db = SqliteDb(":memory:") mgr = MyOmen(db, cars=Cars) mgr.cars = mgr[Cars] mgr.car_drivers = CarDrivers(mgr) Driver = gen_objs.drivers mgr.drivers = mgr[Driver] car1 = mgr.cars.add(Car(id=1, gas_level=2, color="green")) car2 = mgr.cars.add(Car(id=2, gas_level=2, color="green")) driver1 = mgr.drivers.new(name="bob") driver2 = mgr.drivers.new(name="joe") car1.car_drivers.add(CarDriver(carid=car1.id, driverid=driver1.id)) car2.car_drivers.add(CarDriver(carid=car2.id, driverid=driver1.id)) assert len(car1.car_drivers) == 1 assert len(car2.car_drivers) == 1 for cd in car1.car_drivers: car1.car_drivers.remove(cd) assert len(car1.car_drivers) == 0 assert len(car2.car_drivers) == 1 assert len(list(mgr.db.select("drivers"))) == 2 assert len(list(mgr.db.select("car_drivers"))) == 1 car1.car_drivers.add(CarDriver(carid=car1.id, driverid=driver1.id)) assert len(car1.car_drivers) == 1 mgr.car_drivers.remove(carid=car1.id, driverid=driver1.id) # ok to double-remove mgr.car_drivers.remove(carid=car1.id, driverid=driver1.id) # removing None is a no-op (ie: remove.(select_one(criteria....))) mgr.car_drivers.remove(None) assert len(car1.car_drivers) == 0 assert len(car2.car_drivers) == 1 car2.car_drivers.add(CarDriver(carid=car2.id, driverid=driver2.id)) assert len(list(mgr.db.select("car_drivers"))) == 2 with pytest.raises(OmenMoreThanOneError): # kwargs remove is not a generic method for removing all matching things # make your own loop if that's what you want mgr.car_drivers.remove(carid=car2.id)
def test_race_sync(tmp_path): fname = str(tmp_path / "test.txt") db = SqliteDb(fname) mgr = MyOmen(db, cars=Cars) mgr.cars = mgr[Cars] ids = [] def insert(i): h = mgr.cars.row_type(gas_level=i) mgr.cars.add(h) ids.append(h.id) num = 10 pool = ThreadPool(10) pool.map(insert, range(num)) assert mgr.cars.count() == num
def test_rollback(): db = SqliteDb(":memory:") mgr = MyOmen(db, cars=Cars) mgr.cars = mgr[Cars] car = mgr.cars.add(Car(gas_level=2)) with suppress(ValueError): with car: car.gas_level = 3 raise ValueError assert car.gas_level == 2 with car: car.gas_level = 3 raise OmenRollbackError assert car.gas_level == 2
def test_threaded(): db = SqliteDb(":memory:") mgr = MyOmen(db) mgr.cars = Cars(mgr) car = mgr.cars.add(Car(gas_level=0)) pool = ThreadPool(10) def update_stuff(_i): with car: car.gas_level += 1 # lots of threads can update stuff num_t = 10 pool.map(update_stuff, range(num_t)) assert car.gas_level == num_t # written to db assert mgr.db.select_one("cars", id=car.id).gas_level == num_t
def test_weak_cache(caplog): # important to disable log capturing/reporting otherwise refs to car() could be out there! caplog.clear() caplog.set_level("ERROR") db = SqliteDb(":memory:") mgr = MyOmen(db) mgr.cars = Cars(mgr) car = mgr.cars.add(Car(gas_level=0)) # cache works car2 = mgr.cars.select_one(gas_level=0) assert id(car2) == id(car) assert len(mgr.cars._cache.data) == 1 # weak dict cleans up del car del car2 gc.collect() assert not mgr.cars._cache.data
def test_cache_sharing(): db = SqliteDb(":memory:") mgr = MyOmen(db) mgr.cars = Cars(mgr) cache = ObjCache(mgr.cars) car = Car(id=44, gas_level=0, color="green") cache.add(car) assert car in mgr.cars assert car in cache db.insert("cars", id=45, gas_level=45, color="blue") db.insert("cars", id=46, gas_level=46, color="red") assert cache.get(id=44) assert not cache.select_one(id=45) assert not cache.select_one(id=46) assert mgr.cars.select_one(id=45) assert cache.select_one(id=45) assert mgr.cars.select_one(id=46) assert cache.select_one(id=46) db.delete("cars", id=44) # still cached assert cache.get(id=44) assert cache.select_one(id=44) # still cached, select with a different filter does not clear cache assert mgr.cars.select_one(id=45) assert cache.select_one(id=44) # cache updated by select with matching filter assert not mgr.cars.select_one(id=44) assert not cache.select_one(id=44) # select() with no filter clears cache db.delete("cars", id=46) assert len(list(mgr.cars.select())) == 1 assert not cache.select_one(id=46) # reload() clears cache assert cache.get(id=45) db.delete("cars", id=45) cache.reload() assert not cache.select_one(id=45)
def test_cache_sharing_threaded(): db = SqliteDb(":memory:") mgr = MyOmen(db) mgr.cars = Cars(mgr) cache = ObjCache(mgr.cars) db.insert("cars", id=12, gas_level=0, color="green") assert mgr.cars.select_one(id=12) assert cache.select_one(id=12) # all threads update gas_level of cached car, only the first thread reloads the cache def update_stuff(_i): if _i == 0: cache.reload() # if the cache was cleared in in another thread, this returns None (behavior we want to avoid) c = cache.select_one(id=12) assert c with c: c.gas_level += 1 num_t = 10 pool = ThreadPool(num_t) pool.map(update_stuff, range(num_t)) assert cache.select_one(id=12).gas_level == num_t