Commit 928ed9c9 authored by Gauthier Ambard's avatar Gauthier Ambard Committed by Commit Bot

[iOS] Create a ScopedMethodsSwizzler

This scoped swizzler swap the implementation of two instance methods
of a class.

Bug: 901082
Change-Id: I90d4009189b2206c453f9f3847c5fc9e58e94ee4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1911743Reviewed-by: default avatarSylvain Defresne <sdefresne@chromium.org>
Commit-Queue: Gauthier Ambard <gambard@chromium.org>
Cr-Commit-Position: refs/heads/master@{#714893}
parent 58bbc9b2
...@@ -42,6 +42,8 @@ source_set("block_swizzler") { ...@@ -42,6 +42,8 @@ source_set("block_swizzler") {
sources = [ sources = [
"scoped_block_swizzler.h", "scoped_block_swizzler.h",
"scoped_block_swizzler.mm", "scoped_block_swizzler.mm",
"scoped_method_swizzler.h",
"scoped_method_swizzler.mm",
] ]
deps = [ deps = [
"//base", "//base",
...@@ -103,6 +105,7 @@ test("ios_testing_unittests") { ...@@ -103,6 +105,7 @@ test("ios_testing_unittests") {
sources = [ sources = [
"ocmock_complex_type_helper_unittest.mm", "ocmock_complex_type_helper_unittest.mm",
"scoped_block_swizzler_unittest.mm", "scoped_block_swizzler_unittest.mm",
"scoped_method_swizzler_unittest.mm",
] ]
assert_no_deps = ios_assert_no_deps assert_no_deps = ios_assert_no_deps
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_TESTING_SCOPED_METHOD_SWIZZLER_H_
#define IOS_TESTING_SCOPED_METHOD_SWIZZLER_H_
#include <objc/runtime.h>
#include "base/macros.h"
class ScopedMethodSwizzler {
public:
// Constructs a new ScopedMethodSwizzler object and replaces the
// implementation of |selector_to_replace| on the |target| class with the
// given |replacing_selector|. ScopedMethodSwizzler swizzles instance methods.
ScopedMethodSwizzler(Class target,
SEL selector_to_replace,
SEL replacing_selector);
// Destroys the ScopedMethodSwizzler object, removing the swizzled method and
// reinstalling the original method implementation.
virtual ~ScopedMethodSwizzler();
private:
// The method that is to be swizzled.
Method original_method_;
// The method that replaces the original method.
Method replacing_method_;
DISALLOW_COPY_AND_ASSIGN(ScopedMethodSwizzler);
};
#endif // IOS_TESTING_SCOPED_METHOD_SWIZZLER_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/testing/scoped_method_swizzler.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
ScopedMethodSwizzler::ScopedMethodSwizzler(Class klass,
SEL selector_to_replace,
SEL replacing_selector) {
original_method_ = class_getInstanceMethod(klass, selector_to_replace);
replacing_method_ = class_getInstanceMethod(klass, replacing_selector);
BOOL method_added = class_addMethod(
klass, selector_to_replace, method_getImplementation(replacing_method_),
method_getTypeEncoding(replacing_method_));
if (method_added) {
class_replaceMethod(klass, replacing_selector,
method_getImplementation(original_method_),
method_getTypeEncoding(original_method_));
} else {
method_exchangeImplementations(original_method_, replacing_method_);
}
}
ScopedMethodSwizzler::~ScopedMethodSwizzler() {
method_exchangeImplementations(original_method_, replacing_method_);
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/testing/scoped_method_swizzler.h"
#import <Foundation/Foundation.h>
#include "testing/gtest/include/gtest/gtest.h"
#import "testing/gtest_mac.h"
#include "testing/platform_test.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
// Class containing methods that will be swizzled by the unittests.
@interface ScopedMethodSwizzlerTestClass : NSObject
- (NSString*)instanceMethodToSwizzle;
- (NSString*)swizzledInstanceMethod;
@end
namespace {
NSString* const kOriginalInstanceValue = @"Bizz";
NSString* const kSwizzledInstanceValue = @"Buzz";
using ScopedMethodSwizzlerTest = PlatformTest;
// Tests that swizzling an instance method works properly.
TEST_F(ScopedMethodSwizzlerTest, SwizzlingInstanceMethod) {
ScopedMethodSwizzlerTestClass* target =
[[ScopedMethodSwizzlerTestClass alloc] init];
EXPECT_NSEQ(kOriginalInstanceValue, [target instanceMethodToSwizzle]);
EXPECT_NSEQ(kSwizzledInstanceValue, [target swizzledInstanceMethod]);
{
ScopedMethodSwizzler swizzler([ScopedMethodSwizzlerTestClass class],
@selector(instanceMethodToSwizzle),
@selector(swizzledInstanceMethod));
EXPECT_NSEQ(kSwizzledInstanceValue, [target instanceMethodToSwizzle]);
EXPECT_NSEQ(kOriginalInstanceValue, [target swizzledInstanceMethod]);
}
EXPECT_NSEQ(kOriginalInstanceValue, [target instanceMethodToSwizzle]);
EXPECT_NSEQ(kSwizzledInstanceValue, [target swizzledInstanceMethod]);
}
} // namespace
#pragma mark - ScopedMethodSwizzlerTestClass
@implementation ScopedMethodSwizzlerTestClass
- (NSString*)instanceMethodToSwizzle {
return kOriginalInstanceValue;
}
- (NSString*)swizzledInstanceMethod {
return kSwizzledInstanceValue;
}
@end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment