mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-18 19:40:19 +00:00
These have been superseded by -[ASDisplayNode initWithViewBlock:] and -[ASDisplayNode initWithLayerBlock:], respectively -- the new API allows for custom initialisers, but does not support asynchronous display. The old initialisers are still available in ASDisplayNodeInternal.h, for internal subclasses and daring adventurers.
616 lines
22 KiB
Objective-C
616 lines
22 KiB
Objective-C
/* Copyright (c) 2014-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
|
|
#import <objc/runtime.h>
|
|
|
|
#import <QuartzCore/QuartzCore.h>
|
|
#import <UIKit/UIKit.h>
|
|
|
|
#import <XCTest/XCTest.h>
|
|
|
|
#import "_ASDisplayLayer.h"
|
|
#import "_ASAsyncTransactionContainer.h"
|
|
#import "ASDisplayNode.h"
|
|
#import "ASDisplayNodeTestsHelper.h"
|
|
|
|
static UIImage *bogusImage() {
|
|
static UIImage *bogusImage = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
UIGraphicsBeginImageContext(CGSizeMake(10, 10));
|
|
|
|
bogusImage = [UIGraphicsGetImageFromCurrentImageContext() retain];
|
|
|
|
UIGraphicsEndImageContext();
|
|
|
|
});
|
|
|
|
return bogusImage;
|
|
}
|
|
|
|
@interface _ASDisplayLayerTestContainerLayer : CALayer
|
|
@property (nonatomic, assign, readonly) NSUInteger didCompleteTransactionCount;
|
|
@end
|
|
|
|
@implementation _ASDisplayLayerTestContainerLayer
|
|
|
|
- (void)asyncdisplaykit_asyncTransactionContainerDidCompleteTransaction:(_ASAsyncTransaction *)transaction
|
|
{
|
|
_didCompleteTransactionCount++;
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@interface ASDisplayNode (HackForTests)
|
|
- (id)initWithViewClass:(Class)viewClass;
|
|
- (id)initWithLayerClass:(Class)layerClass;
|
|
@end
|
|
|
|
|
|
@interface _ASDisplayLayerTestLayer : _ASDisplayLayer
|
|
{
|
|
BOOL _isInCancelAsyncDisplay;
|
|
BOOL _isInDisplay;
|
|
}
|
|
@property (nonatomic, assign, readonly) NSUInteger displayCount;
|
|
@property (nonatomic, assign, readonly) NSUInteger drawInContextCount;
|
|
@property (nonatomic, assign, readonly) NSUInteger setContentsAsyncCount;
|
|
@property (nonatomic, assign, readonly) NSUInteger setContentsSyncCount;
|
|
@property (nonatomic, copy, readonly) NSString *setContentsCounts;
|
|
- (BOOL)checkSetContentsCountsWithSyncCount:(NSUInteger)syncCount asyncCount:(NSUInteger)asyncCount;
|
|
@end
|
|
|
|
@implementation _ASDisplayLayerTestLayer
|
|
|
|
- (NSString *)setContentsCounts
|
|
{
|
|
return [NSString stringWithFormat:@"syncCount:%tu, asyncCount:%tu", _setContentsSyncCount, _setContentsAsyncCount];
|
|
}
|
|
|
|
- (BOOL)checkSetContentsCountsWithSyncCount:(NSUInteger)syncCount asyncCount:(NSUInteger)asyncCount
|
|
{
|
|
return ((syncCount == _setContentsSyncCount) &&
|
|
(asyncCount == _setContentsAsyncCount));
|
|
}
|
|
|
|
- (void)setContents:(id)contents
|
|
{
|
|
[super setContents:contents];
|
|
|
|
if (self.displaysAsynchronously) {
|
|
if (_isInDisplay) {
|
|
[[NSException exceptionWithName:NSInvalidArgumentException
|
|
reason:@"There is no placeholder logic in _ASDisplayLayer, unknown caller for setContents:"
|
|
userInfo:nil] raise];
|
|
} else if (!_isInCancelAsyncDisplay) {
|
|
_setContentsAsyncCount++;
|
|
}
|
|
} else {
|
|
_setContentsSyncCount++;
|
|
}
|
|
}
|
|
|
|
- (void)display
|
|
{
|
|
_isInDisplay = YES;
|
|
[super display];
|
|
_isInDisplay = NO;
|
|
_displayCount++;
|
|
}
|
|
|
|
- (void)cancelAsyncDisplay
|
|
{
|
|
_isInCancelAsyncDisplay = YES;
|
|
[super cancelAsyncDisplay];
|
|
_isInCancelAsyncDisplay = NO;
|
|
}
|
|
|
|
// This should never get called. This just records if it is.
|
|
- (void)drawInContext:(CGContextRef)context
|
|
{
|
|
[super drawInContext:context];
|
|
_drawInContextCount++;
|
|
}
|
|
|
|
@end
|
|
|
|
typedef NS_ENUM(NSUInteger, _ASDisplayLayerTestDelegateMode)
|
|
{
|
|
_ASDisplayLayerTestDelegateModeNone = 0,
|
|
_ASDisplayLayerTestDelegateModeDrawParameters = 1 << 0,
|
|
_ASDisplayLayerTestDelegateModeWillDisplay = 1 << 1,
|
|
_ASDisplayLayerTestDelegateModeDidDisplay = 1 << 2,
|
|
};
|
|
|
|
typedef NS_ENUM(NSUInteger, _ASDisplayLayerTestDelegateClassModes) {
|
|
_ASDisplayLayerTestDelegateClassModeNone = 0,
|
|
_ASDisplayLayerTestDelegateClassModeDisplay = 1 << 0,
|
|
_ASDisplayLayerTestDelegateClassModeDrawInContext = 1 << 1,
|
|
};
|
|
|
|
@interface _ASDisplayLayerTestDelegate : ASDisplayNode <_ASDisplayLayerDelegate>
|
|
|
|
@property (nonatomic, assign) NSUInteger didDisplayCount;
|
|
@property (nonatomic, assign) NSUInteger drawParametersCount;
|
|
@property (nonatomic, assign) NSUInteger willDisplayCount;
|
|
|
|
// for _ASDisplayLayerTestDelegateModeClassDisplay
|
|
@property (nonatomic, assign) NSUInteger displayCount;
|
|
@property (nonatomic, copy) UIImage *(^displayLayerBlock)();
|
|
|
|
// for _ASDisplayLayerTestDelegateModeClassDrawInContext
|
|
@property (nonatomic, assign) NSUInteger drawRectCount;
|
|
|
|
@end
|
|
|
|
@implementation _ASDisplayLayerTestDelegate {
|
|
_ASDisplayLayerTestDelegateMode _modes;
|
|
}
|
|
|
|
static _ASDisplayLayerTestDelegateClassModes _class_modes;
|
|
|
|
+ (void)setClassModes:(_ASDisplayLayerTestDelegateClassModes)classModes
|
|
{
|
|
_class_modes = classModes;
|
|
}
|
|
|
|
- (id)initWithModes:(_ASDisplayLayerTestDelegateMode)modes
|
|
{
|
|
_modes = modes;
|
|
|
|
if (!(self = [super initWithLayerClass:[_ASDisplayLayerTestLayer class]]))
|
|
return nil;
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)didDisplayAsyncLayer:(_ASDisplayLayer *)layer
|
|
{
|
|
_didDisplayCount++;
|
|
}
|
|
|
|
- (NSObject *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer
|
|
{
|
|
_drawParametersCount++;
|
|
return self;
|
|
}
|
|
|
|
- (void)willDisplayAsyncLayer:(_ASDisplayLayer *)layer
|
|
{
|
|
_willDisplayCount++;
|
|
}
|
|
|
|
- (BOOL)respondsToSelector:(SEL)selector
|
|
{
|
|
if (sel_isEqual(selector, @selector(didDisplayAsyncLayer:))) {
|
|
return (_modes & _ASDisplayLayerTestDelegateModeDidDisplay);
|
|
} else if (sel_isEqual(selector, @selector(drawParametersForAsyncLayer:))) {
|
|
return (_modes & _ASDisplayLayerTestDelegateModeDrawParameters);
|
|
} else if (sel_isEqual(selector, @selector(willDisplayAsyncLayer:))) {
|
|
return (_modes & _ASDisplayLayerTestDelegateModeWillDisplay);
|
|
} else {
|
|
return [super respondsToSelector:selector];
|
|
}
|
|
}
|
|
|
|
+ (BOOL)respondsToSelector:(SEL)selector
|
|
{
|
|
if (sel_isEqual(selector, @selector(displayWithParameters:isCancelled:))) {
|
|
return _class_modes & _ASDisplayLayerTestDelegateClassModeDisplay;
|
|
} else if (sel_isEqual(selector, @selector(drawRect:withParameters:isCancelled:isRasterizing:))) {
|
|
return _class_modes & _ASDisplayLayerTestDelegateClassModeDrawInContext;
|
|
} else {
|
|
return [super respondsToSelector:selector];
|
|
}
|
|
}
|
|
|
|
// DANGER: Don't use the delegate as the parameters in real code; this is not thread-safe and just for accounting in unit tests!
|
|
+ (UIImage *)displayWithParameters:(_ASDisplayLayerTestDelegate *)delegate isCancelled:(asdisplaynode_iscancelled_block_t)sentinelBlock
|
|
{
|
|
UIImage *contents = bogusImage();
|
|
if (delegate->_displayLayerBlock != NULL) {
|
|
contents = delegate->_displayLayerBlock();
|
|
}
|
|
delegate->_displayCount++;
|
|
return contents;
|
|
}
|
|
|
|
// DANGER: Don't use the delegate as the parameters in real code; this is not thread-safe and just for accounting in unit tests!
|
|
+ (void)drawRect:(CGRect)bounds withParameters:(_ASDisplayLayerTestDelegate *)delegate isCancelled:(asdisplaynode_iscancelled_block_t)sentinelBlock isRasterizing:(BOOL)isRasterizing
|
|
{
|
|
delegate->_drawRectCount++;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[_displayLayerBlock release];
|
|
[super dealloc];
|
|
}
|
|
|
|
@end
|
|
|
|
@interface _ASDisplayLayerTests : XCTestCase
|
|
@end
|
|
|
|
@implementation _ASDisplayLayerTests
|
|
|
|
- (void)setUp {
|
|
[super setUp];
|
|
// Force bogusImage() to create+cache its image. This impacts any time-sensitive tests which call the method from
|
|
// within the timed portion of the test. It seems that, in rare cases, this image creation can take a bit too long,
|
|
// causing a test failure.
|
|
bogusImage();
|
|
}
|
|
|
|
// since we're not running in an application, we need to force this display on layer the hierarchy
|
|
- (void)displayLayerRecursively:(CALayer *)layer
|
|
{
|
|
if (layer.needsDisplay) {
|
|
[layer displayIfNeeded];
|
|
}
|
|
for (CALayer *sublayer in layer.sublayers) {
|
|
[self displayLayerRecursively:sublayer];
|
|
}
|
|
}
|
|
|
|
- (void)waitForDisplayQueue
|
|
{
|
|
// make sure we don't lock up the tests indefinitely; fail after 1 sec by using an async barrier
|
|
__block BOOL didHitBarrier = NO;
|
|
dispatch_barrier_async([_ASDisplayLayer displayQueue], ^{
|
|
didHitBarrier = YES;
|
|
});
|
|
XCTAssertTrue(ASDisplayNodeRunRunLoopUntilBlockIsTrue(^BOOL{ return didHitBarrier; }));
|
|
}
|
|
|
|
- (void)waitForLayer:(_ASDisplayLayerTestLayer *)layer asyncDisplayCount:(NSUInteger)count
|
|
{
|
|
// make sure we don't lock up the tests indefinitely; fail after 1 sec of waiting for the setContents async count to increment
|
|
// NOTE: the layer sets its contents async back on the main queue, so we need to wait for main
|
|
XCTAssertTrue(ASDisplayNodeRunRunLoopUntilBlockIsTrue(^BOOL{
|
|
return (layer.setContentsAsyncCount == count);
|
|
}));
|
|
}
|
|
|
|
- (void)waitForAsyncDelegate:(_ASDisplayLayerTestDelegate *)asyncDelegate
|
|
{
|
|
XCTAssertTrue(ASDisplayNodeRunRunLoopUntilBlockIsTrue(^BOOL{
|
|
return (asyncDelegate.didDisplayCount == 1);
|
|
}));
|
|
}
|
|
|
|
- (void)checkDelegateDisplay:(BOOL)displaysAsynchronously
|
|
{
|
|
[_ASDisplayLayerTestDelegate setClassModes:_ASDisplayLayerTestDelegateClassModeDisplay];
|
|
_ASDisplayLayerTestDelegate *asyncDelegate = [[_ASDisplayLayerTestDelegate alloc] initWithModes:_ASDisplayLayerTestDelegateModeDidDisplay | _ASDisplayLayerTestDelegateModeDrawParameters];
|
|
|
|
_ASDisplayLayerTestLayer *layer = (_ASDisplayLayerTestLayer *)asyncDelegate.layer;
|
|
layer.displaysAsynchronously = displaysAsynchronously;
|
|
|
|
if (displaysAsynchronously) {
|
|
dispatch_suspend([_ASDisplayLayer displayQueue]);
|
|
}
|
|
layer.frame = CGRectMake(0.0, 0.0, 100.0, 100.0);
|
|
[layer setNeedsDisplay];
|
|
[layer displayIfNeeded];
|
|
|
|
if (displaysAsynchronously) {
|
|
XCTAssertTrue([layer checkSetContentsCountsWithSyncCount:0 asyncCount:0], @"%@", layer.setContentsCounts);
|
|
XCTAssertEqual(layer.displayCount, 1u);
|
|
XCTAssertEqual(layer.drawInContextCount, 0u);
|
|
dispatch_resume([_ASDisplayLayer displayQueue]);
|
|
[self waitForDisplayQueue];
|
|
[self waitForAsyncDelegate:asyncDelegate];
|
|
XCTAssertTrue([layer checkSetContentsCountsWithSyncCount:0 asyncCount:1], @"%@", layer.setContentsCounts);
|
|
} else {
|
|
XCTAssertTrue([layer checkSetContentsCountsWithSyncCount:1 asyncCount:0], @"%@", layer.setContentsCounts);
|
|
}
|
|
|
|
XCTAssertFalse(layer.needsDisplay);
|
|
XCTAssertEqual(layer.displayCount, 1u);
|
|
XCTAssertEqual(layer.drawInContextCount, 0u);
|
|
XCTAssertEqual(asyncDelegate.didDisplayCount, 1u);
|
|
XCTAssertEqual(asyncDelegate.displayCount, 1u);
|
|
|
|
[asyncDelegate release];
|
|
}
|
|
|
|
- (void)testDelegateDisplaySync
|
|
{
|
|
[self checkDelegateDisplay:NO];
|
|
}
|
|
|
|
- (void)testDelegateDisplayAsync
|
|
{
|
|
[self checkDelegateDisplay:YES];
|
|
}
|
|
|
|
- (void)checkDelegateDrawInContext:(BOOL)displaysAsynchronously
|
|
{
|
|
[_ASDisplayLayerTestDelegate setClassModes:_ASDisplayLayerTestDelegateClassModeDrawInContext];
|
|
_ASDisplayLayerTestDelegate *asyncDelegate = [[_ASDisplayLayerTestDelegate alloc] initWithModes:_ASDisplayLayerTestDelegateModeDidDisplay | _ASDisplayLayerTestDelegateModeDrawParameters];
|
|
|
|
_ASDisplayLayerTestLayer *layer = (_ASDisplayLayerTestLayer *)asyncDelegate.layer;
|
|
layer.displaysAsynchronously = displaysAsynchronously;
|
|
|
|
if (displaysAsynchronously) {
|
|
dispatch_suspend([_ASDisplayLayer displayQueue]);
|
|
}
|
|
layer.frame = CGRectMake(0.0, 0.0, 100.0, 100.0);
|
|
[layer setNeedsDisplay];
|
|
[layer displayIfNeeded];
|
|
|
|
if (displaysAsynchronously) {
|
|
XCTAssertTrue([layer checkSetContentsCountsWithSyncCount:0 asyncCount:0], @"%@", layer.setContentsCounts);
|
|
XCTAssertEqual(layer.displayCount, 1u);
|
|
XCTAssertEqual(layer.drawInContextCount, 0u);
|
|
XCTAssertEqual(asyncDelegate.drawRectCount, 0u);
|
|
dispatch_resume([_ASDisplayLayer displayQueue]);
|
|
[self waitForLayer:layer asyncDisplayCount:1];
|
|
[self waitForAsyncDelegate:asyncDelegate];
|
|
XCTAssertTrue([layer checkSetContentsCountsWithSyncCount:0 asyncCount:1], @"%@", layer.setContentsCounts);
|
|
} else {
|
|
XCTAssertTrue([layer checkSetContentsCountsWithSyncCount:1 asyncCount:0], @"%@", layer.setContentsCounts);
|
|
}
|
|
|
|
XCTAssertFalse(layer.needsDisplay);
|
|
XCTAssertEqual(layer.displayCount, 1u);
|
|
XCTAssertEqual(layer.drawInContextCount, 0u);
|
|
XCTAssertEqual(asyncDelegate.didDisplayCount, 1u);
|
|
XCTAssertEqual(asyncDelegate.displayCount, 0u);
|
|
XCTAssertEqual(asyncDelegate.drawParametersCount, 1u);
|
|
XCTAssertEqual(asyncDelegate.drawRectCount, 1u);
|
|
|
|
[asyncDelegate release];
|
|
}
|
|
|
|
- (void)testDelegateDrawInContextSync
|
|
{
|
|
[self checkDelegateDrawInContext:NO];
|
|
}
|
|
|
|
- (void)testDelegateDrawInContextAsync
|
|
{
|
|
[self checkDelegateDrawInContext:YES];
|
|
}
|
|
|
|
- (void)checkDelegateDisplayAndDrawInContext:(BOOL)displaysAsynchronously
|
|
{
|
|
[_ASDisplayLayerTestDelegate setClassModes:_ASDisplayLayerTestDelegateClassModeDisplay | _ASDisplayLayerTestDelegateClassModeDrawInContext];
|
|
_ASDisplayLayerTestDelegate *asyncDelegate = [[_ASDisplayLayerTestDelegate alloc] initWithModes:_ASDisplayLayerTestDelegateModeDidDisplay | _ASDisplayLayerTestDelegateModeDrawParameters];
|
|
|
|
_ASDisplayLayerTestLayer *layer = (_ASDisplayLayerTestLayer *)asyncDelegate.layer;
|
|
layer.displaysAsynchronously = displaysAsynchronously;
|
|
|
|
if (displaysAsynchronously) {
|
|
dispatch_suspend([_ASDisplayLayer displayQueue]);
|
|
}
|
|
layer.frame = CGRectMake(0.0, 0.0, 100.0, 100.0);
|
|
[layer setNeedsDisplay];
|
|
[layer displayIfNeeded];
|
|
|
|
if (displaysAsynchronously) {
|
|
XCTAssertTrue([layer checkSetContentsCountsWithSyncCount:0 asyncCount:0], @"%@", layer.setContentsCounts);
|
|
XCTAssertEqual(layer.displayCount, 1u);
|
|
XCTAssertEqual(asyncDelegate.drawParametersCount, 1u);
|
|
XCTAssertEqual(asyncDelegate.drawRectCount, 0u);
|
|
dispatch_resume([_ASDisplayLayer displayQueue]);
|
|
[self waitForDisplayQueue];
|
|
[self waitForAsyncDelegate:asyncDelegate];
|
|
XCTAssertTrue([layer checkSetContentsCountsWithSyncCount:0 asyncCount:1], @"%@", layer.setContentsCounts);
|
|
} else {
|
|
XCTAssertTrue([layer checkSetContentsCountsWithSyncCount:1 asyncCount:0], @"%@", layer.setContentsCounts);
|
|
}
|
|
|
|
XCTAssertFalse(layer.needsDisplay);
|
|
XCTAssertEqual(layer.displayCount, 1u);
|
|
XCTAssertEqual(layer.drawInContextCount, 0u);
|
|
XCTAssertEqual(asyncDelegate.didDisplayCount, 1u);
|
|
XCTAssertEqual(asyncDelegate.displayCount, 1u);
|
|
XCTAssertEqual(asyncDelegate.drawParametersCount, 1u);
|
|
XCTAssertEqual(asyncDelegate.drawRectCount, 0u);
|
|
|
|
[asyncDelegate release];
|
|
}
|
|
|
|
- (void)testDelegateDisplayAndDrawInContextSync
|
|
{
|
|
[self checkDelegateDisplayAndDrawInContext:NO];
|
|
}
|
|
|
|
- (void)testDelegateDisplayAndDrawInContextAsync
|
|
{
|
|
[self checkDelegateDisplayAndDrawInContext:YES];
|
|
}
|
|
|
|
- (void)testCancelAsyncDisplay
|
|
{
|
|
[_ASDisplayLayerTestDelegate setClassModes:_ASDisplayLayerTestDelegateClassModeDisplay];
|
|
_ASDisplayLayerTestDelegate *asyncDelegate = [[_ASDisplayLayerTestDelegate alloc] initWithModes:_ASDisplayLayerTestDelegateModeDidDisplay];
|
|
_ASDisplayLayerTestLayer *layer = (_ASDisplayLayerTestLayer *)asyncDelegate.layer;
|
|
|
|
dispatch_suspend([_ASDisplayLayer displayQueue]);
|
|
layer.frame = CGRectMake(0.0, 0.0, 100.0, 100.0);
|
|
[layer setNeedsDisplay];
|
|
XCTAssertTrue(layer.needsDisplay);
|
|
[layer displayIfNeeded];
|
|
|
|
XCTAssertTrue([layer checkSetContentsCountsWithSyncCount:0 asyncCount:0], @"%@", layer.setContentsCounts);
|
|
XCTAssertFalse(layer.needsDisplay);
|
|
XCTAssertEqual(layer.displayCount, 1u);
|
|
XCTAssertEqual(layer.drawInContextCount, 0u);
|
|
|
|
[layer cancelAsyncDisplay];
|
|
|
|
dispatch_resume([_ASDisplayLayer displayQueue]);
|
|
[self waitForDisplayQueue];
|
|
XCTAssertTrue([layer checkSetContentsCountsWithSyncCount:0 asyncCount:0], @"%@", layer.setContentsCounts);
|
|
XCTAssertEqual(layer.displayCount, 1u);
|
|
XCTAssertEqual(layer.drawInContextCount, 0u);
|
|
XCTAssertEqual(asyncDelegate.didDisplayCount, 0u);
|
|
XCTAssertEqual(asyncDelegate.displayCount, 0u);
|
|
XCTAssertEqual(asyncDelegate.drawParametersCount, 0u);
|
|
|
|
[asyncDelegate release];
|
|
}
|
|
|
|
- (void)testTransaction
|
|
{
|
|
_ASDisplayLayerTestDelegateMode delegateModes = _ASDisplayLayerTestDelegateModeDidDisplay | _ASDisplayLayerTestDelegateModeDrawParameters;
|
|
[_ASDisplayLayerTestDelegate setClassModes:_ASDisplayLayerTestDelegateClassModeDisplay];
|
|
|
|
// Setup
|
|
_ASDisplayLayerTestContainerLayer *containerLayer = [[_ASDisplayLayerTestContainerLayer alloc] init];
|
|
containerLayer.asyncdisplaykit_asyncTransactionContainer = YES;
|
|
containerLayer.frame = CGRectMake(0.0, 0.0, 100.0, 100.0);
|
|
|
|
_ASDisplayLayerTestDelegate *layer1Delegate = [[_ASDisplayLayerTestDelegate alloc] initWithModes:delegateModes];
|
|
_ASDisplayLayerTestLayer *layer1 = (_ASDisplayLayerTestLayer *)layer1Delegate.layer;
|
|
layer1.displaysAsynchronously = YES;
|
|
|
|
dispatch_semaphore_t displayAsyncLayer1Sema = dispatch_semaphore_create(0);
|
|
layer1Delegate.displayLayerBlock = ^(_ASDisplayLayer *asyncLayer) {
|
|
dispatch_semaphore_wait(displayAsyncLayer1Sema, DISPATCH_TIME_FOREVER);
|
|
return bogusImage();
|
|
};
|
|
layer1.backgroundColor = [UIColor blackColor].CGColor;
|
|
layer1.frame = CGRectMake(0.0, 0.0, 333.0, 123.0);
|
|
[containerLayer addSublayer:layer1];
|
|
|
|
_ASDisplayLayerTestDelegate *layer2Delegate = [[_ASDisplayLayerTestDelegate alloc] initWithModes:delegateModes];
|
|
_ASDisplayLayerTestLayer *layer2 = (_ASDisplayLayerTestLayer *)layer2Delegate.layer;
|
|
layer2.displaysAsynchronously = YES;
|
|
layer2.backgroundColor = [UIColor blackColor].CGColor;
|
|
layer2.frame = CGRectMake(0.0, 50.0, 97.0, 50.0);
|
|
[containerLayer addSublayer:layer2];
|
|
|
|
dispatch_suspend([_ASDisplayLayer displayQueue]);
|
|
|
|
// display below if needed
|
|
[layer1 setNeedsDisplay];
|
|
[layer2 setNeedsDisplay];
|
|
[containerLayer setNeedsDisplay];
|
|
[self displayLayerRecursively:containerLayer];
|
|
|
|
// check state before running displayQueue
|
|
XCTAssertTrue([layer1 checkSetContentsCountsWithSyncCount:0 asyncCount:0], @"%@", layer1.setContentsCounts);
|
|
XCTAssertEqual(layer1.displayCount, 1u);
|
|
XCTAssertEqual(layer1Delegate.displayCount, 0u);
|
|
XCTAssertTrue([layer2 checkSetContentsCountsWithSyncCount:0 asyncCount:0], @"%@", layer2.setContentsCounts);
|
|
XCTAssertEqual(layer2.displayCount, 1u);
|
|
XCTAssertEqual(layer1Delegate.displayCount, 0u);
|
|
XCTAssertEqual(containerLayer.didCompleteTransactionCount, 0u);
|
|
|
|
// run displayQueue until async display for layer2 has been run
|
|
dispatch_resume([_ASDisplayLayer displayQueue]);
|
|
XCTAssertTrue(ASDisplayNodeRunRunLoopUntilBlockIsTrue(^BOOL{
|
|
return (layer2Delegate.displayCount == 1);
|
|
}));
|
|
|
|
// check layer1 has not had async display run
|
|
XCTAssertTrue([layer1 checkSetContentsCountsWithSyncCount:0 asyncCount:0], @"%@", layer1.setContentsCounts);
|
|
XCTAssertEqual(layer1.displayCount, 1u);
|
|
XCTAssertEqual(layer1Delegate.displayCount, 0u);
|
|
// check layer2 has had async display run
|
|
XCTAssertTrue([layer2 checkSetContentsCountsWithSyncCount:0 asyncCount:0], @"%@", layer2.setContentsCounts);
|
|
XCTAssertEqual(layer2.displayCount, 1u);
|
|
XCTAssertEqual(layer2Delegate.displayCount, 1u);
|
|
XCTAssertEqual(containerLayer.didCompleteTransactionCount, 0u);
|
|
|
|
|
|
// allow layer1 to complete display
|
|
dispatch_semaphore_signal(displayAsyncLayer1Sema);
|
|
[self waitForLayer:layer1 asyncDisplayCount:1];
|
|
|
|
// check that both layers have completed display
|
|
XCTAssertTrue([layer1 checkSetContentsCountsWithSyncCount:0 asyncCount:1], @"%@", layer1.setContentsCounts);
|
|
XCTAssertEqual(layer1.displayCount, 1u);
|
|
XCTAssertEqual(layer1Delegate.displayCount, 1u);
|
|
XCTAssertTrue([layer2 checkSetContentsCountsWithSyncCount:0 asyncCount:1], @"%@", layer2.setContentsCounts);
|
|
XCTAssertEqual(layer2.displayCount, 1u);
|
|
XCTAssertEqual(layer2Delegate.displayCount, 1u);
|
|
|
|
XCTAssertTrue(ASDisplayNodeRunRunLoopUntilBlockIsTrue(^BOOL{
|
|
return (containerLayer.didCompleteTransactionCount == 1);
|
|
}));
|
|
|
|
[containerLayer release];
|
|
dispatch_release(displayAsyncLayer1Sema);
|
|
}
|
|
|
|
- (void)checkSuspendResume:(BOOL)displaysAsynchronously
|
|
{
|
|
[_ASDisplayLayerTestDelegate setClassModes:_ASDisplayLayerTestDelegateClassModeDrawInContext];
|
|
_ASDisplayLayerTestDelegate *asyncDelegate = [[_ASDisplayLayerTestDelegate alloc] initWithModes:_ASDisplayLayerTestDelegateModeDidDisplay | _ASDisplayLayerTestDelegateModeDrawParameters];
|
|
|
|
_ASDisplayLayerTestLayer *layer = (_ASDisplayLayerTestLayer *)asyncDelegate.layer;
|
|
layer.displaysAsynchronously = displaysAsynchronously;
|
|
layer.frame = CGRectMake(0.0, 0.0, 100.0, 100.0);
|
|
|
|
if (displaysAsynchronously) {
|
|
dispatch_suspend([_ASDisplayLayer displayQueue]);
|
|
}
|
|
|
|
// Layer shouldn't display because display is suspended
|
|
layer.displaySuspended = YES;
|
|
[layer setNeedsDisplay];
|
|
[layer displayIfNeeded];
|
|
XCTAssertEqual(layer.displayCount, 0u, @"Should not have displayed because display is suspended, thus -setNeedsDisplay is a no-op");
|
|
XCTAssertFalse(layer.needsDisplay, @"Should not need display");
|
|
if (displaysAsynchronously) {
|
|
XCTAssertTrue([layer checkSetContentsCountsWithSyncCount:0 asyncCount:0], @"%@", layer.setContentsCounts);
|
|
dispatch_resume([_ASDisplayLayer displayQueue]);
|
|
[self waitForDisplayQueue];
|
|
XCTAssertTrue([layer checkSetContentsCountsWithSyncCount:0 asyncCount:0], @"%@", layer.setContentsCounts);
|
|
} else {
|
|
XCTAssertTrue([layer checkSetContentsCountsWithSyncCount:0 asyncCount:0], @"%@", layer.setContentsCounts);
|
|
}
|
|
XCTAssertFalse(layer.needsDisplay);
|
|
XCTAssertEqual(layer.drawInContextCount, 0u);
|
|
XCTAssertEqual(asyncDelegate.drawRectCount, 0u);
|
|
|
|
// Layer should display because display is resumed
|
|
if (displaysAsynchronously) {
|
|
dispatch_suspend([_ASDisplayLayer displayQueue]);
|
|
}
|
|
layer.displaySuspended = NO;
|
|
XCTAssertTrue(layer.needsDisplay);
|
|
[layer displayIfNeeded];
|
|
XCTAssertEqual(layer.displayCount, 1u);
|
|
if (displaysAsynchronously) {
|
|
XCTAssertTrue([layer checkSetContentsCountsWithSyncCount:0 asyncCount:0], @"%@", layer.setContentsCounts);
|
|
XCTAssertEqual(layer.drawInContextCount, 0u);
|
|
XCTAssertEqual(asyncDelegate.drawRectCount, 0u);
|
|
dispatch_resume([_ASDisplayLayer displayQueue]);
|
|
[self waitForLayer:layer asyncDisplayCount:1];
|
|
XCTAssertTrue([layer checkSetContentsCountsWithSyncCount:0 asyncCount:1], @"%@", layer.setContentsCounts);
|
|
} else {
|
|
XCTAssertTrue([layer checkSetContentsCountsWithSyncCount:1 asyncCount:0], @"%@", layer.setContentsCounts);
|
|
}
|
|
XCTAssertEqual(layer.drawInContextCount, 0u);
|
|
XCTAssertEqual(asyncDelegate.drawParametersCount, 1u);
|
|
XCTAssertEqual(asyncDelegate.drawRectCount, 1u);
|
|
XCTAssertFalse(layer.needsDisplay);
|
|
|
|
[asyncDelegate release];
|
|
}
|
|
|
|
- (void)testSuspendResumeAsync
|
|
{
|
|
[self checkSuspendResume:YES];
|
|
}
|
|
|
|
- (void)testSuspendResumeSync
|
|
{
|
|
[self checkSuspendResume:NO];
|
|
}
|
|
|
|
@end
|