block callback not being called from a unit test program in IOS - ios

I have the following setup. I have a object called "View" in which I want to unit test a method which contains two dispatch_async calls with in it.
view.m
typedef void (^onTaskCompletion)(); //defining the block
-(void) viewdidLoad
{
onTaskCompletion block = ^{
// callback into the block };
[self test1:block];
}
-(void) test1:(onTaskCompletion) block
{
//do something
dispatch_async(queue, ^{
// dispatch async into serial queue & do something
dispatch_async(dispatch_get_main_queue){
// calling the block
block();
};
};
}
When I run the IOS APP , the block in -(void) viewdidLoad gets called. Works perfectly fine. But the problem I have is this:
in Tests : XCTestCase (.m fie)
#property (retain) View *view;
-(void) testMyCode
{
onTaskCompletion block = ^{
// Never gets called.
};
[view test1:block];
}
When I try to Unit test this method test1(), The block never gets called.
Note: The break point within test1() method inside the dispatch_get_main_queue() never gets hit when running in test mode but does get hit when I just run the app. Any thoughts as to why it works when the app is run normally but not when running unit tests?

The problem you are facing is that the tests continue onwards even though they are not finished. The solution is to stall the runloop until the async test if finished.
You can use this dead-simple open source macro WAIT_WHILE(<expression>, <time_limit>) found here https://github.com/hfossli/AGAsyncTestHelper
- (void)testAsyncBlockCallback
{
__block BOOL jobDone = NO;
[Manager doSomeOperationOnDone:^(id data) {
jobDone = YES;
}];
WAIT_WHILE(!jobDone, 2.0);
}

If you want to be able to unit test asynchronous code, you will need to wrap the dispatch_async in a class that you can mock. This class would have for example:
- (void)executeInBackground:(void(^)())task;
- (void)executeInForeground:(void(^)())task;
Then, during your tests you can mock this class. Instead of actually calling the tasks, collect the tasks when they are called, and manually have them executed in your test (not actually calling asynchronously):
- (void)executeNextBackgroundTask;
- (void)executeNextForegroundTask;
Then you can explicitly test each order of execution.

Related

How to Perform a Task Only after a Block of Code in Another Thread Is Completed

Sorry about the confusing title, I have this block of code running in its own thread and want to access a class variable inside another class (view controller) everytime its value changed.
To clarify my question here is a simple class structure that represent what I’m trying to accomplish.
#interface classB : NSObject
{
NSThread * _someThread;
}
+ (classB*) instance;
#property(atomic) CVPixelBufferRef pixBufB;
- (void) foo
{
while(1)
{
//decode a frame
//assign data to pixBufB
}
}
- (void) start
{
_someThread = [[NSThread alloc] initWithTarget:self selector:#selector(foo) object:nil];
}
//sampleViewController
#interface sampleViewController : UIViewController
#property(atomic) CVPixelBufferRef pixBuf;
- (void)viewDidLoad
{
[[classB instance] start];
}
- (void) bar
{
_pixBuf = [[classB instance] pixBufB];
}
At the end of each loop cycle in foo, I want to access _pixBufB inside sampleViewController class. Since foo is executed in another thread I can’t simply use a getter, does anyone know how to approach this issue?
Thanks
You can do this work by using NSOperation and NSOperationQueue. You can "task" an assignment to a subclass of NSOperation and overwrite the main method to write a task. Next you need to make an object of NSOperationQueue and add your subclass, and it will start running in the new Queue u created and you can create it as synchronous and asynchronous respectively.
Now you can add a onCompletion Block at the end of the Queue OR the NSOperation itself, and it will be performed. OR you can create another subclass of NSOperation and create your task that you want to perform (foo, here) as another task and addDependency with the First TASK, therefore the second task(foo) will be performed only after the first is finished.
For more about NSOperation and NSOperationQueue, you can visit this link
Have the thread take a block to be called at each point you want to do an update. The block can either be dispatched to the main queue to be called, or it can itself dispatch to the main queue before updating any view controller state.

how to run methods in one specific thread (not main thread)

I call a heartBeats method per 10ms in a specific thread(not main thread), how to call another method at any time in this same thread?
I subclass NSThread like this
#implementation MyThread
{
NSTimeInterval _lastTimeInterval;
}
- (void)main
{
while (true) {
NSTimeInterval timeInterval = [[NSDate date] timeIntervalSince1970]*1000;
if (timeInterval - _lastTimeInterval > 10)
{
[self heartBeats];
_lastTimeInterval = timeInterval;
}
}
}
- (void)heartBeats
{
NSLog(#"heart beats thread: %#", [NSThread currentThread].description);
}
#end
and run it like this
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"main thread: %#", [NSThread currentThread].description);
MyThread *myThread = [[MyThread alloc]init];
[myThread start];
}
- (void)someMethod
{
// do somthing
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#end
Now,here is the question, how to run - (void)someMethod in myThread?
The main method of your NSThread subclass is everything that runs on that thread. You cannot interrupt it to run other code without the main method's cooperation.
What you really should do is throw out that entire loop and replace it with NSRunLoop and NSTimer.
NSRunLoop keeps the thread alive as long as there's something it will need to do, but also sleeps the thread until it needs to do something.
NSTimer does something on a repeating interval as long as it's scheduled on a run loop.
You need your thread to do two things:
send the MyThread object a heartBeats message (you're doing this)
send the view controller a someMethod message (this is what you asked about)
For the latter, you need one additional thing: A run loop source.
So, clear out your main method and replace it with the following:
Get the current NSRunLoop and store it in a local variable.
Create an NSTimer with a 10-second interval, whose target is self and selector is heartBeats. (Slightly cleaner version: Have another method that takes an NSTimer *but ignores it, so your timer calls that method and that method calls heartBeats. The proper way to set up a timer is to give it a method that expects to be called with a timer, but, in practice, giving it a method that takes no arguments works, too.)
If you didn't create the timer using scheduledTimerWith…, add it to the run loop. (The scheduledTimerWith… methods do this for you.)
Create a run loop source and add it to the run loop.
Call [myRunLoop run].
Step 4 bears explaining:
Core Foundation (but not Foundation; I don't know why) has something called “run loop sources”, which are custom objects that can be added to a run loop and will call something once signaled.
Sounds like what you want, to call your view controller method!
First, in the view controller, change myThread from a local variable in viewDidLoad to an instance variable. Rename it _myThread to make that clear.
Next, add a delegate property to MyThread. This should be weak and have type id <MyThreadDelegate>. You'll also need to define a MyThreadDelegate protocol, which should have one method taking no arguments and returning nothing (void).
You should now be able to set _myThread.delegate = self from the view controller, and implement in the view controller the method that you declared in the protocol. The view controller is now the delegate of its MyThread.
In -[MyThread main], create a version-0 CFRunLoopSource. The Create function takes a “context” structure, containing, among other things, the version (0), an “info” pointer (set this to self, i.e., the MyThread) and a Perform callback (a function, which will be called with the info pointer as its only argument).
In your perform callback, you'll need to do something like this:
MyThread *self = (__bridge MyThread *)info;
[self fireDelegateMessage];
In MyThread, implement that fireDelegateMessage method. In there, send self.delegate the message you declared in your protocol.
Next, add a public method to MyThread (i.e., declare it in MyThread.h as well as implementing it in MyThread.m) named something like “requestDelegateMessage”. In this method, call CFRunLoopSourceSignal on the run loop source. (The documentation for that function suggests that you also need to call CFRunLoopWakeUp on the thread's CFRunLoop. Try it without first.)
Lastly, when the view controller wants someMethod to be called on that thread, call [_myThread requestDelegateMessage].
So:
the view controller calls requestDelegateMessage
requestDelegateMessage signals the run loop source (and wakes up the run loop, if that is needed)
the run loop source calls the perform callback on the MyThread's thread
the perform callback calls fireDelegateMessage on the MyThread's thread
fireDelegateMessage calls the view controller's implementation of the delegate method on the MyThread's thread
the view controller calls someMethod on the MyThread's thread

objective c block that waits for another delegate

I have a watchkit app that calls a viewcontroller on an iphone app. I have a delegate for a network connection. I'm trying to use a block so that I don't tightly couple my AppDelegate and my view controller too closely. How can I notify my block when the delegate is finished?
ViewController.m
-(void)getWatchDataWithCompletion:(void(^)(BOOL gotData))completion{
[self setUpAppForWatch];
completion(YES);
}
-(void)finishedMessageParse:(NSMutableData *)messageData{
//the delegate is finish tell the block completion is done.
}
-(void)setUpAppForWatch{
[network call];
}
AppDelegate.m
-(void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)
(NSDictionary *))reply{
[vc getWatchDataWithCompletion:^(BOOL gotData){
if (gotData){
//I'm done reply dictionary
reply(#{#"data":serlizedData})
}];
add new property in viewcontroller:
#property (nonatomic, strong) void(^completion)(BOOL gotData);
-(void)getWatchDataWithCompletion:(void(^)(BOOL gotData))completion{
[self setUpAppForWatch];
self.completion = completion;
}
-(void)finishedMessageParse:(NSMutableData *)messageData{
if (self.completion){
self.completion(YES);
}
}
There're three possible ways.
;tldr - refer to the third one. Else - read everything, it might be useful.
First one
Use private serial queue for performing tasks of finished... method and your block. It will suffice you in case, if finished... always called before block. If not - take a look at the Second one
Use private #property dispatch_queue_t privateSerialQueue; of View Controller.
privateSerialQueue = dispatch_queue_create("PrivateQueue", DISPATCH_QUEUE_SERIAL);
Than, use it like this
-(void)getWatchDataWithCompletion:(void(^)(BOOL gotData))completion{
[self setUpAppForWatch];
dispatch_async(privateSerialQueue, ^(){
completion(YES);
});
}
-(void)finishedMessageParse:(NSMutableData *)messageData{
dispatch_sync(privateSerialQueue, ^(void){
//Here goes whatever you need to do in this method before block start
});
//the delegate is finish tell the block completion is done.
}
Second one
Take a look at dispatch_semaphore_t. Make it a public property of your View Controler
#property (readonly) dispatch_semaphore_t semaphore
Create it with starting value 0. It will let you wait in case your block runs before delegate finished... method and run immediately, if finished has already completed before block. Like this
self.semaphore = dispatch_semaphore_create(0);
Then you can use it this way
-(void)finishedMessageParse:(NSMutableData *)messageData{
//the delegate is finish tell the block completion is done.
dispatch_semaphore_signal(self.semaphore);
}
[vc getWatchDataWithCompletion:^(BOOL gotData){
if (gotData){
//I'm done reply dictionary
dispatch_semaphore_wait(vc.semaphore, DISPATCH_TIME_FOREVER);
reply(#{#"data":serlizedData})
}];
Third one
Came to my mind while writing the two above =)
Some kind of combination of previous two
Use private property of your view controller
#property (readonly) dispatch_semaphore_t semaphore
Initialize it the same way, as in the second (with starting value 0)
self.semaphore = dispatch_semaphore_create(0);
And use it privately like this
-(void)getWatchDataWithCompletion:(void(^)(BOOL gotData))completion{
[self setUpAppForWatch];
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
completion(YES);
}
-(void)finishedMessageParse:(NSMutableData *)messageData{
//the delegate is finish tell the block completion is done.
dispatch_semaphore_signal(self.semaphore);
}
P. S. Hope, it helps you to get to the point. Feel free to ask anything not clear

XCTest exception when using keyValueObservingExpectationForObject:keyPath:handler:

In my unit tests, I am using the -[XCTestCase keyValueObservingExpectationForObject:keyPath:handler:] method in order to ensure that my NSOperation finishes, here is the code from my XCDYouTubeKit project:
- (void) testStartingOnBackgroundThread
{
XCDYouTubeVideoOperation *operation = [[XCDYouTubeVideoOperation alloc] initWithVideoIdentifier:nil languageIdentifier:nil];
[self keyValueObservingExpectationForObject:operation keyPath:#"isFinished" handler:^BOOL(id observedObject, NSDictionary *change)
{
XCTAssertNil([observedObject video]);
XCTAssertNotNil([observedObject error]);
return YES;
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
XCTAssertFalse([NSThread isMainThread]);
[operation start];
});
[self waitForExpectationsWithTimeout:5 handler:nil];
}
This test always passes when I run it locally on my Mac but sometimes it fails on Travis with this error:
failed: caught "NSRangeException", "Cannot remove an observer <_XCKVOExpectation 0x1001846c0> for the key path "isFinished" from <XCDYouTubeVideoOperation 0x1001b9510> because it is not registered as an observer."
Am I doing something wrong?
Your code is correct, you have found a bug in the XCTest framework. Here is an in depth explanation, you can skip to the end of this answer if you are just looking for a workaround.
When you call keyValueObservingExpectationForObject:keyPath:handler:, an _XCKVOExpectation object is created under the hood. It is responsible for observing the object/keyPath you passed. Once the KVO notification has fired, the _safelyUnregister method is called, this is where the observer is removed. Here is the (reverse engineered) implementation of the _safelyUnregister method.
#implementation _XCKVOExpectation
- (void) _safelyUnregister
{
if (!self.hasUnregistered)
{
[self.observedObject removeObserver:self forKeyPath:self.keyPath];
self.hasUnregistered = YES;
}
}
#end
This method is called once again at the end of waitForExpectationsWithTimeout:handler: and when the _XCKVOExpectation object is deallocated. Note that the operation terminates on a background thread but the test is run on the main thread. So you have a race condition: if _safelyUnregister is called on the main thread before the hasUnregistered property is set to YES on the background thread, the observer is removed twice, causing the Cannot remove an observer exception.
So in order to workaround this issue, you have to protect the _safelyUnregister method with a lock. Here is a code snippet for you to compile in your test target that will take care of fixing this bug.
#import <objc/runtime.h>
__attribute__((constructor)) void WorkaroundXCKVOExpectationUnregistrationRaceCondition(void);
__attribute__((constructor)) void WorkaroundXCKVOExpectationUnregistrationRaceCondition(void)
{
SEL _safelyUnregisterSEL = sel_getUid("_safelyUnregister");
Method safelyUnregister = class_getInstanceMethod(objc_lookUpClass("_XCKVOExpectation"), _safelyUnregisterSEL);
void (*_safelyUnregisterIMP)(id, SEL) = (__typeof__(_safelyUnregisterIMP))method_getImplementation(safelyUnregister);
method_setImplementation(safelyUnregister, imp_implementationWithBlock(^(id self) {
#synchronized(self)
{
_safelyUnregisterIMP(self, _safelyUnregisterSEL);
}
}));
}
EDIT
This bug has been fixed in Xcode 7 beta 4.

How do I wait for a method to finish before calling it again?

I am building a simple messaging app using Parse's framework. I have a method called displayMessages. This is called each time the phone receives a push.
However, as this message is doing work in the Parse database I don't want to call it again if it's already running. I want to wait until it is finished and then call it.
I am using the following code:
-(void)receivedPush
{
[self displayMessages];
}
and:
-(void)displayMessages
{
//code here
}
If received push is called I want it to wait until displayMessages is finished before calling it. Could someone please point me in the right direction with this?
UPDATE
I tried using the NSOperationQueue method and realised that although this does work for waiting for displayMessages it doesn't result in the required behavior.
In displayMessages I have: [PFObject deleteAllInBackground:toDelete]; it's actually this I need to wait for completion before calling displayMessages again.
Create a NSOperationQueue and set the maxConcurrentOperationCount to 1. Implement your data access method as an operation (possibly block-type operation) and submit it to the queue. (I like this better than gcd since you can do cancellation or test the number of items already in the queue.)
Note that if the method actually displays things, you'll need to dispatch back to the main queue for UI work.
You could use a NSOperationQueue with maxConcurrentOperationCount set to 1.
Declare the NSOperationQueue as an iVar of your class, initialize it in the init method and set
[_opQueue setMaxConcurrentOperationCount:1];
and then when you receive the push:
- (void)receivedPush {
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(displayMessages) object:nil];
[_opQueue addOperation:op];
}
Shortest and simples would be creating BOOL isExecuting and checking if you can call method based on that (changing values before execution but after check and after execution)
How about this for a fairly lightweight solution:
#property (nonatomic, assign) BOOL needsToDisplayMessages;
#property (nonatomic, assign) BOOL displayingMessages;
Then
-(void)receivedPush
{
if (!self.displayingMessages) {
[self displayMessages];
} else {
self.needsToDisplayMessages = YES;
}
}
-(void)displayMessages
{
self.needsToDisplayMessages = NO;
self.displayingMessages = YES;
//long-running code here
self.displayingMessages = NO;
if (self.needsToDisplayMessages) {
[self displayMessages]
}
(ignoring concurrency issues ... for which you could use GCD in displayMessages or NSOperationQueue as per a couple of the other answers)
With your new updated requirement, you can use deleteAllInBackground:block:. According to document:
"Deletes a collection of objects all at once asynchronously and executes the block when done."
Why not schedule each message handling using:
-(void)receivedPush
{
dispatch_async(dispatch_get_main_queue(), ^{
/* Show the update on the display */
NSLog(#"Handling new messages");
NSArray *newMessages=<populate with new messages>;
[handler displayMessages:newMessages];
});
}
This will queue up your handling of each set as they come in. Only one displayMessages will run at a time.

Resources