def check_iter_locked(db_path, pre_stuff, iter_stuff): """Actual implementation of test_errors_locked, so it can be reused.""" # WAL provides more concurrency; some things won't to block with it enabled. storage = Storage(db_path, wal_enabled=False) feed = FeedData('one') entry = EntryData('one', 'entry', datetime(2010, 1, 1), title='entry') storage.add_feed(feed.url, datetime(2010, 1, 2)) storage.add_or_update_entry( EntryUpdateIntent(entry, entry.updated, datetime(2010, 1, 1), 0, 0)) storage.add_feed('two', datetime(2010, 1, 1)) storage.add_or_update_entry( EntryUpdateIntent(entry._replace(feed_url='two'), entry.updated, datetime(2010, 1, 1), 0, 0)) storage.set_feed_metadata('two', '1', 1) storage.set_feed_metadata('two', '2', 2) storage.add_feed_tag('two', '1') storage.add_feed_tag('two', '2') if pre_stuff: pre_stuff(storage) rv = iter_stuff(storage) next(rv) # shouldn't raise an exception storage = Storage(db_path, timeout=0, wal_enabled=False) storage.mark_as_read_unread(feed.url, entry.id, 1) storage = Storage(db_path, timeout=0) storage.mark_as_read_unread(feed.url, entry.id, 0)
def storage_with_two_entries(storage): storage.add_feed('feed', datetime(2010, 1, 1)) storage.add_or_update_entry( EntryUpdateIntent( EntryData('feed', 'one', datetime(2010, 1, 1)), datetime(2010, 1, 2), datetime(2010, 1, 2), 0, )) storage.add_or_update_entry( EntryUpdateIntent( EntryData('feed', 'two', datetime(2010, 1, 1)), datetime(2010, 1, 2), datetime(2010, 1, 2), 1, )) return storage
def test_important_entry_remains_important_after_update(storage): storage.mark_as_important_unimportant('feed', 'one', True) storage.add_or_update_entry( EntryUpdateIntent( EntryData('feed', 'one', datetime(2010, 1, 1)), datetime(2010, 1, 2), datetime(2010, 1, 2), 0, )) assert { e.id for e in storage.get_entries(datetime(2010, 1, 1), EntryFilterOptions(important=True)) } == {'one'}
def test_entry_remains_read_after_update(storage_with_two_entries): storage = storage_with_two_entries storage.mark_as_read_unread('feed', 'one', True) storage.add_or_update_entry( EntryUpdateIntent( EntryData('feed', 'one', datetime(2010, 1, 1)), datetime(2010, 1, 2), datetime(2010, 1, 2), 0, )) assert { e.id for e in storage.get_entries(datetime(2010, 1, 1), EntryFilterOptions(read=True)) } == {'one'}
def test_get_entries_for_update(storage_cls): storage = storage_cls(':memory:') storage.add_feed('feed', datetime(2010, 1, 1)) storage.add_or_update_entry( EntryUpdateIntent( EntryData('feed', 'one', datetime(2010, 1, 1)), datetime(2010, 1, 2), datetime(2010, 1, 1), 0, )) assert list( storage.get_entries_for_update([ ('feed', 'one'), ('feed', 'two') ])) == [ EntryForUpdate(datetime(2010, 1, 1)), None, ]
def check_errors_locked(db_path, pre_stuff, do_stuff, exc_type): """Actual implementation of test_errors_locked, so it can be reused.""" # WAL provides more concurrency; some things won't to block with it enabled. storage = Storage(db_path, wal_enabled=False) storage.db.execute("PRAGMA busy_timeout = 0;") feed = FeedData('one') entry = EntryData('one', 'entry', datetime(2010, 1, 2)) storage.add_feed(feed.url, datetime(2010, 1, 1)) storage.add_or_update_entry( EntryUpdateIntent(entry, entry.updated, datetime(2010, 1, 1), 0, 0)) in_transaction = threading.Event() can_return_from_transaction = threading.Event() def target(): storage = Storage(db_path, wal_enabled=False) storage.db.isolation_level = None storage.db.execute("BEGIN EXCLUSIVE;") in_transaction.set() can_return_from_transaction.wait() storage.db.execute("ROLLBACK;") if pre_stuff: pre_stuff(storage, feed, entry) thread = threading.Thread(target=target) thread.start() in_transaction.wait() try: with pytest.raises(exc_type) as excinfo: do_stuff(storage, feed, entry) assert 'locked' in str(excinfo.value.__cause__) finally: can_return_from_transaction.set() thread.join()
def add_or_update_entries(storage, feed, entry): storage.add_or_update_entries( [EntryUpdateIntent(entry, entry.updated, datetime(2010, 1, 1), 0, 0)])