matt tighe
01/11/2019, 7:07 PM@Test
    fun `Submits app selection if selections can be made`() {
        mainActivityViewModel.submitAppSelection(selectedApp)
        runBlocking {
            verify(mockAppsStartupFsm).submitEvent(AppSelected(selectedApp))
        }
    }
class MainActivityViewModel {
    fun submitAppSelection(app: App) {
        if (!selectionsCanBeMade()) return
        lastSelectedApp = app
        val coroutineScope = CoroutineScope(Dispatchers.Default)
        coroutineScope.launch { appsStartupFsm.submitEvent(AppSelected(app)) }
    }
}
AppsStartupFsm#submitEvent is a suspending function.
Changing the runBlocking scope to be function level doesn’t seem to help. Is there something I’m missing with verification of suspending functions?matt tighe
01/11/2019, 10:08 PMmatt tighe
01/11/2019, 10:09 PMgroostav
01/11/2019, 10:10 PMgroostav
01/11/2019, 10:10 PMsubmitEvent call to ensure its gone off.
As the untaggable Allen Wang suggested, you could return a `Job?`:
fun submitAppSelection(app: App): Job? {
  if (!selectionsCanBeMade()) return null
  return launch { app.submitEvent(AppSelected) }
}
then, in your test, call mainActivity.submit()?.join()matt tighe
01/11/2019, 10:12 PMmatt tighe
01/11/2019, 10:13 PMdelay for some short amount in the test to ensure that the coroutine is launched?groostav
01/11/2019, 10:13 PMmatt tighe
01/11/2019, 10:14 PMgroostav
01/11/2019, 10:14 PMlaunch(CoroutineDispatchers.Unconfined)matt tighe
01/11/2019, 10:14 PMgroostav
01/11/2019, 10:14 PMunconfined will cause the launch block to start immediatley, and only return back to the caller of launch after the first suspension pointgroostav
01/11/2019, 10:14 PMis the way you’re solving the issue of parallelism just by using the main dispatcher in both places?Exactly, through a great deal of effort I can honeslty say this is the best approach. Start off writing single-threaded concurrent code. Parallelize when you need the performance.
that’s kind of out of the question, as that suspension function sometimes does work that cannot be handled on Android’s main threadthis is really odd. This style of suspend-funs-may-return-fast is in C# but was intentionally made more difficult (ie removed) in kotlinx because of the subtle concurrency bugs it can introduce. Typically we statically know if you will suspend or not, the runtime does not get a say in that.
groostav
01/11/2019, 10:15 PMgroostav
01/11/2019, 10:15 PMsubmitEvent function, is it blocking or is it suspending? Is that an android API call?matt tighe
01/11/2019, 10:16 PMgroostav
01/11/2019, 10:16 PMmatt tighe
01/11/2019, 10:17 PMsubmitEvent is technically marked as a suspending function, but it never actually suspends anything. i guess i’ve been using the suspend modifier to also indicate functions that need to happen off the main threadmatt tighe
01/11/2019, 10:17 PMgroostav
01/11/2019, 10:17 PMJob? is a good quick fix, theres a bunch of places in my code where I've had to do that, particularly when old java is involved. But I think you might be better off doing a withContext and just making submitAppSelection tagged as suspendmatt tighe
01/11/2019, 10:18 PMgroostav
01/11/2019, 10:20 PMmatt tighe
01/11/2019, 10:22 PMmatt tighe
01/11/2019, 10:22 PMsubmitEvent returns (not suspends) will submitAppSelection return.`groostav
01/11/2019, 10:26 PMwithContext never does anything in parallel, it will return only when the block inside it returns. Thus your function is made synchronous. It suspends, and it does not hold on to the Main thread because the thing that might block (the submitEvent) call is not on the MainThread, its on a worker thread.groostav
01/11/2019, 10:29 PMprintln("before withContext!")
withContext(Dispatchers.Anything){
  println("before submitEvent!")
  submitEvent()
  println("after submitEvent!")
}
println("after withContext!")
you would only ever see the exact sequence
before withContext!
before submitEvent!
after submitEvent!
after withContext!groostav
01/11/2019, 10:35 PMI think you should follow thatis technically marked as a suspending function, but it never actually suspends anything. i guess i’ve been using the suspend modifier to also indicate functions that need to happen off the main threadsubmitEvent
suspend up with a withContext. This is exactly what I do with my database facade.
class SuspendingTable {
  suspend fun get(id: UUID): T   {
    return withContext(<http://Dispatchers.IO|Dispatchers.IO> + Trace){
      backingDb.getBlocking(id)
    }
  }
}matt tighe
01/11/2019, 10:40 PMsubmitEvent function with withContext(Dispatchers.Default) i can also remove the withContext call from submitAppSelection, correct?