| // 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([ObjCClassSwizzlerTestOne class], @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 |