diff --git a/CHANGELOG.md b/CHANGELOG.md index 490001659..fad2e17c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Breaking Changes * File format version bumped. * The layout of the lock-file has changed, the lock file format version is bumped and all participants in a multiprocess scenario needs to be up to date so they expect the same format. This requires an update of Studio. (Core upgrade) +* Writing to a frozen realm throws `RealmException` instead of `RealmError`. ([#974](https://github.com/realm/realm-dart/pull/974)) ### Enhancements * Support setting `maxNumberOfActiveVersions` when creating a `Configuration`. ([#1036](https://github.com/realm/realm-dart/pull/1036)) @@ -21,6 +22,7 @@ * Removed the ".tmp_compaction_space" file being left over after compacting a Realm on Windows. (Core upgrade). * Restore fallback to full barrier when F_BARRIERSYNC is not available on Apple platforms. (Core upgrade, since v0.8.0+rc) * Fixed wrong assertion on query error that could result in a crash. (Core upgrade) +* Writing to a read-only realm throws `RealmException` instead of blocking the isolate. ([#974](https://github.com/realm/realm-dart/pull/974)) ### Compatibility * Realm Studio: 13.0.0 or later. diff --git a/lib/src/realm_class.dart b/lib/src/realm_class.dart index d4e83a7cc..7f11e5bfe 100644 --- a/lib/src/realm_class.dart +++ b/lib/src/realm_class.dart @@ -321,20 +321,14 @@ class Realm implements Finalizable { /// Begins a write transaction for this [Realm]. Transaction beginWrite() { - _ensureWritable(); - realmCore.beginWrite(this); - return Transaction._(this); } /// Asynchronously begins a write transaction for this [Realm]. You can supply a /// [CancellationToken] to cancel the operation. Future beginWriteAsync([CancellationToken? cancellationToken]) async { - _ensureWritable(); - await realmCore.beginWriteAsync(this, cancellationToken); - return Transaction._(this); } @@ -496,12 +490,6 @@ class Realm implements Finalizable { } } - void _ensureWritable() { - if (isFrozen) { - throw RealmError('Starting a write transaction on a frozen Realm is not allowed.'); - } - } - /// Compacts a Realm file. A Realm file usually contains free/unused space. /// /// This method removes this free space and the file size is thereby reduced. diff --git a/test/configuration_test.dart b/test/configuration_test.dart index c3f26db3d..aa0b9e2d3 100644 --- a/test/configuration_test.dart +++ b/test/configuration_test.dart @@ -174,16 +174,6 @@ Future main([List? args]) async { expect(cars.length, 1); }); - test('Configuration readOnly - writing on read-only Realms throws', () { - Configuration config = Configuration.local([Car.schema]); - var realm = getRealm(config); - realm.close(); - - config = Configuration.local([Car.schema], isReadOnly: true); - realm = getRealm(config); - expect(() => realm.write(() {}), throws("Can't perform transactions on read-only Realms.")); - }); - test('Configuration inMemory - no files after closing realm', () { Configuration config = Configuration.inMemory([Car.schema]); var realm = getRealm(config); diff --git a/test/realm_test.dart b/test/realm_test.dart index e9eb161ab..221173431 100644 --- a/test/realm_test.dart +++ b/test/realm_test.dart @@ -268,14 +268,26 @@ Future main([List? args]) async { expect(realm.write(() => realm.add(carTwo, update: true)), carTwo); }); - test('Realm write after realm is closed', () { + test('Realm write after realm is closed', () async { var config = Configuration.local([Car.schema]); var realm = getRealm(config); final car = Car('Tesla'); realm.close(); - expect(() => realm.write(() {}), throws("Cannot access realm that has been closed")); + _expectAllWritesToThrow(realm, "Cannot access realm that has been closed"); + _expectAllAsyncWritesToThrow(realm, "Cannot access realm that has been closed"); + }); + + test('Realm write on read-only realms throws', () async { + Configuration config = Configuration.local([Car.schema]); + var realm = getRealm(config); + realm.close(); + + config = Configuration.local([Car.schema], isReadOnly: true); + realm = getRealm(config); + _expectAllWritesToThrow(realm, "Can't perform transactions on read-only Realms."); + _expectAllAsyncWritesToThrow(realm, "Can't perform transactions on read-only Realms."); }); test('Realm query', () { @@ -581,7 +593,7 @@ Future main([List? args]) async { final realm = getRealm(config); realm.write(() { // Second write inside the first one fails but the error is caught - expect(() => realm.write(() {}), throws('The Realm is already in a write transaction')); + _expectAllWritesToThrow(realm, "The Realm is already in a write transaction"); }); }); @@ -719,36 +731,13 @@ Future main([List? args]) async { expect(realm.all().length, 0); }); - test('FrozenRealm cannot write', () { - final config = Configuration.local([Person.schema]); - final realm = getRealm(config); - - final frozenRealm = freezeRealm(realm); - expect(() => frozenRealm.write(() {}), throws("Starting a write transaction on a frozen Realm is not allowed.")); - }); - - test('FrozenRealm cannot beginWrite', () { - final config = Configuration.local([Person.schema]); - final realm = getRealm(config); - - final frozenRealm = freezeRealm(realm); - expect(() => frozenRealm.beginWrite(), throws("Starting a write transaction on a frozen Realm is not allowed.")); - }); - - test('FrozenRealm cannot writeAsync', () async { + test('FrozenRealm cannot write', () async { final config = Configuration.local([Person.schema]); final realm = getRealm(config); final frozenRealm = freezeRealm(realm); - await expectLater(() => frozenRealm.writeAsync(() {}), throws("Starting a write transaction on a frozen Realm is not allowed.")); - }); - - test('FrozenRealm cannot beginWriteAsync', () async { - final config = Configuration.local([Person.schema]); - final realm = getRealm(config); - - final frozenRealm = freezeRealm(realm); - await expectLater(() => frozenRealm.beginWriteAsync(), throws("Starting a write transaction on a frozen Realm is not allowed.")); + _expectAllWritesToThrow(frozenRealm, "Can't perform transactions on a frozen Realm"); + _expectAllAsyncWritesToThrow(frozenRealm, "Can't perform transactions on a frozen Realm"); }); test('realm.freeze when frozen returns the same instance', () { @@ -1018,7 +1007,6 @@ Future main([List? args]) async { test('Realm.beginWriteAsync starts write transaction', () async { final realm = getRealm(Configuration.local([Person.schema])); final transaction = await realm.beginWriteAsync(); - expect(transaction.isOpen, true); }); @@ -1027,7 +1015,6 @@ Future main([List? args]) async { final transaction = await realm.beginWriteAsync(); realm.add(Person('John')); transaction.commit(); - expect(realm.all().length, 1); }); @@ -1036,7 +1023,6 @@ Future main([List? args]) async { final transaction = await realm.beginWriteAsync(); realm.add(Person('John')); await transaction.commitAsync(); - expect(realm.all().length, 1); }); @@ -1054,7 +1040,6 @@ Future main([List? args]) async { final transaction = realm.beginWrite(); realm.add(Person('John')); await transaction.commitAsync(); - expect(realm.all().length, 1); }); @@ -1416,7 +1401,7 @@ Future main([List? args]) async { Future createRealmForCompact(Configuration config) async { var realm = getRealm(config); - + if (config is FlexibleSyncConfiguration) { realm.subscriptions.update((mutableSubscriptions) { mutableSubscriptions.add(realm.query("stringQueryField CONTAINS '$compactTest'")); @@ -1425,7 +1410,7 @@ Future main([List? args]) async { } addDataForCompact(realm); - + if (config is FlexibleSyncConfiguration) { await realm.syncSession.waitForDownload(); await realm.syncSession.waitForUpload(); @@ -1439,7 +1424,7 @@ Future main([List? args]) async { void validateCompact(bool compacted, String realmPath, int beforeCompactSizeSize) async { expect(compacted, true); - final afterCompactSize = await File(realmPath).stat().then((value) => value.size); + final afterCompactSize = await File(realmPath).stat().then((value) => value.size); expect(beforeCompactSizeSize, greaterThan(afterCompactSize)); } @@ -1455,7 +1440,7 @@ Future main([List? args]) async { final realm = getRealm(config); }); - test('Realm - non existing realm can not be compacted', () async { + test('Realm - non existing realm can not be compacted', () async { var config = Configuration.local([Product.schema], path: p.join(Configuration.defaultStoragePath, "${generateRandomString(8)}.realm")); final compacted = Realm.compact(config); expect(compacted, false); @@ -1553,8 +1538,7 @@ Future main([List? args]) async { final path = p.join(Configuration.defaultStoragePath, "${generateRandomString(8)}.realm"); var user = await app.logIn(credentials); List key = List.generate(encryptionKeySize, (i) => random.nextInt(256)); - final config = - Configuration.flexibleSync(user, [Product.schema], encryptionKey: key, path: path); + final config = Configuration.flexibleSync(user, [Product.schema], encryptionKey: key, path: path); final beforeCompactSize = await createRealmForCompact(config); user.logOut(); Future.delayed(Duration(seconds: 5)); @@ -1647,3 +1631,13 @@ When newWhen([tz.TZDateTime? time]) { extension _IterableEx on Iterable { Iterable except(T exclude) => where((o) => o != exclude); } + +void _expectAllWritesToThrow(Realm realm, String exceptionMessage) { + expect(() => realm.write(() {}), throws(exceptionMessage)); + expect(() => realm.beginWrite(), throws(exceptionMessage)); +} + +void _expectAllAsyncWritesToThrow(Realm realm, String exceptionMessage) { + expect(() => realm.writeAsync(() {}), throws(exceptionMessage)); + expect(() => realm.beginWriteAsync(), throws(exceptionMessage)); +} diff --git a/test/test.dart b/test/test.dart index 5859cb9b9..b46a2e09e 100644 --- a/test/test.dart +++ b/test/test.dart @@ -472,7 +472,7 @@ Future tryDeleteRealm(String path) async { for (var i = 0; i < 5; i++) { try { Realm.deleteRealm(path); - + //delete lock file await File('$path.lock').delete().onError((error, stackTrace) => dummy);