[Unit Testing] Add Realistic Text Data Set (#2188)

* Add a mechanism to save attributed strings given to text nodes

* Setup performance tests to have an iteration variable

* Use realistic data in new text node performance test case

* Revert temporary changes
This commit is contained in:
Adlai Holler
2016-09-05 12:30:06 -07:00
committed by GitHub
parent 5db57802b0
commit e233a1cccc
5 changed files with 96 additions and 13 deletions

View File

@@ -29,6 +29,15 @@
#import "CGRect+ASConvenience.h"
/**
* If set, we will record all values set to attributedText into an array
* and once we get 2000, we'll write them all out into a plist file.
*
* This is useful for gathering realistic text data sets from apps for performance
* testing.
*/
#define AS_TEXTNODE_RECORD_ATTRIBUTED_STRINGS 0
static const NSTimeInterval ASTextNodeHighlightFadeOutDuration = 0.15;
static const NSTimeInterval ASTextNodeHighlightFadeInDuration = 0.1;
static const CGFloat ASTextNodeHighlightLightOpacity = 0.11;
@@ -439,7 +448,9 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
}
_attributedText = ASCleanseAttributedStringOfCoreTextAttributes(attributedText);
#if AS_TEXTNODE_RECORD_ATTRIBUTED_STRINGS
[ASTextNode _registerAttributedText:_attributedText];
#endif
// Sync the truncation string with attributes from the updated _attributedString
// Without this, the size calculation of the text with truncation applied will
// not take into account the attributes of attributedText in the last line
@@ -1364,6 +1375,30 @@ static NSAttributedString *DefaultTruncationAttributedString()
return truncationMutableString;
}
#if AS_TEXTNODE_RECORD_ATTRIBUTED_STRINGS
+ (void)_registerAttributedText:(NSAttributedString *)str
{
static NSMutableArray *array;
static NSLock *lock;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
lock = [NSLock new];
array = [NSMutableArray new];
});
[lock lock];
[array addObject:str];
if (array.count % 20 == 0) {
NSLog(@"Got %d strings", (int)array.count);
}
if (array.count == 2000) {
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:@"AttributedStrings.plist"];
NSAssert([NSKeyedArchiver archiveRootObject:array toFile:path], nil);
NSLog(@"Saved to %@", path);
}
[lock unlock];
}
#endif
@end
@implementation ASTextNode (Deprecated)

View File

@@ -15,7 +15,7 @@
NS_ASSUME_NONNULL_BEGIN
typedef void (^ASTestPerformanceCaseBlock)(dispatch_block_t startMeasuring, dispatch_block_t stopMeasuring);
typedef void (^ASTestPerformanceCaseBlock)(NSUInteger i, dispatch_block_t startMeasuring, dispatch_block_t stopMeasuring);
@interface ASPerformanceTestResult : NSObject
@property (nonatomic, readonly) NSTimeInterval timePer1000;

View File

