chromium/chrome/test/base/scoped_bundle_swizzler_mac.mm

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/test/base/scoped_bundle_swizzler_mac.h"

#import <Foundation/Foundation.h>

#include <memory>

#include "base/apple/foundation_util.h"
#include "base/apple/scoped_objc_class_swizzler.h"
#include "base/check.h"
#include "base/strings/sys_string_conversions.h"

static id __strong g_swizzled_main_bundle = nil;

// A donor class that provides a +[NSBundle mainBundle] method that can be
// swapped with NSBundle.
@interface TestBundle : NSProxy
- (instancetype)initWithRealBundle:(NSBundle*)bundle;
+ (NSBundle*)mainBundle;
@end

@implementation TestBundle {
  NSBundle* __strong _mainBundle;
  NSString* __strong _bundleID;
}

+ (NSBundle*)mainBundle {
  return g_swizzled_main_bundle;
}

- (instancetype)initWithRealBundle:(NSBundle*)bundle {
  _mainBundle = bundle;
  _bundleID = base::SysUTF8ToNSString(base::apple::BaseBundleID());
  return self;
}

- (NSString*)bundleIdentifier {
  return _bundleID;
}

- (void)forwardInvocation:(NSInvocation*)invocation {
  invocation.target = _mainBundle;
  [invocation invoke];
}

- (NSMethodSignature*)methodSignatureForSelector:(SEL)sel {
  return [_mainBundle methodSignatureForSelector:sel];
}

@end

ScopedBundleSwizzlerMac::ScopedBundleSwizzlerMac() {
  CHECK(!g_swizzled_main_bundle);

  NSBundle* original_main_bundle = NSBundle.mainBundle;
  g_swizzled_main_bundle =
      [[TestBundle alloc] initWithRealBundle:original_main_bundle];

  class_swizzler_ = std::make_unique<base::apple::ScopedObjCClassSwizzler>(
      [NSBundle class], [TestBundle class], @selector(mainBundle));
}

ScopedBundleSwizzlerMac::~ScopedBundleSwizzlerMac() {
  g_swizzled_main_bundle = nil;
}