logErrorFirst = (func, virtualT, args, source) => {
  let result;
  try {
    result = apply(func, undefined, [virtualT, ...args]);
  } catch (err) {
    virtualT.log(`THROWN from ${source}:`, err);
    throw err;
  }
  if (isPromise(result)) {
    return result.then(
      v => v,
      reason => {
        virtualT.log(`REJECTED from ${source}:`, reason);
        return result;
      },
    );
  } else {
    return result;
  }
}
Calls `func(virtualT, ...args)` passing back approximately its outcome,
but first logging any erroneous outcome to the `virtualT.log`.

   * If `func(...)` returns a non-promise, silently return it.
   * If `func(...)` throws, log what was thrown and then rethrow it.
   * If `func(...)` returns a promise, immediately return a new
     unresolved promise.
      * If the first promise fulfills, silently fulfill the returned promise
        even if the fulfillment was an error.
      * If the first promise rejects, log the rejection reason and then
        reject the returned promise with the same reason.

The delayed rejection of the returned promise is an observable difference
from directly calling `func(...)` but will be equivalent enough for most
testing purposes.

@param {(
  t: import('ava').ExecutionContext,
  ...args: unknown[]
) => unknown} func
@param {import('ava').ExecutionContext} virtualT
@param {unknown[]} args
@param {string} source