Add tests about SDAnimatedImage SDAnimatedImageView and SDAnimatedImage+WebCache

This commit is contained in:
DreamPiggy 2018-02-17 13:50:00 +08:00
parent 4563e714d7
commit 7e83d78ca3
7 changed files with 254 additions and 11 deletions

View File

@ -54,10 +54,10 @@
// These methods are for SDAnimatedImage class only but not for SDWebImageAnimatedCoder.
@optional
/**
Preload all frame image to memory. Then directly return the frame for index without decoding.
Preload all frame image to memory. Then later request can directly return the frame for index without decoding.
This method may be called on background thread.
@note If the image is shared by lots of imageViews, preload all frames will reduce the CPU cost because the decoder may not need to keep re-entrant for randomly index access.
@note If the image is shared by lots of imageViews, preload all frames will reduce the CPU cost because the decoder may not need to keep re-entrant for randomly index access. But this will cause more memory usage.
*/
- (void)preloadAllFrames;
@ -102,11 +102,11 @@
#endif
/**
Preload all frame image to memory. Then directly return the frame for index without decoding.
Preload all frame image to memory. Then later request can directly return the frame for index without decoding.
The preloaded animated image frames will be removed when receiving memory warning.
@note If the image is shared by lots of imageViews, preload all frames will reduce the CPU cost because the decoder may not need to keep re-entrant for randomly index access.
@note Once preload the frames into memory, there is no huge differenec on performance between UIImage's `animatedImageWithImages:duration:` for UIKit. But UIImage's animation have some issue such like blanking or frame resetting. It's recommend to use only if need.
@note If the image is shared by lots of imageViews, preload all frames will reduce the CPU cost because the decoder may not need to keep re-entrant for randomly index access. But this will cause more memory usage.
@note Once preload the frames into memory, there is no huge difference on performance between this and UIImage's `animatedImageWithImages:duration:`. But UIImage's animation have some issue such like blanking or frame restarting working with `UIImageView`. It's recommend to use only if need.
*/
- (void)preloadAllFrames;

View File

@ -387,9 +387,8 @@ didReceiveResponse:(NSURLResponse *)response
if (shouldDecode) {
image = [SDWebImageCoderHelper decodedImageWithImage:image];
}
if (!finished) {
image.sd_isIncremental = YES;
}
// mark the image as progressive (completionBlock one are not mark as progressive)
image.sd_isIncremental = YES;
// We do not keep the progressive decoding image even when `finished`=YES. Because they are for view rendering but not take full function from downloader options. And some coders implementation may not keep consistent between progressive decoding and normal decoding.

View File

