mirror of
https://github.com/immich-app/immich
synced 2026-08-15 13:03:57 +00:00
* fix(mobile): key local image caches by content so on-device edits re-render * use Object.hash and the local asset factory
93 lines
3 KiB
Dart
93 lines
3 KiB
Dart
import 'package:flutter/painting.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
|
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
|
|
import 'package:immich_mobile/presentation/widgets/images/local_image_provider.dart';
|
|
|
|
import '../../../unit/factories/local_asset_factory.dart';
|
|
|
|
class _StubCompleter extends ImageStreamCompleter {}
|
|
|
|
void main() {
|
|
late ImageCache cache;
|
|
late int loads;
|
|
|
|
ImageStreamCompleter load() {
|
|
loads++;
|
|
return _StubCompleter();
|
|
}
|
|
|
|
setUp(() {
|
|
cache = ImageCache();
|
|
loads = 0;
|
|
});
|
|
|
|
group('LocalThumbProvider caching', () {
|
|
test('editing on device re-renders the thumbnail', () {
|
|
cache.putIfAbsent(LocalThumbProvider(id: 'asset-1', assetType: AssetType.image, checksum: 'before'), load);
|
|
cache.putIfAbsent(LocalThumbProvider(id: 'asset-1', assetType: AssetType.image, checksum: 'after'), load);
|
|
|
|
expect(loads, 2);
|
|
});
|
|
|
|
test('an unchanged thumbnail still comes from the cache', () {
|
|
cache.putIfAbsent(LocalThumbProvider(id: 'asset-1', assetType: AssetType.image, checksum: 'same'), load);
|
|
cache.putIfAbsent(LocalThumbProvider(id: 'asset-1', assetType: AssetType.image, checksum: 'same'), load);
|
|
|
|
expect(loads, 1);
|
|
});
|
|
|
|
// The rehash clears the checksum before writing the new one, so the tile has to
|
|
// follow that step too or it waits for the hash to land before showing the edit.
|
|
test('re-renders while the checksum is still being recomputed', () {
|
|
cache.putIfAbsent(LocalThumbProvider(id: 'asset-1', assetType: AssetType.image, checksum: 'before'), load);
|
|
cache.putIfAbsent(LocalThumbProvider(id: 'asset-1', assetType: AssetType.image), load);
|
|
|
|
expect(loads, 2);
|
|
});
|
|
|
|
test('stays cached while the checksum is missing', () {
|
|
cache.putIfAbsent(LocalThumbProvider(id: 'asset-1', assetType: AssetType.image), load);
|
|
cache.putIfAbsent(LocalThumbProvider(id: 'asset-1', assetType: AssetType.image), load);
|
|
|
|
expect(loads, 1);
|
|
});
|
|
});
|
|
|
|
group('factories', () {
|
|
test('thumbnails are keyed by the asset checksum', () {
|
|
final asset = LocalAssetFactory.create().copyWith(checksum: 'abc');
|
|
|
|
final provider = getThumbnailImageProvider(asset) as LocalThumbProvider;
|
|
|
|
expect(provider.checksum, 'abc');
|
|
});
|
|
});
|
|
|
|
group('LocalFullImageProvider caching', () {
|
|
test('editing on device re-renders the full image', () {
|
|
cache.putIfAbsent(
|
|
LocalFullImageProvider(
|
|
id: 'asset-1',
|
|
assetType: AssetType.image,
|
|
size: const Size(100, 100),
|
|
isAnimated: false,
|
|
checksum: 'before',
|
|
),
|
|
load,
|
|
);
|
|
cache.putIfAbsent(
|
|
LocalFullImageProvider(
|
|
id: 'asset-1',
|
|
assetType: AssetType.image,
|
|
size: const Size(100, 100),
|
|
isAnimated: false,
|
|
checksum: 'after',
|
|
),
|
|
load,
|
|
);
|
|
|
|
expect(loads, 2);
|
|
});
|
|
});
|
|
}
|