class Test_schema_upgrader(object):
    def setUp(self):
        # make an in-memory sqlite database to use during testing
        self.connection = Connection_wrapper(
            sqlite.connect(":memory:",
                           detect_types=sqlite.PARSE_DECLTYPES,
                           check_same_thread=False))
        self.cache = Stub_cache()
        cursor = self.connection.cursor()
        cursor.execute(Stub_object.sql_create_table())

        self.fake_files = {
        }  # map of fake filename (full path) to fake file contents
        self.database = Database(self.connection, self.cache)
        self.upgrader = Schema_upgrader(self.database,
                                        glob=self.glob,
                                        read_file=self.read_file)

    def tearDown(self):
        self.database.close()

    def glob(self, glob_pattern):
        """
    A fake glob function that doesn't use the filesystem.
    """
        re_pattern = re.compile(glob_pattern.replace("*", "[^/]*"))

        return [
            filename for filename in self.fake_files.keys()
            if re_pattern.search(filename)
        ]

    def read_file(self, filename):
        """
    A fake read file function that doesn't use the filesystem.
    """
        contents = self.fake_files.get(filename)

        if not contents:
            raise IOError()

        return contents

    def test_upgrade_schema(self, to_version=None):
        if not to_version:
            to_version = u"5.7.11"

        self.fake_files = {
            u"model/delta/5.6.7.sqlite":
            u"create table new_table ( foo text ); insert into new_table values ( 'hi' );",
            u"model/delta/5.6.8.sqlite":
            u"insert into new_table values ( 'bye' );",
            u"model/delta/5.6.10.sqlite":
            u"alter table new_table add column bar text;",
            u"model/delta/5.7.11.sqlite":
            u"insert into new_table values ( 'whee', 'stuff' );",
            u"model/delta/5.7.18.sqlite":
            u"insert into new_table values ( 'more', 'things' );",
        }

        self.upgrader.upgrade_schema(to_version)

        result = self.database.select_many(tuple, u"select * from new_table;")
        if to_version == u"5.7.11":
            assert result == [(u"hi", None), (u"bye", None), ("whee", "stuff")]
        else:
            assert result == [(u"hi", None), (u"bye", None), ("whee", "stuff"),
                              ("more", "things")]

        result = self.database.select_many(tuple,
                                           u"select * from schema_version;")
        if to_version == u"5.7.11":
            assert result == [(5, 7, 11)]
        else:
            assert result == [(5, 7, 18)]

    def test_upgrade_schema_with_schema_version_table(self):
        self.database.execute(
            u"create table schema_version ( major numeric, minor numeric, \"release\" numeric );"
        )
        self.database.execute(
            u"insert into schema_version values ( 0, 0, 0 );")
        self.test_upgrade_schema()

    def test_upgrade_schema_with_schema_version_table_and_starting_version(
            self):
        self.database.execute(
            u"create table schema_version ( major numeric, minor numeric, \"release\" numeric );"
        )
        self.database.execute(
            u"insert into schema_version values ( 5, 6, 6 );")

        self.fake_files[
            u"model/delta/5.6.1.sqlite"] = u"this is not valid sql and should not be executed anyway;"
        self.fake_files[u"model/delta/5.6.6.sqlite"] = u"also invalid;"

        self.test_upgrade_schema()

    def test_upgrade_schema_with_schema_version_table_and_target_version_without_schema(
            self):
        self.database.execute(
            u"create table schema_version ( major numeric, minor numeric, \"release\" numeric );"
        )
        self.database.execute(
            u"insert into schema_version values ( 0, 0, 0 );")
        self.test_upgrade_schema(to_version=u"5.7.20")

    def test_upgrade_schema_with_schema_version_table_and_starting_version_and_target_version_without_schema(
            self):
        self.database.execute(
            u"create table schema_version ( major numeric, minor numeric, \"release\" numeric );"
        )
        self.database.execute(
            u"insert into schema_version values ( 5, 6, 6 );")
        self.test_upgrade_schema(to_version=u"5.7.20")

    def test_upgrade_schema_with_future_ending_version(self):
        self.fake_files = {
            u"model/delta/5.6.7.sqlite":
            u"create table new_table ( foo text ); insert into new_table values ( 'hi' );",
            u"model/delta/5.6.8.sqlite":
            u"insert into new_table values ( 'bye' );",
            u"model/delta/5.6.10.sqlite":
            u"alter table new_table add column bar text;",
            u"model/delta/5.7.11.sqlite":
            u"insert into new_table values ( 'whee', 'stuff' );",
            u"model/delta/5.7.18.sqlite":
            u"insert into new_table values ( 'more', 'and more' );",
        }

        self.upgrader.upgrade_schema(u"5.8.55")

        result = self.database.select_many(tuple, u"select * from new_table;")
        assert result == [(u"hi", None), (u"bye", None), ("whee", "stuff"),
                          ("more", "and more")]

        result = self.database.select_many(tuple,
                                           u"select * from schema_version;")
        assert result == [(5, 7, 18)]

    def test_upgrade_schema_twice(self):
        self.test_upgrade_schema()

        # the second upgrade should have no effect, because at this point it's already upgraded
        self.test_upgrade_schema()

    def test_upgrade_schema_with_filename_with_invalid_version(self):
        # the filename, not composed of all-integer parts, should be skipped
        self.fake_files[
            u"model/delta/5.6.9b.sqlite"] = u"this is not valid sql and should not be executed anyway;"

        self.test_upgrade_schema()

    def test_upgrade_schema_default_to_start_version_of_1_5_4(self):
        # test that if no schema_version table exists, then the starting version is assumed to be 1.5.4
        self.fake_files = {
            u"model/delta/1.5.3.sqlite":
            u"invalid sql;",
            u"model/delta/1.5.4.sqlite":
            u"should not be invoked;",
            u"model/delta/1.5.5.sqlite":
            u"create table new_table ( foo text ); insert into new_table values ( 'hi' );",
            u"model/delta/1.5.6.sqlite":
            u"insert into new_table values ( 'bye' );",
        }

        self.upgrader.upgrade_schema(u"1.5.6")

        result = self.database.select_many(tuple, u"select * from new_table;")
        assert result == [
            (u"hi", ),
            (u"bye", ),
        ]

        result = self.database.select_many(tuple,
                                           u"select * from schema_version;")
        assert result == [(1, 5, 6)]

    def test_apply_schema_delta(self):
        self.database.execute(
            u"create table schema_version ( major numeric, minor numeric, \"release\" numeric );"
        )
        self.database.execute(
            u"insert into schema_version values ( 0, 0, 0 );")

        self.fake_files = {
            u"model/delta/5.6.5.sqlite":
            u"insert into new_table values ( 'should not show up' );",
            u"model/delta/5.6.7.sqlite":
            u"create table new_table ( foo text ); insert into new_table values ( 'hi' );",
            u"model/delta/5.7.18.sqlite":
            u"insert into new_table values ( 'should not be present' );",
        }

        self.upgrader.apply_schema_delta((5, 6, 7),
                                         u"model/delta/5.6.7.sqlite")

        result = self.database.select_many(unicode,
                                           u"select * from new_table;")
        assert result == [u"hi"]

        result = self.database.select_many(tuple,
                                           u"select * from schema_version;")
        assert result == [(5, 6, 7)]

    @raises(IOError)
    def test_apply_schema_delta_with_unknown_file(self):
        self.upgrader.apply_schema_delta((5, 6, 7),
                                         u"model/delta/5.6.7.sqlite")

    def test_version_string_to_tuple(self):
        version = self.upgrader.version_string_to_tuple("2.5.13")

        assert len(version) == 3
        assert version[0] == 2
        assert version[1] == 5
        assert version[2] == 13

    def test_version_string_to_tuple_with_extension(self):
        version = self.upgrader.version_string_to_tuple("2.5.13.sqlite")

        assert len(version) == 3
        assert version[0] == 2
        assert version[1] == 5
        assert version[2] == 13

    @raises(ValueError)
    def test_version_string_to_tuple_with_too_many_parts(self):
        version = self.upgrader.version_string_to_tuple("3.14.159.26.5")

    @raises(ValueError)
    def test_version_string_to_tuple_with_too_few_parts(self):
        version = self.upgrader.version_string_to_tuple("3.14")

    @raises(ValueError)
    def test_version_string_to_tuple_with_non_integer_part(self):
        version = self.upgrader.version_string_to_tuple("2.5b.13")

    @raises(ValueError)
    def test_version_string_to_tuple_with_empty_part(self):
        version = self.upgrader.version_string_to_tuple("2..13")
