diff --git a/lib/services/file_service.dart b/lib/services/file_service.dart index f1ea177..911d013 100644 --- a/lib/services/file_service.dart +++ b/lib/services/file_service.dart @@ -4,10 +4,18 @@ /// 撮影画像の保存・命名・削除を行うサービス. class FileService { - static const _basePath = '/storage/emulated/0/Pictures/MiniTIAS'; + /// 保存先ディレクトリは省略可能(省略時は本番の共有ストレージパス). + /// + /// テストでは一時ディレクトリを渡して実メソッドを直接検証できる. + FileService({String? directoryPath}) + : _directoryPath = directoryPath ?? _defaultBasePath; + + static const _defaultBasePath = '/storage/emulated/0/Pictures/MiniTIAS'; + + final String _directoryPath; /// 保存先ディレクトリのパスを返す. - String get directoryPath => _basePath; + String get directoryPath => _directoryPath; /// [directoryPath] が存在しない場合,再帰的に作成する. Future _ensureDirectory() async { diff --git a/test/services/file_service_test.dart b/test/services/file_service_test.dart index bfef402..00d5f5d 100644 --- a/test/services/file_service_test.dart +++ b/test/services/file_service_test.dart @@ -79,11 +79,11 @@ group('FileService.saveImage', () { late Directory tempDir; - late _FileServiceTestable service; + late FileService service; setUp(() async { tempDir = await Directory.systemTemp.createTemp('smtias_capture_test_'); - service = _FileServiceTestable(tempDir.path); + service = FileService(directoryPath: tempDir.path); }); tearDown(() async { @@ -151,7 +151,9 @@ test('ディレクトリが存在しない場合でも自動作成してファイルを保存する', () async { final nonExistentSubDir = Directory('${tempDir.path}/sub/dir'); - final serviceWithSubDir = _FileServiceTestable(nonExistentSubDir.path); + final serviceWithSubDir = FileService( + directoryPath: nonExistentSubDir.path, + ); expect(await nonExistentSubDir.exists(), isFalse); final pngBytes = Uint8List.fromList([0, 1, 2, 3]); @@ -169,13 +171,13 @@ group('FileService.savePreviewJpeg', () { late Directory tempDir; - late _FileServiceTestablePreview service; + late FileService service; setUp(() async { tempDir = await Directory.systemTemp.createTemp( 'smtias_capture_preview_test_', ); - service = _FileServiceTestablePreview(tempDir.path); + service = FileService(directoryPath: tempDir.path); }); tearDown(() async { @@ -221,8 +223,8 @@ test('ディレクトリが存在しない場合でも自動作成してファイルを保存する', () async { final nonExistentSubDir = Directory('${tempDir.path}/sub/dir'); - final serviceWithSubDir = _FileServiceTestablePreview( - nonExistentSubDir.path, + final serviceWithSubDir = FileService( + directoryPath: nonExistentSubDir.path, ); expect(await nonExistentSubDir.exists(), isFalse); @@ -332,13 +334,13 @@ group('FileService.saveQuantitativeMetadata', () { late Directory tempDir; - late _FileServiceTestableQuantitative service; + late FileService service; setUp(() async { tempDir = await Directory.systemTemp.createTemp( 'smtias_capture_qm_test_', ); - service = _FileServiceTestableQuantitative(tempDir.path); + service = FileService(directoryPath: tempDir.path); }); tearDown(() async { @@ -438,8 +440,8 @@ test('ディレクトリが存在しない場合でも自動作成してファイルを保存する', () async { final nonExistentSubDir = Directory('${tempDir.path}/sub/dir'); - final serviceWithSubDir = _FileServiceTestableQuantitative( - nonExistentSubDir.path, + final serviceWithSubDir = FileService( + directoryPath: nonExistentSubDir.path, ); expect(await nonExistentSubDir.exists(), isFalse); @@ -451,111 +453,73 @@ expect(await File(path).exists(), isTrue); }); }); -} -/// テスト用: 保存先ディレクトリをオーバーライドできる FileService サブクラス. -class _FileServiceTestable extends FileService { - _FileServiceTestable(this._testBasePath); + group('FileService.saveDiagnosticsJson', () { + late Directory tempDir; + late FileService service; - final String _testBasePath; + setUp(() async { + tempDir = await Directory.systemTemp.createTemp( + 'smtias_capture_diag_test_', + ); + service = FileService(directoryPath: tempDir.path); + }); - @override - String get directoryPath => _testBasePath; + tearDown(() async { + if (await tempDir.exists()) { + await tempDir.delete(recursive: true); + } + }); - @override - Future saveImage(Uint8List pngBytes) async { - final directory = Directory(_testBasePath); - if (!await directory.exists()) { - await directory.create(recursive: true); - } - final fileName = await _generateFileNameForTest(); - final filePath = '$_testBasePath/$fileName'; - await File(filePath).writeAsBytes(pngBytes); - return filePath; - } + test('camera_diagnostics_ プレフィックスの .json ファイルが生成される', () async { + final diagnostics = {'make': 'TestMake'}; + final path = await service.saveDiagnosticsJson(diagnostics); - Future _generateFileNameForTest() async { - final now = DateTime.now(); - final timestamp = - '${now.year}' - '${now.month.toString().padLeft(2, '0')}' - '${now.day.toString().padLeft(2, '0')}' - '_' - '${now.hour.toString().padLeft(2, '0')}' - '${now.minute.toString().padLeft(2, '0')}' - '${now.second.toString().padLeft(2, '0')}'; + final fileName = path.split('/').last; + expect(fileName, startsWith('camera_diagnostics_')); + expect(fileName, endsWith('.json')); + expect(await File(path).exists(), isTrue); + }); - final baseName = 'MiniTIAS_$timestamp'; - final candidate = '$baseName.png'; + test('診断内容が JSON として保持される', () async { + final diagnostics = { + 'make': 'TestMake', + 'model': 'TestModel', + 'sensor': { + 'iso_range': [40, 800], + }, + }; + final path = await service.saveDiagnosticsJson(diagnostics); - if (!await File('$_testBasePath/$candidate').exists()) { - return candidate; - } + final jsonStr = await File(path).readAsString(); + final decoded = jsonDecode(jsonStr) as Map; + expect(decoded['make'], 'TestMake'); + expect(decoded['model'], 'TestModel'); + expect((decoded['sensor'] as Map)['iso_range'], [ + 40, + 800, + ]); + }); - var suffix = 1; - while (await File('$_testBasePath/${baseName}_$suffix.png').exists()) { - suffix++; - } - return '${baseName}_$suffix.png'; - } -} + test('インデント付き(pretty print)で書き込まれる', () async { + final diagnostics = {'make': 'TestMake'}; + final path = await service.saveDiagnosticsJson(diagnostics); -/// テスト用: savePreviewJpeg の保存先をオーバーライドする FileService サブクラス. -class _FileServiceTestablePreview extends FileService { - _FileServiceTestablePreview(this._testBasePath); + final jsonStr = await File(path).readAsString(); + // JsonEncoder.withIndent(' ') により改行とインデントを含む + expect(jsonStr, contains('\n')); + expect(jsonStr, contains(' "make"')); + }); - final String _testBasePath; + test('ディレクトリが存在しない場合でも自動作成して保存する', () async { + final nonExistentSubDir = Directory('${tempDir.path}/sub/dir'); + final serviceWithSubDir = FileService( + directoryPath: nonExistentSubDir.path, + ); + expect(await nonExistentSubDir.exists(), isFalse); - @override - String get directoryPath => _testBasePath; - - @override - Future savePreviewJpeg( - String baseName, - Uint8List previewBytes, - ) async { - final directory = Directory(_testBasePath); - if (!await directory.exists()) { - await directory.create(recursive: true); - } - final filePath = '$_testBasePath/$baseName.preview.jpg'; - await File(filePath).writeAsBytes(previewBytes); - return filePath; - } -} - -/// テスト用: saveQuantitativeMetadata の保存先をオーバーライドする FileService サブクラス. -class _FileServiceTestableQuantitative extends FileService { - _FileServiceTestableQuantitative(this._testBasePath); - - final String _testBasePath; - - @override - String get directoryPath => _testBasePath; - - @override - Future saveQuantitativeMetadata( - String baseName, - Map metadata, { - List>? lscMap, - int? lscMapRowCount, - int? lscMapColumnCount, - }) async { - final directory = Directory(_testBasePath); - if (!await directory.exists()) { - await directory.create(recursive: true); - } - - final Map jsonMap = Map.from(metadata); - if (lscMap != null) { - jsonMap['lscMap'] = lscMap; - jsonMap['lscMapRowCount'] = lscMapRowCount; - jsonMap['lscMapColumnCount'] = lscMapColumnCount; - } - - const encoder = JsonEncoder.withIndent(' '); - final filePath = '$_testBasePath/$baseName.meta.json'; - await File(filePath).writeAsString(encoder.convert(jsonMap)); - return filePath; - } + final path = await serviceWithSubDir.saveDiagnosticsJson({'make': 'X'}); + expect(await File(path).exists(), isTrue); + }); + }); }