@ -34,6 +34,7 @@
32B99EAC203B36650017FD66 /* SDWebImageDownloaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3C51E819B46E370092B5E6 /* SDWebImageDownloaderTests.m */; };
32B99EAD203B36690017FD66 /* SDWebImagePrefetcherTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4369C1D01D97F80F007E863A /* SDWebImagePrefetcherTests.m */; };
32B99EAE203B366C0017FD66 /* SDWebCacheCategoriesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4369C2731D9804B1007E863A /* SDWebCacheCategoriesTests.m */; };
32A571562037DB2D002EDAAE /* SDAnimatedImageTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A571552037DB2D002EDAAE /* SDAnimatedImageTest.m */; };
32E6F0321F3A1B4700A945E6 /* SDWebImageTestDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 32E6F0311F3A1B4700A945E6 /* SDWebImageTestDecoder.m */; };
37D122881EC48B5E00D98CEB /* SDMockFileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 37D122871EC48B5E00D98CEB /* SDMockFileManager.m */; };
433BBBB51D7EF5C00086B6E9 /* SDWebImageDecoderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 433BBBB41D7EF5C00086B6E9 /* SDWebImageDecoderTests.m */; };
@ -66,6 +67,7 @@
32B99E8A203AF8690017FD66 /* SDCategoriesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDCategoriesTests.m; sourceTree = "<group>"; };
32B99E92203B2DF90017FD66 /* Tests Mac.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tests Mac.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
32B99E96203B2DF90017FD66 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
32A571552037DB2D002EDAAE /* SDAnimatedImageTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDAnimatedImageTest.m; sourceTree = "<group>"; };
32E6F0301F3A1B4700A945E6 /* SDWebImageTestDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebImageTestDecoder.h; sourceTree = "<group>"; };
32E6F0311F3A1B4700A945E6 /* SDWebImageTestDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageTestDecoder.m; sourceTree = "<group>"; };
37D122861EC48B5E00D98CEB /* SDMockFileManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDMockFileManager.h; sourceTree = "<group>"; };
@ -195,6 +197,7 @@
3254C31F20641077008D1022 /* SDWebImageTransformerTests.m */,
4369C2731D9804B1007E863A /* SDWebCacheCategoriesTests.m */,
32B99E8A203AF8690017FD66 /* SDCategoriesTests.m */,
32A571552037DB2D002EDAAE /* SDAnimatedImageTest.m */,
37D122861EC48B5E00D98CEB /* SDMockFileManager.h */,
37D122871EC48B5E00D98CEB /* SDMockFileManager.m */,
2D7AF05E1F329763000083C2 /* SDTestCase.h */,
@ -469,6 +472,7 @@
files = (
32E6F0321F3A1B4700A945E6 /* SDWebImageTestDecoder.m in Sources */,
3254C32020641077008D1022 /* SDWebImageTransformerTests.m in Sources */,
32A571562037DB2D002EDAAE /* SDAnimatedImageTest.m in Sources */,
1E3C51E919B46E370092B5E6 /* SDWebImageDownloaderTests.m in Sources */,
37D122881EC48B5E00D98CEB /* SDMockFileManager.m in Sources */,
4369C2741D9804B1007E863A /* SDWebCacheCategoriesTests.m in Sources */,

View File

@ -0,0 +1,232 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
* (c) Matt Galloway
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDTestCase.h"
#import <SDWebImage/SDAnimatedImage.h>
#import <SDWebImage/SDAnimatedImageView.h>
#import <SDWebImage/SDWebImageGIFCoder.h>
#import <SDWebImage/UIImage+WebCache.h>
#import <SDWebImage/SDAnimatedImageView+WebCache.h>
#import <KVOController/KVOController.h>
static const NSUInteger kTestGIFFrameCount = 5; // local TestImage.gif loop count
@interface SDAnimatedImageTest : SDTestCase
@property (nonatomic, strong) UIWindow *window;
@end
@implementation SDAnimatedImageTest
- (void)tearDown {
[[SDImageCache sharedImageCache] removeImageForKey:kTestGIFURL fromDisk:YES withCompletion:nil];
[[SDImageCache sharedImageCache] removeImageForKey:kTestWebPURL fromDisk:YES withCompletion:nil];
}
- (void)test01AnimatedImageInitWithData {
NSData *invalidData = [@"invalid data" dataUsingEncoding:NSUTF8StringEncoding];
SDAnimatedImage *image = [[SDAnimatedImage alloc] initWithData:invalidData];
expect(image).beNil();
NSData *validData = [self testGIFData];
image = [[SDAnimatedImage alloc] initWithData:validData scale:2];
expect(image).notTo.beNil(); // image
expect(image.scale).equal(2); // scale
expect(image.animatedImageData).equal(validData); // data
expect(image.animatedImageFormat).equal(SDImageFormatGIF); // format
expect(image.animatedImageLoopCount).equal(0); // loop count
expect(image.animatedImageFrameCount).equal(kTestGIFFrameCount); // frame count
expect([image animatedImageFrameAtIndex:1]).notTo.beNil(); // 1 frame
}
- (void)test02AnimatedImageInitWithContentsOfFile {
SDAnimatedImage *image = [[SDAnimatedImage alloc] initWithContentsOfFile:[self testGIFPath]];
expect(image).notTo.beNil();
expect(image.scale).equal(1); // scale
// enough, other can be test with InitWithData
}
- (void)test03AnimatedImageInitWithAnimatedCoder {
NSData *validData = [self testGIFData];
SDWebImageGIFCoder *coder = [[SDWebImageGIFCoder alloc] initWithAnimatedImageData:validData];
SDAnimatedImage *image = [[SDAnimatedImage alloc] initWithAnimatedCoder:coder scale:1];
expect(image).notTo.beNil();
// enough, other can be test with InitWithData
}
- (void)test04AnimatedImagePreloadFrames {
NSData *validData = [self testGIFData];
SDAnimatedImage *image = [SDAnimatedImage imageWithData:validData];
// Preload all frames
[image preloadAllFrames];
NSArray *preloadAnimatedImageFrames = [image valueForKey:@"preloadAnimatedImageFrames"];
expect(preloadAnimatedImageFrames.count).equal(kTestGIFFrameCount);
// Test one frame
UIImage *frame = [image animatedImageFrameAtIndex:0];
expect(frame).notTo.beNil();
}
- (void)test05AnimatedImageViewSetImage {
SDAnimatedImageView *imageView = [SDAnimatedImageView new];
UIImage *image = [UIImage imageWithData:[self testJPEGData]];
imageView.image = image;
expect(imageView.image).notTo.beNil();
expect(imageView.currentFrame).beNil(); // current frame
}
- (void)test06AnimatedImageViewSetAnimatedImage {
SDAnimatedImageView *imageView = [SDAnimatedImageView new];
SDAnimatedImage *image = [SDAnimatedImage imageWithData:[self testAnimatedWebPData]];
imageView.image = image;
expect(imageView.image).notTo.beNil();
expect(imageView.currentFrame).notTo.beNil(); // current frame
}
- (void)test07AnimatedImageViewRendering {
XCTestExpectation *expectation = [self expectationWithDescription:@"test SDAnimatedImageView rendering"];
SDAnimatedImageView *imageView = [[SDAnimatedImageView alloc] init];
[self.window addSubview:imageView];
NSMutableDictionary *frames = [NSMutableDictionary dictionaryWithCapacity:kTestGIFFrameCount];
[self.KVOController observe:imageView keyPaths:@[NSStringFromSelector(@selector(currentFrameIndex)), NSStringFromSelector(@selector(currentLoopCount))] options:NSKeyValueObservingOptionNew block:^(id _Nullable observer, id _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
NSUInteger frameIndex = imageView.currentFrameIndex;
NSUInteger loopCount = imageView.currentLoopCount;
[frames setObject:@(YES) forKey:@(frameIndex)];
BOOL framesRendered = NO;
if (frames.count >= kTestGIFFrameCount) {
// All frames rendered
framesRendered = YES;
}
BOOL loopFinished = NO;
if (loopCount >= 1) {
// One loop finished
loopFinished = YES;
}
if (framesRendered && loopFinished) {
[imageView stopAnimating];
[expectation fulfill];
}
}];
SDAnimatedImage *image = [SDAnimatedImage imageWithData:[self testGIFData]];
imageView.image = image;
[self waitForExpectationsWithCommonTimeout];
}
- (void)test08AnimatedImageViewSetProgressiveAnimatedImage {
NSData *gifData = [self testGIFData];
SDWebImageGIFCoder *progressiveCoder = [[SDWebImageGIFCoder alloc] initIncremental];
// simulate progressive decode, pass partial data
NSData *partialData = [gifData subdataWithRange:NSMakeRange(0, gifData.length - 1)];
[progressiveCoder updateIncrementalData:partialData finished:NO];
SDAnimatedImage *partialImage = [[SDAnimatedImage alloc] initWithAnimatedCoder:progressiveCoder scale:1];
partialImage.sd_isIncremental = YES;
SDAnimatedImageView *imageView = [[SDAnimatedImageView alloc] init];
imageView.image = partialImage;
BOOL isProgressive = [[imageView valueForKey:@"isProgressive"] boolValue];
expect(isProgressive).equal(YES);
// pass full data
[progressiveCoder updateIncrementalData:gifData finished:YES];
SDAnimatedImage *fullImage = [[SDAnimatedImage alloc] initWithAnimatedCoder:progressiveCoder scale:1];
imageView.image = fullImage;
isProgressive = [[imageView valueForKey:@"isProgressive"] boolValue];
expect(isProgressive).equal(NO);
}
- (void)test09AnimatedImageViewCategory {
XCTestExpectation *expectation = [self expectationWithDescription:@"test SDAnimatedImageView view category"];
SDAnimatedImageView *imageView = [SDAnimatedImageView new];
NSURL *testURL = [NSURL URLWithString:kTestWebPURL];
[imageView sd_setImageWithURL:testURL completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
expect(error).to.beNil();
expect(image).notTo.beNil();
expect([image isKindOfClass:[SDAnimatedImage class]]).beTruthy();
[expectation fulfill];
}];
[self waitForExpectationsWithCommonTimeout];
}
- (void)test10AnimatedImageViewCategoryProgressive {
XCTestExpectation *expectation = [self expectationWithDescription:@"test SDAnimatedImageView view category"];
SDAnimatedImageView *imageView = [SDAnimatedImageView new];
NSURL *testURL = [NSURL URLWithString:kTestGIFURL];
[imageView sd_setImageWithURL:testURL placeholderImage:nil options:SDWebImageProgressiveDownload progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *image = imageView.image;
// Progressive image may be nil when download data is not enough
if (image) {
expect(image.sd_isIncremental).beTruthy();
BOOL isProgressive = [[imageView valueForKey:@"isProgressive"] boolValue];
expect(isProgressive).equal(YES);
}
});
} completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
expect(error).to.beNil();
expect(image).notTo.beNil();
expect([image isKindOfClass:[SDAnimatedImage class]]).beTruthy();
[expectation fulfill];
}];
[self waitForExpectationsWithCommonTimeout];
}
#pragma mark - Helper
- (UIWindow *)window {
if (!_window) {
_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
}
return _window;
}
- (NSString *)testGIFPath {
NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
NSString *testPath = [testBundle pathForResource:@"TestImage" ofType:@"gif"];
return testPath;
}
- (NSData *)testGIFData {
NSData *testData = [NSData dataWithContentsOfFile:[self testGIFPath]];
return testData;
}
- (NSString *)testAnimatedWebPPath {
NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
NSString *testPath = [testBundle pathForResource:@"TestImageAnimated" ofType:@"webp"];
return testPath;
}
- (NSData *)testAnimatedWebPData {
return [NSData dataWithContentsOfFile:[self testAnimatedWebPPath]];
}
- (NSString *)testJPEGPath {
NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
NSString *testPath = [testBundle pathForResource:@"TestImage" ofType:@"jpg"];
return testPath;
}
- (NSData *)testJPEGData {
NSData *testData = [NSData dataWithContentsOfFile:[self testJPEGPath]];
return testData;
}
@end

