BOOL FBLoadXCTestIfNeeded()

in Shims/Shimulator/TestLoadingShim/FBXCTestMain.m [45:82]


BOOL FBLoadXCTestIfNeeded()
{
  FBDebugLog(@"Env: %@", [NSProcessInfo processInfo].environment);

  if (objc_lookUpClass("XCTest")) {
    FBDebugLog(@"[XCTestMainEntryPoint] XCTest already loaded");
    return YES;
  }
  FBDebugLog(@"[XCTestMainEntryPoint] Loading XCTest framework");
  if (dlopen("XCTest.framework/XCTest", RTLD_LAZY)) {
    FBDebugLog(@"[XCTestMainEntryPoint] XCTest loaded");
    return YES;
  }
  FBDebugLog(@"[XCTestMainEntryPoint] Failed to load XCTest.framework. %@", [NSString stringWithUTF8String:dlerror()]);

  // Even though XCTest.framework actually is located in one of the `DYLD_FALLBACK_FRAMEWORK_PATH` directories, starting
  // on Xcode13.0/iOS15.0, dlopen does not look into those directories, failing to load XCTest.
  // As a last attempt, idb tries itself to find XCTest.framework and passes the absolute path to `dlopen`
  NSArray<NSString *> *fallbackFrameworkDirs = [[[NSProcessInfo processInfo].environment objectForKey:@"DYLD_FALLBACK_FRAMEWORK_PATH"] componentsSeparatedByString:@":"];

  FBDebugLog(@"[XCTestMainEntryPoint] Explictly looking for XCTest.framework in DYLD_FALLBACK_FRAMEWORK_PATH: %@", fallbackFrameworkDirs);

  for(NSString *frameworkDir in fallbackFrameworkDirs) {
    NSString *possibleLocation = [frameworkDir stringByAppendingPathComponent:@"XCTest.framework/XCTest"];
    if ([NSFileManager.defaultManager fileExistsAtPath:possibleLocation isDirectory:nil]) {
      if (dlopen([possibleLocation cStringUsingEncoding:NSUTF8StringEncoding], RTLD_LAZY)) {
        FBDebugLog(@"[XCTestMainEntryPoint] Found and loaded XCTest from %@", possibleLocation);
        return YES;
      } else {
        FBDebugLog(@"[XCTestMainEntryPoint] Failed to load XCTest.framework. %@", [NSString stringWithUTF8String:dlerror()]);
      }
    } else {
      FBDebugLog(@"[XCTestMainEntryPoint] XCTest not found at %@", possibleLocation);
    }
  }
  FBDebugLog(@"[XCTestMainEntryPoint] Could not load XCTest.framework");
  return NO;
}