Exemple #2
0
class Test_database( object ):
  def setUp( self ):
    # make an in-memory sqlite database to use during testing
    self.connection = Connection_wrapper( sqlite.connect( ":memory:", detect_types = sqlite.PARSE_DECLTYPES, check_same_thread = False ) )
    self.cache = Stub_cache()
    cursor = self.connection.cursor()
    cursor.execute( Stub_object.sql_create_table() )

    self.database = Database( self.connection, self.cache )

  def tearDown( self ):
    self.database.close()

  def test_save_and_load( self ):
    basic_obj = Stub_object( object_id = "5", value = 1 )
    original_revision = basic_obj.revision

    self.database.save( basic_obj )
    obj = self.database.load( Stub_object, basic_obj.object_id )

    assert obj.object_id == basic_obj.object_id
    assert obj.revision.replace( tzinfo = utc ) == original_revision
    assert obj.value == basic_obj.value

  def test_save_and_load_without_commit( self ):
    basic_obj = Stub_object( object_id = "5", value = 1 )
    original_revision = basic_obj.revision

    self.database.save( basic_obj, commit = False )
    self.connection.rollback() # if commit wasn't called, this should back out the save
    obj = self.database.load( Stub_object, basic_obj.object_id )

    assert obj == None

  def test_save_and_load_with_explicit_commit( self ):
    basic_obj = Stub_object( object_id = "5", value = 1 )
    original_revision = basic_obj.revision

    self.database.save( basic_obj, commit = False )
    self.database.commit()
    self.connection.rollback() # should have no effect because of the call to commit
    obj = self.database.load( Stub_object, basic_obj.object_id )

    assert obj.object_id == basic_obj.object_id
    assert obj.revision.replace( tzinfo = utc ) == original_revision
    assert obj.value == basic_obj.value

  def test_select_one( self ):
    basic_obj = Stub_object( object_id = "5", value = 1 )
    original_revision = basic_obj.revision

    self.database.save( basic_obj )
    obj = self.database.select_one( Stub_object, Stub_object.sql_load( basic_obj.object_id ) )

    assert obj.object_id == basic_obj.object_id
    assert obj.revision.replace( tzinfo = utc ) == original_revision
    assert obj.value == basic_obj.value

  def test_select_datetime( self ):
    # this revision (with .504099) happens to test for a bug caused by floating point rounding errors
    original_revision = "2008-01-01 01:00:42.504099+00:00"
    basic_obj = Stub_object( object_id = "5", revision = original_revision, value = 1 )

    self.database.save( basic_obj )
    obj = self.database.select_one( Stub_object, Stub_object.sql_load( basic_obj.object_id ) )

    assert obj.object_id == basic_obj.object_id
    assert str( obj.revision.replace( tzinfo = utc ) ) == original_revision
    assert obj.value == basic_obj.value

  def test_select_datetime_with_many_fractional_digits( self ):
    original_revision = "2008-01-01 01:00:42.5032429489284+00:00"
    basic_obj = Stub_object( object_id = "5", revision = original_revision, value = 1 )

    self.database.save( basic_obj )
    obj = self.database.select_one( Stub_object, Stub_object.sql_load( basic_obj.object_id ) )

    assert obj.object_id == basic_obj.object_id
    assert str( obj.revision.replace( tzinfo = utc ) ) == "2008-01-01 01:00:42.503242+00:00"
    assert obj.value == basic_obj.value

  def test_select_datetime_with_zero_fractional_seconds( self ):
    original_revision = "2008-01-01 01:00:42.0+00:00"
    basic_obj = Stub_object( object_id = "5", revision = original_revision, value = 1 )

    self.database.save( basic_obj )
    obj = self.database.select_one( Stub_object, Stub_object.sql_load( basic_obj.object_id ) )

    assert obj.object_id == basic_obj.object_id
    assert str( obj.revision.replace( tzinfo = utc ) ) == "2008-01-01 01:00:42+00:00"
    assert obj.value == basic_obj.value

  def test_select_one_tuple( self ):
    obj = self.database.select_one( tuple, Stub_object.sql_tuple() )

    assert len( obj ) == 2
    assert obj[ 0 ] == 1
    assert obj[ 1 ] == 2

  def test_select_many( self ):
    basic_obj = Stub_object( object_id = "5", value = 1 )
    original_revision = basic_obj.revision
    basic_obj2 = Stub_object( object_id = "6", value = 2 )
    original_revision2 = basic_obj2.revision

    self.database.save( basic_obj )
    self.database.save( basic_obj2 )
    objs = self.database.select_many( Stub_object, Stub_object.sql_load_em_all() )

    assert len( objs ) == 2
    assert objs[ 0 ].object_id == basic_obj.object_id
    assert objs[ 0 ].revision.replace( tzinfo = utc ) == original_revision
    assert objs[ 0 ].value == basic_obj.value
    assert objs[ 1 ].object_id == basic_obj2.object_id
    assert objs[ 1 ].revision.replace( tzinfo = utc ) == original_revision2
    assert objs[ 1 ].value == basic_obj2.value

  def test_select_many_tuples( self ):
    objs = self.database.select_many( tuple, Stub_object.sql_tuple() )

    assert len( objs ) == 1
    assert len( objs[ 0 ] ) == 2
    assert objs[ 0 ][ 0 ] == 1
    assert objs[ 0 ][ 1 ] == 2

  def test_select_many_with_no_matches( self ):
    objs = self.database.select_many( Stub_object, Stub_object.sql_load_em_all() )

    assert len( objs ) == 0

  def test_save_and_load_revision( self ):
    basic_obj = Stub_object( object_id = "5", value = 1 )
    original_revision = basic_obj.revision

    self.database.save( basic_obj )
    basic_obj.value = 2

    self.database.save( basic_obj )
    obj = self.database.load( Stub_object, basic_obj.object_id )

    assert obj.object_id == basic_obj.object_id
    assert obj.revision.replace( tzinfo = utc ) == basic_obj.revision
    assert obj.value == basic_obj.value

    revised = self.database.load( Stub_object, basic_obj.object_id, revision = original_revision )

    assert revised.object_id == basic_obj.object_id
    assert revised.value == 1
    assert revised.revision.replace( tzinfo = utc ) == original_revision

  def test_execute( self ):
    basic_obj = Stub_object( object_id = "5", value = 1 )
    original_revision = basic_obj.revision

    self.database.execute( basic_obj.sql_create() )
    obj = self.database.load( Stub_object, basic_obj.object_id )

    assert obj.object_id == basic_obj.object_id
    assert obj.revision.replace( tzinfo = utc ) == original_revision
    assert obj.value == basic_obj.value

  def test_execute_without_commit( self ):
    basic_obj = Stub_object( object_id = "5", value = 1 )
    original_revision = basic_obj.revision

    self.database.execute( basic_obj.sql_create(), commit = False )
    self.connection.rollback()
    obj = self.database.load( Stub_object, basic_obj.object_id )

    assert obj == None

  def test_execute_with_explicit_commit( self ):
    basic_obj = Stub_object( object_id = "5", value = 1 )
    original_revision = basic_obj.revision

    self.database.execute( basic_obj.sql_create(), commit = False )
    self.database.commit()
    obj = self.database.load( Stub_object, basic_obj.object_id )

    assert obj.object_id == basic_obj.object_id
    assert obj.revision.replace( tzinfo = utc ) == original_revision
    assert obj.value == basic_obj.value

  def test_load_unknown( self ):
    basic_obj = Stub_object( object_id = "5", value = 1 )
    obj = self.database.load( Stub_object, basic_obj.object_id )

    assert obj == None

  def test_next_id( self ):
    next_id = self.database.next_id( Stub_object )
    assert next_id
    assert self.database.load( Stub_object, next_id )
    prev_ids = [ next_id ]

    next_id = self.database.next_id( Stub_object )
    assert next_id
    assert next_id not in prev_ids
    assert self.database.load( Stub_object, next_id )
    prev_ids.append( next_id )

    next_id = self.database.next_id( Stub_object )
    assert next_id
    assert next_id not in prev_ids
    assert self.database.load( Stub_object, next_id )

  def test_next_id_without_commit( self ):
    next_id = self.database.next_id( Stub_object, commit = False )
    self.connection.rollback()
    assert self.database.load( Stub_object, next_id ) == None

  def test_next_id_with_explicit_commit( self ):
    next_id = self.database.next_id( Stub_object, commit = False )
    self.database.commit()
    assert next_id
    assert self.database.load( Stub_object, next_id )

  def test_synchronize( self ):
    def make_objects():
      for i in range( 50 ):
        object_id = self.database.next_id( Stub_object )
        basic_obj = Stub_object( object_id, value = 1 )
        original_revision = basic_obj.revision

        self.database.execute( basic_obj.sql_create() )
        obj = self.database.load( Stub_object, basic_obj.object_id )

        assert obj.object_id == basic_obj.object_id
        delta = abs( obj.revision.replace( tzinfo = utc ) - original_revision )
        assert delta <= timedelta( seconds = 0.000001 )
        assert obj.value == basic_obj.value

        object_id = self.database.next_id( Stub_object )

    # if synchronization (locking) is working properly, then these two threads should be able to run
    # simultaneously without error. without locking, SQLite will raise
    thread1 = Thread( target = make_objects )
    thread2 = Thread( target = make_objects )
    thread1.start()
    thread2.start()

    thread1.join()
    thread2.join()

  def test_backend( self ):
    assert self.database.backend == Persistent.SQLITE_BACKEND
