|  | // Copyright 2014 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 "base/mac/scoped_objc_class_swizzler.h" | 
|  |  | 
|  | #import "base/mac/scoped_nsobject.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | @interface ObjCClassSwizzlerTestOne : NSObject | 
|  | + (NSInteger)function; | 
|  | - (NSInteger)method; | 
|  | - (NSInteger)modifier; | 
|  | @end | 
|  |  | 
|  | @interface ObjCClassSwizzlerTestTwo : NSObject | 
|  | + (NSInteger)function; | 
|  | - (NSInteger)method; | 
|  | - (NSInteger)modifier; | 
|  | @end | 
|  |  | 
|  | @implementation ObjCClassSwizzlerTestOne : NSObject | 
|  |  | 
|  | + (NSInteger)function { | 
|  | return 10; | 
|  | } | 
|  |  | 
|  | - (NSInteger)method { | 
|  | // Multiply by a modifier to ensure |self| in a swizzled implementation | 
|  | // refers to the original object. | 
|  | return 1 * [self modifier]; | 
|  | } | 
|  |  | 
|  | - (NSInteger)modifier { | 
|  | return 3; | 
|  | } | 
|  |  | 
|  | @end | 
|  |  | 
|  | @implementation ObjCClassSwizzlerTestTwo : NSObject | 
|  |  | 
|  | + (NSInteger)function { | 
|  | return 20; | 
|  | } | 
|  |  | 
|  | - (NSInteger)method { | 
|  | return 2 * [self modifier]; | 
|  | } | 
|  |  | 
|  | - (NSInteger)modifier { | 
|  | return 7; | 
|  | } | 
|  |  | 
|  | @end | 
|  |  | 
|  | @interface ObjCClassSwizzlerTestOne (AlternateCategory) | 
|  | - (NSInteger)alternate; | 
|  | @end | 
|  |  | 
|  | @implementation ObjCClassSwizzlerTestOne (AlternateCategory) | 
|  | - (NSInteger)alternate { | 
|  | return 3 * [self modifier]; | 
|  | } | 
|  | @end | 
|  |  | 
|  | @interface ObjCClassSwizzlerTestOneChild : ObjCClassSwizzlerTestOne | 
|  | - (NSInteger)childAlternate; | 
|  | @end | 
|  |  | 
|  | @implementation ObjCClassSwizzlerTestOneChild | 
|  | - (NSInteger)childAlternate { | 
|  | return 5 * [self modifier]; | 
|  | } | 
|  | @end | 
|  |  | 
|  | namespace base { | 
|  | namespace mac { | 
|  |  | 
|  | TEST(ObjCClassSwizzlerTest, SwizzleInstanceMethods) { | 
|  | base::scoped_nsobject<ObjCClassSwizzlerTestOne> object_one( | 
|  | [[ObjCClassSwizzlerTestOne alloc] init]); | 
|  | base::scoped_nsobject<ObjCClassSwizzlerTestTwo> object_two( | 
|  | [[ObjCClassSwizzlerTestTwo alloc] init]); | 
|  | EXPECT_EQ(3, [object_one method]); | 
|  | EXPECT_EQ(14, [object_two method]); | 
|  |  | 
|  | { | 
|  | base::mac::ScopedObjCClassSwizzler swizzler( | 
|  | [ObjCClassSwizzlerTestOne class], | 
|  | [ObjCClassSwizzlerTestTwo class], | 
|  | @selector(method)); | 
|  | EXPECT_EQ(6, [object_one method]); | 
|  | EXPECT_EQ(7, [object_two method]); | 
|  |  | 
|  | IMP original = swizzler.GetOriginalImplementation(); | 
|  | id expected_result = reinterpret_cast<id>(3); | 
|  | EXPECT_EQ(expected_result, original(object_one, @selector(method))); | 
|  | } | 
|  |  | 
|  | EXPECT_EQ(3, [object_one method]); | 
|  | EXPECT_EQ(14, [object_two method]); | 
|  | } | 
|  |  | 
|  | TEST(ObjCClassSwizzlerTest, SwizzleClassMethods) { | 
|  | EXPECT_EQ(10, [ObjCClassSwizzlerTestOne function]); | 
|  | EXPECT_EQ(20, [ObjCClassSwizzlerTestTwo function]); | 
|  |  | 
|  | { | 
|  | base::mac::ScopedObjCClassSwizzler swizzler( | 
|  | [ObjCClassSwizzlerTestOne class], | 
|  | [ObjCClassSwizzlerTestTwo class], | 
|  | @selector(function)); | 
|  | EXPECT_EQ(20, [ObjCClassSwizzlerTestOne function]); | 
|  | EXPECT_EQ(10, [ObjCClassSwizzlerTestTwo function]); | 
|  |  | 
|  | IMP original = swizzler.GetOriginalImplementation(); | 
|  | id expected_result = reinterpret_cast<id>(10); | 
|  | EXPECT_EQ(expected_result, original(nil, @selector(function))); | 
|  | } | 
|  |  | 
|  | EXPECT_EQ(10, [ObjCClassSwizzlerTestOne function]); | 
|  | EXPECT_EQ(20, [ObjCClassSwizzlerTestTwo function]); | 
|  | } | 
|  |  | 
|  | TEST(ObjCClassSwizzlerTest, SwizzleViaCategory) { | 
|  | base::scoped_nsobject<ObjCClassSwizzlerTestOne> object_one( | 
|  | [[ObjCClassSwizzlerTestOne alloc] init]); | 
|  | EXPECT_EQ(3, [object_one method]); | 
|  |  | 
|  | { | 
|  | base::mac::ScopedObjCClassSwizzler swizzler( | 
|  | [ObjCClassSwizzlerTestOne class], | 
|  | @selector(method), | 
|  | @selector(alternate)); | 
|  | EXPECT_EQ(9, [object_one method]); | 
|  |  | 
|  | IMP original = swizzler.GetOriginalImplementation(); | 
|  | id expected_result = reinterpret_cast<id>(3); | 
|  | EXPECT_EQ(expected_result, original(object_one, @selector(method))); | 
|  | } | 
|  |  | 
|  | EXPECT_EQ(3, [object_one method]); | 
|  | } | 
|  |  | 
|  | TEST(ObjCClassSwizzlerTest, SwizzleViaInheritance) { | 
|  | base::scoped_nsobject<ObjCClassSwizzlerTestOneChild> child( | 
|  | [[ObjCClassSwizzlerTestOneChild alloc] init]); | 
|  | EXPECT_EQ(3, [child method]); | 
|  |  | 
|  | { | 
|  | base::mac::ScopedObjCClassSwizzler swizzler( | 
|  | [ObjCClassSwizzlerTestOneChild class], | 
|  | @selector(method), | 
|  | @selector(childAlternate)); | 
|  | EXPECT_EQ(15, [child method]); | 
|  |  | 
|  | IMP original = swizzler.GetOriginalImplementation(); | 
|  | id expected_result = reinterpret_cast<id>(3); | 
|  | EXPECT_EQ(expected_result, original(child, @selector(method))); | 
|  | } | 
|  |  | 
|  | EXPECT_EQ(3, [child method]); | 
|  | } | 
|  |  | 
|  | }  // namespace mac | 
|  | }  // namespace base |