FBElementCache.m 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. /**
  2. * Copyright (c) 2015-present, Facebook, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. */
  9. #import "FBElementCache.h"
  10. #import "LRUCache.h"
  11. #import "FBAlert.h"
  12. #import "FBExceptions.h"
  13. #import "FBXCodeCompatibility.h"
  14. #import "XCTestPrivateSymbols.h"
  15. #import "XCUIElement.h"
  16. #import "XCUIElement+FBCaching.h"
  17. #import "XCUIElement+FBUtilities.h"
  18. #import "XCUIElement+FBWebDriverAttributes.h"
  19. #import "XCUIElement+FBUID.h"
  20. #import "XCUIElement+FBResolve.h"
  21. #import "XCUIElementQuery.h"
  22. const int ELEMENT_CACHE_SIZE = 1024;
  23. @interface FBElementCache ()
  24. @property (nonatomic, strong) LRUCache *elementCache;
  25. @property (nonatomic) BOOL elementsNeedReset;
  26. @end
  27. @implementation FBElementCache
  28. - (instancetype)init
  29. {
  30. self = [super init];
  31. if (!self) {
  32. return nil;
  33. }
  34. _elementCache = [[LRUCache alloc] initWithCapacity:ELEMENT_CACHE_SIZE];
  35. _elementsNeedReset = NO;
  36. return self;
  37. }
  38. - (NSString *)storeElement:(XCUIElement *)element
  39. {
  40. NSString *uuid = element.fb_cacheId;
  41. if (nil == uuid) {
  42. return nil;
  43. }
  44. @synchronized (self.elementCache) {
  45. [self.elementCache setObject:element forKey:uuid];
  46. self.elementsNeedReset = YES;
  47. }
  48. return uuid;
  49. }
  50. - (XCUIElement *)elementForUUID:(NSString *)uuid
  51. {
  52. return [self elementForUUID:uuid resolveForAdditionalAttributes:nil andMaxDepth:nil];
  53. }
  54. - (XCUIElement *)elementForUUID:(NSString *)uuid
  55. resolveForAdditionalAttributes:(NSArray <NSString *> *)additionalAttributes
  56. andMaxDepth:(NSNumber *)maxDepth
  57. {
  58. if (!uuid) {
  59. NSString *reason = [NSString stringWithFormat:@"Cannot extract cached element for UUID: %@", uuid];
  60. @throw [NSException exceptionWithName:FBInvalidArgumentException reason:reason userInfo:@{}];
  61. }
  62. XCUIElement *element;
  63. @synchronized (self.elementCache) {
  64. [self resetElements];
  65. element = [self.elementCache objectForKey:uuid];
  66. }
  67. if (nil == element) {
  68. NSString *reason = [NSString stringWithFormat:@"The element identified by \"%@\" is either not present or it has expired from the internal cache. Try to find it again", uuid];
  69. @throw [NSException exceptionWithName:FBStaleElementException reason:reason userInfo:@{}];
  70. }
  71. // This will throw FBStaleElementException exception if the element is stale
  72. // or resolve the element and set lastSnapshot property
  73. if (nil == additionalAttributes) {
  74. [element fb_takeSnapshot];
  75. } else {
  76. NSMutableArray *attributes = [NSMutableArray arrayWithArray:FBStandardAttributeNames()];
  77. [attributes addObjectsFromArray:additionalAttributes];
  78. [element fb_snapshotWithAttributes:attributes.copy maxDepth:maxDepth];
  79. }
  80. element.fb_isResolvedFromCache = @(YES);
  81. return element;
  82. }
  83. - (BOOL)hasElementWithUUID:(NSString *)uuid
  84. {
  85. if (nil == uuid) {
  86. return NO;
  87. }
  88. @synchronized (self.elementCache) {
  89. return nil != [self.elementCache objectForKey:(NSString *)uuid];
  90. }
  91. }
  92. - (void)resetElements
  93. {
  94. if (!self.elementsNeedReset) {
  95. return;
  96. }
  97. for (XCUIElement *element in self.elementCache.allObjects) {
  98. element.lastSnapshot = nil;
  99. if (nil != element.query) {
  100. element.query.rootElementSnapshot = nil;
  101. }
  102. element.fb_isResolvedFromCache = @(NO);
  103. }
  104. self.elementsNeedReset = NO;
  105. }
  106. @end