| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845 |
- /*
- * 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 "SDInternalMacros.h"
- #import "SDImageFramePool.h"
- #import <KVOController/KVOController.h>
- static const NSUInteger kTestGIFFrameCount = 5; // local TestImage.gif loop count
- // Check whether the coder is called
- @interface SDImageAPNGTestCoder : SDImageAPNGCoder
- @property (nonatomic, class, assign) BOOL isCalled;
- @end
- @implementation SDImageAPNGTestCoder
- static BOOL _isCalled;
- + (BOOL)isCalled {
- return _isCalled;
- }
- + (void)setIsCalled:(BOOL)isCalled {
- _isCalled = isCalled;
- }
- + (instancetype)sharedCoder {
- static SDImageAPNGTestCoder *coder;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- coder = [[SDImageAPNGTestCoder alloc] init];
- });
- return coder;
- }
- - (instancetype)initWithAnimatedImageData:(NSData *)data options:(SDImageCoderOptions *)options {
- SDImageAPNGTestCoder.isCalled = YES;
- return [super initWithAnimatedImageData:data options:options];
- }
- @end
- // Internal header
- @interface SDAnimatedImageView ()
- @property (nonatomic, assign) BOOL isProgressive;
- @property (nonatomic, strong) SDAnimatedImagePlayer *player;
- @end
- @interface SDAnimatedImagePlayer ()
- @property (nonatomic, strong) SDImageFramePool *framePool;
- @end
- @interface SDAnimatedImageTest : SDTestCase
- @end
- @implementation SDAnimatedImageTest
- - (void)test01AnimatedImageInitWithData {
- NSData *invalidData = [@"invalid data" dataUsingEncoding:NSUTF8StringEncoding];
- SDAnimatedImage *image = [[SDAnimatedImage alloc] initWithData:invalidData];
- expect(image).beNil();
-
- NSData *validData = [self testGIFData];
- image = [[SDAnimatedImage alloc] initWithData:validData scale:2];
- expect(image).notTo.beNil(); // image
- expect(image.scale).equal(2); // scale
- expect(image.animatedImageData).equal(validData); // data
- expect(image.animatedImageFormat).equal(SDImageFormatGIF); // format
- expect(image.animatedImageLoopCount).equal(0); // loop count
- expect(image.animatedImageFrameCount).equal(kTestGIFFrameCount); // frame count
- expect([image animatedImageFrameAtIndex:1]).notTo.beNil(); // 1 frame
- }
- - (void)test02AnimatedImageInitWithContentsOfFile {
- SDAnimatedImage *image = [[SDAnimatedImage alloc] initWithContentsOfFile:[self testGIFPath]];
- expect(image).notTo.beNil();
- expect(image.scale).equal(1); // scale
-
- // Test Retina File Path should result @2x scale
- NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
- NSString *testPath = [testBundle pathForResource:@"1@2x" ofType:@"gif"];
- image = [[SDAnimatedImage alloc] initWithContentsOfFile:testPath];
- expect(image).notTo.beNil();
- expect(image.scale).equal(2); // scale
- }
- - (void)test03AnimatedImageInitWithAnimatedCoder {
- NSData *validData = [self testGIFData];
- SDImageGIFCoder *coder = [[SDImageGIFCoder alloc] initWithAnimatedImageData:validData options:nil];
- SDAnimatedImage *image = [[SDAnimatedImage alloc] initWithAnimatedCoder:coder scale:1];
- expect(image).notTo.beNil();
- // enough, other can be test with InitWithData
- }
- - (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
- SDAnimatedImage *image = [SDAnimatedImage imageNamed:@"TestImage.gif" inBundle:bundle];
- #endif
- expect(image).notTo.beNil();
- expect([image.animatedImageData isEqualToData:[self testGIFData]]).beTruthy();
- }
- - (void)test05AnimatedImagePreloadFrames {
- NSData *validData = [self testGIFData];
- SDAnimatedImage *image = [SDAnimatedImage imageWithData:validData];
-
- // Preload all frames
- [image preloadAllFrames];
-
- NSArray *loadedAnimatedImageFrames = [image valueForKey:@"loadedAnimatedImageFrames"]; // Access the internal property, only for test and may be changed in the future
- expect(loadedAnimatedImageFrames.count).equal(kTestGIFFrameCount);
-
- // Test one frame
- UIImage *frame = [image animatedImageFrameAtIndex:0];
- expect(frame).notTo.beNil();
-
- // Unload all frames
- [image unloadAllFrames];
- }
- - (void)test06AnimatedImageViewSetImage {
- SDAnimatedImageView *imageView = [SDAnimatedImageView new];
- UIImage *image = [[UIImage alloc] initWithData:[self testJPEGData]];
- imageView.image = image;
- expect(imageView.image).notTo.beNil();
- expect(imageView.currentFrame).beNil(); // current frame
- }
- - (void)test08AnimatedImageViewSetAnimatedImageGIF {
- SDAnimatedImageView *imageView = [SDAnimatedImageView new];
- SDAnimatedImage *image = [SDAnimatedImage imageWithData:[self testGIFData]];
- imageView.image = image;
- expect(imageView.image).notTo.beNil();
- expect(imageView.player).notTo.beNil();
- }
- - (void)test09AnimatedImageViewSetAnimatedImageAPNG {
- SDAnimatedImageView *imageView = [SDAnimatedImageView new];
- SDAnimatedImage *image = [SDAnimatedImage imageWithData:[self testAPNGPData]];
- imageView.image = image;
- expect(imageView.image).notTo.beNil();
- expect(imageView.player).notTo.beNil();
- }
- - (void)test10AnimatedImageInitWithCoder {
- SDAnimatedImage *image1 = [SDAnimatedImage imageWithContentsOfFile:[self testGIFPath]];
- expect(image1).notTo.beNil();
- 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
- expect(image1.scale).equal(image2.scale);
- expect(image1.size).equal(image2.size);
- expect(image1.animatedImageFormat).equal(image2.animatedImageFormat);
- expect(image1.animatedImageData).equal(image2.animatedImageData);
- expect(image1.animatedImageLoopCount).equal(image2.animatedImageLoopCount);
- expect(image1.animatedImageFrameCount).equal(image2.animatedImageFrameCount);
- }
- - (void)test11AnimatedImageViewIntrinsicContentSize {
- // Test that SDAnimatedImageView.intrinsicContentSize return the correct value of image size
- SDAnimatedImageView *imageView = [SDAnimatedImageView new];
- SDAnimatedImage *image = [SDAnimatedImage imageWithData:[self testAPNGPData]];
- imageView.image = image;
- expect(imageView.intrinsicContentSize).equal(image.size);
- }
- - (void)test12AnimatedImageViewLayerContents {
- // Test that SDAnimatedImageView with built-in UIImage/NSImage will actually setup the layer for display
- SDAnimatedImageView *imageView = [SDAnimatedImageView new];
- UIImage *image = [[UIImage alloc] initWithData:[self testJPEGData]];
- imageView.image = image;
- #if SD_MAC
- expect(imageView.wantsUpdateLayer).beTruthy();
- #else
- expect(imageView.layer).notTo.beNil();
- #endif
- }
- - (void)test13AnimatedImageViewInitWithImage {
- // Test that -[SDAnimatedImageView initWithImage:] this convenience initializer not crash
- SDAnimatedImage *image = [SDAnimatedImage imageWithData:[self testAPNGPData]];
- SDAnimatedImageView *imageView;
- #if SD_UIKIT
- imageView = [[SDAnimatedImageView alloc] initWithImage:image];
- #else
- if (@available(macOS 10.12, *)) {
- imageView = [SDAnimatedImageView imageViewWithImage:image];
- }
- #endif
- expect(imageView.image).equal(image);
- }
- - (void)test14AnimatedImageViewStopPlayingWhenHidden {
- SDAnimatedImageView *imageView = [SDAnimatedImageView new];
- #if SD_UIKIT
- [self.window addSubview:imageView];
- #else
- [self.window.contentView addSubview:imageView];
- #endif
- SDAnimatedImage *image = [SDAnimatedImage imageWithData:[self testGIFData]];
- imageView.image = image;
- #if SD_UIKIT
- [imageView startAnimating];
- #else
- imageView.animates = YES;
- #endif
- SDAnimatedImagePlayer *player = imageView.player;
- expect(player).notTo.beNil();
- expect(player.isPlaying).beTruthy();
- imageView.hidden = YES;
- expect(player.isPlaying).beFalsy();
- }
- - (void)test20AnimatedImageViewRendering {
- XCTestExpectation *expectation = [self expectationWithDescription:@"test SDAnimatedImageView rendering"];
- SDAnimatedImageView *imageView = [[SDAnimatedImageView alloc] init];
- #if SD_UIKIT
- [self.window addSubview:imageView];
- #else
- [self.window.contentView addSubview:imageView];
- #endif
-
- NSMutableDictionary *frames = [NSMutableDictionary dictionaryWithCapacity:kTestGIFFrameCount];
-
- [self.KVOController observe:imageView keyPaths:@[NSStringFromSelector(@selector(currentFrameIndex)), NSStringFromSelector(@selector(currentLoopCount))] options:NSKeyValueObservingOptionNew block:^(id _Nullable observer, id _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
- NSUInteger frameIndex = imageView.currentFrameIndex;
- NSUInteger loopCount = imageView.currentLoopCount;
- [frames setObject:@(YES) forKey:@(frameIndex)];
-
- BOOL framesRendered = NO;
- if (frames.count >= kTestGIFFrameCount) {
- // All frames rendered
- framesRendered = YES;
- }
- BOOL loopFinished = NO;
- if (loopCount >= 1) {
- // One loop finished
- loopFinished = YES;
- }
- if (framesRendered && loopFinished) {
- #if SD_UIKIT
- [imageView stopAnimating];
- #else
- imageView.animates = NO;
- #endif
- [imageView removeFromSuperview];
- [expectation fulfill];
- }
- }];
-
- SDAnimatedImage *image = [SDAnimatedImage imageWithData:[self testGIFData]];
- imageView.image = image;
-
- [self waitForExpectationsWithCommonTimeout];
- }
- - (void)test21AnimatedImageViewSetProgressiveAnimatedImage {
- NSData *gifData = [self testGIFData];
- SDImageGIFCoder *progressiveCoder = [[SDImageGIFCoder alloc] initIncrementalWithOptions:nil];
- // simulate progressive decode, pass partial data
- NSData *partialData = [gifData subdataWithRange:NSMakeRange(0, gifData.length - 1)];
- [progressiveCoder updateIncrementalData:partialData finished:NO];
-
- SDAnimatedImage *partialImage = [[SDAnimatedImage alloc] initWithAnimatedCoder:progressiveCoder scale:1];
- partialImage.sd_isIncremental = YES;
-
- SDAnimatedImageView *imageView = [[SDAnimatedImageView alloc] init];
- imageView.image = partialImage;
-
- BOOL isProgressive = imageView.isProgressive;
- expect(isProgressive).equal(YES);
-
- // pass full data
- [progressiveCoder updateIncrementalData:gifData finished:YES];
-
- SDAnimatedImage *fullImage = [[SDAnimatedImage alloc] initWithAnimatedCoder:progressiveCoder scale:1];
-
- imageView.image = fullImage;
-
- isProgressive = imageView.isProgressive;
- expect(isProgressive).equal(NO);
- }
- - (void)test22AnimatedImageViewCategory {
- XCTestExpectation *expectation = [self expectationWithDescription:@"test SDAnimatedImageView view category"];
- SDAnimatedImageView *imageView = [SDAnimatedImageView new];
- NSURL *testURL = [NSURL URLWithString:kTestGIFURL];
- [imageView sd_setImageWithURL:testURL completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
- expect(error).to.beNil();
- expect(image).notTo.beNil();
- expect([image isKindOfClass:[SDAnimatedImage class]]).beTruthy();
- [expectation fulfill];
- }];
- [self waitForExpectationsWithCommonTimeout];
- }
- - (void)test23AnimatedImageViewCategoryProgressive {
- 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;
- // Progressive image may be nil when download data is not enough
- if (image) {
- expect(image.sd_isIncremental).beTruthy();
- expect([image.class conformsToProtocol:@protocol(SDAnimatedImage)]).beTruthy();
- BOOL isProgressive = imageView.isProgressive;
- expect(isProgressive).equal(YES);
- }
- });
- } completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
- expect(error).to.beNil();
- expect(image).notTo.beNil();
- expect([image isKindOfClass:[SDAnimatedImage class]]).beTruthy();
- expect(cacheType).equal(SDImageCacheTypeNone);
- [expectation fulfill];
- }];
- [self waitForExpectationsWithTimeout:kAsyncTestTimeout * 2 handler:nil];
- }
- - (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(cacheType).equal(SDImageCacheTypeDisk);
- expect([image isKindOfClass:[SDAnimatedImage class]]).beTruthy();
- [expectation fulfill];
- }];
- [self waitForExpectationsWithCommonTimeout];
- }
- - (void)test25AnimatedImageStopAnimatingNormal {
- XCTestExpectation *expectation = [self expectationWithDescription:@"test SDAnimatedImageView stopAnimating normal behavior"];
-
- SDAnimatedImageView *imageView = [SDAnimatedImageView new];
-
- #if SD_UIKIT
- [self.window addSubview:imageView];
- #else
- [self.window.contentView addSubview:imageView];
- #endif
- // This APNG duration is 2s
- SDAnimatedImage *image = [SDAnimatedImage imageWithData:[self testAPNGPData]];
- imageView.image = image;
-
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- // 0.5s is not finished, frame index should not be 0
- expect(imageView.player.framePool.currentFrameCount).beGreaterThan(0);
- expect(imageView.currentFrameIndex).beGreaterThan(0);
- });
-
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- #if SD_UIKIT
- [imageView stopAnimating];
- #else
- imageView.animates = NO;
- #endif
- expect(imageView.player.framePool.currentFrameCount).beGreaterThan(0);
- expect(imageView.currentFrameIndex).beGreaterThan(0);
-
- [imageView removeFromSuperview];
- [expectation fulfill];
- });
-
- [self waitForExpectationsWithCommonTimeout];
- }
- - (void)test26AnimatedImageStopAnimatingClearBuffer {
- XCTestExpectation *expectation = [self expectationWithDescription:@"test SDAnimatedImageView stopAnimating clear buffer when stopped"];
-
- SDAnimatedImageView *imageView = [SDAnimatedImageView new];
- imageView.clearBufferWhenStopped = YES;
- imageView.resetFrameIndexWhenStopped = YES;
-
- #if SD_UIKIT
- [self.window addSubview:imageView];
- #else
- [self.window.contentView addSubview:imageView];
- #endif
- // This APNG duration is 2s
- SDAnimatedImage *image = [SDAnimatedImage imageWithData:[self testAPNGPData]];
- imageView.image = image;
-
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- // 0.5s is not finished, frame index should not be 0
- expect(imageView.player.framePool.currentFrameCount).beGreaterThan(0);
- expect(imageView.currentFrameIndex).beGreaterThan(0);
- });
-
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- #if SD_UIKIT
- [imageView stopAnimating];
- #else
- imageView.animates = NO;
- #endif
- expect(imageView.player.framePool.currentFrameCount).equal(0);
-
- [imageView removeFromSuperview];
- [expectation fulfill];
- });
-
- [self waitForExpectationsWithCommonTimeout];
- }
- - (void)test27AnimatedImageProgressiveAnimation {
- XCTestExpectation *expectation = [self expectationWithDescription:@"test SDAnimatedImageView progressive animation rendering"];
-
- // Simulate progressive download
- NSData *fullData = [self testAPNGPData];
- NSUInteger length = fullData.length;
-
- SDAnimatedImageView *imageView = [SDAnimatedImageView new];
- #if SD_UIKIT
- [self.window addSubview:imageView];
- #else
- [self.window.contentView addSubview:imageView];
- #endif
-
- __block NSUInteger previousFrameIndex = 0;
- @weakify(imageView);
- // Observe to check rendering behavior using frame index
- [self.KVOController observe:imageView keyPath:NSStringFromSelector(@selector(currentFrameIndex)) options:NSKeyValueObservingOptionNew block:^(id _Nullable observer, id _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
- @strongify(imageView);
- NSUInteger currentFrameIndex = [change[NSKeyValueChangeNewKey] unsignedIntegerValue];
- printf("Animation Frame Index: %lu\n", (unsigned long)currentFrameIndex);
-
- // The last time should not be progressive
- if (currentFrameIndex == 0 && !imageView.isProgressive) {
- [self.KVOController unobserve:imageView];
- [expectation fulfill];
- } else {
- // Each progressive rendering should render new frame index, no backward and should stop at last frame index
- expect(currentFrameIndex - previousFrameIndex).beGreaterThanOrEqualTo(0);
- previousFrameIndex = currentFrameIndex;
- }
- }];
-
- SDImageAPNGCoder *coder = [[SDImageAPNGCoder alloc] initIncrementalWithOptions:nil];
- // Setup Data
- NSData *setupData = [fullData subdataWithRange:NSMakeRange(0, length / 3.0)];
- [coder updateIncrementalData:setupData finished:NO];
- imageView.shouldIncrementalLoad = YES;
- __block SDAnimatedImage *progressiveImage = [[SDAnimatedImage alloc] initWithAnimatedCoder:coder scale:1];
- progressiveImage.sd_isIncremental = YES;
- imageView.image = progressiveImage;
- expect(imageView.isProgressive).beTruthy();
-
- __block NSUInteger partialFrameCount;
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- // Partial Data
- NSData *partialData = [fullData subdataWithRange:NSMakeRange(0, length * 2.0 / 3.0)];
- [coder updateIncrementalData:partialData finished:NO];
- partialFrameCount = [coder animatedImageFrameCount];
- expect(partialFrameCount).beGreaterThan(1);
- progressiveImage = [[SDAnimatedImage alloc] initWithAnimatedCoder:coder scale:1];
- progressiveImage.sd_isIncremental = YES;
- imageView.image = progressiveImage;
- expect(imageView.isProgressive).beTruthy();
- });
-
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- // Full Data
- [coder updateIncrementalData:fullData finished:YES];
- progressiveImage = [[SDAnimatedImage alloc] initWithAnimatedCoder:coder scale:1];
- progressiveImage.sd_isIncremental = NO;
- imageView.image = progressiveImage;
- NSUInteger fullFrameCount = [coder animatedImageFrameCount];
- expect(fullFrameCount).beGreaterThan(partialFrameCount);
- expect(imageView.isProgressive).beFalsy();
- });
-
- [self waitForExpectationsWithCommonTimeout];
- }
- - (void)test28AnimatedImageAutoPlayAnimatedImage {
- XCTestExpectation *expectation = [self expectationWithDescription:@"test SDAnimatedImageView AutoPlayAnimatedImage behavior"];
-
- SDAnimatedImageView *imageView = [SDAnimatedImageView new];
- imageView.autoPlayAnimatedImage = NO;
-
- #if SD_UIKIT
- [self.window addSubview:imageView];
- #else
- [self.window.contentView addSubview:imageView];
- #endif
- // This APNG duration is 2s
- SDAnimatedImage *image = [SDAnimatedImage imageWithData:[self testAPNGPData]];
- imageView.image = image;
- #if SD_UIKIT
- expect(imageView.animating).equal(NO);
- #else
- expect(imageView.animates).equal(NO);
- #endif
-
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- #if SD_UIKIT
- expect(imageView.animating).equal(NO);
- #else
- expect(imageView.animates).equal(NO);
- #endif
-
- #if SD_UIKIT
- [imageView startAnimating];
- #else
- imageView.animates = YES;
- #endif
- });
-
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- #if SD_UIKIT
- expect(imageView.animating).equal(YES);
- #else
- expect(imageView.animates).equal(YES);
- #endif
-
- #if SD_UIKIT
- [imageView stopAnimating];
- #else
- imageView.animates = NO;
- #endif
-
- [imageView removeFromSuperview];
- [expectation fulfill];
- });
-
- [self waitForExpectationsWithCommonTimeout];
- }
- - (void)test29AnimatedImageSeekFrame {
- XCTestExpectation *expectation = [self expectationWithDescription:@"test SDAnimatedImageView stopAnimating normal behavior"];
-
- SDAnimatedImageView *imageView = [SDAnimatedImageView new];
-
- #if SD_UIKIT
- [self.window addSubview:imageView];
- #else
- [self.window.contentView addSubview:imageView];
- #endif
- // seeking through local image should return non-null images
- SDAnimatedImage *image = [SDAnimatedImage imageWithData:[self testAPNGPData]];
- imageView.autoPlayAnimatedImage = NO;
- imageView.image = image;
-
- __weak SDAnimatedImagePlayer *player = imageView.player;
- __block NSUInteger i = 0;
- [player setAnimationFrameHandler:^(NSUInteger index, UIImage * _Nonnull frame) {
- expect(index).equal(i);
- expect(frame).notTo.beNil();
- i++;
- if (i < player.totalFrameCount) {
- [player seekToFrameAtIndex:i loopCount:0];
- } else {
- [expectation fulfill];
- }
- }];
- [player seekToFrameAtIndex:i loopCount:0];
-
- [self waitForExpectationsWithCommonTimeout];
- }
- - (void)test30AnimatedImageCoderPriority {
- [SDImageCodersManager.sharedManager addCoder:SDImageAPNGTestCoder.sharedCoder];
- [SDAnimatedImage imageWithData:[self testAPNGPData]];
- expect(SDImageAPNGTestCoder.isCalled).equal(YES);
- }
- #if SD_UIKIT
- - (void)test31AnimatedImageViewSetAnimationImages {
- SDAnimatedImageView *imageView = [SDAnimatedImageView new];
- UIImage *image = [[UIImage alloc] initWithData:[self testJPEGData]];
- imageView.animationImages = @[image];
- expect(imageView.animationImages).notTo.beNil();
- }
- - (void)test32AnimatedImageViewNotStopPlayingAnimationImagesWhenHidden {
- SDAnimatedImageView *imageView = [SDAnimatedImageView new];
- [self.window addSubview:imageView];
- UIImage *image = [[UIImage alloc] initWithData:[self testJPEGData]];
- imageView.animationImages = @[image];
- [imageView startAnimating];
- expect(imageView.animating).beTruthy();
- imageView.hidden = YES;
- expect(imageView.animating).beTruthy();
- }
- #endif
- - (void)test33AnimatedImagePlaybackModeReverse {
- XCTestExpectation *expectation = [self expectationWithDescription:@"test SDAnimatedImageView playback reverse mode"];
-
- SDAnimatedImageView *imageView = [SDAnimatedImageView new];
-
- #if SD_UIKIT
- [self.window addSubview:imageView];
- #else
- [self.window.contentView addSubview:imageView];
- #endif
-
- SDAnimatedImage *image = [SDAnimatedImage imageWithData:[self testAPNGPData]];
- imageView.autoPlayAnimatedImage = NO;
- imageView.image = image;
-
- __weak SDAnimatedImagePlayer *player = imageView.player;
- player.playbackMode = SDAnimatedImagePlaybackModeReverse;
- __block NSInteger i = player.totalFrameCount - 1;
- __weak typeof(imageView) wimageView = imageView;
- [player setAnimationFrameHandler:^(NSUInteger index, UIImage * _Nonnull frame) {
- expect(index).equal(i);
- expect(frame).notTo.beNil();
- if (index == 0) {
- [expectation fulfill];
- // Stop Animation to avoid extra callback
- [wimageView.player stopPlaying];
- [wimageView removeFromSuperview];
- return;
- }
- i--;
- }];
-
- [player startPlaying];
-
- [self waitForExpectationsWithTimeout:15 handler:nil];
- }
- - (void)test34AnimatedImagePlaybackModeBounce {
- XCTestExpectation *expectation = [self expectationWithDescription:@"test SDAnimatedImageView playback bounce mode"];
-
- SDAnimatedImageView *imageView = [SDAnimatedImageView new];
-
- #if SD_UIKIT
- [self.window addSubview:imageView];
- #else
- [self.window.contentView addSubview:imageView];
- #endif
-
- SDAnimatedImage *image = [SDAnimatedImage imageWithData:[self testAPNGPData]];
- imageView.autoPlayAnimatedImage = NO;
- imageView.image = image;
-
- __weak SDAnimatedImagePlayer *player = imageView.player;
- player.playbackMode = SDAnimatedImagePlaybackModeBounce;
- __block NSInteger i = 0;
- __block BOOL flag = NO;
- __block NSUInteger cnt = 0;
- __weak typeof(imageView) wimageView = imageView;
- [player setAnimationFrameHandler:^(NSUInteger index, UIImage * _Nonnull frame) {
- expect(index).equal(i);
- expect(frame).notTo.beNil();
-
- if (index >= player.totalFrameCount - 1) {
- cnt++;
- flag = YES;
- } else if (cnt != 0 && index == 0) {
- cnt++;
- flag = NO;
- }
-
- if (!flag) {
- i++;
- } else {
- i--;
- }
- if (cnt >= 2) {
- [expectation fulfill];
- // Stop Animation to avoid extra callback
- [wimageView.player stopPlaying];
- [wimageView removeFromSuperview];
- }
- }];
-
- [player startPlaying];
-
- [self waitForExpectationsWithTimeout:15 handler:nil];
- }
- - (void)test35AnimatedImagePlaybackModeReversedBounce {
- XCTestExpectation *expectation = [self expectationWithDescription:@"test SDAnimatedImageView playback reverse bounce mode"];
-
- SDAnimatedImageView *imageView = [SDAnimatedImageView new];
-
- #if SD_UIKIT
- [self.window addSubview:imageView];
- #else
- [self.window.contentView addSubview:imageView];
- #endif
-
- SDAnimatedImage *image = [SDAnimatedImage imageWithData:[self testAPNGPData]];
- imageView.autoPlayAnimatedImage = NO;
- imageView.image = image;
-
- __weak SDAnimatedImagePlayer *player = imageView.player;
- player.playbackMode = SDAnimatedImagePlaybackModeReversedBounce;
- __block NSInteger i = player.totalFrameCount - 1;
- __block BOOL flag = false;
- __block NSUInteger cnt = 0;
- __weak typeof(imageView) wimageView = imageView;
- [player setAnimationFrameHandler:^(NSUInteger index, UIImage * _Nonnull frame) {
- expect(index).equal(i);
- expect(frame).notTo.beNil();
-
- if (cnt != 0 && index >= player.totalFrameCount - 1) {
- cnt++;
- flag = false;
- } else if (index == 0) {
- cnt++;
- flag = YES;
- }
-
- if (flag) {
- i++;
- } else {
- i--;
- }
- if (cnt >= 2) {
- [expectation fulfill];
- // Stop Animation to avoid extra callback
- [wimageView.player stopPlaying];
- [wimageView removeFromSuperview];
- }
- }];
- [player startPlaying];
-
- [self waitForExpectationsWithTimeout:15 handler:nil];
- }
- #if !SD_TV
- - (void)test36AnimatedImageMemoryCost {
- if (@available(iOS 14, tvOS 14, macOS 11, watchOS 7, *)) {
- [[SDImageCodersManager sharedManager] addCoder:[SDImageAWebPCoder sharedCoder]];
- UIImage *image = [UIImage sd_imageWithData:[NSData dataWithContentsOfFile:[self testMemotyCostImagePath]]];
- NSUInteger cost = [image sd_memoryCost];
- #if SD_UIKIT
- expect(image.images.count).equal(5333);
- #endif
- expect(image.sd_imageFrameCount).equal(16);
- expect(image.scale).equal(1);
- #if SD_MAC
- /// Frame count is 1 in mac.
- expect(cost).equal(image.size.width * image.size.height * 4);
- #else
- expect(cost).equal(16 * image.size.width * image.size.height * 4);
- #endif
- [[SDImageCodersManager sharedManager] removeCoder:[SDImageAWebPCoder sharedCoder]];
- }
- }
- #endif
- - (void)test37AnimatedImageWithStaticDataBehavior {
- UIImage *image = [[SDAnimatedImage alloc] initWithData:[self testJPEGData]];
- // UIImage+Metadata.h
- expect(image).notTo.beNil();
- expect(image.sd_isAnimated).beFalsy();
- expect(image.sd_imageFormat).equal(SDImageFormatJPEG);
- expect(image.sd_imageFrameCount).equal(1);
- expect(image.sd_imageLoopCount).equal(0);
- // SDImageCoderHelper.h
- UIImage *decodedImage = [SDImageCoderHelper decodedImageWithImage:image policy:SDImageForceDecodePolicyAutomatic];
- expect(decodedImage).notTo.equal(image);
- // SDWebImageDefine.h
- UIImage *scaledImage = SDScaledImageForScaleFactor(2.0, image);
- expect(scaledImage).notTo.equal(image);
- }
- #pragma mark - Helper
- - (NSString *)testGIFPath {
- NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
- NSString *testPath = [testBundle pathForResource:@"TestImage" ofType:@"gif"];
- return testPath;
- }
- - (NSData *)testGIFData {
- NSData *testData = [NSData dataWithContentsOfFile:[self testGIFPath]];
- return testData;
- }
- - (NSString *)testAPNGPPath {
- NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
- NSString *testPath = [testBundle pathForResource:@"TestImageAnimated" ofType:@"apng"];
- return testPath;
- }
- - (NSString *)testMemotyCostImagePath {
- NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
- NSString *testPath = [testBundle pathForResource:@"TestAnimatedImageMemory" ofType:@"webp"];
- return testPath;
- }
- - (NSData *)testAPNGPData {
- return [NSData dataWithContentsOfFile:[self testAPNGPPath]];
- }
- - (NSString *)testJPEGPath {
- NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
- NSString *testPath = [testBundle pathForResource:@"TestImage" ofType:@"jpg"];
- return testPath;
- }
- - (NSData *)testJPEGData {
- NSData *testData = [NSData dataWithContentsOfFile:[self testJPEGPath]];
- return testData;
- }
- @end
|