@Override
  public <T> ListenableFuture<PluginExecutionResult<T>> executeAsync(
      PluginExecutorConfig<T> executorConfig) {
    // Executes the core plugin logic within the thread pool.
    return FluentFuture.from(
            pluginExecutionThreadPool.submit(
                () -> {
                  executionStopwatch.start();
                  return executorConfig.pluginExecutionLogic().call();
                }))
        // Terminate plugin if it runs over 1 hour.
        .withTimeout(Duration.ofHours(1), pluginExecutionThreadPool)
        // If execution succeeded, build successful execution result.
        .transform(resultData -> buildSucceededResult(resultData, executorConfig), directExecutor())
        // If execution failed, build failed execution result.
        .catching(
            Throwable.class,
            exception -> buildFailedResult(exception, executorConfig),
            directExecutor());
  }
@Test
  public void executeAsync_whenFailedWithUnknownException_returnsFailedResult()
      throws ExecutionException, InterruptedException {
    PluginExecutorConfig<String> executorConfig =
        PluginExecutorConfig.<String>builder()
            .setMatchedPlugin(FAKE_MATCHING_RESULT)
            .setPluginExecutionLogic(
                () -> {
                  throw new RuntimeException("test unknown exception");
                })
            .build();

    PluginExecutionResult<String> executionResult =
        new PluginExecutorImpl(PLUGIN_EXECUTION_THREAD_POOL, executionStopWatch)
            .executeAsync(executorConfig)
            .get();

    assertThat(executionResult.exception()).isPresent();
    assertThat(executionResult.exception().get())
        .hasCauseThat()
        .isInstanceOf(RuntimeException.class);
    assertThat(executionResult.exception().get())
        .hasMessageThat()
        .contains(
            String.format("Plugin execution error on '%s'.", FAKE_MATCHING_RESULT.pluginId()));
    assertThat(executionResult.isSucceeded()).isFalse();
    assertThat(executionResult.executionStopwatch().elapsed()).isEqualTo(TICK_DURATION);
    assertThat(executionResult.resultData()).isEmpty();
  }