672 lines
41 KiB
Objective-C
672 lines
41 KiB
Objective-C
/*
|
|
* This file is part of the SDWebImage package.
|
|
* (c) Olivier Poitrey <rs@dailymotion.com>
|
|
*
|
|
* For the full copyright and license information, please view the LICENSE
|
|
* file that was distributed with this source code.
|
|
*/
|
|
|
|
#import "SDTestCase.h"
|
|
#import "SDWebImageTestTransformer.h"
|
|
#import "SDWebImageTestCache.h"
|
|
#import "SDWebImageTestLoader.h"
|
|
#if __has_include(<SDWebImageWebPCoder/SDWebImageWebPCoder.h>)
|
|
#import <SDWebImageWebPCoder/SDWebImageWebPCoder.h>
|
|
#endif
|
|
|
|
// Keep strong references for object
|
|
@interface SDObjectContainer<ObjectType> : NSObject
|
|
@property (nonatomic, strong, readwrite) ObjectType object;
|
|
@end
|
|
|
|
@implementation SDObjectContainer
|
|
@end
|
|
|
|
@interface SDWebImageManagerTests : SDTestCase
|
|
|
|
@end
|
|
|
|
@implementation SDWebImageManagerTests
|
|
|
|
- (void)test01ThatSharedManagerIsNotEqualToInitManager {
|
|
SDWebImageManager *manager = [[SDWebImageManager alloc] init];
|
|
expect(manager).toNot.equal([SDWebImageManager sharedManager]);
|
|
}
|
|
|
|
- (void)test02ThatDownloadInvokesCompletionBlockWithCorrectParamsAsync {
|
|
__block XCTestExpectation *expectation = [self expectationWithDescription:@"Image download completes"];
|
|
|
|
NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL];
|
|
|
|
[SDImageCache.sharedImageCache removeImageFromMemoryForKey:kTestJPEGURL];
|
|
[SDImageCache.sharedImageCache removeImageFromDiskForKey:kTestJPEGURL];
|
|
SDObjectContainer<SDWebImageCombinedOperation *> *container = [SDObjectContainer new];
|
|
container.object = [[SDWebImageManager sharedManager] loadImageWithURL:originalImageURL
|
|
options:0
|
|
progress:nil
|
|
completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
|
|
expect(image).toNot.beNil();
|
|
expect(error).to.beNil();
|
|
expect(originalImageURL).to.equal(imageURL);
|
|
|
|
// When download, the cache operation will reset to nil since it's always finished
|
|
SDWebImageCombinedOperation *operation = container.object;
|
|
expect(container).notTo.beNil();
|
|
expect(operation.cacheOperation).beNil();
|
|
expect(operation.loaderOperation).notTo.beNil();
|
|
container.object = nil;
|
|
|
|
[expectation fulfill];
|
|
expectation = nil;
|
|
}];
|
|
expect([[SDWebImageManager sharedManager] isRunning]).to.equal(YES);
|
|
|
|
[self waitForExpectationsWithTimeout:kAsyncTestTimeout * 2 handler:nil];
|
|
}
|
|
|
|
- (void)test03ThatDownloadWithIncorrectURLInvokesCompletionBlockWithAnErrorAsync {
|
|
__block XCTestExpectation *expectation = [self expectationWithDescription:@"Image download completes"];
|
|
|
|
NSURL *originalImageURL = [NSURL URLWithString:@"http://static2.dmcdn.net/static/video/656/177/44771656:jpeg_preview_small.png"];
|
|
|
|
[[SDWebImageManager sharedManager] loadImageWithURL:originalImageURL
|
|
options:0
|
|
progress:nil
|
|
completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
|
|
expect(image).to.beNil();
|
|
expect(error).toNot.beNil();
|
|
expect(originalImageURL).to.equal(imageURL);
|
|
|
|
[expectation fulfill];
|
|
expectation = nil;
|
|
}];
|
|
|
|
[self waitForExpectationsWithCommonTimeout];
|
|
}
|
|
|
|
- (void)test06CancellAll {
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"Cancel should callback with error"];
|
|
|
|
// need a bigger image here, that is why we don't use kTestJPEGURL
|
|
// if the image is too small, it will get downloaded before we can cancel :)
|
|
NSURL *url = [NSURL URLWithString:@"https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif"];
|
|
[[SDWebImageManager sharedManager] loadImageWithURL:url options:0 progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
|
expect(error).notTo.beNil();
|
|
expect(error.code).equal(SDWebImageErrorCancelled);
|
|
}];
|
|
|
|
[[SDWebImageManager sharedManager] cancelAll];
|
|
|
|
// doesn't cancel immediately - since it uses dispatch async
|
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, kMinDelayNanosecond), dispatch_get_main_queue(), ^{
|
|
expect([[SDWebImageManager sharedManager] isRunning]).to.equal(NO);
|
|
[expectation fulfill];
|
|
});
|
|
|
|
[self waitForExpectationsWithTimeout:kAsyncTestTimeout * 2 handler:nil];
|
|
}
|
|
|
|
- (void)test07ThatLoadImageWithSDWebImageRefreshCachedWorks {
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"Image download twice with SDWebImageRefresh failed"];
|
|
NSURL *originalImageURL = [NSURL URLWithString:@"https://placehold.co/10x10.png"];
|
|
__block BOOL firstCompletion = NO;
|
|
[[SDWebImageManager sharedManager] loadImageWithURL:originalImageURL options:SDWebImageRefreshCached progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
|
expect(image).toNot.beNil();
|
|
expect(error).to.beNil();
|
|
// #1993, load image with SDWebImageRefreshCached twice should not fail if the first time success.
|
|
|
|
// Because we call completion before remove the operation from queue, so need a dispatch to avoid get the same operation again. Attention this trap.
|
|
// One way to solve this is use another `NSURL instance` because we use `NSURL` as key but not `NSString`. However, this is implementation detail and no guarantee in the future.
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
NSURL *newImageURL = [NSURL URLWithString:@"https://placehold.co/10x10.png"];
|
|
[[SDWebImageManager sharedManager] loadImageWithURL:newImageURL options:SDWebImageRefreshCached progress:nil completed:^(UIImage * _Nullable image2, NSData * _Nullable data2, NSError * _Nullable error2, SDImageCacheType cacheType2, BOOL finished2, NSURL * _Nullable imageURL2) {
|
|
expect(image2).toNot.beNil();
|
|
expect(error2).to.beNil();
|
|
if (!firstCompletion) {
|
|
firstCompletion = YES;
|
|
[expectation fulfill];
|
|
}
|
|
}];
|
|
});
|
|
}];
|
|
|
|
[self waitForExpectationsWithTimeout:kAsyncTestTimeout * 2 handler:nil];
|
|
}
|
|
|
|
- (void)test08ThatImageTransformerWork {
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"Image transformer work"];
|
|
NSURL *url = [NSURL URLWithString:@"https://placehold.co/80x60.png"];
|
|
SDWebImageTestTransformer *transformer = [[SDWebImageTestTransformer alloc] init];
|
|
|
|
transformer.testImage = [[UIImage alloc] initWithContentsOfFile:[self testJPEGPath]];
|
|
SDImageCache *cache = [[SDImageCache alloc] initWithNamespace:@"Transformer"];
|
|
SDWebImageManager *manager = [[SDWebImageManager alloc] initWithCache:cache loader:SDWebImageDownloader.sharedDownloader];
|
|
NSString *key = [manager cacheKeyForURL:url];
|
|
NSString *transformedKey = [manager cacheKeyForURL:url context:@{SDWebImageContextImageTransformer : transformer}];
|
|
|
|
manager.transformer = transformer;
|
|
[cache removeImageFromDiskForKey:key];
|
|
[cache removeImageFromMemoryForKey:key];
|
|
[cache removeImageFromDiskForKey:transformedKey];
|
|
[cache removeImageFromMemoryForKey:transformedKey];
|
|
// Test encode options with transformer (because data is not available)
|
|
SDImageCoderOptions *encodeOptions = @{SDImageCoderEncodeMaxPixelSize : @(CGSizeMake(40, 30))};
|
|
[manager loadImageWithURL:url options:SDWebImageTransformAnimatedImage | SDWebImageTransformVectorImage | SDWebImageWaitStoreCache context:@{SDWebImageContextImageEncodeOptions : encodeOptions} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
|
expect(image).equal(transformer.testImage);
|
|
// Query the encoded data again
|
|
NSData *encodedData = [cache diskImageDataForKey:transformedKey];
|
|
UIImage *encodedImage = [UIImage sd_imageWithData:encodedData];
|
|
CGSize encodedImageSize = encodedImage.size;
|
|
expect(encodedImageSize.width).equal(40);
|
|
expect(encodedImageSize.height).equal(30);
|
|
[expectation fulfill];
|
|
}];
|
|
|
|
[self waitForExpectationsWithCommonTimeout];
|
|
}
|
|
|
|
- (void)test09ThatCacheKeyFilterWork {
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"Cache key filter work"];
|
|
NSURL *url = [NSURL URLWithString:kTestJPEGURL];
|
|
|
|
NSString *cacheKey = @"kTestJPEGURL";
|
|
SDWebImageCacheKeyFilter *cacheKeyFilter = [SDWebImageCacheKeyFilter cacheKeyFilterWithBlock:^NSString * _Nullable(NSURL * _Nonnull imageURL) {
|
|
if ([url isEqual:imageURL]) {
|
|
return cacheKey;
|
|
} else {
|
|
return url.absoluteString;
|
|
}
|
|
}];
|
|
|
|
SDWebImageManager *manager = [[SDWebImageManager alloc] initWithCache:[SDImageCache sharedImageCache] loader:[SDWebImageDownloader sharedDownloader]];
|
|
manager.cacheKeyFilter = cacheKeyFilter;
|
|
// Check download and retrieve custom cache key
|
|
[manager loadImageWithURL:url options:0 context:@{SDWebImageContextStoreCacheType : @(SDImageCacheTypeMemory)} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
|
expect(cacheType).equal(SDImageCacheTypeNone);
|
|
|
|
// Check memory cache exist
|
|
[manager.imageCache containsImageForKey:cacheKey cacheType:SDImageCacheTypeMemory completion:^(SDImageCacheType containsCacheType) {
|
|
expect(containsCacheType).equal(SDImageCacheTypeMemory);
|
|
|
|
[expectation fulfill];
|
|
}];
|
|
}];
|
|
|
|
[self waitForExpectationsWithCommonTimeout];
|
|
}
|
|
|
|
- (void)test10ThatCacheSerializerWork {
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"Cache serializer work"];
|
|
NSURL *url = [NSURL URLWithString:kTestJPEGURL];
|
|
__block NSData *imageData;
|
|
|
|
SDWebImageCacheSerializer *cacheSerializer = [SDWebImageCacheSerializer cacheSerializerWithBlock:^NSData * _Nullable(UIImage * _Nonnull image, NSData * _Nullable data, NSURL * _Nullable imageURL) {
|
|
imageData = [image sd_imageDataAsFormat:SDImageFormatPNG];
|
|
return imageData;
|
|
}];
|
|
|
|
SDWebImageManager *manager = [[SDWebImageManager alloc] initWithCache:[SDImageCache sharedImageCache] loader:[SDWebImageDownloader sharedDownloader]];
|
|
manager.cacheSerializer = cacheSerializer;
|
|
// Check download and store custom disk data
|
|
[[SDImageCache sharedImageCache] removeImageForKey:kTestJPEGURL withCompletion:^{
|
|
[manager loadImageWithURL:url options:0 progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
|
// Dispatch to let store disk finish
|
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, kMinDelayNanosecond), dispatch_get_main_queue(), ^{
|
|
NSData *diskImageData = [[SDImageCache sharedImageCache] diskImageDataForKey:kTestJPEGURL];
|
|
expect(diskImageData).equal(imageData); // disk data equal to serializer data
|
|
|
|
[expectation fulfill];
|
|
});
|
|
}];
|
|
}];
|
|
|
|
[self waitForExpectationsWithCommonTimeout];
|
|
}
|
|
|
|
- (void)test11ThatOptionsProcessorWork {
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"Options processor work"];
|
|
__block BOOL optionsProcessorCalled = NO;
|
|
|
|
SDWebImageManager *manager = [[SDWebImageManager alloc] initWithCache:[SDImageCache sharedImageCache] loader:[SDWebImageDownloader sharedDownloader]];
|
|
manager.optionsProcessor = [SDWebImageOptionsProcessor optionsProcessorWithBlock:^SDWebImageOptionsResult * _Nullable(NSURL * _Nonnull url, SDWebImageOptions options, SDWebImageContext * _Nullable context) {
|
|
if ([url.absoluteString isEqualToString:kTestPNGURL]) {
|
|
optionsProcessorCalled = YES;
|
|
return [[SDWebImageOptionsResult alloc] initWithOptions:0 context:@{SDWebImageContextImageScaleFactor : @(3)}];
|
|
} else {
|
|
return nil;
|
|
}
|
|
}];
|
|
|
|
NSURL *url = [NSURL URLWithString:kTestPNGURL];
|
|
[[SDImageCache sharedImageCache] removeImageForKey:kTestPNGURL withCompletion:^{
|
|
[manager loadImageWithURL:url options:0 progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
|
expect(image.scale).equal(3);
|
|
expect(optionsProcessorCalled).beTruthy();
|
|
|
|
[expectation fulfill];
|
|
}];
|
|
}];
|
|
|
|
[self waitForExpectationsWithCommonTimeout];
|
|
}
|
|
|
|
- (void)test12ThatStoreCacheTypeWork {
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"Image store cache type (including transformer) work"];
|
|
|
|
// Use a fresh manager && cache to avoid get effected by other test cases
|
|
SDImageCache *cache = [[SDImageCache alloc] initWithNamespace:@"SDWebImageStoreCacheType"];
|
|
[cache clearDiskOnCompletion:nil];
|
|
SDWebImageManager *manager = [[SDWebImageManager alloc] initWithCache:cache loader:SDWebImageDownloader.sharedDownloader];
|
|
SDWebImageTestTransformer *transformer = [[SDWebImageTestTransformer alloc] init];
|
|
transformer.testImage = [[UIImage alloc] initWithContentsOfFile:[self testJPEGPath]];
|
|
manager.transformer = transformer;
|
|
|
|
// test: original image -> disk only, transformed image -> memory only
|
|
SDWebImageContext *context = @{SDWebImageContextOriginalStoreCacheType : @(SDImageCacheTypeDisk), SDWebImageContextStoreCacheType : @(SDImageCacheTypeMemory)};
|
|
NSURL *url = [NSURL URLWithString:kTestAPNGPURL];
|
|
NSString *originalKey = [manager cacheKeyForURL:url];
|
|
NSString *transformedKey = [manager cacheKeyForURL:url context:context];
|
|
|
|
[manager loadImageWithURL:url options:SDWebImageTransformAnimatedImage context:context progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
|
expect(image).equal(transformer.testImage);
|
|
// the transformed image should not inherite any attribute from original one
|
|
expect(image.sd_imageFormat).equal(SDImageFormatJPEG);
|
|
expect(image.sd_isAnimated).beFalsy();
|
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2*kMinDelayNanosecond), dispatch_get_main_queue(), ^{
|
|
// original -> disk only
|
|
UIImage *originalImage = [cache imageFromMemoryCacheForKey:originalKey];
|
|
expect(originalImage).beNil();
|
|
NSData *originalData = [cache diskImageDataForKey:originalKey];
|
|
expect(originalData).notTo.beNil();
|
|
originalImage = [UIImage sd_imageWithData:originalData];
|
|
expect(originalImage).notTo.beNil();
|
|
expect(originalImage.sd_imageFormat).equal(SDImageFormatPNG);
|
|
expect(originalImage.sd_isAnimated).beTruthy();
|
|
// transformed -> memory only
|
|
[manager.imageCache containsImageForKey:transformedKey cacheType:SDImageCacheTypeAll completion:^(SDImageCacheType transformedCacheType) {
|
|
expect(transformedCacheType).equal(SDImageCacheTypeMemory);
|
|
[cache clearDiskOnCompletion:nil];
|
|
[expectation fulfill];
|
|
}];
|
|
});
|
|
}];
|
|
|
|
[self waitForExpectationsWithCommonTimeout];
|
|
}
|
|
|
|
- (void)test13ThatScaleDownLargeImageUseThumbnailDecoding {
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"SDWebImageScaleDownLargeImages should translate to thumbnail decoding"];
|
|
NSURL *originalImageURL = [NSURL URLWithString:@"https://placehold.co/2000x2000.png"]; // Max size for this API
|
|
NSUInteger defaultLimitBytes = SDImageCoderHelper.defaultScaleDownLimitBytes;
|
|
SDImageCoderHelper.defaultScaleDownLimitBytes = 1000 * 1000 * 4; // Limit 1000x1000 pixel
|
|
// From v5.5.0, the `SDWebImageScaleDownLargeImages` translate to `SDWebImageContextImageThumbnailPixelSize`, and works for progressive loading
|
|
[SDWebImageManager.sharedManager loadImageWithURL:originalImageURL options:SDWebImageFromLoaderOnly | SDWebImageScaleDownLargeImages | SDWebImageProgressiveLoad progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
|
expect(image).notTo.beNil();
|
|
expect(image.size).equal(CGSizeMake(1000, 1000));
|
|
if (finished) {
|
|
expect(image.sd_isIncremental).beFalsy();
|
|
[expectation fulfill];
|
|
} else {
|
|
expect(image.sd_isIncremental).beTruthy();
|
|
}
|
|
}];
|
|
|
|
[self waitForExpectationsWithCommonTimeoutUsingHandler:^(NSError * _Nullable error) {
|
|
SDImageCoderHelper.defaultScaleDownLimitBytes = defaultLimitBytes;
|
|
}];
|
|
}
|
|
|
|
- (void)test13ThatScaleDownLargeImageEXIFOrientationImage {
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"SDWebImageScaleDownLargeImages works on EXIF orientation image"];
|
|
NSURL *originalImageURL = [NSURL URLWithString:@"https://raw.githubusercontent.com/recurser/exif-orientation-examples/master/Landscape_2.jpg"];
|
|
[SDWebImageManager.sharedManager loadImageWithURL:originalImageURL options:SDWebImageScaleDownLargeImages context:@{SDWebImageContextImageForceDecodePolicy : @(SDImageForceDecodePolicyNever)} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
|
expect(image).notTo.beNil();
|
|
#if SD_UIKIT
|
|
// The UIGraphicsImageRenderer will correct image to Up(1) orientation
|
|
// So we disable that to test the behavior
|
|
UIImageOrientation orientation = [SDImageCoderHelper imageOrientationFromEXIFOrientation:kCGImagePropertyOrientationUpMirrored];
|
|
expect(image.imageOrientation).equal(orientation);
|
|
#endif
|
|
if (finished) {
|
|
expect(image.sd_isIncremental).beFalsy();
|
|
[expectation fulfill];
|
|
} else {
|
|
expect(image.sd_isIncremental).beTruthy();
|
|
}
|
|
}];
|
|
|
|
[self waitForExpectationsWithCommonTimeout];
|
|
}
|
|
|
|
- (void)test14ThatCustomCacheAndLoaderWorks {
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"Custom Cache and Loader during manger query"];
|
|
NSURL *url = [NSURL URLWithString:@"https://placehold.co/100x100.png"];
|
|
SDWebImageContext *context = @{
|
|
SDWebImageContextImageCache : SDWebImageTestCache.sharedCache,
|
|
SDWebImageContextImageLoader : SDWebImageTestLoader.sharedLoader
|
|
};
|
|
[SDWebImageTestCache.sharedCache clearWithCacheType:SDImageCacheTypeAll completion:nil];
|
|
[SDWebImageManager.sharedManager loadImageWithURL:url options:SDWebImageWaitStoreCache context:context progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
|
expect(image).notTo.beNil();
|
|
expect(image.size.width).equal(100);
|
|
expect(image.size.height).equal(100);
|
|
expect(data).notTo.beNil();
|
|
NSString *cacheKey = [SDWebImageManager.sharedManager cacheKeyForURL:imageURL];
|
|
// Check Disk Cache (SDWebImageWaitStoreCache behavior)
|
|
[SDWebImageTestCache.sharedCache containsImageForKey:cacheKey cacheType:SDImageCacheTypeDisk completion:^(SDImageCacheType containsCacheType) {
|
|
expect(containsCacheType).equal(SDImageCacheTypeDisk);
|
|
[expectation fulfill];
|
|
}];
|
|
}];
|
|
|
|
[self waitForExpectationsWithCommonTimeout];
|
|
}
|
|
|
|
- (void)test15ThatQueryCacheTypeWork {
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"Image query cache type works"];
|
|
NSURL *url = [NSURL URLWithString:@"https://placehold.co/101x101.png"];
|
|
NSString *key = [SDWebImageManager.sharedManager cacheKeyForURL:url];
|
|
NSData *testImageData = [NSData dataWithContentsOfFile:[self testJPEGPath]];
|
|
[SDImageCache.sharedImageCache storeImageDataToDisk:testImageData forKey:key];
|
|
|
|
// Query memory first
|
|
[SDWebImageManager.sharedManager loadImageWithURL:url options:SDWebImageFromCacheOnly context:@{SDWebImageContextQueryCacheType : @(SDImageCacheTypeMemory)} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
|
expect(image).beNil();
|
|
expect(cacheType).equal(SDImageCacheTypeNone);
|
|
// Query disk secondly
|
|
[SDWebImageManager.sharedManager loadImageWithURL:url options:SDWebImageFromCacheOnly context:@{SDWebImageContextQueryCacheType : @(SDImageCacheTypeDisk)} progress:nil completed:^(UIImage * _Nullable image2, NSData * _Nullable data2, NSError * _Nullable error2, SDImageCacheType cacheType2, BOOL finished2, NSURL * _Nullable imageURL2) {
|
|
expect(image2).notTo.beNil();
|
|
expect(cacheType2).equal(SDImageCacheTypeDisk);
|
|
[SDImageCache.sharedImageCache removeImageFromDiskForKey:key];
|
|
[expectation fulfill];
|
|
}];
|
|
}];
|
|
|
|
[self waitForExpectationsWithCommonTimeout];
|
|
}
|
|
|
|
- (void)test15ThatOriginalQueryCacheTypeWork {
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"Image original query cache type with transformer works"];
|
|
NSURL *url = [NSURL URLWithString:@"https://placehold.co/102x102.png"];
|
|
SDWebImageTestTransformer *transformer = [[SDWebImageTestTransformer alloc] init];
|
|
transformer.testImage = [[UIImage alloc] initWithContentsOfFile:[self testJPEGPath]];
|
|
NSString *originalKey = [SDWebImageManager.sharedManager cacheKeyForURL:url];
|
|
NSString *transformedKey = [SDWebImageManager.sharedManager cacheKeyForURL:url context:@{SDWebImageContextImageTransformer : transformer}];
|
|
|
|
[[SDImageCache sharedImageCache] removeImageFromDiskForKey:originalKey];
|
|
[[SDImageCache sharedImageCache] removeImageFromMemoryForKey:originalKey];
|
|
[[SDImageCache sharedImageCache] removeImageFromDiskForKey:transformedKey];
|
|
[[SDImageCache sharedImageCache] removeImageFromMemoryForKey:transformedKey];
|
|
[[SDWebImageManager sharedManager] loadImageWithURL:url options:SDWebImageWaitStoreCache context:@{SDWebImageContextImageTransformer : transformer, SDWebImageContextOriginalStoreCacheType : @(SDImageCacheTypeAll)} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
|
// Get the transformed image
|
|
expect(image).equal(transformer.testImage);
|
|
// Now, the original image is stored into memory/disk cache
|
|
UIImage *originalImage = [SDImageCache.sharedImageCache imageFromMemoryCacheForKey:originalKey];
|
|
expect(originalImage.size).equal(CGSizeMake(102, 102));
|
|
// Query again with original cache type, which should not trigger any download
|
|
UIImage *transformedImage = [SDImageCache.sharedImageCache imageFromMemoryCacheForKey:transformedKey];
|
|
expect(image).equal(transformedImage);
|
|
[SDImageCache.sharedImageCache removeImageFromDiskForKey:transformedKey];
|
|
[SDImageCache.sharedImageCache removeImageFromMemoryForKey:transformedKey];
|
|
[SDWebImageManager.sharedManager loadImageWithURL:url options:SDWebImageWaitStoreCache | SDWebImageFromCacheOnly context:@{SDWebImageContextImageTransformer : transformer, SDWebImageContextOriginalQueryCacheType : @(SDImageCacheTypeAll)} progress:nil completed:^(UIImage * _Nullable image2, NSData * _Nullable data2, NSError * _Nullable error2, SDImageCacheType cacheType2, BOOL finished2, NSURL * _Nullable imageURL2) {
|
|
// Get the transformed image
|
|
expect(image2).equal(transformer.testImage);
|
|
expect(data).beNil(); // Currently, the thumbnailed and transformed image always data is nil, to avoid confuse user (the image and data represent no longer match)
|
|
[SDImageCache.sharedImageCache removeImageFromMemoryForKey:originalKey];
|
|
[SDImageCache.sharedImageCache removeImageFromDiskForKey:originalKey];
|
|
[expectation fulfill];
|
|
}];
|
|
}];
|
|
|
|
[self waitForExpectationsWithCommonTimeout];
|
|
}
|
|
|
|
- (void)test16ThatTransformerUseDifferentCacheForOriginalAndTransformedImage {
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"Image transformer use different cache instance for original image and transformed image works"];
|
|
NSURL *url = [NSURL URLWithString:@"https://placehold.co/103x103.png"];
|
|
SDWebImageTestTransformer *transformer = [[SDWebImageTestTransformer alloc] init];
|
|
transformer.testImage = [[UIImage alloc] initWithContentsOfFile:[self testJPEGPath]];
|
|
NSString *originalKey = [SDWebImageManager.sharedManager cacheKeyForURL:url];
|
|
NSString *transformedKey = [SDWebImageManager.sharedManager cacheKeyForURL:url context:@{SDWebImageContextImageTransformer : transformer}];
|
|
|
|
SDImageCache *transformerCache = [[SDImageCache alloc] initWithNamespace:@"TransformerCache"];
|
|
SDImageCache *originalCache = [[SDImageCache alloc] initWithNamespace:@"OriginalCache"];
|
|
|
|
[[SDWebImageManager sharedManager] loadImageWithURL:url options:SDWebImageWaitStoreCache context:
|
|
@{SDWebImageContextImageTransformer : transformer,
|
|
SDWebImageContextOriginalImageCache : originalCache,
|
|
SDWebImageContextImageCache : transformerCache,
|
|
SDWebImageContextOriginalStoreCacheType: @(SDImageCacheTypeMemory),
|
|
SDWebImageContextStoreCacheType: @(SDImageCacheTypeMemory)} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
|
// Get the transformed image
|
|
expect(image).equal(transformer.testImage);
|
|
expect(data).beNil(); // Currently, the thumbnailed and transformed image always data is nil, to avoid confuse user (the image and data represent no longer match)
|
|
// Now, the original image is stored into originalCache
|
|
UIImage *originalImage = [originalCache imageFromMemoryCacheForKey:originalKey];
|
|
expect(originalImage.size).equal(CGSizeMake(103, 103));
|
|
expect([transformerCache imageFromMemoryCacheForKey:originalKey]).beNil();
|
|
|
|
// The transformed image is stored into transformerCache
|
|
UIImage *transformedImage = [transformerCache imageFromMemoryCacheForKey:transformedKey];
|
|
expect(image).equal(transformedImage);
|
|
expect([originalCache imageFromMemoryCacheForKey:transformedKey]).beNil();
|
|
|
|
[originalCache clearWithCacheType:SDImageCacheTypeAll completion:nil];
|
|
[transformerCache clearWithCacheType:SDImageCacheTypeAll completion:nil];
|
|
|
|
[expectation fulfill];
|
|
}];
|
|
|
|
[self waitForExpectationsWithTimeout:kAsyncTestTimeout * 10 handler:nil];
|
|
}
|
|
|
|
- (void)test17ThatThumbnailCacheQueryNotWriteToWrongKey {
|
|
// 1. When query thumbnail decoding for SDImageCache, the thumbnailed image should not stored into full size key
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"Thumbnail for cache should not store the wrong key"];
|
|
|
|
// 500x500
|
|
CGSize fullSize = CGSizeMake(500, 500);
|
|
SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc] initWithSize:fullSize];
|
|
UIImage *fullSizeImage = [renderer imageWithActions:^(CGContextRef _Nonnull context) {
|
|
CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0);
|
|
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
|
|
CGContextFillRect(context, CGRectMake(0, 0, fullSize.width, fullSize.height));
|
|
}];
|
|
expect(fullSizeImage.size).equal(fullSize);
|
|
|
|
NSString *fullSizeKey = @"kTestRectangle";
|
|
// Disk only
|
|
[SDImageCache.sharedImageCache storeImageDataToDisk:fullSizeImage.sd_imageData forKey:fullSizeKey];
|
|
|
|
CGSize thumbnailSize = CGSizeMake(100, 100);
|
|
NSString *thumbnailKey = SDThumbnailedKeyForKey(fullSizeKey, thumbnailSize, YES);
|
|
// thumbnail size key miss, full size key hit
|
|
[SDImageCache.sharedImageCache queryCacheOperationForKey:fullSizeKey options:0 context:@{SDWebImageContextImageThumbnailPixelSize : @(thumbnailSize)} done:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) {
|
|
expect(image.size).equal(thumbnailSize);
|
|
expect(cacheType).equal(SDImageCacheTypeDisk);
|
|
// Check the full image should not be in memory cache (because we have only full data + thumbnail image)
|
|
// Check the thumbnail image should be in memory cache (because we have only full data + thumbnail image)
|
|
expect([SDImageCache.sharedImageCache imageFromMemoryCacheForKey:fullSizeKey].size).equal(CGSizeZero);
|
|
expect([SDImageCache.sharedImageCache imageFromDiskCacheForKey:fullSizeKey]).notTo.beNil();
|
|
expect([SDImageCache.sharedImageCache imageFromMemoryCacheForKey:thumbnailKey].size).equal(thumbnailSize);
|
|
expect([SDImageCache.sharedImageCache imageFromDiskCacheForKey:thumbnailKey]).beNil();
|
|
|
|
[SDImageCache.sharedImageCache removeImageFromDiskForKey:fullSizeKey];
|
|
[SDImageCache.sharedImageCache removeImageFromMemoryForKey:thumbnailKey];
|
|
[expectation fulfill];
|
|
}];
|
|
|
|
[self waitForExpectationsWithCommonTimeout];
|
|
}
|
|
|
|
- (void)test18ThatThumbnailLoadingCanUseFullSizeCache {
|
|
// 2. When using SDWebImageManager to load thumbnail image, it will prefers the full size image and thumbnail decoding on the fly, no network
|
|
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"Thumbnail for loading should prefers full size cache when thumbnail cache miss, like Transformer behavior"];
|
|
|
|
// 500x500
|
|
CGSize fullSize = CGSizeMake(500, 500);
|
|
SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc] initWithSize:fullSize];
|
|
UIImage *fullSizeImage = [renderer imageWithActions:^(CGContextRef _Nonnull context) {
|
|
CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0);
|
|
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
|
|
CGContextFillRect(context, CGRectMake(0, 0, fullSize.width, fullSize.height));
|
|
}];
|
|
expect(fullSizeImage.size).equal(fullSize);
|
|
NSURL *url = [NSURL URLWithString:@"https://placehold.co/500x500.png"];
|
|
NSString *fullSizeKey = [SDWebImageManager.sharedManager cacheKeyForURL:url];
|
|
NSData *fullSizeData = fullSizeImage.sd_imageData;
|
|
[SDImageCache.sharedImageCache storeImageDataToDisk:fullSizeData forKey:fullSizeKey];
|
|
|
|
CGSize thumbnailSize = CGSizeMake(100, 100);
|
|
NSString *thumbnailKey = SDThumbnailedKeyForKey(fullSizeKey, thumbnailSize, YES);
|
|
[SDImageCache.sharedImageCache removeImageFromDiskForKey:thumbnailKey];
|
|
// Load with thumbnail, should use full size cache instead to decode and scale down
|
|
[SDWebImageManager.sharedManager loadImageWithURL:url options:SDWebImageWaitStoreCache context:@{SDWebImageContextImageThumbnailPixelSize : @(thumbnailSize)} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
|
expect(image.size).equal(thumbnailSize);
|
|
expect(data).beNil(); // Currently, the thumbnailed and transformed image always data is nil, to avoid confuse user (the image and data represent no longer match)
|
|
expect(cacheType).equal(SDImageCacheTypeDisk);
|
|
expect(finished).beTruthy();
|
|
|
|
// The thumbnail one should stored into memory and disk cache with thumbnail key as well
|
|
expect([SDImageCache.sharedImageCache imageFromMemoryCacheForKey:thumbnailKey].size).equal(thumbnailSize);
|
|
expect([SDImageCache.sharedImageCache imageFromDiskCacheForKey:thumbnailKey].size).equal(thumbnailSize);
|
|
|
|
[expectation fulfill];
|
|
}];
|
|
|
|
[self waitForExpectationsWithCommonTimeout];
|
|
}
|
|
|
|
- (void)test19ThatDifferentThumbnailLoadShouldCallbackDifferentSize {
|
|
// 3. Current SDWebImageDownloader use the **URL** as primiary key to bind operation, however, different loading pipeline may ask different image size for same URL, this design does not match
|
|
// We move the logic into SDWebImageDownloaderOperation, which decode each callback's thumbnail size with different decoding pipeline, and callback independently
|
|
// Note the progressiveLoad does not support this and always callback first size
|
|
|
|
NSURL *url = [NSURL URLWithString:@"https://placehold.co/501x501.png"];
|
|
NSString *fullSizeKey = [SDWebImageManager.sharedManager cacheKeyForURL:url];
|
|
[SDImageCache.sharedImageCache removeImageFromDiskForKey:fullSizeKey];
|
|
for (int i = 490; i < 500; i++) {
|
|
// 490x490, ..., 499x499
|
|
CGSize thumbnailSize = CGSizeMake(i, i);
|
|
NSString *thumbnailKey = SDThumbnailedKeyForKey(fullSizeKey, thumbnailSize, YES);
|
|
[SDImageCache.sharedImageCache removeImageFromDiskForKey:thumbnailKey];
|
|
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Different thumbnail loading for same URL should callback different image size: (%dx%d)", i, i]];
|
|
[SDImageCache.sharedImageCache removeImageFromDiskForKey:url.absoluteString];
|
|
__block SDWebImageCombinedOperation *operation;
|
|
operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:0 context:@{SDWebImageContextImageThumbnailPixelSize : @(thumbnailSize)} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
|
expect(image.size).equal(thumbnailSize);
|
|
expect(data).beNil(); // Currently, the thumbnailed and transformed image always data is nil, to avoid confuse user (the image and data represent no longer match)
|
|
expect(cacheType).equal(SDImageCacheTypeNone);
|
|
expect(finished).beTruthy();
|
|
|
|
NSURLRequest *request = ((SDWebImageDownloadToken *)operation.loaderOperation).request;
|
|
NSLog(@"thumbnail image size: (%dx%d) loaded with the shared request: %p", i, i, request);
|
|
[expectation fulfill];
|
|
}];
|
|
}
|
|
|
|
[self waitForExpectationsWithTimeout:kAsyncTestTimeout * 5 handler:nil];
|
|
}
|
|
|
|
- (void)test20ThatContextPassDecodeOptionsWorks {
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"The SDWebImageContextImageDecodeOptions should passed to the coder"];
|
|
NSURL *url = [NSURL URLWithString:@"https://placehold.co/502x502.png"];
|
|
SDImageCoderOptions *originalDecodeOptions = @{@"Foo": @"Bar", SDImageCoderDecodeScaleFactor : @(2)}; // This will be override
|
|
|
|
[SDWebImageManager.sharedManager loadImageWithURL:url options:0 context:@{SDWebImageContextImageScaleFactor : @(1), SDWebImageContextImageDecodeOptions : originalDecodeOptions} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
|
SDImageCoderOptions *decodeOptions = image.sd_decodeOptions;
|
|
expect(decodeOptions.count).beGreaterThan(originalDecodeOptions.count);
|
|
expect(decodeOptions[@"Foo"]).equal(@"Bar");
|
|
expect(decodeOptions[SDImageCoderDecodeScaleFactor]).equal(1);
|
|
[expectation fulfill];
|
|
}];
|
|
[self waitForExpectationsWithCommonTimeout];
|
|
}
|
|
|
|
- (void)test21ThatQueryOriginalDiskCacheFromThumbnailShouldNotWriteBackDiskCache {
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"Using original disk cache to do thumbnail decoding or transformer, should not save back disk data again"];
|
|
|
|
NSURL *url = [NSURL URLWithString:@"https://placehold.co/503x503.png"];
|
|
NSString *originalKey = url.absoluteString;
|
|
// 1. Store the disk data to original cache
|
|
CGSize fullSize = CGSizeMake(503, 503);
|
|
SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc] initWithSize:fullSize];
|
|
UIImage *fullSizeImage = [renderer imageWithActions:^(CGContextRef _Nonnull context) {
|
|
CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0);
|
|
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
|
|
CGContextFillRect(context, CGRectMake(0, 0, fullSize.width, fullSize.height));
|
|
}];
|
|
NSData *fullSizeData = fullSizeImage.sd_imageData;
|
|
[SDImageCache.sharedImageCache storeImageDataToDisk:fullSizeData forKey:originalKey];
|
|
|
|
// 2. Query thumbnail size, should hit full disk cache instead of download
|
|
SDWebImageContext *context = @{
|
|
SDWebImageContextImageThumbnailPixelSize: @(CGSizeMake(100, 100)),
|
|
SDWebImageContextOriginalQueryCacheType: @(SDImageCacheTypeAll),
|
|
SDWebImageContextOriginalStoreCacheType: @(SDImageCacheTypeDisk),
|
|
SDWebImageContextQueryCacheType: @(SDImageCacheTypeAll),
|
|
SDWebImageContextStoreCacheType: @(SDImageCacheTypeAll) // these 4 are all default values
|
|
};
|
|
NSString *key = [SDWebImageManager.sharedManager cacheKeyForURL:url context:context]; // Thumbnail key
|
|
[SDImageCache.sharedImageCache removeImageFromDiskForKey:key];
|
|
[SDWebImageManager.sharedManager loadImageWithURL:url options:SDWebImageWaitStoreCache context:context progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
|
expect(cacheType).equal(SDImageCacheTypeDisk);
|
|
expect(data).beNil(); // Currently, the thumbnailed and transformed image always data is nil, to avoid confuse user (the image and data represent no longer match)
|
|
expect(image.size.width).equal(100);
|
|
expect(image.size.height).equal(100);
|
|
// 3. Check full size disk cache again, should equal to original stored one
|
|
NSData *currentFullSizeData = [SDImageCache.sharedImageCache diskImageDataForKey:originalKey];
|
|
expect(currentFullSizeData).equal(fullSizeData);
|
|
// 4. Some extra check that thumbnailed image should store to disk/memory as well to wait for next time query
|
|
expect([SDImageCache.sharedImageCache diskImageDataExistsWithKey:key]).beTruthy();
|
|
expect([SDImageCache.sharedImageCache imageFromMemoryCacheForKey:key]).beTruthy();
|
|
|
|
[expectation fulfill];
|
|
}];
|
|
|
|
[self waitForExpectationsWithCommonTimeout];
|
|
}
|
|
|
|
- (void)test22ThatForceDecodePolicyAutomatic {
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"Automatic policy with ICC profile colorspace image should force-decode"];
|
|
NSURL *url = [NSURL URLWithString:@"http://photodb.illusdolphin.net/media/15292/browsertest.jpg"];
|
|
SDImageCoderHelper.defaultDecodeSolution = SDImageCoderDecodeSolutionCoreGraphics; // Temp set
|
|
[SDWebImageManager.sharedManager loadImageWithURL:url options:SDWebImageFromLoaderOnly context:@{SDWebImageContextImageForceDecodePolicy : @(SDImageForceDecodePolicyAutomatic)} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
|
expect(image).notTo.beNil();
|
|
expect(image.sd_isDecoded).beTruthy();
|
|
CGImageRef cgImage = image.CGImage;
|
|
CGColorSpaceRef colorspace = CGImageGetColorSpace(cgImage);
|
|
expect(colorspace).equal([SDImageCoderHelper colorSpaceGetDeviceRGB]);
|
|
// Revert back
|
|
SDImageCoderHelper.defaultDecodeSolution = SDImageCoderDecodeSolutionAutomatic;
|
|
|
|
[expectation fulfill];
|
|
}];
|
|
[self waitForExpectationsWithCommonTimeout];
|
|
}
|
|
|
|
#if __has_include(<SDWebImageWebPCoder/SDWebImageWebPCoder.h>)
|
|
- (void)test22ThatForceDecodePolicyAlways {
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"Always policy with WebP image (libwebp) should force-decode"];
|
|
NSURL *url = [NSURL URLWithString:@"https://www.gstatic.com/webp/gallery/4.webp"];
|
|
[SDWebImageManager.sharedManager loadImageWithURL:url options:SDWebImageFromLoaderOnly context:@{SDWebImageContextImageCoder : SDImageWebPCoder.sharedCoder, SDWebImageContextImageForceDecodePolicy : @(SDImageForceDecodePolicyAlways)} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
|
|
expect(image).notTo.beNil();
|
|
expect(image.sd_isDecoded).beTruthy();
|
|
CGImageRef cgImage = image.CGImage;
|
|
CGColorSpaceRef colorspace = CGImageGetColorSpace(cgImage);
|
|
expect(colorspace).equal([SDImageCoderHelper colorSpaceGetDeviceRGB]);
|
|
|
|
[expectation fulfill];
|
|
}];
|
|
[self waitForExpectationsWithCommonTimeout];
|
|
}
|
|
#endif
|
|
|
|
- (NSString *)testJPEGPath {
|
|
NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
|
|
return [testBundle pathForResource:@"TestImage" ofType:@"jpg"];
|
|
}
|
|
|
|
@end
|