Commit 5c1bd07b authored by Guillaume BIENKOWSKI's avatar Guillaume BIENKOWSKI

Add a unit-test framework for iOS.

Launch using the following command:

   xcodebuild -sdk iphonesimulator -project linphone.xcodeproj -scheme "LinphoneTester" test RUN_UNIT_TEST_WITH_IOS_SIM=YES
parent 5d162519
//
// DTObjectBlockExecutor.h
// DTFoundation
//
// Created by Oliver Drobnik on 12.02.13.
// Copyright (c) 2013 Cocoanetics. All rights reserved.
//
/**
This class is used by [NSObject addDeallocBlock:] to execute blocks on dealloc
*/
@interface DTObjectBlockExecutor : NSObject
/**
Convenience method to create a block executor with a deallocation block
@param block The block to execute when the created receiver is being deallocated
*/
+ (id)blockExecutorWithDeallocBlock:(void(^)())block;
/**
Block to execute when dealloc of the receiver is called
*/
@property (nonatomic, copy) void (^deallocBlock)();
@end
//
// DTObjectBlockExecutor.m
// DTFoundation
//
// Created by Oliver Drobnik on 12.02.13.
// Copyright (c) 2013 Cocoanetics. All rights reserved.
//
#import "DTObjectBlockExecutor.h"
@implementation DTObjectBlockExecutor
+ (id)blockExecutorWithDeallocBlock:(void(^)())block
{
DTObjectBlockExecutor *executor = [[DTObjectBlockExecutor alloc] init];
executor.deallocBlock = block; // copy
return executor;
}
- (void)dealloc
{
if (_deallocBlock)
{
_deallocBlock();
_deallocBlock = nil;
}
}
@end
//
// Prefix header
//
// The contents of this file are implicitly included at the beginning of every source file.
//
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#endif
//
// LinphoneTester_Tests.m
// LinphoneTester Tests
//
// Created by guillaume on 10/09/2014.
//
//
#import <XCTest/XCTest.h>
#include "linphone/linphonecore.h"
#include "linphone/liblinphone_tester.h"
#import "NSObject+DTRuntime.h"
@interface LinphoneTester_Tests : XCTestCase
@end
@implementation LinphoneTester_Tests {
NSString* bundlePath;
NSString* documentPath;
}
static void linphone_log_function(OrtpLogLevel lev, const char *fmt, va_list args) {
NSString* log = [[NSString alloc] initWithFormat:[NSString stringWithUTF8String:fmt] arguments:args];
NSLog(@"%@",log);
}
void LSLog(NSString* fmt, ...){
va_list args;
va_start(args, fmt);
linphone_log_function(ORTP_MESSAGE, [fmt UTF8String], args);
va_end(args);
}
- (id)init {
self = [super init];
if( self ){
bundlePath = [[NSBundle mainBundle] bundlePath];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
documentPath = [paths objectAtIndex:0];
LSLog(@"Bundle path: %@", bundlePath);
LSLog(@"Document path: %@", documentPath);
liblinphone_tester_set_fileprefix([bundlePath UTF8String]);
liblinphone_tester_set_writable_dir_prefix( ms_strdup([documentPath UTF8String]) );
}
return self;
}
+ (NSArray*)skippedSuites {
NSArray* skipped_suites = @[@"Flexisip"];
return skipped_suites;
}
+ (void)initialize {
liblinphone_tester_init();
int count = liblinphone_tester_nb_test_suites();
for (int i=0; i<count; i++) {
const char* suite = liblinphone_tester_test_suite_name(i);
LSLog(@"Running '%s' suite", suite);
int test_count = liblinphone_tester_nb_tests(suite);
for( int k = 0; k<test_count; k++){
const char* test =liblinphone_tester_test_name(suite, k);
NSString* sSuite = [NSString stringWithUTF8String:suite];
NSString* sTest = [NSString stringWithUTF8String:test];
if( [[LinphoneTester_Tests skippedSuites] containsObject:sSuite] ) continue;
// prepend test_ so that it gets found by introspection
NSString* safesTest = [sTest stringByReplacingOccurrencesOfString:@" " withString:@"_"];
NSString* safesSuite = [sSuite stringByReplacingOccurrencesOfString:@" " withString:@"_"];
NSString *selectorName = [NSString stringWithFormat:@"test_%@__%@", safesSuite, safesTest];
[LinphoneTester_Tests addInstanceMethodWithSelectorName:selectorName block:^(LinphoneTester_Tests* myself) {
[myself testForSuite:sSuite andTest:sTest];
}];
}
}
}
- (void)setUp
{
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown
{
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testForSuite:(NSString*)suite andTest:(NSString*)test
{
XCTAssertFalse(liblinphone_tester_run_tests([suite UTF8String], [test UTF8String]), @"Suite '%@' / Test '%@' failed", suite, test);
}
@end
//
// NSObject+DTRuntime.h
// DTFoundation
//
// Created by Oliver Drobnik on 4/25/12.
// Copyright (c) 2012 Cocoanetics. All rights reserved.
//
/**
Methods to dynamically modify objects at runtime
*/
@interface NSObject (DTRuntime)
/**-------------------------------------------------------------------------------------
@name Blocks
---------------------------------------------------------------------------------------
*/
/**
Adds a block to be executed as soon as the receiver's memory is deallocated
@param block The block to execute when the receiver is being deallocated
*/
- (void)addDeallocBlock:(void(^)())block;
/**
Adds a new instance method to a class. All instances of this class will have this method.
The block captures `self` in the calling context. To allow access to the instance from within the block it is passed as parameter to the block.
@param selectorName The name of the method.
@param block The block to execute for the instance method, a pointer to the instance is passed as the only parameter.
@returns `YES` if the operation was successful
*/
+ (BOOL)addInstanceMethodWithSelectorName:(NSString *)selectorName block:(void(^)(id))block;
/**-------------------------------------------------------------------------------------
@name Method Swizzling
---------------------------------------------------------------------------------------
*/
/**
Exchanges two method implementations. After the call methods to the first selector will now go to the second one and vice versa.
@param selector The first method
@param otherSelector The second method
*/
+ (void)swizzleMethod:(SEL)selector withMethod:(SEL)otherSelector;
/**
Exchanges two class method implementations. After the call methods to the first selector will now go to the second one and vice versa.
@param selector The first method
@param otherSelector The second method
*/
+ (void)swizzleClassMethod:(SEL)selector withMethod:(SEL)otherSelector;
@end
//
// NSObject_DTRuntime.h
// DTFoundation
//
// Created by Oliver Drobnik on 4/25/12.
// Copyright (c) 2012 Cocoanetics. All rights reserved.
//
#import <objc/runtime.h>
#import "DTObjectBlockExecutor.h"
@implementation NSObject (DTRuntime)
static char DTRuntimeDeallocBlocks;
#pragma mark - Blocks
- (void)addDeallocBlock:(void(^)())block
{
// don't accept NULL block
NSParameterAssert(block);
NSMutableArray *deallocBlocks = objc_getAssociatedObject(self, &DTRuntimeDeallocBlocks);
// add array of dealloc blocks if not existing yet
if (!deallocBlocks)
{
deallocBlocks = [[NSMutableArray alloc] init];
objc_setAssociatedObject(self, &DTRuntimeDeallocBlocks, deallocBlocks, OBJC_ASSOCIATION_RETAIN);
}
DTObjectBlockExecutor *executor = [DTObjectBlockExecutor blockExecutorWithDeallocBlock:block];
[deallocBlocks addObject:executor];
}
+ (BOOL)addInstanceMethodWithSelectorName:(NSString *)selectorName block:(void(^)(id))block
{
// don't accept nil name
NSParameterAssert(selectorName);
// don't accept NULL block
NSParameterAssert(block);
// See http://stackoverflow.com/questions/6357663/casting-a-block-to-a-void-for-dynamic-class-method-resolution
#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_7
void *impBlockForIMP = (void *)objc_unretainedPointer(block);
#else
id impBlockForIMP = (__bridge id)objc_unretainedPointer(block);
#endif
IMP myIMP = imp_implementationWithBlock(impBlockForIMP);
SEL selector = NSSelectorFromString(selectorName);
return class_addMethod(self, selector, myIMP, "v@:");
}
#pragma mark - Method Swizzling
+ (void)swizzleMethod:(SEL)selector withMethod:(SEL)otherSelector
{
// my own class is being targetted
Class c = [self class];
// get the methods from the selectors
Method originalMethod = class_getInstanceMethod(c, selector);
Method otherMethod = class_getInstanceMethod(c, otherSelector);
if (class_addMethod(c, selector, method_getImplementation(otherMethod), method_getTypeEncoding(otherMethod)))
{
class_replaceMethod(c, otherSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}
else
{
method_exchangeImplementations(originalMethod, otherMethod);
}
}
+ (void)swizzleClassMethod:(SEL)selector withMethod:(SEL)otherSelector
{
// my own class is being targetted
Class c = [self class];
// get the methods from the selectors
Method originalMethod = class_getClassMethod(c, selector);
Method otherMethod = class_getClassMethod(c, otherSelector);
// if (class_addMethod(c, selector, method_getImplementation(otherMethod), method_getTypeEncoding(otherMethod)))
// {
// class_replaceMethod(c, otherSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
// }
// else
// {
method_exchangeImplementations(originalMethod, otherMethod);
// }
}
@end
......@@ -47,20 +47,20 @@ static void linphone_log_function(OrtpLogLevel lev, const char *fmt, va_list arg
NSString* log = [[NSString alloc] initWithFormat:[NSString stringWithUTF8String:fmt] arguments:args];
NSLog(@"%@",log);
[logsBuffer addObject:log];
if (logsBuffer.count >= kLogsBufferCapacity ) {
[lastLogs addObjectsFromArray:logsBuffer];
if( lastLogs.count >= kLastLogsCapacity - kLogsBufferCapacity ){
[lastLogs removeObjectsInRange:NSMakeRange(0, kLogsBufferCapacity)];
}
[[NSNotificationCenter defaultCenter] postNotificationName:kLogsUpdateNotification
object:nil
userInfo:@{@"newlogs": [logsBuffer copy]}];
[logsBuffer removeAllObjects];
}
// [logsBuffer addObject:log];
//
// if (logsBuffer.count >= kLogsBufferCapacity ) {
// [lastLogs addObjectsFromArray:logsBuffer];
//
// if( lastLogs.count >= kLastLogsCapacity - kLogsBufferCapacity ){
// [lastLogs removeObjectsInRange:NSMakeRange(0, kLogsBufferCapacity)];
// }
// [[NSNotificationCenter defaultCenter] postNotificationName:kLogsUpdateNotification
// object:nil
// userInfo:@{@"newlogs": [logsBuffer copy]}];
// [logsBuffer removeAllObjects];
// }
}
......
//
// LinphoneTesterTests.m
// LinphoneTesterTests
//
// Created by guillaume on 28/05/2014.
//
//
#import <XCTest/XCTest.h>
@interface LinphoneTesterTests : XCTestCase
@end
@implementation LinphoneTesterTests
- (void)setUp
{
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown
{
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testExample
{
XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__);
}
@end
This diff is collapsed.
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