Exemple #3
0
class Test_database(object):
    def setUp(self):
        # make an in-memory sqlite database to use during testing
        self.connection = Connection_wrapper(
            sqlite.connect(":memory:",
                           detect_types=sqlite.PARSE_DECLTYPES,
                           check_same_thread=False))
        self.cache = Stub_cache()
        cursor = self.connection.cursor()
        cursor.execute(Stub_object.sql_create_table())

        self.database = Database(self.connection, self.cache)

    def tearDown(self):
        self.database.close()

    def test_save_and_load(self):
        basic_obj = Stub_object(object_id="5", value=1)
        original_revision = basic_obj.revision

        self.database.save(basic_obj)
        obj = self.database.load(Stub_object, basic_obj.object_id)

        assert obj.object_id == basic_obj.object_id
        assert obj.revision.replace(tzinfo=utc) == original_revision
        assert obj.value == basic_obj.value

    def test_save_and_load_without_commit(self):
        basic_obj = Stub_object(object_id="5", value=1)
        original_revision = basic_obj.revision

        self.database.save(basic_obj, commit=False)
        self.connection.rollback(
        )  # if commit wasn't called, this should back out the save
        obj = self.database.load(Stub_object, basic_obj.object_id)

        assert obj == None

    def test_save_and_load_with_explicit_commit(self):
        basic_obj = Stub_object(object_id="5", value=1)
        original_revision = basic_obj.revision

        self.database.save(basic_obj, commit=False)
        self.database.commit()
        self.connection.rollback(
        )  # should have no effect because of the call to commit
        obj = self.database.load(Stub_object, basic_obj.object_id)

        assert obj.object_id == basic_obj.object_id
        assert obj.revision.replace(tzinfo=utc) == original_revision
        assert obj.value == basic_obj.value

    def test_select_one(self):
        basic_obj = Stub_object(object_id="5", value=1)
        original_revision = basic_obj.revision

        self.database.save(basic_obj)
        obj = self.database.select_one(
            Stub_object, Stub_object.sql_load(basic_obj.object_id))

        assert obj.object_id == basic_obj.object_id
        assert obj.revision.replace(tzinfo=utc) == original_revision
        assert obj.value == basic_obj.value

    def test_select_datetime(self):
        # this revision (with .504099) happens to test for a bug caused by floating point rounding errors
        original_revision = "2008-01-01 01:00:42.504099+00:00"
        basic_obj = Stub_object(object_id="5",
                                revision=original_revision,
                                value=1)

        self.database.save(basic_obj)
        obj = self.database.select_one(
            Stub_object, Stub_object.sql_load(basic_obj.object_id))

        assert obj.object_id == basic_obj.object_id
        assert str(obj.revision.replace(tzinfo=utc)) == original_revision
        assert obj.value == basic_obj.value

    def test_select_datetime_with_many_fractional_digits(self):
        original_revision = "2008-01-01 01:00:42.5032429489284+00:00"
        basic_obj = Stub_object(object_id="5",
                                revision=original_revision,
                                value=1)

        self.database.save(basic_obj)
        obj = self.database.select_one(
            Stub_object, Stub_object.sql_load(basic_obj.object_id))

        assert obj.object_id == basic_obj.object_id
        assert str(obj.revision.replace(
            tzinfo=utc)) == "2008-01-01 01:00:42.503242+00:00"
        assert obj.value == basic_obj.value

    def test_select_datetime_with_zero_fractional_seconds(self):
        original_revision = "2008-01-01 01:00:42.0+00:00"
        basic_obj = Stub_object(object_id="5",
                                revision=original_revision,
                                value=1)

        self.database.save(basic_obj)
        obj = self.database.select_one(
            Stub_object, Stub_object.sql_load(basic_obj.object_id))

        assert obj.object_id == basic_obj.object_id
        assert str(
            obj.revision.replace(tzinfo=utc)) == "2008-01-01 01:00:42+00:00"
        assert obj.value == basic_obj.value

    def test_select_one_tuple(self):
        obj = self.database.select_one(tuple, Stub_object.sql_tuple())

        assert len(obj) == 2
        assert obj[0] == 1
        assert obj[1] == 2

    def test_select_many(self):
        basic_obj = Stub_object(object_id="5", value=1)
        original_revision = basic_obj.revision
        basic_obj2 = Stub_object(object_id="6", value=2)
        original_revision2 = basic_obj2.revision

        self.database.save(basic_obj)
        self.database.save(basic_obj2)
        objs = self.database.select_many(Stub_object,
                                         Stub_object.sql_load_em_all())

        assert len(objs) == 2
        assert objs[0].object_id == basic_obj.object_id
        assert objs[0].revision.replace(tzinfo=utc) == original_revision
        assert objs[0].value == basic_obj.value
        assert objs[1].object_id == basic_obj2.object_id
        assert objs[1].revision.replace(tzinfo=utc) == original_revision2
        assert objs[1].value == basic_obj2.value

    def test_select_many_tuples(self):
        objs = self.database.select_many(tuple, Stub_object.sql_tuple())

        assert len(objs) == 1
        assert len(objs[0]) == 2
        assert objs[0][0] == 1
        assert objs[0][1] == 2

    def test_select_many_with_no_matches(self):
        objs = self.database.select_many(Stub_object,
                                         Stub_object.sql_load_em_all())

        assert len(objs) == 0

    def test_save_and_load_revision(self):
        basic_obj = Stub_object(object_id="5", value=1)
        original_revision = basic_obj.revision

        self.database.save(basic_obj)
        basic_obj.value = 2

        self.database.save(basic_obj)
        obj = self.database.load(Stub_object, basic_obj.object_id)

        assert obj.object_id == basic_obj.object_id
        assert obj.revision.replace(tzinfo=utc) == basic_obj.revision
        assert obj.value == basic_obj.value

        revised = self.database.load(Stub_object,
                                     basic_obj.object_id,
                                     revision=original_revision)

        assert revised.object_id == basic_obj.object_id
        assert revised.value == 1
        assert revised.revision.replace(tzinfo=utc) == original_revision

    def test_execute(self):
        basic_obj = Stub_object(object_id="5", value=1)
        original_revision = basic_obj.revision

        self.database.execute(basic_obj.sql_create())
        obj = self.database.load(Stub_object, basic_obj.object_id)

        assert obj.object_id == basic_obj.object_id
        assert obj.revision.replace(tzinfo=utc) == original_revision
        assert obj.value == basic_obj.value

    def test_execute_without_commit(self):
        basic_obj = Stub_object(object_id="5", value=1)
        original_revision = basic_obj.revision

        self.database.execute(basic_obj.sql_create(), commit=False)
        self.connection.rollback()
        obj = self.database.load(Stub_object, basic_obj.object_id)

        assert obj == None

    def test_execute_with_explicit_commit(self):
        basic_obj = Stub_object(object_id="5", value=1)
        original_revision = basic_obj.revision

        self.database.execute(basic_obj.sql_create(), commit=False)
        self.database.commit()
        obj = self.database.load(Stub_object, basic_obj.object_id)

        assert obj.object_id == basic_obj.object_id
        assert obj.revision.replace(tzinfo=utc) == original_revision
        assert obj.value == basic_obj.value

    def test_load_unknown(self):
        basic_obj = Stub_object(object_id="5", value=1)
        obj = self.database.load(Stub_object, basic_obj.object_id)

        assert obj == None

    def test_next_id(self):
        next_id = self.database.next_id(Stub_object)
        assert next_id
        assert self.database.load(Stub_object, next_id)
        prev_ids = [next_id]

        next_id = self.database.next_id(Stub_object)
        assert next_id
        assert next_id not in prev_ids
        assert self.database.load(Stub_object, next_id)
        prev_ids.append(next_id)

        next_id = self.database.next_id(Stub_object)
        assert next_id
        assert next_id not in prev_ids
        assert self.database.load(Stub_object, next_id)

    def test_next_id_without_commit(self):
        next_id = self.database.next_id(Stub_object, commit=False)
        self.connection.rollback()
        assert self.database.load(Stub_object, next_id) == None

    def test_next_id_with_explicit_commit(self):
        next_id = self.database.next_id(Stub_object, commit=False)
        self.database.commit()
        assert next_id
        assert self.database.load(Stub_object, next_id)

    def test_synchronize(self):
        def make_objects():
            for i in range(50):
                object_id = self.database.next_id(Stub_object)
                basic_obj = Stub_object(object_id, value=1)
                original_revision = basic_obj.revision

                self.database.execute(basic_obj.sql_create())
                obj = self.database.load(Stub_object, basic_obj.object_id)

                assert obj.object_id == basic_obj.object_id
                delta = abs(
                    obj.revision.replace(tzinfo=utc) - original_revision)
                assert delta <= timedelta(seconds=0.000001)
                assert obj.value == basic_obj.value

                object_id = self.database.next_id(Stub_object)

        # if synchronization (locking) is working properly, then these two threads should be able to run
        # simultaneously without error. without locking, SQLite will raise
        thread1 = Thread(target=make_objects)
        thread2 = Thread(target=make_objects)
        thread1.start()
        thread2.start()

        thread1.join()
        thread2.join()

    def test_backend(self):
        assert self.database.backend == Persistent.SQLITE_BACKEND