SDWebImage/Tests/Tests/SDWebCacheCategoriesTests.m

624 lines
33 KiB
Objective-C

/*
* 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 <KVOController/KVOController.h>
@interface SDWebCacheCategoriesTests : SDTestCase
@property (nonatomic, strong) UIWindow *window;
@end
@implementation SDWebCacheCategoriesTests
- (void)testUIImageViewSetImageWithURL {
XCTestExpectation *expectation = [self expectationWithDescription:@"UIImageView setImageWithURL"];
UIImageView *imageView = [[UIImageView alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL];
[imageView sd_setImageWithURL:originalImageURL
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
expect(image).toNot.beNil();
expect(error).to.beNil();
expect(originalImageURL).to.equal(imageURL);
expect(imageView.image).to.equal(image);
[expectation fulfill];
}];
expect(imageView.sd_imageURL).equal(originalImageURL);
[self waitForExpectationsWithCommonTimeout];
}
- (void)testUIImageViewSetImageWithURLDiskSync {
NSData *imageData = [NSData dataWithContentsOfFile:[self testJPEGPath]];
// Ensure the image is cached in disk but not memory
[SDImageCache.sharedImageCache removeImageFromMemoryForKey:kTestJPEGURL];
[SDImageCache.sharedImageCache removeImageFromDiskForKey:kTestJPEGURL];
[SDImageCache.sharedImageCache storeImageDataToDisk:imageData forKey:kTestJPEGURL];
UIImageView *imageView = [[UIImageView alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL];
[imageView sd_setImageWithURL:originalImageURL
placeholderImage:nil
options:SDWebImageQueryDiskDataSync
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
expect(image).toNot.beNil();
expect(error).to.beNil();
expect(originalImageURL).to.equal(imageURL);
expect(imageView.image).to.equal(image);
}];
expect(imageView.sd_imageURL).equal(originalImageURL);
expect(imageView.image).toNot.beNil();
}
#if SD_UIKIT
- (void)testUIImageViewSetHighlightedImageWithURL {
XCTestExpectation *expectation = [self expectationWithDescription:@"UIImageView setHighlightedImageWithURL"];
UIImageView *imageView = [[UIImageView alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL];
[imageView sd_setHighlightedImageWithURL:originalImageURL
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
expect(image).toNot.beNil();
expect(error).to.beNil();
expect(originalImageURL).to.equal(imageURL);
expect(imageView.highlightedImage).to.equal(image);
[expectation fulfill];
}];
[self waitForExpectationsWithCommonTimeout];
}
#endif
- (void)testMKAnnotationViewSetImageWithURL {
XCTestExpectation *expectation = [self expectationWithDescription:@"MKAnnotationView setImageWithURL"];
MKAnnotationView *annotationView = [[MKAnnotationView alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL];
[annotationView sd_setImageWithURL:originalImageURL
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
expect(image).toNot.beNil();
expect(error).to.beNil();
expect(originalImageURL).to.equal(imageURL);
expect(annotationView.image).to.equal(image);
[expectation fulfill];
}];
[self waitForExpectationsWithCommonTimeout];
}
#if SD_UIKIT
- (void)testUIButtonSetImageWithURLNormalState {
XCTestExpectation *expectation = [self expectationWithDescription:@"UIButton setImageWithURL normalState"];
UIButton *button = [[UIButton alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL];
[button sd_setImageWithURL:originalImageURL
forState:UIControlStateNormal
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
expect(image).toNot.beNil();
expect(error).to.beNil();
expect(originalImageURL).to.equal(imageURL);
expect([button imageForState:UIControlStateNormal]).to.equal(image);
[expectation fulfill];
}];
[self waitForExpectationsWithCommonTimeout];
}
- (void)testUIButtonSetImageWithURLHighlightedState {
XCTestExpectation *expectation = [self expectationWithDescription:@"UIButton setImageWithURL highlightedState"];
UIButton *button = [[UIButton alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL];
[button sd_setImageWithURL:originalImageURL
forState:UIControlStateHighlighted
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
expect(image).toNot.beNil();
expect(error).to.beNil();
expect(originalImageURL).to.equal(imageURL);
expect([button imageForState:UIControlStateHighlighted]).to.equal(image);
[expectation fulfill];
}];
[self waitForExpectationsWithCommonTimeout];
}
- (void)testUIButtonSetBackgroundImageWithURLNormalState {
XCTestExpectation *expectation = [self expectationWithDescription:@"UIButton setBackgroundImageWithURL normalState"];
UIButton *button = [[UIButton alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL];
[button sd_setBackgroundImageWithURL:originalImageURL
forState:UIControlStateNormal
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
expect(image).toNot.beNil();
expect(error).to.beNil();
expect(originalImageURL).to.equal(imageURL);
expect([button backgroundImageForState:UIControlStateNormal]).to.equal(image);
[expectation fulfill];
}];
[self waitForExpectationsWithCommonTimeout];
}
- (void)testUIButtonBackgroundImageCancelCurrentImageLoad {
UIButton *button = [[UIButton alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL];
[button sd_setBackgroundImageWithURL:originalImageURL forState:UIControlStateNormal];
[button sd_cancelBackgroundImageLoadForState:UIControlStateNormal];
NSString *backgroundImageOperationKey = [self testBackgroundImageOperationKeyForState:UIControlStateNormal];
expect([button sd_imageLoadOperationForKey:backgroundImageOperationKey]).beNil();
}
#endif
#if SD_MAC
- (void)testNSButtonSetImageWithURL {
XCTestExpectation *expectation = [self expectationWithDescription:@"NSButton setImageWithURL"];
NSButton *button = [[NSButton alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL];
[button sd_setImageWithURL:originalImageURL
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
expect(image).toNot.beNil();
expect(error).to.beNil();
expect(originalImageURL).to.equal(imageURL);
expect(button.image).to.equal(image);
[expectation fulfill];
}];
[self waitForExpectationsWithCommonTimeout];
}
- (void)testNSButtonSetAlternateImageWithURL {
XCTestExpectation *expectation = [self expectationWithDescription:@"NSButton setAlternateImageWithURL"];
NSButton *button = [[NSButton alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL];
[button sd_setAlternateImageWithURL:originalImageURL
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
expect(image).toNot.beNil();
expect(error).to.beNil();
expect(originalImageURL).to.equal(imageURL);
expect(button.alternateImage).to.equal(image);
[expectation fulfill];
}];
[self waitForExpectationsWithCommonTimeout];
}
#endif
- (void)testUIViewInternalSetImageWithURL {
XCTestExpectation *expectation = [self expectationWithDescription:@"UIView internalSetImageWithURL"];
UIView *view = [[UIView alloc] init];
#if SD_MAC
view.wantsLayer = YES;
#endif
NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL];
UIImage *placeholder = [[UIImage alloc] initWithContentsOfFile:[self testJPEGPath]];
[view sd_internalSetImageWithURL:originalImageURL
placeholderImage:placeholder
options:0
context:nil
setImageBlock:^(UIImage * _Nullable image, NSData * _Nullable imageData, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
if (!imageData && cacheType == SDImageCacheTypeNone) {
// placeholder
expect(image).to.equal(placeholder);
} else {
// cache or download
expect(image).toNot.beNil();
}
view.layer.contents = (__bridge id _Nullable)(image.CGImage);
}
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();
expect(originalImageURL).to.equal(imageURL);
expect((__bridge CGImageRef)view.layer.contents == image.CGImage).to.beTruthy();
[expectation fulfill];
}];
[self waitForExpectationsWithCommonTimeout];
}
- (void)testUIViewCancelWithDelayPlaceholderShouldCallbackOnceBeforeSecond {
XCTestExpectation *expectation1 = [self expectationWithDescription:@"UIView internalSetImageWithURL call 1"];
XCTestExpectation *expectation2 = [self expectationWithDescription:@"UIView internalSetImageWithURL call 2"];
UIView *imageView = [[UIView alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL];
[SDImageCache.sharedImageCache removeImageFromDiskForKey:kTestJPEGURL];
[SDImageCache.sharedImageCache removeImageFromMemoryForKey:kTestJPEGURL];
__block NSUInteger calledSetImageTimes = 0;
__block NSUInteger calledSetImageTimes2 = 0;
NSString *operationKey = NSUUID.UUID.UUIDString;
UIImage *placeholder1 = UIImage.new;
id<SDWebImageOperation> op1 = [imageView sd_internalSetImageWithURL:originalImageURL placeholderImage:placeholder1 options:SDWebImageDelayPlaceholder context:@{ SDWebImageContextSetImageOperationKey:operationKey} setImageBlock:^(UIImage * _Nullable image, NSData * _Nullable imageData, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
// Should called before second query (We changed the cache callback in sync when cancelled)
expect(calledSetImageTimes2).equal(0);
calledSetImageTimes++;
} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
if (calledSetImageTimes == 1) {
[expectation1 fulfill];
}
}];
[op1 cancel];
UIImage *placeholder2 = UIImage.new;
[imageView sd_internalSetImageWithURL:originalImageURL placeholderImage:placeholder2 options:0 context:@{ SDWebImageContextSetImageOperationKey:operationKey} setImageBlock:^(UIImage * _Nullable image, NSData * _Nullable imageData, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
if (calledSetImageTimes2 == 0) {
expect(image).equal(placeholder2);
} else {
expect(image).notTo.beNil();
}
calledSetImageTimes2++;
} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
if (calledSetImageTimes2 == 2) {
[expectation2 fulfill];
}
}];
[self waitForExpectationsWithCommonTimeout];
}
- (void)testUIViewCancelWithoutDelayPlaceholderShouldCallbackOnceBeforeSecond {
XCTestExpectation *expectation1 = [self expectationWithDescription:@"UIView internalSetImageWithURL call 1"];
XCTestExpectation *expectation2 = [self expectationWithDescription:@"UIView internalSetImageWithURL call 2"];
UIView *imageView = [[UIView alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL];
[SDImageCache.sharedImageCache removeImageFromDiskForKey:kTestJPEGURL];
[SDImageCache.sharedImageCache removeImageFromMemoryForKey:kTestJPEGURL];
__block NSUInteger calledSetImageTimes = 0;
__block NSUInteger calledSetImageTimes2 = 0;
NSString *operationKey = NSUUID.UUID.UUIDString;
UIImage *placeholder1 = UIImage.new;
id<SDWebImageOperation> op1 = [imageView sd_internalSetImageWithURL:originalImageURL placeholderImage:placeholder1 options:0 context:@{ SDWebImageContextSetImageOperationKey:operationKey} setImageBlock:^(UIImage * _Nullable image, NSData * _Nullable imageData, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
// Should called before second query (We changed the cache callback in sync when cancelled)
expect(calledSetImageTimes2).equal(0);
calledSetImageTimes++;
} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
if (calledSetImageTimes == 1) {
[expectation1 fulfill];
}
}];
[op1 cancel];
UIImage *placeholder2 = UIImage.new;
[imageView sd_internalSetImageWithURL:originalImageURL placeholderImage:placeholder2 options:0 context:@{ SDWebImageContextSetImageOperationKey:operationKey} setImageBlock:^(UIImage * _Nullable image, NSData * _Nullable imageData, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
if (calledSetImageTimes2 == 0) {
expect(image).equal(placeholder2);
} else {
expect(image).notTo.beNil();
}
calledSetImageTimes2++;
} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
if (calledSetImageTimes2 == 2) {
[expectation2 fulfill];
}
}];
[self waitForExpectationsWithCommonTimeout];
}
- (void)testUIViewCancelCurrentImageLoad {
UIView *imageView = [[UIView alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL];
[SDImageCache.sharedImageCache removeImageFromDiskForKey:kTestJPEGURL];
[SDImageCache.sharedImageCache removeImageFromMemoryForKey: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_latestOperationKey).beNil();
expect([imageView sd_imageLoadOperationForKey:operationKey]).beNil();
}
- (void)testUIViewCancelCurrentImageLoadWithTransition {
UIView *imageView = [[UIView alloc] init];
NSURL *firstImageUrl = [NSURL URLWithString:kTestJPEGURL];
NSURL *secondImageUrl = [NSURL URLWithString:kTestPNGURL];
// First, reset our caches
[SDImageCache.sharedImageCache removeImageFromDiskForKey:kTestJPEGURL];
[SDImageCache.sharedImageCache removeImageFromMemoryForKey:kTestPNGURL];
// Next, lets put our second image into memory, so that the next time
// we load it, it will come from memory, and thus shouldUseTransition will be NO
XCTestExpectation *firstLoadExpectation = [self expectationWithDescription:@"First image loaded"];
[imageView sd_internalSetImageWithURL:secondImageUrl placeholderImage:nil options:0 context:nil setImageBlock:nil progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
[firstLoadExpectation fulfill];
}];
[self waitForExpectations:@[firstLoadExpectation]
timeout:5.0];
// Now, lets load a new image using a transition
XCTestExpectation *secondLoadExpectation = [self expectationWithDescription:@"Second image loaded"];
XCTestExpectation *transitionPreparesExpectation = [self expectationWithDescription:@"Transition prepares"];
// Build a custom transition with a completion block that
// we do not expect to be called, because we cancel in the
// middle of a transition
XCTestExpectation *transitionCompletionExpecation = [self expectationWithDescription:@"Transition completed"];
transitionCompletionExpecation.inverted = YES;
SDWebImageTransition *customTransition = [SDWebImageTransition new];
customTransition.duration = 1.0;
customTransition.prepares = ^(__kindof UIView * _Nonnull view, UIImage * _Nullable image, NSData * _Nullable imageData, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
[transitionPreparesExpectation fulfill];
};
customTransition.completion = ^(BOOL finished) {
[transitionCompletionExpecation fulfill];
};
// Now, load our first image URL (maybe as part of a UICollectionView)
// We use a custom context to ensure a unique ImageOperationKey for every load
// that is requested
NSMutableDictionary *context = [NSMutableDictionary new];
context[SDWebImageContextSetImageOperationKey] = firstImageUrl.absoluteString;
imageView.sd_imageTransition = customTransition;
[imageView sd_internalSetImageWithURL:firstImageUrl placeholderImage:nil options:0 context:context setImageBlock:nil progress:nil completed:nil];
[self waitForExpectations:@[transitionPreparesExpectation] timeout:5.0];
// At this point, our transition has started, and so we cancel the load operation,
// perhaps as a result of a call to `prepareForReuse` in a UICollectionViewCell
[imageView sd_cancelCurrentImageLoad];
// Now, we update our context's imageOperationKey and URL, perhaps
// because of a re-use of a UICollectionViewCell. In this case,
// we are assigning an image URL that is already present in the
// memory cache
context[SDWebImageContextSetImageOperationKey] = secondImageUrl.absoluteString;
[imageView sd_internalSetImageWithURL:secondImageUrl placeholderImage:nil options:0 context:context setImageBlock:nil progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
[secondLoadExpectation fulfill];
}];
// The original load operation's transitionCompletionExpecation should never
// be called (it has been inverted, above)
[self waitForExpectations:@[secondLoadExpectation, transitionCompletionExpecation]
timeout:2.0];
}
- (void)testUIViewCancelCallbackWithError {
XCTestExpectation *expectation = [self expectationWithDescription:@"UIView internalSetImageWithURL cancel callback error"];
UIView *imageView = [[UIView alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL];
[SDImageCache.sharedImageCache removeImageFromDiskForKey:kTestJPEGURL];
[SDImageCache.sharedImageCache removeImageFromMemoryForKey:kTestJPEGURL];
[imageView sd_internalSetImageWithURL:originalImageURL placeholderImage:nil options:0 context:nil setImageBlock:nil 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);
[expectation fulfill];
}];
[imageView sd_cancelCurrentImageLoad];
[self waitForExpectationsWithCommonTimeout];
}
- (void)testUIViewImageProgressKVOWork {
XCTestExpectation *expectation = [self expectationWithDescription:@"UIView imageProgressKVO failed"];
UIView *view = [[UIView alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL];
[self.KVOController observe:view.sd_imageProgress keyPath:NSStringFromSelector(@selector(fractionCompleted)) options:NSKeyValueObservingOptionNew block:^(id _Nullable observer, id _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
NSProgress *progress = object;
NSNumber *completedValue = change[NSKeyValueChangeNewKey];
expect(progress.fractionCompleted).equal(completedValue.doubleValue);
// mark that KVO is called
[progress setUserInfoObject:@(YES) forKey:NSStringFromSelector(@selector(testUIViewImageProgressKVOWork))];
}];
// Clear the disk cache to force download from network
[[SDImageCache sharedImageCache] removeImageForKey:kTestJPEGURL withCompletion:^{
[view sd_internalSetImageWithURL:originalImageURL placeholderImage:nil options:0 context:nil setImageBlock:nil progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
expect(view.sd_imageProgress.fractionCompleted).equal(1.0);
expect([view.sd_imageProgress.userInfo[NSStringFromSelector(_cmd)] boolValue]).equal(YES);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithCommonTimeout];
}
- (void)testUIViewTransitionFromNetworkWork {
XCTestExpectation *expectation = [self expectationWithDescription:@"UIView transition from network does not work"];
// Attach a window, or CALayer will not submit drawing
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
// Cover each convenience method
imageView.sd_imageTransition = SDWebImageTransition.fadeTransition;
imageView.sd_imageTransition = SDWebImageTransition.flipFromTopTransition;
imageView.sd_imageTransition = SDWebImageTransition.flipFromLeftTransition;
imageView.sd_imageTransition = SDWebImageTransition.flipFromBottomTransition;
imageView.sd_imageTransition = SDWebImageTransition.flipFromRightTransition;
imageView.sd_imageTransition = SDWebImageTransition.curlUpTransition;
imageView.sd_imageTransition = SDWebImageTransition.curlDownTransition;
imageView.sd_imageTransition.duration = 1;
#if SD_UIKIT
[self.window addSubview:imageView];
#else
imageView.wantsLayer = YES;
[self.window.contentView addSubview:imageView];
#endif
UIImage *placeholder = [[UIImage alloc] initWithContentsOfFile:[self testJPEGPath]];
NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL];
__weak typeof(imageView) wimageView = imageView;
[imageView sd_setImageWithURL:originalImageURL
placeholderImage:placeholder
options:SDWebImageForceTransition
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
__strong typeof(wimageView) simageView = imageView;
// Delay to let CALayer commit the transition in next runloop
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, kMinDelayNanosecond), dispatch_get_main_queue(), ^{
// Check current view contains layer animation
NSArray *animationKeys = simageView.layer.animationKeys;
expect(animationKeys.count).beGreaterThan(0);
[expectation fulfill];
});
}];
[self waitForExpectationsWithCommonTimeout];
}
- (void)testUIViewTransitionFromDiskWork {
XCTestExpectation *expectation = [self expectationWithDescription:@"UIView transition from disk does not work"];
// Attach a window, or CALayer will not submit drawing
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
imageView.sd_imageTransition = SDWebImageTransition.fadeTransition;
imageView.sd_imageTransition.duration = 1;
#if SD_UIKIT
[self.window addSubview:imageView];
#else
imageView.wantsLayer = YES;
[self.window.contentView addSubview:imageView];
#endif
NSData *imageData = [NSData dataWithContentsOfFile:[self testJPEGPath]];
UIImage *placeholder = [[UIImage alloc] initWithData:imageData];
// Ensure the image is cached in disk but not memory
[SDImageCache.sharedImageCache removeImageFromMemoryForKey:kTestJPEGURL];
[SDImageCache.sharedImageCache removeImageFromDiskForKey:kTestJPEGURL];
[SDImageCache.sharedImageCache storeImageDataToDisk:imageData forKey:kTestJPEGURL];
NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL];
__weak typeof(imageView) wimageView = imageView;
[imageView sd_setImageWithURL:originalImageURL
placeholderImage:placeholder
options:SDWebImageFromCacheOnly // Ensure we queired from disk cache
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
[SDImageCache.sharedImageCache removeImageFromMemoryForKey:kTestJPEGURL];
[SDImageCache.sharedImageCache removeImageFromDiskForKey:kTestJPEGURL];
__strong typeof(wimageView) simageView = imageView;
// Delay to let CALayer commit the transition in next runloop
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, kMinDelayNanosecond), dispatch_get_main_queue(), ^{
// Check current view contains layer animation
NSArray *animationKeys = simageView.layer.animationKeys;
expect(animationKeys.count).beGreaterThan(0);
[expectation fulfill];
});
}];
[self waitForExpectationsWithCommonTimeout];
}
- (void)testUIViewIndicatorWork {
XCTestExpectation *expectation = [self expectationWithDescription:@"UIView indicator does not work"];
UIImageView *imageView = [[UIImageView alloc] init];
imageView.sd_imageIndicator = SDWebImageActivityIndicator.grayIndicator;
// Cover each convience method, finally use progress indicator for test
imageView.sd_imageIndicator = SDWebImageActivityIndicator.grayLargeIndicator;
imageView.sd_imageIndicator = SDWebImageActivityIndicator.whiteIndicator;
imageView.sd_imageIndicator = SDWebImageActivityIndicator.whiteLargeIndicator;
#if SD_IOS
imageView.sd_imageIndicator = SDWebImageProgressIndicator.barIndicator;
#endif
imageView.sd_imageIndicator = SDWebImageProgressIndicator.defaultIndicator;
// Test setter trigger removeFromSuperView
expect(imageView.subviews.count).equal(1);
NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL];
__weak typeof(imageView) wimageView = imageView;
[imageView sd_setImageWithURL:originalImageURL
placeholderImage:nil options:SDWebImageFromLoaderOnly progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
dispatch_async(dispatch_get_main_queue(), ^{
__strong typeof(wimageView) simageView = imageView;
UIView *indicatorView = simageView.subviews.firstObject;
expect(indicatorView).equal(simageView.sd_imageIndicator.indicatorView);
if (receivedSize <= 0 || expectedSize <= 0) {
return;
}
// Base on current implementation, since we dispatch the progressBlock to main queue, the indicator's progress state should be synchonized
double progress = 0;
double imageProgress = (double)receivedSize / (double)expectedSize;
#if SD_UIKIT
progress = ((UIProgressView *)simageView.sd_imageIndicator.indicatorView).progress;
#else
progress = ((NSProgressIndicator *)simageView.sd_imageIndicator.indicatorView).doubleValue / 100;
#endif
expect(progress).equal(imageProgress);
});
} completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
__strong typeof(wimageView) simageView = imageView;
double progress = 0;
#if SD_UIKIT
progress = ((UIProgressView *)simageView.sd_imageIndicator.indicatorView).progress;
#else
progress = ((NSProgressIndicator *)simageView.sd_imageIndicator.indicatorView).doubleValue / 100;
#endif
// Finish progress is 1
expect(progress).equal(1);
[expectation fulfill];
}];
[self waitForExpectationsWithCommonTimeout];
}
- (void)testUIViewOperationKeyContextWorks {
XCTestExpectation *expectation = [self expectationWithDescription:@"UIView operation key context should pass through"];
UIView *view = [[UIView alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:kTestJPEGURL];
SDWebImageManager *customManager = [[SDWebImageManager alloc] initWithCache:SDImageCachesManager.sharedManager loader:SDImageLoadersManager.sharedManager];
customManager.optionsProcessor = [SDWebImageOptionsProcessor optionsProcessorWithBlock:^SDWebImageOptionsResult * _Nullable(NSURL * _Nullable url, SDWebImageOptions options, SDWebImageContext * _Nullable context) {
// expect manager does not exist, avoid retain cycle
expect(context[SDWebImageContextCustomManager]).beNil();
// expect operation key to be the image view class
expect(context[SDWebImageContextSetImageOperationKey]).equal(NSStringFromClass(view.class));
return [[SDWebImageOptionsResult alloc] initWithOptions:options context:context];
}];
[view sd_internalSetImageWithURL:originalImageURL
placeholderImage:nil
options:0
context:@{SDWebImageContextCustomManager: customManager}
setImageBlock:nil
progress:nil
completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
[expectation fulfill];
}];
[self waitForExpectationsWithCommonTimeout];
}
#pragma mark - Helper
- (UIWindow *)window {
if (!_window) {
UIScreen *mainScreen = [UIScreen mainScreen];
#if SD_UIKIT
_window = [[UIWindow alloc] initWithFrame:mainScreen.bounds];
#else
_window = [[NSWindow alloc] initWithContentRect:mainScreen.frame styleMask:0 backing:NSBackingStoreBuffered defer:NO screen:mainScreen];
#endif
}
return _window;
}
- (NSString *)testJPEGPath {
NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
return [testBundle pathForResource:@"TestImage" ofType:@"jpg"];
}
#if SD_UIKIT
- (NSString *)testBackgroundImageOperationKeyForState:(UIControlState)state {
return [NSString stringWithFormat:@"UIButtonBackgroundImageOperation%lu", (unsigned long)state];
}
#endif
@end