Skip to content

Commit

Permalink
Improved automated tests
Browse files Browse the repository at this point in the history
Fixed bugs
Added support for in-memory database (without import/export)
  • Loading branch information
JaffaKetchup committed Apr 3, 2024
1 parent 0d38b5b commit 75cf594
Show file tree
Hide file tree
Showing 13 changed files with 529 additions and 53 deletions.
5 changes: 5 additions & 0 deletions lib/src/backend/impls/objectbox/backend/backend.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'dart:isolate';

import 'package:collection/collection.dart';
import 'package:flutter/services.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';

Expand Down Expand Up @@ -39,16 +40,20 @@ final class FMTCObjectBoxBackend implements FMTCBackend {
/// specify the application group (of less than 20 chars). See
/// [the ObjectBox docs](https://docs.objectbox.io/getting-started) for
/// details.
///
/// Avoid using [useInMemoryDatabase] outside of testing purposes.
@override
Future<void> initialise({
String? rootDirectory,
int maxDatabaseSize = 10000000,
String? macosApplicationGroup,
@visibleForTesting bool useInMemoryDatabase = false,
}) =>
FMTCObjectBoxBackendInternal._instance.initialise(
rootDirectory: rootDirectory,
maxDatabaseSize: maxDatabaseSize,
macosApplicationGroup: macosApplicationGroup,
useInMemoryDatabase: useInMemoryDatabase,
);

/// {@macro fmtc.backend.uninitialise}
Expand Down
28 changes: 16 additions & 12 deletions lib/src/backend/impls/objectbox/backend/internal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@ class _ObjectBoxBackendImpl implements FMTCObjectBoxBackendInternal {
@override
String get friendlyIdentifier => 'ObjectBox';

@override
Directory? rootDirectory;

void get expectInitialised => _sendPort ?? (throw RootUnavailable());

late String rootDirectory;

// Worker communication protocol storage

SendPort? _sendPort;
Expand Down Expand Up @@ -117,16 +116,21 @@ class _ObjectBoxBackendImpl implements FMTCObjectBoxBackendInternal {
required String? rootDirectory,
required int maxDatabaseSize,
required String? macosApplicationGroup,
required bool useInMemoryDatabase,
}) async {
if (_sendPort != null) throw RootAlreadyInitialised();

this.rootDirectory = await Directory(
path.join(
rootDirectory ??
(await getApplicationDocumentsDirectory()).absolute.path,
'fmtc',
),
).create(recursive: true);
if (useInMemoryDatabase) {
this.rootDirectory = Store.inMemoryPrefix + (rootDirectory ?? 'fmtc');
} else {
await Directory(
this.rootDirectory = path.join(
rootDirectory ??
(await getApplicationDocumentsDirectory()).absolute.path,
'fmtc',
),
).create(recursive: true);
}

// Prepare to recieve `SendPort` from worker
_workerResOneShot[0] = Completer();
Expand Down Expand Up @@ -200,7 +204,7 @@ class _ObjectBoxBackendImpl implements FMTCObjectBoxBackendInternal {
_worker,
(
sendPort: receivePort.sendPort,
rootDirectory: this.rootDirectory!,
rootDirectory: this.rootDirectory,
maxDatabaseSize: maxDatabaseSize,
macosApplicationGroup: macosApplicationGroup,
rootIsolateToken: ServicesBinding.rootIsolateToken!,
Expand Down Expand Up @@ -389,7 +393,7 @@ class _ObjectBoxBackendImpl implements FMTCObjectBoxBackendInternal {
required String url,
}) async =>
(await _sendCmdOneShot(
type: _WorkerCmdType.deleteStore,
type: _WorkerCmdType.deleteTile,
args: {'storeName': storeName, 'url': url},
))!['wasOrphan'];

Expand Down
57 changes: 28 additions & 29 deletions lib/src/backend/impls/objectbox/backend/internal_worker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ enum _WorkerCmdType {
Future<void> _worker(
({
SendPort sendPort,
Directory rootDirectory,
String rootDirectory,
int maxDatabaseSize,
String? macosApplicationGroup,
RootIsolateToken rootIsolateToken,
Expand All @@ -85,7 +85,7 @@ Future<void> _worker(
late final Store root;
try {
root = await openStore(
directory: input.rootDirectory.absolute.path,
directory: input.rootDirectory,
maxDBSizeInKB: input.maxDatabaseSize, // Defaults to 10 GB
macosApplicationGroup: input.macosApplicationGroup,
);
Expand Down Expand Up @@ -238,7 +238,11 @@ Future<void> _worker(
root.close();

if (cmd.args['deleteRoot'] == true) {
input.rootDirectory.deleteSync(recursive: true);
if (input.rootDirectory.startsWith(Store.inMemoryPrefix)) {
Store.removeDbFiles(input.rootDirectory);
} else {
Directory(input.rootDirectory).deleteSync(recursive: true);
}
}

sendRes(id: cmd.id);
Expand All @@ -248,8 +252,8 @@ Future<void> _worker(
sendRes(
id: cmd.id,
data: {
'size': Store.dbFileSize(input.rootDirectory.absolute.path) /
1024, // Convert to KiB
'size':
Store.dbFileSize(input.rootDirectory) / 1024, // Convert to KiB
},
);
case _WorkerCmdType.rootSize:
Expand Down Expand Up @@ -318,7 +322,7 @@ Future<void> _worker(
size: 0,
hits: 0,
misses: 0,
metadataJson: '',
metadataJson: '{}',
),
mode: PutMode.insert,
);
Expand Down Expand Up @@ -465,6 +469,8 @@ Future<void> _worker(
case _WorkerCmdType.writeTile:
final storeName = cmd.args['storeName']! as String;
final url = cmd.args['url']! as String;

// TODO: `null` `bytes` is never actually used. Do we need to keep it?
final bytes = cmd.args['bytes'] as Uint8List?;

final tiles = root.box<ObjectBoxTile>();
Expand Down Expand Up @@ -509,10 +515,6 @@ Future<void> _worker(
..length += 1
..size += existingTile.bytes.lengthInBytes,
);
updateRootStatistics(
deltaLength: 1,
deltaSize: existingTile.bytes.lengthInBytes,
);
}
case (false, false): // Existing tile, update required
final storesToUpdate = <String, ObjectBoxStore>{};
Expand Down Expand Up @@ -715,12 +717,12 @@ Future<void> _worker(
(throw StoreNotExists(storeName: storeName));
query.close();

final Map<String, dynamic> json =
store.metadataJson == '' ? {} : jsonDecode(store.metadataJson);
json[key] = value;

stores.put(
store..metadataJson = jsonEncode(json),
store
..metadataJson = jsonEncode(
(jsonDecode(store.metadataJson) as Map<String, dynamic>)
..[key] = value,
),
mode: PutMode.update,
);
},
Expand All @@ -743,13 +745,12 @@ Future<void> _worker(
(throw StoreNotExists(storeName: storeName));
query.close();

final Map<String, dynamic> json =
store.metadataJson == '' ? {} : jsonDecode(store.metadataJson);
// ignore: cascade_invocations
json.addAll(kvs);

stores.put(
store..metadataJson = jsonEncode(json),
store
..metadataJson = jsonEncode(
(jsonDecode(store.metadataJson) as Map<String, dynamic>)
..addAll(kvs),
),
mode: PutMode.update,
);
},
Expand All @@ -776,8 +777,8 @@ Future<void> _worker(
query.close();

final metadata =
jsonDecode(store.metadataJson) as Map<String, String>;
final removedVal = metadata.remove(key);
jsonDecode(store.metadataJson) as Map<String, dynamic>;
final removedVal = metadata.remove(key) as String?;

stores.put(
store..metadataJson = jsonEncode(metadata),
Expand Down Expand Up @@ -805,7 +806,7 @@ Future<void> _worker(
query.close();

stores.put(
store..metadataJson = jsonEncode(<String, String>{}),
store..metadataJson = '{}',
mode: PutMode.update,
);
},
Expand Down Expand Up @@ -903,7 +904,7 @@ Future<void> _worker(

final outputDir = path.dirname(outputPath);

if (outputDir == input.rootDirectory.absolute.path) {
if (path.equals(outputDir, input.rootDirectory)) {
throw ExportInRootDirectoryForbidden();
}

Expand Down Expand Up @@ -1028,8 +1029,7 @@ Future<void> _worker(
final strategy = cmd.args['strategy'] as ImportConflictStrategy;
final storesToImport = cmd.args['stores'] as List<String>?;

final importDir =
path.join(input.rootDirectory.absolute.path, 'import_tmp');
final importDir = path.join(input.rootDirectory, 'import_tmp');
final importDirIO = Directory(importDir)..createSync();

final importFile =
Expand Down Expand Up @@ -1437,8 +1437,7 @@ Future<void> _worker(
case _WorkerCmdType.listImportableStores:
final importPath = cmd.args['path']! as String;

final importDir =
path.join(input.rootDirectory.absolute.path, 'import_tmp');
final importDir = path.join(input.rootDirectory, 'import_tmp');
final importDirIO = Directory(importDir)..createSync();

final importFile =
Expand Down
14 changes: 7 additions & 7 deletions lib/src/backend/interfaces/backend/internal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
// A full license can be found at .\LICENSE

import 'dart:async';
import 'dart:io';
import 'dart:typed_data';

import 'package:meta/meta.dart';

import '../../../../flutter_map_tile_caching.dart';
import '../../export_internal.dart';

Expand Down Expand Up @@ -32,12 +33,6 @@ abstract interface class FMTCBackendInternal
/// Generic description/name of this backend
abstract final String friendlyIdentifier;

/// The filesystem directory in use
///
/// May also be used as an indicator as to whether the root has been
/// initialised.
Directory? get rootDirectory;

/// {@template fmtc.backend.realSize}
/// Retrieve the actual total size of the database in KiBs
///
Expand Down Expand Up @@ -86,6 +81,8 @@ abstract interface class FMTCBackendInternal
///
/// This operation cannot be undone! Ensure you confirm with the user that
/// this action is expected.
///
/// Does nothing if the store does not already exist.
/// {@endtemplate}
Future<void> deleteStore({
required String storeName,
Expand All @@ -99,6 +96,8 @@ abstract interface class FMTCBackendInternal
///
/// This operation cannot be undone! Ensure you confirm with the user that
/// this action is expected.
///
/// Does nothing if the store does not already exist.
/// {@endtemplate}
Future<void> resetStore({
required String storeName,
Expand Down Expand Up @@ -173,6 +172,7 @@ abstract interface class FMTCBackendInternal
/// * `null` : if there was no existing tile
/// * `true` : if the tile itself could be deleted (it was orphaned)
/// * `false`: if the tile still belonged to at least one other store
@visibleForTesting
Future<bool?> deleteTile({
required String storeName,
required String url,
Expand Down
2 changes: 0 additions & 2 deletions lib/src/bulk_download/manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ part of '../../flutter_map_tile_caching.dart';
Future<void> _downloadManager(
({
SendPort sendPort,
String rootDirectory,
DownloadableRegion region,
String storeName,
int parallelThreads,
Expand Down Expand Up @@ -189,7 +188,6 @@ Future<void> _downloadManager(
(
sendPort: downloadThreadReceivePort.sendPort,
storeName: input.storeName,
rootDirectory: input.rootDirectory,
options: input.region.options,
maxBufferLength: threadBufferLength,
skipExistingTiles: input.skipExistingTiles,
Expand Down
1 change: 0 additions & 1 deletion lib/src/bulk_download/thread.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ Future<void> _singleDownloadThread(
({
SendPort sendPort,
String storeName,
String rootDirectory,
TileLayer options,
int maxBufferLength,
bool skipExistingTiles,
Expand Down
1 change: 0 additions & 1 deletion lib/src/store/download.dart
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,6 @@ class DownloadManagement {
_downloadManager,
(
sendPort: receivePort.sendPort,
rootDirectory: FMTCBackendAccess.internal.rootDirectory!.absolute.path,
region: region,
storeName: _storeDirectory.storeName,
parallelThreads: parallelThreads,
Expand Down
2 changes: 1 addition & 1 deletion lib/src/store/metadata.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class StoreMetadata {
.setBulkMetadata(storeName: _storeName, kvs: kvs);

/// {@macro fmtc.backend.removeMetadata}
Future<void> remove({
Future<String?> remove({
required String key,
}) =>
FMTCBackendAccess.internal
Expand Down
3 changes: 3 additions & 0 deletions lib/src/store/store.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,7 @@ class FMTCStore {

@override
int get hashCode => storeName.hashCode;

@override
String toString() => 'FMTCStore(storeName: $storeName)';
}
Loading

0 comments on commit 75cf594

Please sign in to comment.