From 873d9eddce23eac14fbaafc0586d03a9c605be4b Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Fri, 26 Apr 2019 19:03:29 +0800 Subject: [PATCH 1/5] Added many test case to ensure the code behavior. Upgrade the test coverage --- Configs/Test-Shared.xcconfig | 3 + .../project.pbxproj | 6 ++ Tests/Tests/SDAnimatedImageTest.m | 8 +- Tests/Tests/SDImageCacheTests.m | 74 ++++++++++++++++++ Tests/Tests/SDImageCoderTests.m | 9 +++ Tests/Tests/SDImageTransformerTests.m | 31 +++++++- Tests/Tests/SDUtilsTests.m | 77 +++++++++++++++++++ Tests/Tests/SDWebCacheCategoriesTests.m | 11 +++ Tests/Tests/SDWebImageDownloaderTests.m | 12 +++ 9 files changed, 228 insertions(+), 3 deletions(-) create mode 100644 Tests/Tests/SDUtilsTests.m diff --git a/Configs/Test-Shared.xcconfig b/Configs/Test-Shared.xcconfig index f8229336..3f259c96 100644 --- a/Configs/Test-Shared.xcconfig +++ b/Configs/Test-Shared.xcconfig @@ -5,3 +5,6 @@ // Implicitly include the named header. The path given should either be a project relative path or an absolute path. GCC_PREFIX_HEADER = + +// This is a list of paths to folders to be searched by the compiler for included or imported header files when compiling C, Objective-C, C++, or Objective-C++. Paths are delimited by whitespace, so any paths with spaces in them need to be properly quoted. +HEADER_SEARCH_PATHS = $(inherited) "$(SRCROOT)/../SDWebImage/Private" diff --git a/Tests/SDWebImage Tests.xcodeproj/project.pbxproj b/Tests/SDWebImage Tests.xcodeproj/project.pbxproj index ac0ead4a..c2cdde6c 100644 --- a/Tests/SDWebImage Tests.xcodeproj/project.pbxproj +++ b/Tests/SDWebImage Tests.xcodeproj/project.pbxproj @@ -10,6 +10,8 @@ 1E3C51E919B46E370092B5E6 /* SDWebImageDownloaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3C51E819B46E370092B5E6 /* SDWebImageDownloaderTests.m */; }; 2D7AF0601F329763000083C2 /* SDTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D7AF05F1F329763000083C2 /* SDTestCase.m */; }; 320630412085A37C006E0FA4 /* SDAnimatedImageTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A571552037DB2D002EDAAE /* SDAnimatedImageTest.m */; }; + 3222417F2272F808002429DB /* SDUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3222417E2272F808002429DB /* SDUtilsTests.m */; }; + 322241802272F808002429DB /* SDUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3222417E2272F808002429DB /* SDUtilsTests.m */; }; 3226ECBB20754F7700FAFACF /* SDWebImageTestDownloadOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 3226ECBA20754F7700FAFACF /* SDWebImageTestDownloadOperation.m */; }; 3226ECBC20754F7700FAFACF /* SDWebImageTestDownloadOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 3226ECBA20754F7700FAFACF /* SDWebImageTestDownloadOperation.m */; }; 323B8E1F20862322008952BE /* SDWebImageTestLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 323B8E1E20862322008952BE /* SDWebImageTestLoader.m */; }; @@ -77,6 +79,7 @@ 1E3C51E819B46E370092B5E6 /* SDWebImageDownloaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageDownloaderTests.m; sourceTree = ""; }; 2D7AF05E1F329763000083C2 /* SDTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDTestCase.h; sourceTree = ""; }; 2D7AF05F1F329763000083C2 /* SDTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDTestCase.m; sourceTree = ""; }; + 3222417E2272F808002429DB /* SDUtilsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDUtilsTests.m; sourceTree = ""; }; 3226ECB920754F7700FAFACF /* SDWebImageTestDownloadOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDWebImageTestDownloadOperation.h; sourceTree = ""; }; 3226ECBA20754F7700FAFACF /* SDWebImageTestDownloadOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageTestDownloadOperation.m; sourceTree = ""; }; 323B8E1D20862322008952BE /* SDWebImageTestLoader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDWebImageTestLoader.h; sourceTree = ""; }; @@ -231,6 +234,7 @@ 3254C31F20641077008D1022 /* SDImageTransformerTests.m */, 4369C2731D9804B1007E863A /* SDWebCacheCategoriesTests.m */, 32B99E8A203AF8690017FD66 /* SDCategoriesTests.m */, + 3222417E2272F808002429DB /* SDUtilsTests.m */, 32A571552037DB2D002EDAAE /* SDAnimatedImageTest.m */, 37D122861EC48B5E00D98CEB /* SDMockFileManager.h */, 37D122871EC48B5E00D98CEB /* SDMockFileManager.m */, @@ -489,6 +493,7 @@ 32B99E9D203B2F7D0017FD66 /* SDWebImageTestCoder.m in Sources */, 32B99E9E203B2F810017FD66 /* SDMockFileManager.m in Sources */, 32B99EAB203B36620017FD66 /* SDWebImageManagerTests.m in Sources */, + 322241802272F808002429DB /* SDUtilsTests.m in Sources */, 32B99EA9203B34B60017FD66 /* SDImageCoderTests.m in Sources */, 3264FF30205D42CB00F6BD48 /* SDWebImageTestTransformer.m in Sources */, 320630412085A37C006E0FA4 /* SDAnimatedImageTest.m in Sources */, @@ -512,6 +517,7 @@ 2D7AF0601F329763000083C2 /* SDTestCase.m in Sources */, 328BB6DD20825E9800760D6C /* SDWebImageTestCache.m in Sources */, 4369C1D11D97F80F007E863A /* SDWebImagePrefetcherTests.m in Sources */, + 3222417F2272F808002429DB /* SDUtilsTests.m in Sources */, DA248D69195475D800390AB0 /* SDImageCacheTests.m in Sources */, DA248D6B195476AC00390AB0 /* SDWebImageManagerTests.m in Sources */, 32B99E8B203AF8690017FD66 /* SDCategoriesTests.m in Sources */, diff --git a/Tests/Tests/SDAnimatedImageTest.m b/Tests/Tests/SDAnimatedImageTest.m index dc8e91b6..6cfea799 100644 --- a/Tests/Tests/SDAnimatedImageTest.m +++ b/Tests/Tests/SDAnimatedImageTest.m @@ -64,6 +64,7 @@ static const NSUInteger kTestGIFFrameCount = 5; // local TestImage.gif loop coun - (void)test04AnimatedImageImageNamed { NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + expect([SDAnimatedImage imageNamed:@"TestImage.gif"]).beNil(); // Not in main bundle #if SD_UIKIT SDAnimatedImage *image = [SDAnimatedImage imageNamed:@"TestImage.gif" inBundle:bundle compatibleWithTraitCollection:nil]; #else @@ -86,6 +87,9 @@ static const NSUInteger kTestGIFFrameCount = 5; // local TestImage.gif loop coun // Test one frame UIImage *frame = [image animatedImageFrameAtIndex:0]; expect(frame).notTo.beNil(); + + // Unload all frames + [image unloadAllFrames]; } - (void)test06AnimatedImageViewSetImage { @@ -117,7 +121,9 @@ static const NSUInteger kTestGIFFrameCount = 5; // local TestImage.gif loop coun expect(image1).notTo.beNil(); NSData *encodedData = [NSKeyedArchiver archivedDataWithRootObject:image1]; expect(encodedData).notTo.beNil(); - SDAnimatedImage *image2 = [NSKeyedUnarchiver unarchiveObjectWithData:encodedData]; + NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:encodedData]; + unarchiver.requiresSecureCoding = YES; + SDAnimatedImage *image2 = [unarchiver decodeObjectOfClass:SDAnimatedImage.class forKey:NSKeyedArchiveRootObjectKey]; expect(image2).notTo.beNil(); // Check each property diff --git a/Tests/Tests/SDImageCacheTests.m b/Tests/Tests/SDImageCacheTests.m index af110c18..cf5436e3 100644 --- a/Tests/Tests/SDImageCacheTests.m +++ b/Tests/Tests/SDImageCacheTests.m @@ -186,6 +186,18 @@ static NSString *kTestImageKeyPNG = @"TestImageKey.png"; [self waitForExpectationsWithCommonTimeout]; } +- (void)test13DeleteOldFiles { + XCTestExpectation *expectation = [self expectationWithDescription:@"deleteOldFiles"]; + [SDImageCache sharedImageCache].config.maxDiskAge = 1; // 1 second to mark all as out-dated + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [[SDImageCache sharedImageCache] deleteOldFilesWithCompletionBlock:^{ + expect(SDImageCache.sharedImageCache.totalDiskCount).equal(0); + [expectation fulfill]; + }]; + }); + [self waitForExpectationsWithCommonTimeout]; +} + - (void)test20InitialCacheSize{ expect([[SDImageCache sharedImageCache] totalDiskSize]).to.equal(0); } @@ -381,6 +393,68 @@ static NSString *kTestImageKeyPNG = @"TestImageKey.png"; expect(exist).beTruthy(); } +- (void)test45DiskCacheRemoveExpiredData { + NSString *cachePath = [[self userCacheDirectory] stringByAppendingPathComponent:@"disk"]; + SDImageCacheConfig *config = SDImageCacheConfig.defaultCacheConfig; + config.maxDiskAge = 1; // 1 second + config.maxDiskSize = 10; // 10 KB + SDDiskCache *diskCache = [[SDDiskCache alloc] initWithCachePath:cachePath config:config]; + [diskCache removeAllData]; + expect(diskCache.totalSize).equal(0); + expect(diskCache.totalCount).equal(0); + // 20KB -> maxDiskSize + NSUInteger length = 20; + void *bytes = malloc(length); + NSData *data = [NSData dataWithBytes:bytes length:length]; + free(bytes); + [diskCache setData:data forKey:@"20KB"]; + expect(diskCache.totalSize).equal(length); + expect(diskCache.totalCount).equal(1); + [diskCache removeExpiredData]; + expect(diskCache.totalSize).equal(0); + expect(diskCache.totalCount).equal(0); + // 1KB with 5s -> maxDiskAge + XCTestExpectation *expectation = [self expectationWithDescription:@"SDDiskCache removeExpireData timeout"]; + length = 1; + bytes = malloc(length); + data = [NSData dataWithBytes:bytes length:length]; + free(bytes); + [diskCache setData:data forKey:@"1KB"]; + expect(diskCache.totalSize).equal(length); + expect(diskCache.totalCount).equal(1); + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [diskCache removeExpiredData]; + expect(diskCache.totalSize).equal(0); + expect(diskCache.totalCount).equal(0); + [expectation fulfill]; + }); + [self waitForExpectationsWithTimeout:5 handler:nil]; +} + +#if SD_UIKIT +- (void)test46MemoryCacheWeakCache { + SDMemoryCache *memoryCache = [[SDMemoryCache alloc] init]; + memoryCache.config.shouldUseWeakMemoryCache = NO; + memoryCache.config.maxMemoryCost = 10; + memoryCache.config.maxMemoryCount = 5; + expect(memoryCache.countLimit).equal(5); + expect(memoryCache.totalCostLimit).equal(10); + // Don't use weak cache + NSObject *object = [NSObject new]; + [memoryCache setObject:object forKey:@"1"]; + [[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidReceiveMemoryWarningNotification object:nil]; + NSObject *cachedObject = [memoryCache objectForKey:@"1"]; + expect(cachedObject).beNil(); + // Use weak cache + memoryCache.config.shouldUseWeakMemoryCache = YES; + object = [NSObject new]; + [memoryCache setObject:object forKey:@"1"]; + [[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidReceiveMemoryWarningNotification object:nil]; + cachedObject = [memoryCache objectForKey:@"1"]; + expect(object).equal(cachedObject); +} +#endif + #pragma mark - SDImageCache & SDImageCachesManager - (void)test50SDImageCacheQueryOp { XCTestExpectation *expectation = [self expectationWithDescription:@"SDImageCache query op works"]; diff --git a/Tests/Tests/SDImageCoderTests.m b/Tests/Tests/SDImageCoderTests.m index e4caa48f..4ee55e10 100644 --- a/Tests/Tests/SDImageCoderTests.m +++ b/Tests/Tests/SDImageCoderTests.m @@ -17,6 +17,7 @@ - (void)test01ThatDecodedImageWithNilImageReturnsNil { expect([UIImage sd_decodedImageWithImage:nil]).to.beNil(); + expect([UIImage sd_decodedAndScaledDownImageWithImage:nil]).to.beNil(); } #if SD_UIKIT @@ -120,6 +121,14 @@ } } +- (void)test15ThatCodersManagerWorks { + SDImageCodersManager.sharedManager.coders = @[SDImageIOCoder.sharedCoder]; + expect([SDImageCodersManager.sharedManager canDecodeFromData:nil]).beTruthy(); // Image/IO will return YES for future format + expect([SDImageCodersManager.sharedManager decodedImageWithData:nil options:nil]).beNil(); + expect([SDImageCodersManager.sharedManager canEncodeToFormat:SDImageFormatWebP]).beFalsy(); + expect([SDImageCodersManager.sharedManager encodedDataWithImage:nil format:SDImageFormatUndefined options:nil]).beNil(); +} + - (void)verifyCoder:(id)coder withLocalImageURL:(NSURL *)imageUrl supportsEncoding:(BOOL)supportsEncoding diff --git a/Tests/Tests/SDImageTransformerTests.m b/Tests/Tests/SDImageTransformerTests.m index 8c9283d0..ab823dfd 100644 --- a/Tests/Tests/SDImageTransformerTests.m +++ b/Tests/Tests/SDImageTransformerTests.m @@ -163,13 +163,40 @@ #endif CGFloat borderWidth = 1; UIColor *borderCoder = [UIColor blackColor]; + BOOL horizontal = YES; + BOOL vertical = YES; + CGRect cropRect = CGRectMake(0, 0, 50, 50); + UIColor *tintColor = [UIColor clearColor]; + CGFloat blurRadius = 5; + CIFilter *filter = [CIFilter filterWithName:@"CIColorInvert"]; + SDImageResizingTransformer *transformer1 = [SDImageResizingTransformer transformerWithSize:size scaleMode:scaleMode]; SDImageRotationTransformer *transformer2 = [SDImageRotationTransformer transformerWithAngle:angle fitSize:fitSize]; SDImageRoundCornerTransformer *transformer3 = [SDImageRoundCornerTransformer transformerWithRadius:radius corners:corners borderWidth:borderWidth borderColor:borderCoder]; - SDImagePipelineTransformer *pipelineTransformer = [SDImagePipelineTransformer transformerWithTransformers:@[transformer1, transformer2, transformer3]]; + SDImageFlippingTransformer *transformer4 = [SDImageFlippingTransformer transformerWithHorizontal:horizontal vertical:vertical]; + SDImageCroppingTransformer *transformer5 = [SDImageCroppingTransformer transformerWithRect:cropRect]; + SDImageTintTransformer *transformer6 = [SDImageTintTransformer transformerWithColor:tintColor]; + SDImageBlurTransformer *transformer7 = [SDImageBlurTransformer transformerWithRadius:blurRadius]; + SDImageFilterTransformer *transformer8 = [SDImageFilterTransformer transformerWithFilter:filter]; + + // Chain all built-in transformers for test case + SDImagePipelineTransformer *pipelineTransformer = [SDImagePipelineTransformer transformerWithTransformers:@[transformer1, transformer2, transformer3, transformer4, transformer5, transformer6, transformer7, transformer8]]; + NSArray *transformerKeys = @[ + @"SDImageResizingTransformer({100.000000,100.000000},2)", + @"SDImageRotationTransformer(0.785398,0)", + @"SDImageRoundCornerTransformer(50.000000,18446744073709551615,1.000000,#ff000000)", + @"SDImageFlippingTransformer(1,1)", + @"SDImageCroppingTransformer({0.000000,0.000000,50.000000,50.000000})", + @"SDImageTintTransformer(#00000000)", + @"SDImageBlurTransformer(5.000000)", + @"SDImageFilterTransformer(CIColorInvert)" + ]; + NSString *transformerKey = [transformerKeys componentsJoinedByString:@"-"]; // SDImageTransformerKeySeparator + expect([pipelineTransformer.transformerKey isEqualToString:transformerKey]).beTruthy(); UIImage *transformedImage = [pipelineTransformer transformedImageWithImage:self.testImage forKey:@"Test"]; - expect(CGSizeEqualToSize(transformedImage.size, size)).beTruthy(); + expect(transformedImage).notTo.beNil(); + expect(CGSizeEqualToSize(transformedImage.size, cropRect.size)).beTruthy(); } - (void)test10TransformerKeyForCacheKey { diff --git a/Tests/Tests/SDUtilsTests.m b/Tests/Tests/SDUtilsTests.m new file mode 100644 index 00000000..a63161b1 --- /dev/null +++ b/Tests/Tests/SDUtilsTests.m @@ -0,0 +1,77 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * (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 "SDWeakProxy.h" +#import "SDInternalMacros.h" + +@interface SDUtilsTests : SDTestCase + +@end + +@implementation SDUtilsTests + +- (void)testSDWeakProxy { + NSObject *object = [NSObject new]; + SDWeakProxy *proxy = [SDWeakProxy proxyWithTarget:object]; + SEL sel = @selector(hash); + NSMethodSignature *signature = [proxy methodSignatureForSelector:sel]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; + [proxy forwardInvocation:invocation]; + void *returnValue; + [invocation getReturnValue:&returnValue]; + expect(returnValue).beNil(); + expect([((NSObject *)proxy) forwardingTargetForSelector:sel]).equal(object); + expect(proxy.isProxy).beTruthy(); + expect([proxy respondsToSelector:sel]).equal([object respondsToSelector:sel]); + expect([proxy isEqual:object]).beTruthy(); + expect(proxy.hash).equal(object.hash); + expect(proxy.superclass).equal(object.superclass); + expect(proxy.class).equal(object.class); + expect([proxy isKindOfClass:NSObject.class]).equal([object isKindOfClass:NSObject.class]); + expect([proxy isMemberOfClass:NSObject.class]).equal([object isMemberOfClass:NSObject.class]); + expect([proxy conformsToProtocol:@protocol(NSObject)]).equal([object conformsToProtocol:@protocol(NSObject)]); + expect([proxy.description isEqualToString:object.description]).beTruthy(); + expect([proxy.debugDescription isEqualToString:object.debugDescription]).beTruthy(); +} + +- (void)testSDScaledImageForKey { + // Test nil + expect(SDScaledImageForKey(nil, nil)).beNil(); + // Test @2x + NSData *data = [NSData dataWithContentsOfFile:[self testGIFPath]]; + UIImage * image = [UIImage sd_imageWithGIFData:data]; + expect(image.sd_isAnimated).beTruthy(); + expect(image.scale).equal(1); + + UIImage *scaledImage = SDScaledImageForKey(@"test@2x.gif", image); + expect(scaledImage.scale).equal(2); +} + +- (void)testInternalMacro { + @weakify(self); + @onExit { + @strongify(self); + expect(self).notTo.beNil(); + }; +} + +#pragma mark - Helper + +- (NSString *)testJPEGPath { + NSBundle *testBundle = [NSBundle bundleForClass:[self class]]; + return [testBundle pathForResource:@"TestImage" ofType:@"jpg"]; +} + +- (NSString *)testGIFPath { + NSBundle *testBundle = [NSBundle bundleForClass:[self class]]; + return [testBundle pathForResource:@"TestImage" ofType:@"gif"]; +} + +@end diff --git a/Tests/Tests/SDWebCacheCategoriesTests.m b/Tests/Tests/SDWebCacheCategoriesTests.m index 9b5d9b07..3fc7ee3e 100644 --- a/Tests/Tests/SDWebCacheCategoriesTests.m +++ b/Tests/Tests/SDWebCacheCategoriesTests.m @@ -31,6 +31,7 @@ expect(imageView.image).to.equal(image); [expectation fulfill]; }]; + expect(imageView.sd_imageURL).equal(originalImageURL); [self waitForExpectationsWithCommonTimeout]; } @@ -189,6 +190,16 @@ [self waitForExpectationsWithCommonTimeout]; } +- (void)testUIViewCancelCurrentImageLoad { + UIView *imageView = [[UIView alloc] init]; + NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL]; + [imageView sd_internalSetImageWithURL:originalImageURL placeholderImage:nil options:0 context:nil setImageBlock:nil progress:nil completed:nil]; + [imageView sd_cancelCurrentImageLoad]; + NSString *operationKey = NSStringFromClass(UIView.class); + expect([imageView sd_imageLoadOperationForKey:operationKey]).beNil(); + [imageView sd_cancelImageLoadOperationWithKey:operationKey]; +} + - (void)testUIViewImageProgressKVOWork { XCTestExpectation *expectation = [self expectationWithDescription:@"UIView imageProgressKVO failed"]; UIView *view = [[UIView alloc] init]; diff --git a/Tests/Tests/SDWebImageDownloaderTests.m b/Tests/Tests/SDWebImageDownloaderTests.m index 02edfaea..e76b3a78 100644 --- a/Tests/Tests/SDWebImageDownloaderTests.m +++ b/Tests/Tests/SDWebImageDownloaderTests.m @@ -443,6 +443,10 @@ XCTestExpectation *expectation = [self expectationWithDescription:@"Custom image not works"]; SDWebImageTestLoader *loader = [[SDWebImageTestLoader alloc] init]; NSURL *imageURL = [NSURL URLWithString:kTestJPEGURL]; + expect([loader canRequestImageForURL:imageURL]).beTruthy(); + NSError *imageError = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:nil]; + expect([loader shouldBlockFailedURLWithURL:imageURL error:imageError]).equal(NO); + [loader requestImageWithURL:imageURL options:0 context:nil progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) { expect(targetURL).notTo.beNil(); } completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) { @@ -456,7 +460,15 @@ - (void)test31ThatLoadersManagerWorks { XCTestExpectation *expectation = [self expectationWithDescription:@"Loaders manager not works"]; + SDWebImageTestLoader *loader = [[SDWebImageTestLoader alloc] init]; + [[SDImageLoadersManager sharedManager] addLoader:loader]; + [[SDImageLoadersManager sharedManager] removeLoader:loader]; + [SDImageLoadersManager sharedManager].loaders = @[SDWebImageDownloader.sharedDownloader, loader]; NSURL *imageURL = [NSURL URLWithString:kTestJPEGURL]; + expect([[SDImageLoadersManager sharedManager] canRequestImageForURL:imageURL]).beTruthy(); + NSError *imageError = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:nil]; + expect([loader shouldBlockFailedURLWithURL:imageURL error:imageError]).equal(NO); + [[SDImageLoadersManager sharedManager] requestImageWithURL:imageURL options:0 context:nil progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) { expect(targetURL).notTo.beNil(); } completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) { From c1d58106dc65ace7cbe52c56c4e161f49d24cc55 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Fri, 26 Apr 2019 22:11:34 +0800 Subject: [PATCH 2/5] Fix one bug of the macOS API `SDScaledImageForKey`, which does not modify the bitmapImageRep's size when scale is not equal to 1 --- SDWebImage/SDWebImageDefine.m | 1 + 1 file changed, 1 insertion(+) diff --git a/SDWebImage/SDWebImageDefine.m b/SDWebImage/SDWebImageDefine.m index d339020a..254d5aa5 100644 --- a/SDWebImage/SDWebImageDefine.m +++ b/SDWebImage/SDWebImageDefine.m @@ -98,6 +98,7 @@ inline UIImage * _Nullable SDScaledImageForScaleFactor(CGFloat scale, UIImage * if (bitmapImageRep) { NSSize size = NSMakeSize(image.size.width / scale, image.size.height / scale); animatedImage = [[NSImage alloc] initWithSize:size]; + bitmapImageRep.size = size; [animatedImage addRepresentation:bitmapImageRep]; } #endif From bf047b6e1be8e52f529e896f62c94860d4dc290e Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Fri, 26 Apr 2019 23:32:48 +0800 Subject: [PATCH 3/5] Update more test cases for code coverage --- Tests/Tests/SDAnimatedImageTest.m | 29 ++++++++++++++++++----- Tests/Tests/SDCategoriesTests.m | 3 +++ Tests/Tests/SDImageCacheTests.m | 2 ++ Tests/Tests/SDImageCoderTests.m | 11 +++++---- Tests/Tests/SDImageTransformerTests.m | 8 +------ Tests/Tests/SDWebImageDownloaderTests.m | 31 ++++++++++++++++++++----- Tests/Tests/SDWebImagePrefetcherTests.m | 17 ++++++++++++++ 7 files changed, 77 insertions(+), 24 deletions(-) diff --git a/Tests/Tests/SDAnimatedImageTest.m b/Tests/Tests/SDAnimatedImageTest.m index 6cfea799..5a1f2f05 100644 --- a/Tests/Tests/SDAnimatedImageTest.m +++ b/Tests/Tests/SDAnimatedImageTest.m @@ -27,10 +27,6 @@ static const NSUInteger kTestGIFFrameCount = 5; // local TestImage.gif loop coun @implementation SDAnimatedImageTest -- (void)tearDown { - [[SDImageCache sharedImageCache] removeImageForKey:kTestGIFURL fromDisk:YES withCompletion:nil]; -} - - (void)test01AnimatedImageInitWithData { NSData *invalidData = [@"invalid data" dataUsingEncoding:NSUTF8StringEncoding]; SDAnimatedImage *image = [[SDAnimatedImage alloc] initWithData:invalidData]; @@ -119,11 +115,16 @@ static const NSUInteger kTestGIFFrameCount = 5; // local TestImage.gif loop coun - (void)test10AnimatedImageInitWithCoder { SDAnimatedImage *image1 = [SDAnimatedImage imageWithContentsOfFile:[self testGIFPath]]; expect(image1).notTo.beNil(); - NSData *encodedData = [NSKeyedArchiver archivedDataWithRootObject:image1]; + NSMutableData *encodedData = [NSMutableData data]; + NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:encodedData]; + archiver.requiresSecureCoding = YES; + [archiver encodeObject:image1 forKey:NSKeyedArchiveRootObjectKey]; + [archiver finishEncoding]; expect(encodedData).notTo.beNil(); NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:encodedData]; unarchiver.requiresSecureCoding = YES; SDAnimatedImage *image2 = [unarchiver decodeObjectOfClass:SDAnimatedImage.class forKey:NSKeyedArchiveRootObjectKey]; + [unarchiver finishDecoding]; expect(image2).notTo.beNil(); // Check each property @@ -238,9 +239,11 @@ static const NSUInteger kTestGIFFrameCount = 5; // local TestImage.gif loop coun } - (void)test23AnimatedImageViewCategoryProgressive { - XCTestExpectation *expectation = [self expectationWithDescription:@"test SDAnimatedImageView view category"]; + XCTestExpectation *expectation = [self expectationWithDescription:@"test SDAnimatedImageView view category progressive"]; SDAnimatedImageView *imageView = [SDAnimatedImageView new]; NSURL *testURL = [NSURL URLWithString:kTestGIFURL]; + [SDImageCache.sharedImageCache removeImageFromMemoryForKey:testURL.absoluteString]; + [SDImageCache.sharedImageCache removeImageFromDiskForKey:testURL.absoluteString]; [imageView sd_setImageWithURL:testURL placeholderImage:nil options:SDWebImageProgressiveLoad progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) { dispatch_async(dispatch_get_main_queue(), ^{ UIImage *image = imageView.image; @@ -261,6 +264,20 @@ static const NSUInteger kTestGIFFrameCount = 5; // local TestImage.gif loop coun [self waitForExpectationsWithCommonTimeout]; } +- (void)test24AnimatedImageViewCategoryDiskCache { + XCTestExpectation *expectation = [self expectationWithDescription:@"test SDAnimatedImageView view category disk cache"]; + SDAnimatedImageView *imageView = [SDAnimatedImageView new]; + NSURL *testURL = [NSURL URLWithString:kTestGIFURL]; + [SDImageCache.sharedImageCache removeImageFromMemoryForKey:testURL.absoluteString]; + [imageView sd_setImageWithURL:testURL placeholderImage:nil 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) { diff --git a/Tests/Tests/SDCategoriesTests.m b/Tests/Tests/SDCategoriesTests.m index 3364fabb..f38fe1bc 100644 --- a/Tests/Tests/SDCategoriesTests.m +++ b/Tests/Tests/SDCategoriesTests.m @@ -26,6 +26,7 @@ // Test invalid format CFStringRef type = [NSData sd_UTTypeFromImageFormat:SDImageFormatUndefined]; expect(CFStringCompare(kUTTypePNG, type, 0)).equal(kCFCompareEqualTo); + expect([NSData sd_imageFormatFromUTType:kUTTypeImage]).equal(SDImageFormatUndefined); } - (void)test02UIImageMultiFormatCategory { @@ -39,6 +40,8 @@ // Test image encode PNG data = [image sd_imageDataAsFormat:SDImageFormatPNG]; expect(data).notTo.beNil(); + // Test image decode PNG + expect([UIImage sd_imageWithData:data scale:1 firstFrameOnly:YES]).notTo.beNil(); // Test image encode JPEG with compressionQuality NSData *jpegData1 = [image sd_imageDataAsFormat:SDImageFormatJPEG compressionQuality:1]; NSData *jpegData2 = [image sd_imageDataAsFormat:SDImageFormatJPEG compressionQuality:0.5]; diff --git a/Tests/Tests/SDImageCacheTests.m b/Tests/Tests/SDImageCacheTests.m index cf5436e3..741f7c99 100644 --- a/Tests/Tests/SDImageCacheTests.m +++ b/Tests/Tests/SDImageCacheTests.m @@ -133,6 +133,7 @@ static NSString *kTestImageKeyPNG = @"TestImageKey.png"; XCTFail(@"Image should not be in cache"); } }]; + [[SDImageCache sharedImageCache] storeImageToMemory:image forKey:kTestImageKeyJPEG]; [[SDImageCache sharedImageCache] clearMemory]; expect([[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kTestImageKeyJPEG]).to.beNil(); [self waitForExpectationsWithCommonTimeout]; @@ -269,6 +270,7 @@ static NSString *kTestImageKeyPNG = @"TestImageKey.png"; NSData *imageData = [image sd_imageDataAsFormat:SDImageFormatJPEG]; [[SDImageCache sharedImageCache] storeImageDataToDisk:imageData forKey:kTestImageKeyJPEG]; + expect([[SDImageCache sharedImageCache] diskImageDataExistsWithKey:kTestImageKeyJPEG]).beTruthy(); UIImage *storedImageFromMemory = [[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kTestImageKeyJPEG]; expect(storedImageFromMemory).to.equal(nil); diff --git a/Tests/Tests/SDImageCoderTests.m b/Tests/Tests/SDImageCoderTests.m index 4ee55e10..d2450904 100644 --- a/Tests/Tests/SDImageCoderTests.m +++ b/Tests/Tests/SDImageCoderTests.m @@ -122,11 +122,12 @@ } - (void)test15ThatCodersManagerWorks { - SDImageCodersManager.sharedManager.coders = @[SDImageIOCoder.sharedCoder]; - expect([SDImageCodersManager.sharedManager canDecodeFromData:nil]).beTruthy(); // Image/IO will return YES for future format - expect([SDImageCodersManager.sharedManager decodedImageWithData:nil options:nil]).beNil(); - expect([SDImageCodersManager.sharedManager canEncodeToFormat:SDImageFormatWebP]).beFalsy(); - expect([SDImageCodersManager.sharedManager encodedDataWithImage:nil format:SDImageFormatUndefined options:nil]).beNil(); + SDImageCodersManager *manager = [[SDImageCodersManager alloc] init]; + manager.coders = @[SDImageIOCoder.sharedCoder]; + expect([manager canDecodeFromData:nil]).beTruthy(); // Image/IO will return YES for future format + expect([manager decodedImageWithData:nil options:nil]).beNil(); + expect([manager canEncodeToFormat:SDImageFormatWebP]).beFalsy(); + expect([manager encodedDataWithImage:nil format:SDImageFormatUndefined options:nil]).beNil(); } - (void)verifyCoder:(id)coder diff --git a/Tests/Tests/SDImageTransformerTests.m b/Tests/Tests/SDImageTransformerTests.m index ab823dfd..2575af0e 100644 --- a/Tests/Tests/SDImageTransformerTests.m +++ b/Tests/Tests/SDImageTransformerTests.m @@ -8,15 +8,9 @@ */ #import "SDTestCase.h" +#import "UIColor+HexString.h" #import -// Internal header -@interface UIColor (HexString) - -@property (nonatomic, copy, readonly, nonnull) NSString *sd_hexString; - -@end - @interface SDImageTransformerTests : SDTestCase @property (nonatomic, strong) UIImage *testImage; diff --git a/Tests/Tests/SDWebImageDownloaderTests.m b/Tests/Tests/SDWebImageDownloaderTests.m index e76b3a78..061159e1 100644 --- a/Tests/Tests/SDWebImageDownloaderTests.m +++ b/Tests/Tests/SDWebImageDownloaderTests.m @@ -179,6 +179,24 @@ [self waitForExpectationsWithCommonTimeout]; } +- (void)test11ThatCancelAllDownloadWorks { + XCTestExpectation *expectation = [self expectationWithDescription:@"CancelAllDownloads"]; + + NSURL *imageURL = [NSURL URLWithString:kTestJPEGURL]; + [[SDWebImageDownloader sharedDownloader] downloadImageWithURL:imageURL completed:nil]; + expect([SDWebImageDownloader sharedDownloader].currentDownloadCount).to.equal(1); + + [[SDWebImageDownloader sharedDownloader] cancelAllDownloads]; + + // doesn't cancel immediately - since it uses dispatch async + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, kMinDelayNanosecond), dispatch_get_main_queue(), ^{ + expect([SDWebImageDownloader sharedDownloader].currentDownloadCount).to.equal(0); + [expectation fulfill]; + }); + + [self waitForExpectationsWithCommonTimeout]; +} + - (void)test12ThatWeCanUseAnotherSessionForEachDownloadOperation { XCTestExpectation *expectation = [self expectationWithDescription:@"Owned session"]; NSURL *url = [NSURL URLWithString:kTestJPEGURL]; @@ -461,15 +479,16 @@ - (void)test31ThatLoadersManagerWorks { XCTestExpectation *expectation = [self expectationWithDescription:@"Loaders manager not works"]; SDWebImageTestLoader *loader = [[SDWebImageTestLoader alloc] init]; - [[SDImageLoadersManager sharedManager] addLoader:loader]; - [[SDImageLoadersManager sharedManager] removeLoader:loader]; - [SDImageLoadersManager sharedManager].loaders = @[SDWebImageDownloader.sharedDownloader, loader]; + SDImageLoadersManager *manager = [[SDImageLoadersManager alloc] init]; + [manager addLoader:loader]; + [manager removeLoader:loader]; + manager.loaders = @[SDWebImageDownloader.sharedDownloader, loader]; NSURL *imageURL = [NSURL URLWithString:kTestJPEGURL]; - expect([[SDImageLoadersManager sharedManager] canRequestImageForURL:imageURL]).beTruthy(); + expect([manager canRequestImageForURL:imageURL]).beTruthy(); NSError *imageError = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:nil]; - expect([loader shouldBlockFailedURLWithURL:imageURL error:imageError]).equal(NO); + expect([manager shouldBlockFailedURLWithURL:imageURL error:imageError]).equal(NO); - [[SDImageLoadersManager sharedManager] requestImageWithURL:imageURL options:0 context:nil progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) { + [manager requestImageWithURL:imageURL options:0 context:nil progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) { expect(targetURL).notTo.beNil(); } completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) { expect(error).to.beNil(); diff --git a/Tests/Tests/SDWebImagePrefetcherTests.m b/Tests/Tests/SDWebImagePrefetcherTests.m index 98695756..d79fda2b 100644 --- a/Tests/Tests/SDWebImagePrefetcherTests.m +++ b/Tests/Tests/SDWebImagePrefetcherTests.m @@ -9,6 +9,12 @@ #import "SDTestCase.h" +@interface SDWebImagePrefetcher () + +@property (strong, atomic, nonnull) NSMutableSet *runningTokens; + +@end + @interface SDWebImagePrefetcherTests : SDTestCase @property (nonatomic, strong) SDWebImagePrefetcher *prefetcher; @@ -134,6 +140,17 @@ [self waitForExpectationsWithTimeout:kAsyncTestTimeout * 20 handler:nil]; } +- (void)test06PrefetchCancelToken { + NSArray *imageURLs = @[@"http://via.placeholder.com/20x20.jpg", + @"http://via.placeholder.com/30x30.jpg", + @"http://via.placeholder.com/40x40.jpg"]; + SDWebImagePrefetcher *prefetcher = [[SDWebImagePrefetcher alloc] init]; + SDWebImagePrefetchToken *token = [prefetcher prefetchURLs:imageURLs]; + expect(prefetcher.runningTokens.count).equal(1); + [token cancel]; + expect(prefetcher.runningTokens.count).equal(0); +} + - (void)imagePrefetcher:(SDWebImagePrefetcher *)imagePrefetcher didFinishWithTotalCount:(NSUInteger)totalCount skippedCount:(NSUInteger)skippedCount { expect(imagePrefetcher).to.equal(self.prefetcher); self.skippedCount = skippedCount; From 430bebe274b4874f4786e260e1b8864911560d7e Mon Sep 17 00:00:00 2001 From: kinarob Date: Mon, 6 May 2019 11:33:26 +0800 Subject: [PATCH 4/5] Add cacheType checking in animatedImageView category --- Tests/Tests/SDAnimatedImageTest.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Tests/Tests/SDAnimatedImageTest.m b/Tests/Tests/SDAnimatedImageTest.m index 5a1f2f05..1605998c 100644 --- a/Tests/Tests/SDAnimatedImageTest.m +++ b/Tests/Tests/SDAnimatedImageTest.m @@ -259,6 +259,7 @@ static const NSUInteger kTestGIFFrameCount = 5; // local TestImage.gif loop coun expect(error).to.beNil(); expect(image).notTo.beNil(); expect([image isKindOfClass:[SDAnimatedImage class]]).beTruthy(); + expect(cacheType).equal(SDImageCacheTypeNone); [expectation fulfill]; }]; [self waitForExpectationsWithCommonTimeout]; @@ -272,6 +273,7 @@ static const NSUInteger kTestGIFFrameCount = 5; // local TestImage.gif loop coun [imageView sd_setImageWithURL:testURL placeholderImage:nil completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) { expect(error).to.beNil(); expect(image).notTo.beNil(); + expect(cacheType).equal(SDImageCacheTypeDisk); expect([image isKindOfClass:[SDAnimatedImage class]]).beTruthy(); [expectation fulfill]; }]; From 9f4bc99fe834305d4af60fc0c6891f254c5e6a37 Mon Sep 17 00:00:00 2001 From: kinarob Date: Mon, 6 May 2019 14:38:31 +0800 Subject: [PATCH 5/5] add macros checking in SDImageFilterTransformer --- Tests/Tests/SDImageTransformerTests.m | 23 +++++++++++++++++++---- Tests/Tests/SDWebCacheCategoriesTests.m | 1 - 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Tests/Tests/SDImageTransformerTests.m b/Tests/Tests/SDImageTransformerTests.m index 2575af0e..14a72088 100644 --- a/Tests/Tests/SDImageTransformerTests.m +++ b/Tests/Tests/SDImageTransformerTests.m @@ -162,7 +162,6 @@ CGRect cropRect = CGRectMake(0, 0, 50, 50); UIColor *tintColor = [UIColor clearColor]; CGFloat blurRadius = 5; - CIFilter *filter = [CIFilter filterWithName:@"CIColorInvert"]; SDImageResizingTransformer *transformer1 = [SDImageResizingTransformer transformerWithSize:size scaleMode:scaleMode]; SDImageRotationTransformer *transformer2 = [SDImageRotationTransformer transformerWithAngle:angle fitSize:fitSize]; @@ -171,10 +170,24 @@ SDImageCroppingTransformer *transformer5 = [SDImageCroppingTransformer transformerWithRect:cropRect]; SDImageTintTransformer *transformer6 = [SDImageTintTransformer transformerWithColor:tintColor]; SDImageBlurTransformer *transformer7 = [SDImageBlurTransformer transformerWithRadius:blurRadius]; - SDImageFilterTransformer *transformer8 = [SDImageFilterTransformer transformerWithFilter:filter]; +#if SD_UIKIT || SD_MAC + CIFilter *filter = [CIFilter filterWithName:@"CIColorInvert"]; + SDImageFilterTransformer *transformer8 = [SDImageFilterTransformer transformerWithFilter:filter]; +#endif // Chain all built-in transformers for test case - SDImagePipelineTransformer *pipelineTransformer = [SDImagePipelineTransformer transformerWithTransformers:@[transformer1, transformer2, transformer3, transformer4, transformer5, transformer6, transformer7, transformer8]]; + SDImagePipelineTransformer *pipelineTransformer = [SDImagePipelineTransformer transformerWithTransformers:@[ + transformer1, + transformer2, + transformer3, + transformer4, + transformer5, + transformer6, + transformer7, +#if SD_UIKIT || SD_MAC + transformer8, +#endif + ]]; NSArray *transformerKeys = @[ @"SDImageResizingTransformer({100.000000,100.000000},2)", @"SDImageRotationTransformer(0.785398,0)", @@ -183,7 +196,9 @@ @"SDImageCroppingTransformer({0.000000,0.000000,50.000000,50.000000})", @"SDImageTintTransformer(#00000000)", @"SDImageBlurTransformer(5.000000)", - @"SDImageFilterTransformer(CIColorInvert)" +#if SD_UIKIT || SD_MAC + @"SDImageFilterTransformer(CIColorInvert)", +#endif ]; NSString *transformerKey = [transformerKeys componentsJoinedByString:@"-"]; // SDImageTransformerKeySeparator expect([pipelineTransformer.transformerKey isEqualToString:transformerKey]).beTruthy(); diff --git a/Tests/Tests/SDWebCacheCategoriesTests.m b/Tests/Tests/SDWebCacheCategoriesTests.m index 3fc7ee3e..fb3cec08 100644 --- a/Tests/Tests/SDWebCacheCategoriesTests.m +++ b/Tests/Tests/SDWebCacheCategoriesTests.m @@ -197,7 +197,6 @@ [imageView sd_cancelCurrentImageLoad]; NSString *operationKey = NSStringFromClass(UIView.class); expect([imageView sd_imageLoadOperationForKey:operationKey]).beNil(); - [imageView sd_cancelImageLoadOperationWithKey:operationKey]; } - (void)testUIViewImageProgressKVOWork {