Skip to content

Commit

Permalink
Closes #27
Browse files Browse the repository at this point in the history
  • Loading branch information
vittoriom committed Nov 23, 2013
1 parent 6c77fbe commit a005219
Show file tree
Hide file tree
Showing 9 changed files with 319 additions and 62 deletions.
4 changes: 2 additions & 2 deletions VMDInstrumenter.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'VMDInstrumenter'
s.version = '0.7'
s.version = '0.8'
s.summary = 'A simple Objective-C singleton to instrument, trace, and suppress selectors at runtime.'
s.author = {
'Vittorio Monaco' => 'vittorio.monaco1@gmail.com'
Expand All @@ -9,7 +9,7 @@ Pod::Spec.new do |s|
:git => 'https://github.com/vittoriom/VMInstrumenter.git',
:tag => s.version.to_s
}
s.source_files = 'Classes', 'VMInstrumenter/*.{h,m}'
s.source_files = 'Classes', 'VMInstrumenter/*.{h,m}', 'VMInstrumenter/RuntimeClasses/*.{h,m}'
s.license = 'BSD'
s.homepage = 'https://github.com/vittoriom/VMInstrumenter'
s.requires_arc = true
Expand Down
10 changes: 10 additions & 0 deletions VMInstrumenter/RuntimeWrappers/VMDClass.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,23 @@ @implementation VMDClass {

+ (VMDClass *) classWithClass:(Class)classToInspect
{
if(!classToInspect)
return nil;

VMDClass *wrapper = [VMDClass new];
wrapper->_classToInspect = classToInspect;
return wrapper;
}

- (void) addMethodWithSelector:(SEL)selector implementation:(IMP)implementation andSignature:(const char*)signature
{
if(!selector)
@throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:@"The selector specified is not valid. Can't add new method"
userInfo:@{
@"error" : @"Selector is nil"
}];

class_addMethod(_classToInspect, selector, implementation, signature);
}

