예제 #1
0
def test_disable_allow_auto():
    db = SqliteDb(":memory:")
    with patch.object(Cars, "allow_auto", False):
        mgr = MyOmen(db)
        mgr.cars = Cars(mgr)
        with pytest.raises(OmenNoPkError):
            mgr.cars.add(Car(gas_level=0, color="green"))
예제 #2
0
def test_thread_locked_writer_only():
    db = SqliteDb(":memory:")
    mgr = MyOmen(db)
    mgr.cars = Cars(mgr)
    car = mgr.cars.add(Car(gas_level=0, color=str(0)))
    num_t = 15
    num_w = 3
    pool = ThreadPool(num_t)

    # to reproduce the problem with this, you need to catch python while switching contexts
    # in between setattr calls in a non-atomic "apply" function
    # this is basically impossible without sticking a time sleep in there
    # even with 100 attributes and 5000 threads it never failed
    # so this test case only tests if the atomic apply is totally/deeply broken

    def update_stuff(i):
        if i < num_w:
            with car:
                car.gas_level += 1
                car.color = str(car.gas_level)
                time.sleep(0.1)
        else:
            with pytest.raises(OmenUseWithError):
                car.gas_level += 1

    # lots of threads can update stuff
    pool.map(update_stuff, range(num_t))
    assert car.gas_level == num_w
예제 #3
0
def test_threaded_reads():
    db = SqliteDb(":memory:")
    mgr = MyOmen(db)
    mgr.cars = Cars(mgr)
    car = mgr.cars.add(Car(gas_level=0, color=str(0)))
    pool = ThreadPool(50)

    # to reproduce the problem with this, you need to catch python while switching contexts
    # in between setattr calls in a non-atomic "apply" function
    # this is basically impossible without sticking a time sleep in there
    # even with 100 attributes and 5000 threads it never failed
    # so this test case only tests if the atomic apply is totally/deeply broken

    def update_stuff(_i):
        time.sleep(0.00001)
        assert car.color == str(car.gas_level)
        with car:
            car.gas_level += 1
            time.sleep(0.00001)
            car.color = str(car.gas_level)
        assert car.color == str(car.gas_level)
        time.sleep(0.00001)
        assert car.color == str(car.gas_level)

    # lots of threads can update stuff
    num_t = 10
    pool.map(update_stuff, range(num_t))
    assert car.gas_level == num_t
예제 #4
0
def test_type_checking():
    db = SqliteDb(":memory:")
    mgr = MyOmen(db)
    Car._type_check = True

    mgr.cars = Cars(mgr)
    car = mgr.cars.add(Car(gas_level=0, color="green"))

    with pytest.raises(TypeError):
        with car:
            car.color = None

    with pytest.raises(TypeError):
        with car:
            car.color = 4

    with pytest.raises(TypeError):
        with car:
            car.gas_level = "hello"

    with pytest.raises(TypeError):
        with car:
            car.color = b"ggh"

    car._type_check = False
    # notanorm integrity error because color is a required field
    with pytest.raises(notanorm.errors.IntegrityError):
        with car:
            car.color = None

    # sqlite allows this, so we do too, since type checking is off
    with car:
        car.color = b"ggh"
예제 #5
0
def test_readme(tmp_path):
    fname = str(tmp_path / "test.txt")
    db = SqliteDb(fname)

    mgr = MyOmen(db)

    # cars pk is autoincrement
    assert Cars.allow_auto
    mgr.cars = Cars(mgr)

    # by default, you can always iterate on tables
    assert mgr.cars.count() == 0

    car = Car()  # creates a default car (black, full tank)
    car.color = "red"
    car.gas_level = 0.5
    car.doors.add(gen_objs.doors_row(type="a"))
    car.doors.add(gen_objs.doors_row(type="b"))
    car.doors.add(gen_objs.doors_row(type="c"))
    car.doors.add(gen_objs.doors_row(type="d"))

    assert not car.id

    mgr.cars.add(car)

    # cars have ids, generated by the db
    assert car.id

    with pytest.raises(AttributeError, match=r".*gas.*"):
        mgr.cars.add(Car(color="red", gas=0.3))

    mgr.cars.add(
        Car(
            color="red",
            gas_level=0.3,
            doors=[gen_objs.doors_row(type=str(i)) for i in range(4)],
        ))

    assert sum(1 for _ in mgr.cars.select(color="red")) == 2  # 2

    log.info("cars: %s", list(mgr.cars.select(color="red")))

    car = mgr.cars.select_one(color="red", gas_level=0.3)

    assert not car._is_new

    with car:
        with pytest.raises(AttributeError, match=r".*gas.*"):
            car.gas = 0.9
        car.gas_level = 0.9
        types = set()
        for door in car.doors:
            types.add(int(door.type))
        assert types == set(range(4))

    # doors are inserted
    assert len(car.doors) == 4
예제 #6
0
def test_sync_on_getattr():
    db = SqliteDb(":memory:")
    mgr = MyOmen(db)
    mgr.cars = Cars(mgr)
    car = Car(id=44, gas_level=0, color="green")
    mgr.cars.add(car)
    car._sync_on_getattr = True
    assert car.color == "green"
    db.update("cars", id=car.id, color="blue")
    assert car.color == "blue"
예제 #7
0
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
예제 #8
0
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")
예제 #9
0
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"
예제 #10
0
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
예제 #11
0
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)
예제 #12
0
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
예제 #13
0
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)
예제 #14
0
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