Skip to content

Commit d679c57

Browse files
draft In-memory: run tests with in-memory, add Store.deleteDbFiles() #96
1 parent e7b9dea commit d679c57

File tree

6 files changed

+83
-33
lines changed

6 files changed

+83
-33
lines changed

.gitlab-ci.yml

+3
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ test-generator:linux:x64:
7676
# Print log for every completed test.
7777
# Together, this will produce sequential log output, making it easier to attribute native logs.
7878
- dart test --concurrency=1 --reporter expanded
79+
# Run again using in-memory database
80+
- export OBX_IN_MEMORY=true
81+
- dart test --concurrency=1 --reporter expanded
7982

8083
test-lib:linux:x64:
8184
extends: .test

objectbox/lib/src/native/store.dart

+13
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,19 @@ class Store implements Finalizable {
484484
}
485485
}
486486

487+
/// Danger zone! This will delete all files in the given directory!
488+
///
489+
/// TODO Add docs
490+
static void removeDbFiles(String? directoryPath) {
491+
final path = _safeDirectoryPath(directoryPath);
492+
final cStr = path.toNativeUtf8();
493+
try {
494+
checkObx(C.remove_db_files(cStr.cast()));
495+
} finally {
496+
malloc.free(cStr);
497+
}
498+
}
499+
487500
/// Returns a store reference you can use to create a new store instance with
488501
/// a single underlying native store. See [Store.fromReference] for more details.
489502
ByteData get reference => _reference;

objectbox_test/README.md

