Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this test using FakeAsync just hang on the "await" even though the future has completed?

I'm trying to write a test using FakeAsync but it seems to hang on my awaits. Here's a stripped down example:

  test('danny', () async {
    await FakeAsync().run((FakeAsync async) async {
      print('1');
      final a = Future<bool>.delayed(const Duration(seconds: 5))
          .then((_) => print('Delayed future completed!'))
          .then((_) => true);

      print('2');
      async.elapse(const Duration(seconds: 30));
      // Tried all this too...
      // async.flushMicrotasks();
      // async.flushTimers();
      // async.elapse(const Duration(seconds: 30));
      // async.flushMicrotasks();
      // async.flushTimers();
      // async.elapseBlocking(const Duration(seconds: 30));

      print('3');
      await a;

      print('4');
      expect(1, 2);
    });
  });

This code outputs:

1
2
Delayed future completed!
3
// hangs and never prints '4'

The async.elapse call is allowing the future to be completed, but it still hangs on await a. Why?

like image 338
Danny Tuppeny Avatar asked Jun 30 '20 11:06

Danny Tuppeny


1 Answers

This seems to occur because although the Future is completed, the await call requires the microtask queue to be processed in order to continue (but it can't, since nobody is calling async.elapse after the await).

As a workaround, contiually pumping the microstask queue while the function is running seems to work - for example calling this function in place of FakeAsync.run:

/// Runs a callback using FakeAsync.run while continually pumping the
/// microtask queue. This avoids a deadlock when tests `await` a Future
/// which queues a microtask that will not be processed unless the queue
/// is flushed.
Future<T> runFakeAsync<T>(Future<T> Function(FakeAsync time) f) async {
    return FakeAsync().run((FakeAsync time) async {
    bool pump = true;
    final Future<T> future = f(time).whenComplete(() => pump = false);
    while (pump) {
        time.flushMicrotasks();
    }
    return future;
    }) as Future<T>;
}
like image 84
Danny Tuppeny Avatar answered Oct 23 '22 02:10

Danny Tuppeny