mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-24 07:05:35 +00:00
[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:
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}, ^{
|
||||
|
||||
@@ -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();
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user