XCUIApplicationProcess+FBQuiescence.m 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  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 "XCUIApplicationProcess+FBQuiescence.h"
  10. #import <objc/runtime.h>
  11. #import "FBConfiguration.h"
  12. #import "FBExceptions.h"
  13. #import "FBLogger.h"
  14. #import "FBSettings.h"
  15. static void (*original_waitForQuiescenceIncludingAnimationsIdle)(id, SEL, BOOL);
  16. static void (*original_waitForQuiescenceIncludingAnimationsIdlePreEvent)(id, SEL, BOOL, BOOL);
  17. static void swizzledWaitForQuiescenceIncludingAnimationsIdle(id self, SEL _cmd, BOOL includingAnimations)
  18. {
  19. NSString *bundleId = [self bundleID];
  20. if (![[self fb_shouldWaitForQuiescence] boolValue] || FBConfiguration.waitForIdleTimeout < DBL_EPSILON) {
  21. [FBLogger logFmt:@"Quiescence checks are disabled for %@ application. Making it to believe it is idling",
  22. bundleId];
  23. return;
  24. }
  25. NSTimeInterval desiredTimeout = FBConfiguration.waitForIdleTimeout;
  26. NSTimeInterval previousTimeout = _XCTApplicationStateTimeout();
  27. _XCTSetApplicationStateTimeout(desiredTimeout);
  28. [FBLogger logFmt:@"Waiting up to %@s until %@ is in idle state (%@ animations)",
  29. @(desiredTimeout), bundleId, includingAnimations ? @"including" : @"excluding"];
  30. @try {
  31. original_waitForQuiescenceIncludingAnimationsIdle(self, _cmd, includingAnimations);
  32. } @finally {
  33. _XCTSetApplicationStateTimeout(previousTimeout);
  34. }
  35. }
  36. static void swizzledWaitForQuiescenceIncludingAnimationsIdlePreEvent(id self, SEL _cmd, BOOL includingAnimations, BOOL isPreEvent)
  37. {
  38. NSString *bundleId = [self bundleID];
  39. if (![[self fb_shouldWaitForQuiescence] boolValue] || FBConfiguration.waitForIdleTimeout < DBL_EPSILON) {
  40. [FBLogger logFmt:@"Quiescence checks are disabled for %@ application. Making it to believe it is idling",
  41. bundleId];
  42. return;
  43. }
  44. NSTimeInterval desiredTimeout = FBConfiguration.waitForIdleTimeout;
  45. NSTimeInterval previousTimeout = _XCTApplicationStateTimeout();
  46. _XCTSetApplicationStateTimeout(desiredTimeout);
  47. [FBLogger logFmt:@"Waiting up to %@s until %@ is in idle state (%@ animations)",
  48. @(desiredTimeout), bundleId, includingAnimations ? @"including" : @"excluding"];
  49. @try {
  50. original_waitForQuiescenceIncludingAnimationsIdlePreEvent(self, _cmd, includingAnimations, isPreEvent);
  51. } @finally {
  52. _XCTSetApplicationStateTimeout(previousTimeout);
  53. }
  54. }
  55. @implementation XCUIApplicationProcess (FBQuiescence)
  56. #pragma clang diagnostic push
  57. #pragma clang diagnostic ignored "-Wobjc-load-method"
  58. #pragma clang diagnostic ignored "-Wcast-function-type-strict"
  59. + (void)load
  60. {
  61. Method waitForQuiescenceIncludingAnimationsIdleMethod = class_getInstanceMethod(self.class, @selector(waitForQuiescenceIncludingAnimationsIdle:));
  62. Method waitForQuiescenceIncludingAnimationsIdlePreEventMethod = class_getInstanceMethod(self.class, @selector(waitForQuiescenceIncludingAnimationsIdle:isPreEvent:));
  63. if (nil != waitForQuiescenceIncludingAnimationsIdleMethod) {
  64. IMP swizzledImp = (IMP)swizzledWaitForQuiescenceIncludingAnimationsIdle;
  65. original_waitForQuiescenceIncludingAnimationsIdle = (void (*)(id, SEL, BOOL)) method_setImplementation(waitForQuiescenceIncludingAnimationsIdleMethod, swizzledImp);
  66. } else if (nil != waitForQuiescenceIncludingAnimationsIdlePreEventMethod) {
  67. IMP swizzledImp = (IMP)swizzledWaitForQuiescenceIncludingAnimationsIdlePreEvent;
  68. original_waitForQuiescenceIncludingAnimationsIdlePreEvent = (void (*)(id, SEL, BOOL, BOOL)) method_setImplementation(waitForQuiescenceIncludingAnimationsIdlePreEventMethod, swizzledImp);
  69. } else {
  70. [FBLogger log:@"Could not find method -[XCUIApplicationProcess waitForQuiescenceIncludingAnimationsIdle:]"];
  71. }
  72. }
  73. #pragma clang diagnostic pop
  74. static char XCUIAPPLICATIONPROCESS_SHOULD_WAIT_FOR_QUIESCENCE;
  75. @dynamic fb_shouldWaitForQuiescence;
  76. - (NSNumber *)fb_shouldWaitForQuiescence
  77. {
  78. id result = objc_getAssociatedObject(self, &XCUIAPPLICATIONPROCESS_SHOULD_WAIT_FOR_QUIESCENCE);
  79. if (nil == result) {
  80. return @(YES);
  81. }
  82. return (NSNumber *)result;
  83. }
  84. - (void)setFb_shouldWaitForQuiescence:(NSNumber *)value
  85. {
  86. objc_setAssociatedObject(self, &XCUIAPPLICATIONPROCESS_SHOULD_WAIT_FOR_QUIESCENCE, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  87. }
  88. - (void)fb_waitForQuiescenceIncludingAnimationsIdle:(bool)waitForAnimations
  89. {
  90. if ([self respondsToSelector:@selector(waitForQuiescenceIncludingAnimationsIdle:)]) {
  91. [self waitForQuiescenceIncludingAnimationsIdle:waitForAnimations];
  92. } else if ([self respondsToSelector:@selector(waitForQuiescenceIncludingAnimationsIdle:isPreEvent:)]) {
  93. [self waitForQuiescenceIncludingAnimationsIdle:waitForAnimations isPreEvent:NO];
  94. } else {
  95. @throw [NSException exceptionWithName:FBIncompatibleWdaException
  96. reason:@"The current WebDriverAgent build is not compatible to your device OS version"
  97. userInfo:@{}];
  98. }
  99. }
  100. @end