[image_picker] Add native tests for image, metadata, photo-asset, and PHPicker save utilities - #12536
Conversation
… and PHPicker save utilities.
There was a problem hiding this comment.
Code Review
This pull request adds comprehensive unit tests for image, metadata, photo asset, and path operation utilities in the iOS image picker package. The review feedback highlights opportunities to improve test robustness and hygiene, specifically by cleaning up temporary files and guarding against potential crashes caused by passing nil URLs to file management APIs.
| XCTAssertNotNil(destination); | ||
| XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:destination.path]); | ||
| [[NSFileManager defaultManager] removeItemAtPath:sourcePath error:nil]; | ||
| [[NSFileManager defaultManager] removeItemAtURL:destination error:nil]; |
There was a problem hiding this comment.
If saveVideoFromURL: returns nil, destination will be nil. Passing a nil URL to removeItemAtURL:error: throws an NSInvalidArgumentException and will crash the test runner instead of failing the test gracefully. We should guard the cleanup call and the file existence check to ensure they only run if destination is non-nil.
XCTAssertNotNil(destination);
if (destination) {
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:destination.path]);
}
[[NSFileManager defaultManager] removeItemAtPath:sourcePath error:nil];
if (destination) {
[[NSFileManager defaultManager] removeItemAtURL:destination error:nil];
}There was a problem hiding this comment.
Fixed! Good catch, XCTAssertNotNil doesn’t abort the test, so removeItemAtURL: could be called with nil and throw. Guarded the existence check and cleanup so they only run when destination is non-nil
| XCTAssertEqualObjects([NSURL fileURLWithPath:savedPath].pathExtension, @"jpg"); | ||
| } |
There was a problem hiding this comment.
The temporary file created at savedPath is not cleaned up after the test completes, which leaks files in the temporary directory. We should remove the file at savedPath at the end of the test.
XCTAssertEqualObjects([NSURL fileURLWithPath:savedPath].pathExtension, @"jpg");
if (savedPath) {
[[NSFileManager defaultManager] removeItemAtPath:savedPath error:nil];
}
}There was a problem hiding this comment.
Agreed and fixed. The test now removes savedPath after the assertion.
| savedPathBlock:^(NSString *savedPath, FlutterError *error) { | ||
| XCTAssertNil(error); | ||
| XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:savedPath]); | ||
| [pathExpectation fulfill]; | ||
| }]; |
There was a problem hiding this comment.
The temporary file created at savedPath by the operation is not cleaned up, leaking files in the temporary directory. We should delete the file at savedPath inside the completion block.
| savedPathBlock:^(NSString *savedPath, FlutterError *error) { | |
| XCTAssertNil(error); | |
| XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:savedPath]); | |
| [pathExpectation fulfill]; | |
| }]; | |
| savedPathBlock:^(NSString *savedPath, FlutterError *error) { | |
| XCTAssertNil(error); | |
| XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:savedPath]); | |
| if (savedPath) { | |
| [[NSFileManager defaultManager] removeItemAtPath:savedPath error:nil]; | |
| } | |
| [pathExpectation fulfill]; | |
| }]; |
There was a problem hiding this comment.
Agreed and fixed. The copied file is now deleted after the operation finishes (in addition to the source .mov).
…onTests to ensure temporary files are removed after assertions. This prevents potential file clutter during test execution.
Adds native unit tests for previously untested paths in
FLTImagePickerImageUtil,FLTImagePickerMetaDataUtil,FLTImagePickerPhotoAssetUtil, andFLTPHPickerSaveImageToPathOperationbefore the Objective-C → Swift migration. Production code is unchanged.This is a tests-only change, so it does not bump the package version or CHANGELOG.
Native
RunnerTestsgo from 72 tests to 88 tests (+16).New test cases
ImageUtilTests:testScaledGIFImage_UsesClampedDelayWhenUnclampedDelayMissing— GIF scaling uses the clamped delay when unclamped delay is missingMetaDataUtilTests:testGetImageMIMETypeFromImageDataUnknownReturnsOther— unrecognized image bytes map to the Other MIME typetestConvertImageIgnoresQualityForPNG— PNG conversion ignores JPEG quality and stays PNGtestConvertImageDefaultsNonJPEGNonPNGToJPEG— GIF and other non-JPEG/PNG data convert to JPEGPhotoAssetUtilTests:testGetAssetFromImagePickerInfoShouldReturnNilIfNotAvailable— picker info without a PHAsset returnsniltestGetAssetFromImagePickerInfoShouldReturnAssetIfPresent— picker info with a PHAsset returns that assettestSaveVideoFromURLReturnsNilWhenSourceIsUnreadable— missing source file returnsniltestSaveVideoFromURLCopiesReadableFile— a readable video is copied to a new pathtestSaveVideoFromURLReturnsNilWhenCopyFails— copy failure returnsniltestSaveImageWithOriginalImageDataNilUsesDefaultJPEG—niloriginal data saves as.jpgtestCreateFileReturnsPathWhenWriteFails— create-file still returns a path when the write failsPickerSaveImageToPathOperationTests:testInitWithNilResultReturnsNil—nilPHPicker result does not create an operationtestStartWhenCancelledFinishesWithoutSaving— a cancelled operation finishes without calling the save callbacktestSaveVideoCopiesFile— a video item is copied to the saved pathtestSaveVideoFailsWhenLoadReturnsError— a load error is reported asinvalid_imagetestSaveVideoFailsWhenCopyFails— a copy error is reported asflutter_image_picker_copy_video_errorFirst part of
image_picker_ioscoverage backfill before the Obj-C → Swift migration for flutter/flutter#119107Pre-Review Checklist
[shared_preferences]///).If you need help, consider asking for advice on the #hackers-new channel on Discord.
Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the
gemini-code-assistbot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.Footnotes
Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. ↩ ↩2