nicojs / typed-inject

Type safe dependency injection for TypeScript
Apache License 2.0
448 stars 23 forks source link

Add a way to dispose all child injectors #24

Closed nicojs closed 4 years ago

nicojs commented 4 years ago

Until now, a Injector does not store references to its child instances. We might want to add support for that in order to support more complex scenario's where you would want to dispose the entire dependency injection tree all at once.

An example from the Stryker source code:

    let mutantInstrumenterInjector: Injector<MutantInstrumenterContext> | undefined;
    let dryRunExecutorInjector: Injector<DryRunContext> | undefined;
    let mutationRunExecutorInjector: Injector<MutationTestContext> | undefined;
    try {
      // 1. Prepare. Load Stryker configuration, load the input files and starts the logging server
      const prepareExecutor = this.injector.provideValue(coreTokens.cliOptions, this.cliOptions).injectClass(PrepareExecutor);
      mutantInstrumenterInjector = await prepareExecutor.execute();

      // 2. Mutate and instrument the files and write to the sandbox.
      const mutantInstrumenter = mutantInstrumenterInjector.injectClass(MutantInstrumenterExecutor);
      dryRunExecutorInjector = await mutantInstrumenter.execute();

      // 3. Perform a 'dry run' (initial test run). Runs the tests without active mutants and collects coverage.
      const dryRunExecutor = dryRunExecutorInjector.injectClass(DryRunExecutor);
      mutationRunExecutorInjector = await dryRunExecutor.execute();

      // 4. Actual mutation testing. Will check every mutant and if valid run it in an available test runner.
      const mutationRunExecutor = mutationRunExecutorInjector.injectClass(MutationTestExecutor);
      const mutantResults = await mutationRunExecutor.execute();
      return mutantResults;
    } finally {
      if (mutationRunExecutorInjector) {
        await mutationRunExecutorInjector.dispose();
      } else if (dryRunExecutorInjector) {
        await dryRunExecutorInjector.dispose();
      } else if (mutantInstrumenterInjector) {
        await mutantInstrumenterInjector.dispose();
      }
    }

We provide the Injector in each executor which builds upon it and returns it. In the finally clause, where we do clean up, we need the reference of the deepest child injector, since that would dispose of other injectors.

Suggestion:

   const rootInjector = new RootInjector();
    try {
      // 1. Prepare. Load Stryker configuration, load the input files and starts the logging server
      const prepareExecutor = rootInjector.provideValue(coreTokens.cliOptions, this.cliOptions).injectClass(PrepareExecutor);
      const mutantInstrumenterInjector = await prepareExecutor.execute();

      // 2. Mutate and instrument the files and write to the sandbox.
      const mutantInstrumenter = mutantInstrumenterInjector.injectClass(MutantInstrumenterExecutor);
      const dryRunExecutorInjector = await mutantInstrumenter.execute();

      // 3. Perform a 'dry run' (initial test run). Runs the tests without active mutants and collects coverage.
      const dryRunExecutor = dryRunExecutorInjector.injectClass(DryRunExecutor);
      cosnt mutationRunExecutorInjector = await dryRunExecutor.execute();

      // 4. Actual mutation testing. Will check every mutant and if valid run it in an available test runner.
      const mutationRunExecutor = mutationRunExecutorInjector.injectClass(MutationTestExecutor);
      const mutantResults = await mutationRunExecutor.execute();
      return mutantResults;
    } finally {
       await rootInjector.dispose();
    }