View File

@ -17,6 +17,8 @@ FOUNDATION_EXPORT const int64_t kAsyncTestTimeout;
FOUNDATION_EXPORT const int64_t kMinDelayNanosecond;
FOUNDATION_EXPORT NSString * _Nonnull const kTestJpegURL;
FOUNDATION_EXPORT NSString * _Nonnull const kTestPNGURL;
FOUNDATION_EXPORT NSString * _Nonnull const kTestGIFURL;
FOUNDATION_EXPORT NSString * _Nonnull const kTestWebPURL;
@interface SDTestCase : XCTestCase

View File

@ -13,6 +13,8 @@ const int64_t kAsyncTestTimeout = 5;
const int64_t kMinDelayNanosecond = NSEC_PER_MSEC * 100; // 0.1s
NSString *const kTestJpegURL = @"http://via.placeholder.com/50x50.jpg";
NSString *const kTestPNGURL = @"http://via.placeholder.com/50x50.png";
NSString *const kTestGIFURL = @"https://media.giphy.com/media/UEsrLdv7ugRTq/giphy.gif";
NSString *const kTestWebPURL = @"http://littlesvr.ca/apng/images/SteamEngine.webp";
@implementation SDTestCase

View File

@ -25,7 +25,7 @@
return image;
}
- (instancetype)initIncrementally
- (instancetype)initIncremental
{
self = [super init];
if (self) {
@ -33,13 +33,17 @@
return self;
}
- (UIImage *)incrementallyDecodedImageWithData:(NSData *)data finished:(BOOL)finished {
- (void)updateIncrementalData:(NSData *)data finished:(BOOL)finished {
return;
}
- (UIImage *)incrementalDecodedImageWithOptions:(SDWebImageCoderOptions *)options {
NSString * testImagePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"TestImage" ofType:@"gif"];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:testImagePath];
return image;
}
- (BOOL)canIncrementallyDecodeFromData:(nullable NSData *)data {
- (BOOL)canIncrementalDecodeFromData:(NSData *)data {
return YES;
}