+6
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,9 @@ Or for better log output (e.g. to attribute native logs to a test):
2020
# Print log for every completed test.
2121
dart test --concurrency=1 --reporter expanded
2222
```
23+
24+
To run tests using an in-memory database:
25+
```
26+
export OBX_IN_MEMORY=true
27+
dart test
28+
```

objectbox_test/test/isolates_test.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ void main() {
5656
/// the directory path to attach to an existing store.
5757
test('single store using attach', () async {
5858
Store.debugLogs = true;
59-
await testUsingStoreFromIsolate(storeCreatorAttach, (env) => env.dir.path);
59+
await testUsingStoreFromIsolate(storeCreatorAttach, (env) => env.dbDirPath);
6060
});
6161
}
6262

@@ -104,7 +104,7 @@ Future<void> testUsingStoreFromIsolate(Store Function(dynamic) storeCreator,
104104

105105
// Pass the store to the isolate
106106
final env = TestEnv('isolates');
107-
expect(Store.isOpen('testdata-isolates'), true);
107+
expect(Store.isOpen(env.dbDirPath), true);
108108

109109
expect(await call(storeRefGetter(env)), equals('store set'));
110110

objectbox_test/test/store_test.dart

+21-19
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import 'test_env.dart';
1313

1414
void main() {
1515
test('store reference', () {
16-
final env = TestEnv('store');
16+
final env = TestEnv('store-ref');
1717
final store1 = env.store;
1818
final store2 = Store.fromReference(getObjectBoxModel(), store1.reference);
1919
expect(store1, isNot(store2));
@@ -31,19 +31,19 @@ void main() {
3131
test('store attach fails if same isolate', () {
3232
final env = TestEnv('store');
3333
expect(
34-
() => Store.attach(getObjectBoxModel(), env.dir.path),
34+
() => Store.attach(getObjectBoxModel(), env.dbDirPath),
3535
throwsA(predicate((UnsupportedError e) =>
3636
e.message!.contains('Cannot create multiple Store instances'))));
3737
env.closeAndDelete();
3838
});
3939

4040
test('store attach remains open if main store closed', () async {
41-
final env = TestEnv('store');
41+
final env = TestEnv('store-attach');
4242
final store1 = env.store;
4343
final receivePort = ReceivePort();
4444
final received = StreamQueue<dynamic>(receivePort);
4545
await Isolate.spawn(storeAttachIsolate,
46-
StoreAttachIsolateInit(receivePort.sendPort, env.dir.path));
46+
StoreAttachIsolateInit(receivePort.sendPort, env.dbDirPath));
4747
final commandPort = await received.next as SendPort;
4848

4949
// Check native instance pointer is different.
@@ -54,7 +54,7 @@ void main() {
5454
expect(id, 1);
5555
// Close original store to test store remains open until all refs closed.
5656
store1.close();
57-
expect(true, Store.isOpen('testdata-store'));
57+
expect(Store.isOpen(env.dbDirPath), true);
5858

5959
// Read data with attached store.
6060
commandPort.send(id);
@@ -65,14 +65,14 @@ void main() {
6565
// Close attached store, should close store completely.
6666
commandPort.send(null);
6767
await received.next;
68-
expect(false, Store.isOpen('testdata-store'));
68+
expect(Store.isOpen(env.dbDirPath), false);
6969

7070
// Dispose StreamQueue.
7171
await received.cancel();
7272
});
7373

7474
test('store attach with configuration', () {
75-
final env = TestEnv('store');
75+
final env = TestEnv('store-config');
7676
// Get store config.
7777
final storeConfig = env.store.configuration();
7878
expect(storeConfig.id, isNot(0));
@@ -102,18 +102,20 @@ void main() {
102102
});
103103

104104
test('store is open', () {
105-
expect(false, Store.isOpen(''));
106-
expect(false, Store.isOpen('testdata-store'));
107-
final env = TestEnv('store');
108-
expect(false, env.store.isClosed());
109-
expect(true, Store.isOpen('testdata-store'));
105+
final name = 'store-open';
106+
expect(Store.isOpen(''), false);
107+
expect(Store.isOpen(TestEnv.testDbDirPath(false, name)), false);
108+
expect(Store.isOpen(TestEnv.testDbDirPath(true, name)), false);
109+
final env = TestEnv(name);
110+
expect(env.store.isClosed(), false);
111+
expect(Store.isOpen(env.dbDirPath), true);
110112
env.closeAndDelete();
111-
expect(true, env.store.isClosed());
112-
expect(false, Store.isOpen('testdata-store'));
113+
expect(env.store.isClosed(), true);
114+
expect(Store.isOpen(env.dbDirPath), false);
113115
});
114116

115117
test('transactions', () {
116-
final env = TestEnv('store');
118+
final env = TestEnv('store-tx');
117119
expect(TxMode.values.length, 2);
118120
for (var mode in TxMode.values) {
119121
// Returned value falls through.
@@ -143,7 +145,7 @@ void main() {
143145
});
144146

145147
test('async transactions', () async {
146-
final env = TestEnv('store');
148+
final env = TestEnv('store-async-tx');
147149
expect(TxMode.values.length, 2);
148150
for (var mode in TxMode.values) {
149151
// Returned value falls through.
@@ -346,7 +348,7 @@ void main() {
346348
});
347349

348350
test('store run in isolate', () async {
349-
final env = TestEnv('store');
351+
final env = TestEnv('store-isolate');
350352
final id = env.box.put(TestEntity(tString: 'foo'));
351353
final futureResult = env.store.runAsync(_readStringAndRemove, id);
352354
print('Count in main isolate: ${env.box.count()}');
@@ -357,7 +359,7 @@ void main() {
357359
});
358360

359361
test('store runAsync returns isolate error', () async {
360-
final env = TestEnv('store');
362+
final env = TestEnv('store-run-async-isolate');
361363
try {
362364
await env.store.runAsync(_producesIsolateError, 'nothing');
363365
fail("Should throw RemoteError");
@@ -368,7 +370,7 @@ void main() {
368370
});
369371

370372
test('store runAsync returns callback error', () async {
371-
final env = TestEnv('store');
373+
final env = TestEnv('store-run-async-callback');
372374
try {
373375
await env.store.runAsync(_producesCallbackError, 'nothing');
374376
fail("Should throw error produced by callback");

objectbox_test/test/test_env.dart

+38-12
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ import 'entity.dart';
66
import 'objectbox.g.dart';
77

88
class TestEnv {
9-
final Directory dir;
9+
/// If environment variable OBX_IN_MEMORY=true is set, this will be true.
10+
///
11+
/// If tests should be run with an in-memory database, so not using files.
12+
final bool isInMemory;
13+
final String dbDirPath;
1014
final Store store;
1115

1216
/// If environment variable TEST_SHORT=1 is set, this will be true.
@@ -15,36 +19,58 @@ class TestEnv {
1519
/// This was used to speed up valgrind analysis and avoid CI timeouts.
1620
final bool short;
1721

22+
static String testDbDirPath(bool inMemory, String name) =>
23+
inMemory ? "memory:testdata-$name" : "testdata-$name";
24+
1825
factory TestEnv(String name, {bool? queryCaseSensitive, int? debugFlag}) {
19-
final dir = Directory('testdata-$name');
20-
if (dir.existsSync()) dir.deleteSync(recursive: true);
26+
final inMemory = Platform.environment["OBX_IN_MEMORY"] == "true";
27+
if (inMemory) {
28+
print("Using in-memory database for testing");
29+
}
30+
final String dbDirPath = testDbDirPath(inMemory, name);
31+
// Ensure there is no leftover data from a previous test failure.
32+
_removeDbFiles(inMemory, dbDirPath);
33+
2134
final Store store;
2235
var modelDefinition = getObjectBoxModel();
2336
try {
2437
store = queryCaseSensitive == null
25-
? Store(modelDefinition, directory: dir.path, debugFlags: debugFlag)
38+
? Store(modelDefinition, directory: dbDirPath, debugFlags: debugFlag)
2639
: Store(modelDefinition,
27-
directory: dir.path,
40+
directory: dbDirPath,
2841
debugFlags: debugFlag,
2942
queriesCaseSensitiveDefault: queryCaseSensitive);
3043
} catch (ex) {
31-
print("$dir exists: ${dir.existsSync()}");
32-
print("Store is open in directory: ${Store.isOpen(dir.path)}");
44+
if (!inMemory) {
45+
final dir = Directory(dbDirPath);
46+
print("$dir exists: ${dir.existsSync()}");
47+
}
48+
print("Store is open: ${Store.isOpen(dbDirPath)}");
3349
print("Model Info: ${modelDefinition.model.toMap(forModelJson: true)}");
3450
rethrow;
3551
}
36-
return TestEnv._(
37-
dir, store, Platform.environment.containsKey('TEST_SHORT'));
52+
return TestEnv._(inMemory, dbDirPath, store,
53+
Platform.environment.containsKey('TEST_SHORT'));
3854
}
3955

40-
TestEnv._(this.dir, this.store, this.short);
56+
TestEnv._(this.isInMemory, this.dbDirPath, this.store, this.short);
4157

4258
Box<TestEntity> get box => store.box();
4359

60+
/// Call once done with this to clean up.
4461
void closeAndDelete() {
4562
store.close();
46-
if (dir.existsSync()) {
47-
dir.deleteSync(recursive: true);
63+
_removeDbFiles(isInMemory, dbDirPath);
64+
}
65+
66+
static void _removeDbFiles(bool isInMemory, String dbDirPath) {
67+
// Note: removeDbFiles does not remove the directory, so do it manually.
68+
Store.removeDbFiles(dbDirPath);
69+
if (!isInMemory) {
70+
final dir = Directory(dbDirPath);
71+
if (dir.existsSync()) {
72+
dir.deleteSync(recursive: true);
73+
}
4874
}
4975
}
5076
}

0 commit comments

Comments
 (0)