public final async function runTestsAsync()

in src/Framework/HackTest.hack [51:230]


  public final async function runTestsAsync(
    (function(\ReflectionMethod): bool) $method_filter,
    (function(ProgressEvent): Awaitable<void>) $progress_callback,
  ): Awaitable<void> {
    $progress = new _Private\Progress(
      $progress_callback,
      $this->filename,
      static::class,
    );

    await static::beforeFirstTestAsync();
    await using new _Private\OnScopeExitAsync(
      async () ==> await static::afterLastTestAsync(),
    );

    foreach ($this->methods as $method) {
      $to_run = vec[];
      if (!$method_filter($method)) {
        continue;
      }

      $this->clearExpectedException();
      $exception = $method->getAttribute('ExpectedException');
      if ($exception !== null) {
        $exception_message = $method->getAttribute('ExpectedExceptionMessage');
        $msg = null;
        $code = null;
        if ($exception_message !== null) {
          $msg = (string)C\onlyx($exception_message);
        }
        $exception_code = $method->getAttribute('ExpectedExceptionCode');
        if ($exception_code !== null) {
          $code = (string)C\onlyx($exception_code);
        }
        $this->setExpectedException((string)C\onlyx($exception), $msg, $code);
      }

      $providers = vec[];
      $provider = $method->getAttributeClass(DataProvider::class)?->provider;
      if ($provider is nonnull) {
        $providers[] = $provider;
      }

      $method_name = $method->getName();
      if (C\is_empty($providers)) {
        /* HH_IGNORE_ERROR[2011] this is unsafe */
        $to_run[] = tuple($method_name, null, () ==> $this->$method_name());
      } else {
        if (C\count($providers) > 1) {
          throw new InvalidTestMethodException(
            Str\format(
              'There can only be one data provider in %s',
              $method_name,
            ),
          );
        }
        await $progress->invokingDataProviderAsync($method_name);
        $provider = C\onlyx($providers);
        await $this->beforeEachTestAsync();
        $this->setUpNeeded = false;
        try {
          if (Str\contains($provider, '::')) {
            list($class, $method) = Str\split($provider, '::', 2);
            $rm = new \ReflectionMethod($class, $method);
            $tuples = $rm->invoke(null);
          } else {
            $rm = new \ReflectionMethod($this, $provider);
            if ($rm->isStatic()) {
              $tuples = $rm->invoke(null);
            } else {
              $tuples = $rm->invoke($this);
            }
          }
          if ($tuples is Awaitable<_>) {
            $tuples = await $tuples;
          }
          $tuples = $tuples as KeyedContainer<_, _>;
          if (C\is_empty($tuples)) {
            throw new InvalidDataProviderException(
              Str\format(
                'This test depends on a provider (%s) that returns no data.',
                $provider,
              ),
            );
          }
        } catch (\Throwable $e) {
          await $this->afterEachTestAsync();
          await $progress->testFinishedWithExceptionAsync($provider, null, $e);
          continue;
        }

        foreach ($tuples as $idx => $tuple) {
          $tuple = vec($tuple as Traversable<_>);
          $to_run[] = tuple(
            $method_name,
            tuple($idx as arraykey, $tuple as Container<_>),
            /* HH_IGNORE_ERROR[2011] this is unsafe */
            () ==> $this->$method_name(...$tuple),
          );
        }
      }

      foreach ($to_run as list($method, $data_provider_row, $runnable)) {
        await $progress->testStartingAsync($method, $data_provider_row);
        if ($this->setUpNeeded) {
          await $this->beforeEachTestAsync();
        } else {
          $this->setUpNeeded = true;
        }
        if ($exception === null) {
          $this->clearExpectedException();
        }
        $clean = false;
        try {
          $res = $runnable();
          if ($res is Awaitable<_>) {
            await $res;
          }
          /* HH_IGNORE_ERROR[6002] this is used in catch block */
          $clean = true;
          await $this->afterEachTestAsync();
          if ($this->expectedException !== null) {
            throw new ExpectationFailedException(
              Str\format(
                'Failed asserting that %s was thrown',
                $this->expectedException,
              ),
            );
          }
          await $progress->testPassedAsync($method, $data_provider_row);
        } catch (\Throwable $e) {
          if (!$clean) {
            await $this->afterEachTestAsync();
          }
          $pass = false;
          if (
            $this->expectedException !== null &&
            !($e is SkippedTestException) &&
            \is_a($e, $this->expectedException)
          ) {
            $pass = true;
            $message = (string)$e->getMessage();
            $expected_message = (string)$this->expectedExceptionMessage;
            if (!Str\contains($message, $expected_message)) {
              $e = new ExpectationFailedException(
                Str\format(
                  'Failed asserting that the exception message \'%s\' contains \'%s\'',
                  $e->getMessage(),
                  $expected_message,
                ),
              );
              $pass = false;
            } else if (
              $this->expectedExceptionCode !== null &&
              $this->expectedExceptionCode !== $e->getCode()
            ) {
              $exception_code = (int)$this->expectedExceptionCode;
              $e = new ExpectationFailedException(
                Str\format(
                  'Failed asserting that the exception code %d is equal to %d',
                  (int)$e->getCode(),
                  $exception_code,
                ),
              );
              $pass = false;
            }
          }
          if ($pass) {
            await $progress->testPassedAsync($method, $data_provider_row);
          } else {
            await $progress->testFinishedWithExceptionAsync(
              $method,
              $data_provider_row,
              $e,
            );
          }
        }
      }
    }
  }