Expand Down
3 changes: 3 additions & 0 deletions VMInstrumenter/RuntimeWrappers/VMDIvar.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ @implementation VMDIvar {

+ (VMDIvar *) ivarWithIvar:(Ivar)ivar
{
if(!ivar)
return nil;

VMDIvar *ivarObject = [VMDIvar new];

ivarObject->_ivar = ivar;
Expand Down
5 changes: 5 additions & 0 deletions VMInstrumenter/RuntimeWrappers/VMDMethod.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ + (VMDMethod *) methodWithMethod:(Method)method

- (void) exchangeImplementationWithMethod:(VMDMethod *)newMethod
{
if(!newMethod || [newMethod isEqual:self])
@throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:newMethod ? @"Method specified is the same as self" : @"No valid method provided"
userInfo:nil];

method_exchangeImplementations(_method, newMethod.underlyingMethod);
}

Expand Down
3 changes: 3 additions & 0 deletions VMInstrumenter/RuntimeWrappers/VMDProperty.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ @implementation VMDProperty {

+ (VMDProperty *) propertyWithObjectiveCProperty:(objc_property_t)property
{
if(!property)
return nil;

VMDProperty *propertyToReturn = [VMDProperty new];
propertyToReturn->_property = property;

Expand Down
127 changes: 98 additions & 29 deletions VMInstrumenter_Tests/VMDClass_Tests.m
Original file line number Diff line number Diff line change
@@ -1,101 +1,170 @@
#import "VMDClass.h"
#import "VMDHelper.h"
#import "VMDMethod.h"

@interface VMDClass_Test : NSObject

@property (nonatomic, strong) NSString *testProperty;

- (void) doFoo;

@end

@implementation VMDClass_Test {
NSNumber *testIvar;
}

- (void) doFoo
{

}

@end

SPEC_BEGIN(VMDClass_Tests)

describe(@"VMDClass", ^{
context(@"when creating a wrapper", ^{
it(@"Should not return nil if a class is specified", ^{

VMDClass *sut = [VMDClass classWithClass:[self class]];
[[sut shouldNot] beNil];
});

it(@"Should return nil if a class is not specified", ^{

VMDClass *sut = [VMDClass classWithClass:nil];
[[sut should] beNil];
sut = [VMDClass classWithClass:NSClassFromString(@"Nonexisting_Class")];
[[sut should] beNil];
});
});

context(@"when adding a new method", ^{
it(@"should raise when arguments are not valid", ^{

VMDClass *sut = [VMDClass classWithClass:[self class]];
[[theBlock(^{
[sut addMethodWithSelector:nil implementation:imp_implementationWithBlock(^{}) andSignature:"v:@"];
}) should] raise];
});

it(@"Should correctly add a new method otherwise", ^{

VMDClass *sut = [VMDClass classWithClass:[self class]];
[[theBlock(^{
[sut addMethodWithSelector:@selector(fileType) implementation:imp_implementationWithBlock(^{}) andSignature:"v:@"];
}) shouldNot] raise];
});
});

context(@"when getting a VMDMethod", ^{
it(@"Should return nil if the method doesn't exist", ^{

__block VMDClass *sut;

beforeEach(^{
sut = [VMDClass classWithClass:[VMDHelper class]];
});

it(@"Should return a valid VMDMethod for instance methods", ^{

it(@"Should raise if the method doesn't exist", ^{
[[theBlock(^{
[sut getMethodFromSelector:@selector(fileType) orThrowExceptionWithReason:@"No reason"];
}) should] raise];
});

it(@"Should return a valid VMDMethod for class methods", ^{

VMDMethod *method = [sut getMethodFromSelector:@selector(generatePlausibleSelectorNameForSelectorToInstrument:ofClass:) orThrowExceptionWithReason:@"no reason"];
[[method shouldNot] beNil];
[[[method name] should] equal:NSStringFromSelector(@selector(generatePlausibleSelectorNameForSelectorToInstrument:ofClass:))];
});

it(@"Should return a valid VMDMethod for instance methods", ^{
VMDClass *anotherSut = [VMDClass classWithClass:[VMDClass_Test class]];
VMDMethod *method = [anotherSut getMethodFromSelector:@selector(doFoo) orThrowExceptionWithReason:@"No reason"];
[[method shouldNot] beNil];
[[[method name] should] equal:NSStringFromSelector(@selector(doFoo))];
});
});

context(@"when getting a class method", ^{
it(@"Should return nil if the method is not valid", ^{

__block VMDClass *sut;

beforeEach(^{
sut = [VMDClass classWithClass:[VMDHelper class]];
});

it(@"Should return nil if the parameter is an instance method", ^{

it(@"Should return nil if the method is not valid", ^{
VMDMethod *method = [sut getMethodFromClassSelector:@selector(doFoo)];
[[method should] beNil];
});

it(@"Should return a valid VMDMethod otherwise", ^{

VMDMethod *method = [sut getMethodFromClassSelector:@selector(generatePlausibleSelectorNameForSelectorToInstrument:ofClass:)];
[[method shouldNot] beNil];
[[method.name should] equal:NSStringFromSelector(@selector(generatePlausibleSelectorNameForSelectorToInstrument:ofClass:))];
});
});

context(@"when getting an instance method", ^{
it(@"Should return nil if the method is not valid", ^{

__block VMDClass *sut;

beforeEach(^{
sut = [VMDClass classWithClass:[VMDClass_Test class]];
});

it(@"Should return nil if the parameter is a class method", ^{

it(@"Should return nil if the method is not valid", ^{
VMDMethod *method = [sut getMethodFromInstanceSelector:@selector(dominantLanguage)];
[[method should] beNil];
});

it(@"Should return a valid VMDMethod otherwise", ^{

VMDMethod *method = [sut getMethodFromInstanceSelector:@selector(doFoo)];
[[method shouldNot] beNil];
[[method.name should] equal:NSStringFromSelector(@selector(doFoo))];
});
});

context(@"when checking if the method is an instance or a class method", ^{
it(@"Should correctly return if the method is an instance method", ^{

VMDClass *sut = [VMDClass classWithClass:[VMDClass_Test class]];
[[theValue([sut isInstanceMethod:@selector(doFoo)]) should] beTrue];
});

it(@"Should correctly return if the method is a class method", ^{

VMDClass *sut = [VMDClass classWithClass:[VMDHelper class]];
[[theValue([sut isClassMethod:@selector(generatePlausibleSelectorNameForSelectorToInstrument:ofClass:)]) should] beTrue];
});

it(@"Should return NO if the method doesn't belong to the class", ^{
//2 tests here
VMDClass *sut = [VMDClass classWithClass:[VMDClass_Test class]];
[[theValue([sut isClassMethod:@selector(generatePlausibleSelectorNameForSelectorToInstrument:ofClass:)]) should] beFalse];
[[theValue([sut isInstanceMethod:@selector(generatePlausibleSelectorNameForSelectorToInstrument:ofClass:)]) should] beFalse];
});
});

context(@"when getting properties", ^{
it(@"should correctly return the method list", ^{
__block VMDClass *sut;

beforeEach(^{
sut = [VMDClass classWithClass:[VMDClass_Test class]];
});

it(@"should correctly return the method list", ^{
NSArray *methods = [sut methods];
//Getter, setter, doFoo and .cxx_destruct
[[theValue(methods.count) should] equal:theValue(4)];
});

it(@"should correctly return the ivars list", ^{

NSArray *ivars = [sut ivars];
//The ivar, the ivar backing the property
[[theValue(ivars.count) should] equal:theValue(2)];
});

it(@"should correctly return the properties list", ^{

});

it(@"should correctly return the metaclass", ^{

NSArray *properties = [sut properties];
[[theValue(properties.count) should] equal:theValue(1)];
});

it(@"should correctly return the underlying Class value", ^{

Class underlying = [sut underlyingClass];
[[theValue(underlying) should] equal:theValue([VMDClass_Test class])];
});
});
});
Expand Down
Loading

0 comments on commit a005219

Please sign in to comment.