Waiting for an external event before continue in unit test - unit-testing

Context:
I'm writing unit test for a gRPC service. I want to verify that the method of the mock on the server side is called. I'm using easy mock. To be sure we get the response of gRPC (whatever it is) I need to suspend the thread before easy mock verify the calls.
So I tried something like this using LockSupport:
#Test
public void alphaMethodTest() throws Exception
{
Dummy dummy = createNiceMock(Dummy.class);
dummy.alphaMethod(anyBoolean());
expectLastCall().once();
EasyMock.replay(dummy);
DummyServiceGrpcImpl dummyServiceGrpc = new DummyServiceGrpcImpl();
bcreuServiceGrpc.setDummy(dummy);
DummyServiceGrpc.DummyServiceStub stub = setupDummyServiceStub();
Thread thread = Thread.currentThread();
stub.alphaMethod(emptyRequest, new StreamObserver<X>(){
#Override
public void onNext(X value) {
LockSupport.unpark(thread);
}
}
Instant expirationTime = Instant.now().plus(pDuration);
LockSupport.parkUntil(expirationTime.toEpochMilli());
verify(dummy);
}
But I have many tests like this one (around 40) and I suspect threading issue. I usually get one or two failing the verify step, sometime all of them pass. I try to use a ReentrantLock with Condition instead. But again some are failing (IllegalMonitorStateException on the signalAll):
#Test
public void alphaMethodTest() throws Exception
{
Dummy dummy = createNiceMock(Dummy.class);
dummy.alphaMethod(anyBoolean());
expectLastCall().once();
EasyMock.replay(dummy);
DummyServiceGrpcImpl dummyServiceGrpc = new DummyServiceGrpcImpl();
bcreuServiceGrpc.setDummy(dummy);
DummyServiceGrpc.DummyServiceStub stub = setupDummyServiceStub();
ReentrantLock lock = new ReentrantLock();
Condition conditionPromiseTerminated = lock.newCondition();
stub.alphaMethod(emptyRequest, new StreamObserver<X>(){
#Override
public void onNext(X value) {
conditionPromiseTerminated.signalAll();
}
}
Instant expirationTime = Instant.now().plus(pDuration);
conditionPromiseTerminated.awaitUntil(new Date(expirationTime.toEpochMilli()));
verify(dummy);
}
I'm sorry not providing runnable example for you, my current code is using a private API :/.
Do you think LockSupport may cause trouble because of the multiple tests running? Am I missing something using lock support or reentrant lock. Do you think of any other class of the concurrent API that would suit better my needs?

LockSupport is a bit dangerous, you will need to read the documentation closely and find out that:
The call spuriously (that is, for no reason) returns.
So when you think your code will do some "waiting", it might simply return immediately. The simplest reason for that would be this for example, but there could be other reasons too.
When using ReentrantLock, all of them should fail with IllegalMonitorStateException, because you never acquire the lock via ReentrantLock::lock. And stop using new Date(...), it is deprecated for a reason.
I think you are over-complicating things, you could do the same signaling with a plain lock, a simplified example:
public static void main(String[] args) {
Object lock = new Object();
Thread first = new Thread(() -> {
synchronized (lock) {
System.out.println("Locked");
try {
System.out.println("Sleeping");
lock.wait();
System.out.println("Waked up");
} catch (InterruptedException e) {
// these are your tests, no one should interrupt
// unless it's yourself
throw new RuntimeException(e);
}
}
});
first.start();
sleepOneSecond();
Thread second = new Thread(() -> {
synchronized (lock) {
System.out.println("notifying waiting threads");
lock.notify();
}
});
second.start();
}
private static void sleepOneSecond() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Notice the output:
Locked
Sleeping
notifying waiting threads
Waked up
It should be obvious how the "communication" (signaling) between threads happens.

Related

Kotlin runTest with delay() is not working

I am testing a coroutine that blocks. Here is my production code:
interface Incrementer {
fun inc()
}
class MyViewModel : Incrementer, CoroutineScope {
override val coroutineContext: CoroutineContext
get() = Dispatchers.IO
private val _number = MutableStateFlow(0)
fun getNumber(): StateFlow<Int> = _number.asStateFlow()
override fun inc() {
launch(coroutineContext) {
delay(100)
_number.tryEmit(1)
}
}
}
And my test:
class IncTest {
#BeforeEach
fun setup() {
Dispatchers.setMain(StandardTestDispatcher())
}
#AfterEach
fun teardown() {
Dispatchers.resetMain()
}
#Test
fun incrementOnce() = runTest {
val viewModel = MyViewModel()
val results = mutableListOf<Int>()
val resultJob = viewModel.getNumber()
.onEach(results::add)
.launchIn(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
launch(StandardTestDispatcher(testScheduler)) {
viewModel.inc()
}.join()
assertEquals(listOf(0, 1), results)
resultJob.cancel()
}
}
How would I go about testing my inc() function? (The interface is carved in stone, so I can't turn inc() into a suspend function.)
There are two problems here:
You want to wait for the work done in the coroutine that viewModel.inc() launches internally.
Ideally, the 100ms delay should be fast-forwarded during tests so that it doesn't actually take 100ms to execute.
Let's start with problem #2 first: for this, you need to be able to modify MyViewModel (but not inc), and change the class so that instead of using a hardcoded Dispatchers.IO, it receives a CoroutineContext as a parameter. With this, you could pass in a TestDispatcher in tests, which would use virtual time to fast-forward the delay. You can see this pattern described in the Injecting TestDispatchers section of the Android docs.
class MyViewModel(coroutineContext: CoroutineContext) : Incrementer {
private val scope = CoroutineScope(coroutineContext)
private val _number = MutableStateFlow(0)
fun getNumber(): StateFlow<Int> = _number.asStateFlow()
override fun inc() {
scope.launch {
delay(100)
_number.tryEmit(1)
}
}
}
Here, I've also done some minor cleanup:
Made MyViewModel contain a CoroutineScope instead of implementing the interface, which is an officially recommended practice
Removed the coroutineContext parameter passed to launch, as it doesn't do anything in this case - the same context is in the scope anyway, so it'll already be used
For problem #1, waiting for work to complete, you have a few options:
If you've passed in a TestDispatcher, you can manually advance the coroutine created inside inc using testing methods like advanceUntilIdle. This is not ideal, because you're relying on implementation details a lot, and it's something you couldn't do in production. But it'll work if you can't use the nicer solution below.
viewModel.inc()
advanceUntilIdle() // Returns when all pending coroutines are done
The proper solution would be for inc to let its callers know when it's done performing its work. You could make it a suspending method instead of launching a new coroutine internally, but you stated that you can't modify the method to make it suspending. An alternative - if you're able to make this change - would be to create the new coroutine in inc using the async builder, returning the Deferred object that that creates, and then await()-ing at the call site.
override fun inc(): Deferred<Unit> {
scope.async {
delay(100)
_number.tryEmit(1)
}
}
// In the test...
viewModel.inc().await()
If you're not able to modify either the method or the class, there's no way to avoid the delay() call causing a real 100ms delay. In this case, you can force your test to wait for that amount of time before proceeding. A regular delay() within runTest would be fast-forwarded thanks to it using a TestDispatcher for the coroutine it creates, but you can get away with one of these solutions:
// delay() on a different dispatcher
viewModel.inc()
withContext(Dispatchers.Default) { delay(100) }
// Use blocking sleep
viewModel.inc()
Thread.sleep(100)
For some final notes about the test code:
Since you're doing Dispatchers.setMain, you don't need to pass in testScheduler into the TestDispatchers you create. They'll grab the scheduler from Main automatically if they find a TestDispatcher there, as described in its docs.
Instead of creating a new scope to pass in to launchIn, you could simply pass in this, the receiver of runTest, which points to a TestScope.

Deadlock instead of an exception when calling get() from the inside FutureTask.run()

Recently, due to an error in my code, I created a recursive call (infinite loop, in fact). One of the methods involved in the loop used a pre-computed task. It can be summarized as:
ConcurrentHashMap<String, FutureTask<Integer>> cache = new ConcurrentHashMap<>();
int method1() {
var task = cache.computeIfAbsent("key", k -> new FutureTask<>(this::method2));
task.run();
try {
return task.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
int method2() {
if (1 + 1 != 4) { // this wasn't so obvious of course...
return method1();
}
return 1;
}
In short: FutureTask's get() was called while inside its run() execution.
To my surprise, it resulted in a deadlock, as get() makes no checks and parks the current thread unconditionally.
I know I shot myself in the foot with low-level primitives, but is there a reason or use case why get() doesn't make a check and
throw an exception when called from the same thread as the runner thread of the task?

In UWP application, future.wait() keep waiting while trying to synchronize the response from async methods

I am working on developing an UWP application which would load the file from Application local data on click of a Button. For this, I need the StorageFolder object for Application LocalFolder using StorageFolder::GetFolderFromPathAsync() method then i will have to use GetFileAsync() method to read the StorageFile object to read.
I have written the templates to wait for the response from async methods like GetFolderFromPathAsync(), GetFileAsync(), etc. before proceeding.
template <typename T>
T syncAsyncTask(concurrency::task<T> mainTask) {
std::shared_ptr<std::promise<T>> done = std::make_shared<std::promise<T>>();
auto future = done->get_future();
asyncTaskExceptionHandler<T>(mainTask, [&done](bool didFail, T result) {
done->set_value(didFail ? nullptr : result);
});
future.wait();
return future.get();
}
template <typename T, typename CallbackLambda>
void asyncTaskExceptionHandler(concurrency::task<T> mainTask, CallbackLambda&& onResult) {
auto t1 = mainTask.then([onResult = std::move(onResult)](concurrency::task<T> t) {
bool didFail = true;
T result;
try {
result = t.get();
didFail = false;
}
catch (concurrency::task_canceled&) {
OutputDebugStringA("Win10 call was canceled.");
}
catch (Platform::Exception^ e) {
OutputDebugStringA("Error during a Win10 call:");
}
catch (std::exception&) {
OutputDebugStringA("There was a C++ exception during a Win10 call.");
}
catch (...) {
OutputDebugStringA("There was a generic exception during a Win10 call.");
}
onResult(didFail, result);
});
}
Issue :
When i call syncAsyncTask() method with any task to get
its response, it keeps waiting at future.wait() as mainTask never
complete and promise never set its value.
See below code :
void testStorage::MainPage::Btn_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
Windows::Storage::StorageFolder^ localFolder = Windows::Storage::ApplicationData::Current->LocalFolder;
auto task = concurrency::create_task(Windows::Storage::StorageFolder::GetFolderFromPathAsync(localFolder->Path));
auto folder = syncAsyncTask<Windows::Storage::StorageFolder^>(task);
printString(folder->Path);
}
void printString(Platform::String^ text) {
std::wstring fooW(text->Begin());
std::string fooA(fooW.begin(), fooW.end());
const char* charStr = fooA.c_str();
OutputDebugStringA(charStr);
}
Running environment :
VS2017
Tried with C++14 and C++17, facing same issue.
Windows 10 RS5 Build#17763
Has anyone ever faced this issue?
Please help!! Thanks in advance.
I was able to take the above code and create a simple application that reproduced this issue. Long story short, I was able to get future.wait() to return by telling the continuation in asyncTaskExceptionHandler to run on a background thread:
template <typename T, typename CallbackLambda>
void asyncTaskExceptionHandler(concurrency::task<T> mainTask, CallbackLambda&& onResult) {
// debug
printString(mainTask.is_apartment_aware().ToString());
auto t1 = mainTask.then([onResult = std::move(onResult)](concurrency::task<T> t) {
bool didFail = true;
T result;
try {
result = t.get();
didFail = false;
}
catch (concurrency::task_canceled&) {
OutputDebugStringA("Win10 call was canceled.");
}
catch (Platform::Exception^ e) {
OutputDebugStringA("Error during a Win10 call:");
}
catch (std::exception&) {
OutputDebugStringA("There was a C++ exception during a Win10 call.");
}
catch (...) {
OutputDebugStringA("There was a generic exception during a Win10 call.");
}
// It works with this
}, concurrency::task_continuation_context::use_arbitrary());
}
Assuming the code I used was correct, what I believe to be happening is that we created a deadlock. What we are saying with the above code is:
On the UI/STA thread, create/handle an async operation from GetFolderFromPathAsync
Pass this task off to our syncAsyncTask, which in turn passes this off to asyncTaskExceptionHandler.
asyncTaskExceptionHandler adds a continuation to this task which schedules it to run. By default, tasks run on the thread that called them. In this case, it is the UI/STA thread!
Once the thread is scheduled, we return back to syncAsyncTask to finish. After our call to asyncTaskExceptionHandler we have a future.wait() which blocks until the promise value is set.
This prevents our UI thread from finished execution of the syncAsyncTask, but also prevents our continuation from running since it is scheduled to run on the same thread that is blocking!
In other words, we are waiting on the UI thread for an operation to complete that cannot begin until the UI thread is finished, thus causing our deadlock.
By using concurrency::task_continuation_context::use_arbitrary() we tell the task that it's okay to use a background thread if necessary (which in this case it is) and everything completes as intended.
For documentation on this, as well as some example code illustrating async behavior, see the Creating Asynchronous Operations in C++ for UWP Apps documentation.

Ensuring that only one instance of a function is running?

I'm just getting into concurrent programming. Most probably my issue is very common, but since I can't find a good name for it, I can't google it.
I have a C++ UWP application where I try to apply MVVM pattern, but I guess that the pattern or even being UWP is not relevant.
First, I have a service interface that exposes an operation:
struct IService
{
virtual task<int> Operation() = 0;
};
Of course, I provide a concrete implementation, but it is not relevant for this discussion. The operation is potentially long-running: it makes an HTTP request.
Then I have a class that uses the service (again, irrelevant details omitted):
class ViewModel
{
unique_ptr<IService> service;
public:
task<void> Refresh();
};
I use coroutines:
task<void> ViewModel::Refresh()
{
auto result = co_await service->Operation();
// use result to update UI
}
The Refresh function is invoked on timer every minute, or in response to a user request. What I want is: if a Refresh operation is already in progress when a new one is started or requested, then abandon the second one and just wait for the first one to finish (or time out). In other words, I don't want to queue all the calls to Refresh - if a call is already in progress, I prefer to skip a call until the next timer tick.
My attempt (probably very naive) was:
mutex refresh;
task<void> ViewModel::Refresh()
{
unique_lock<mutex> lock(refresh, try_to_lock);
if (!lock)
{
// lock.release(); commented out as harmless but useless => irrelevant
co_return;
}
auto result = co_await service->Operation();
// use result to update UI
}
Edit after the original post: I commented out the line in the code snippet above, as it makes no difference. The issue is still the same.
But of course an assertion fails: unlock of unowned mutex. I guess that the problem is the unlock of mutex by unique_lock destructor, which happens in the continuation of the coroutine and on a different thread (other than the one it was originally locked on).
Using Visual C++ 2017.
use std::atomic_bool:
std::atomic_bool isRunning = false;
if (isRunning.exchange(true, std::memory_order_acq_rel) == false){
try{
auto result = co_await Refresh();
isRunning.store(false, std::memory_order_release);
//use result
}
catch(...){
isRunning.store(false, std::memory_order_release);
throw;
}
}
Two possible improvements : wrap isRunning.store in a RAII class and use std::shared_ptr<std::atomic_bool> if the lifetime if the atomic_bool is scoped.

Unit testing a mutex lock

Our business has recently moved to a TDD style and I'm new at writing unit tests. The C# (.net 3.5) piece I'm writing now should be able to verify a separate process is running, as I understand it the best way using the Mutex class.
So I have a method in my SrsUpdaterController class like so...
public bool IsUpdaterRunning()
{
Mutex srsUpdaterMutex = new Mutex(false, SRS_UPDATERGUID);
if (srsUpdaterMutex.WaitOne(0)) //If SRS Updater is running
{
srsUpdaterMutex.ReleaseMutex();
srsUpdaterMutex.Close();
return false;
}
else
{
return true;
}
}
and I have a test
[TestMethod()]
public void IsUpdaterRunningTrueTest()
{
SrsUpdaterController target = new SrsUpdaterController();
string mutexGuid = SrsUpdaterController.SRS_UPDATERGUID;
bool expected = true;
bool actual;
Mutex srsUpdaterMutex = new Mutex(false, mutexGuid);
srsUpdaterMutex.WaitOne(3000);
actual = target.IsUpdaterRunning();
srsUpdaterMutex.ReleaseMutex();
srsUpdaterMutex.Close();
Assert.AreEqual(expected, actual);
}
It doesn't work because the unit test and the IsUpdaterRunning method are called by the same thread, and so Windows is "smart" enough to not make the process block its self. The problem is I WANT the process to block its self so that it simulates the mutex being claimed. Is there any way to do this? Or am I approaching unit testing/process synchronization/mutex management all wrong?
(note, I did also try locking the Mutex on a separate thread launched from the test, but it still allowed me to claim the mutex in both places. Which is ok because I'd rather avoid threads when possible)
Thanks in advance!