@@ -95,10 +95,10 @@
{
__block CFTimeInterval time = 0;
for (NSInteger i = 0; i < _iterationCount; i++) {
__block CFAbsoluteTime start = 0;
__block CFTimeInterval start = 0;
__block BOOL calledStop = NO;
@autoreleasepool {
block(^{
block(i, ^{
ASDisplayNodeAssert(start == 0, @"Called startMeasuring block twice.");
start = CACurrentMediaTime();
}, ^{

View File

@@ -32,6 +32,54 @@ static NSString *const kTestCaseUIKitWithNoContext = @"UIKitNoContext";
static NSString *const kTestCaseUIKitWithFreshContext = @"UIKitFreshContext";
static NSString *const kTestCaseUIKitWithReusedContext = @"UIKitReusedContext";
+ (NSArray<NSAttributedString *> *)realisticDataSet
{
static NSArray *array;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *file = [[NSBundle bundleForClass:self] pathForResource:@"AttributedStringsFixture0" ofType:@"plist" inDirectory:@"TestResources"];
if (file != nil) {
array = [NSKeyedUnarchiver unarchiveObjectWithFile:file];
}
NSAssert([array isKindOfClass:[NSArray class]], nil);
NSSet *unique = [NSSet setWithArray:array];
NSLog(@"Loaded realistic text data set with %d attributed strings, %d unique.", (int)array.count, (int)unique.count);
});
return array;
}
- (void)testPerformance_RealisticData
{
NSArray *data = [self.class realisticDataSet];
CGSize maxSize = CGSizeMake(355, CGFLOAT_MAX);
CGSize __block uiKitSize, __block asdkSize;
ASPerformanceTestContext *ctx = [[ASPerformanceTestContext alloc] init];
[ctx addCaseWithName:kTestCaseUIKit block:^(NSUInteger i, dispatch_block_t _Nonnull startMeasuring, dispatch_block_t _Nonnull stopMeasuring) {
NSAttributedString *text = data[i % data.count];
startMeasuring();
uiKitSize = [text boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine context:nil].size;
stopMeasuring();
}];
uiKitSize.width = ASCeilPixelValue(uiKitSize.width);
uiKitSize.height = ASCeilPixelValue(uiKitSize.height);
ctx.results[kTestCaseUIKit].userInfo[@"size"] = NSStringFromCGSize(uiKitSize);
[ctx addCaseWithName:kTestCaseASDK block:^(NSUInteger i, dispatch_block_t _Nonnull startMeasuring, dispatch_block_t _Nonnull stopMeasuring) {
ASTextNode *node = [[ASTextNode alloc] init];
NSAttributedString *text = data[i % data.count];
startMeasuring();
node.attributedText = text;
asdkSize = [node measure:maxSize];
stopMeasuring();
}];
ctx.results[kTestCaseASDK].userInfo[@"size"] = NSStringFromCGSize(asdkSize);
ASXCTAssertEqualSizes(uiKitSize, asdkSize);
ASXCTAssertRelativePerformanceInRange(ctx, kTestCaseASDK, 0.2, 0.5);
}
- (void)testPerformance_TwoParagraphLatinNoTruncation
{
NSAttributedString *text = [ASTextNodePerformanceTests twoParagraphLatinText];
@@ -40,7 +88,7 @@ static NSString *const kTestCaseUIKitWithReusedContext = @"UIKitReusedContext";
CGSize __block uiKitSize, __block asdkSize;
ASPerformanceTestContext *ctx = [[ASPerformanceTestContext alloc] init];
[ctx addCaseWithName:kTestCaseUIKit block:^(dispatch_block_t _Nonnull startMeasuring, dispatch_block_t _Nonnull stopMeasuring) {
[ctx addCaseWithName:kTestCaseUIKit block:^(NSUInteger i, dispatch_block_t _Nonnull startMeasuring, dispatch_block_t _Nonnull stopMeasuring) {
startMeasuring();
uiKitSize = [text boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine context:nil].size;
stopMeasuring();
@@ -49,7 +97,7 @@ static NSString *const kTestCaseUIKitWithReusedContext = @"UIKitReusedContext";
uiKitSize.height = ASCeilPixelValue(uiKitSize.height);
ctx.results[kTestCaseUIKit].userInfo[@"size"] = NSStringFromCGSize(uiKitSize);
[ctx addCaseWithName:kTestCaseASDK block:^(dispatch_block_t _Nonnull startMeasuring, dispatch_block_t _Nonnull stopMeasuring) {
[ctx addCaseWithName:kTestCaseASDK block:^(NSUInteger i, dispatch_block_t _Nonnull startMeasuring, dispatch_block_t _Nonnull stopMeasuring) {
ASTextNode *node = [[ASTextNode alloc] init];
startMeasuring();
node.attributedText = text;
@@ -70,7 +118,7 @@ static NSString *const kTestCaseUIKitWithReusedContext = @"UIKitReusedContext";
CGSize __block uiKitSize, __block asdkSize;
ASPerformanceTestContext *testCtx = [[ASPerformanceTestContext alloc] init];
[testCtx addCaseWithName:kTestCaseUIKit block:^(dispatch_block_t _Nonnull startMeasuring, dispatch_block_t _Nonnull stopMeasuring) {
[testCtx addCaseWithName:kTestCaseUIKit block:^(NSUInteger i, dispatch_block_t _Nonnull startMeasuring, dispatch_block_t _Nonnull stopMeasuring) {
startMeasuring();
uiKitSize = [text boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine context:nil].size;
stopMeasuring();
@@ -79,7 +127,7 @@ static NSString *const kTestCaseUIKitWithReusedContext = @"UIKitReusedContext";
uiKitSize.height = ASCeilPixelValue(uiKitSize.height);
testCtx.results[kTestCaseUIKit].userInfo[@"size"] = NSStringFromCGSize(uiKitSize);
[testCtx addCaseWithName:kTestCaseASDK block:^(dispatch_block_t _Nonnull startMeasuring, dispatch_block_t _Nonnull stopMeasuring) {
[testCtx addCaseWithName:kTestCaseASDK block:^(NSUInteger i, dispatch_block_t _Nonnull startMeasuring, dispatch_block_t _Nonnull stopMeasuring) {
ASTextNode *node = [[ASTextNode alloc] init];
startMeasuring();
node.attributedText = text;
@@ -101,7 +149,7 @@ static NSString *const kTestCaseUIKitWithReusedContext = @"UIKitReusedContext";
NSStringDrawingOptions options = NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine;
__block CGSize size;
// nil context
[ctx addCaseWithName:kTestCaseUIKitWithNoContext block:^(dispatch_block_t _Nonnull startMeasuring, dispatch_block_t _Nonnull stopMeasuring) {
[ctx addCaseWithName:kTestCaseUIKitWithNoContext block:^(NSUInteger i, dispatch_block_t _Nonnull startMeasuring, dispatch_block_t _Nonnull stopMeasuring) {
startMeasuring();
size = [text boundingRectWithSize:maxSize options:options context:nil].size;
stopMeasuring();
@@ -109,7 +157,7 @@ static NSString *const kTestCaseUIKitWithReusedContext = @"UIKitReusedContext";
ctx.results[kTestCaseUIKitWithNoContext].userInfo[@"size"] = NSStringFromCGSize(size);
// Fresh context
[ctx addCaseWithName:kTestCaseUIKitWithFreshContext block:^(dispatch_block_t _Nonnull startMeasuring, dispatch_block_t _Nonnull stopMeasuring) {
[ctx addCaseWithName:kTestCaseUIKitWithFreshContext block:^(NSUInteger i, dispatch_block_t _Nonnull startMeasuring, dispatch_block_t _Nonnull stopMeasuring) {
NSStringDrawingContext *stringDrawingCtx = [[NSStringDrawingContext alloc] init];
startMeasuring();
size = [text boundingRectWithSize:maxSize options:options context:stringDrawingCtx].size;
@@ -119,7 +167,7 @@ static NSString *const kTestCaseUIKitWithReusedContext = @"UIKitReusedContext";
// Reused context
NSStringDrawingContext *stringDrawingCtx = [[NSStringDrawingContext alloc] init];
[ctx addCaseWithName:kTestCaseUIKitWithReusedContext block:^(dispatch_block_t _Nonnull startMeasuring, dispatch_block_t _Nonnull stopMeasuring) {
[ctx addCaseWithName:kTestCaseUIKitWithReusedContext block:^(NSUInteger i, dispatch_block_t _Nonnull startMeasuring, dispatch_block_t _Nonnull stopMeasuring) {
startMeasuring();
size = [text boundingRectWithSize:maxSize options:options context:stringDrawingCtx].size;
stopMeasuring();
@@ -142,7 +190,7 @@ static NSString *const kTestCaseUIKitWithReusedContext = @"UIKitReusedContext";
// No caching, reused ctx
NSStringDrawingContext *defaultCtx = [[NSStringDrawingContext alloc] init];
XCTAssertFalse([[defaultCtx valueForKey:@"cachesLayout"] boolValue]);
[ctx addCaseWithName:kTestCaseUIKit block:^(dispatch_block_t _Nonnull startMeasuring, dispatch_block_t _Nonnull stopMeasuring) {
[ctx addCaseWithName:kTestCaseUIKit block:^(NSUInteger i, dispatch_block_t _Nonnull startMeasuring, dispatch_block_t _Nonnull stopMeasuring) {
startMeasuring();
uncachedSize = [text boundingRectWithSize:maxSize options:options context:defaultCtx].size;
stopMeasuring();
@@ -153,7 +201,7 @@ static NSString *const kTestCaseUIKitWithReusedContext = @"UIKitReusedContext";
// Caching
NSStringDrawingContext *cachingCtx = [[NSStringDrawingContext alloc] init];
[cachingCtx setValue:@YES forKey:@"cachesLayout"];
[ctx addCaseWithName:kTestCaseUIKitPrivateCaching block:^(dispatch_block_t _Nonnull startMeasuring, dispatch_block_t _Nonnull stopMeasuring) {
[ctx addCaseWithName:kTestCaseUIKitPrivateCaching block:^(NSUInteger i, dispatch_block_t _Nonnull startMeasuring, dispatch_block_t _Nonnull stopMeasuring) {
startMeasuring();
cachedSize = [text boundingRectWithSize:maxSize options:options context:cachingCtx].size;
stopMeasuring();