chromium/ash/webui/diagnostics_ui/resources/fake_system_routine_controller.ts

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import {FakeMethodResolver} from 'chrome://resources/ash/common/fake_method_resolver.js';
import {assert} from 'chrome://resources/js/assert.js';
import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';

import {PowerRoutineResult, RoutineResult, RoutineResultInfo, RoutineRunnerInterface, RoutineType, StandardRoutineResult, SystemRoutineControllerInterface} from './system_routine_controller.mojom-webui.js';

/**
 * @fileoverview
 * Implements a fake version of the SystemRoutineController mojo interface.
 */

/**
 * Type for methods needed for the fake SystemRoutineController implementation.
 */
export type FakeSystemRoutineControllerInterface =
    SystemRoutineControllerInterface&{
      setDelayTimeInMillisecondsForTesting(delayMilliseconds: number): void,
      getSupportedRoutines(): Promise<{routines: RoutineType[]}>,
      getAllRoutines(): RoutineType[],
    };

export class FakeSystemRoutineController implements
    FakeSystemRoutineControllerInterface {
  private methods: FakeMethodResolver = new FakeMethodResolver();
  private routineResults: Map<RoutineType, RoutineResult> = new Map();
  /**
   * Controls the delay resolving routines. By default this is 0 and routines
   * resolve immediately, but still asynchronously.
   */
  private delayTimeMilliseconds: number = 0;
  /**
   * Holds the resolver for the next routine waiting to be resolved. This
   * will be null if no routines are in progress.
   */
  private resolver: PromiseResolver<any>|null = null;
  // Holds the remote that is called on completion.
  private remote: RoutineRunnerInterface|null = null;
  // Holds the type of the routine currently running.
  private routineType: RoutineType|null = null;

  constructor() {
    this.registerMethods();
  }

  getAllRoutines(): RoutineType[] {
    return [
      RoutineType.kBatteryCharge,
      RoutineType.kBatteryDischarge,
      RoutineType.kCpuStress,
      RoutineType.kCpuCache,
      RoutineType.kCpuFloatingPoint,
      RoutineType.kCpuPrime,
      RoutineType.kMemory,
      RoutineType.kCaptivePortal,
      RoutineType.kDnsLatency,
      RoutineType.kDnsResolution,
      RoutineType.kDnsResolverPresent,
      RoutineType.kGatewayCanBePinged,
      RoutineType.kHasSecureWiFiConnection,
      RoutineType.kHttpFirewall,
      RoutineType.kHttpsFirewall,
      RoutineType.kHttpsLatency,
      RoutineType.kLanConnectivity,
      RoutineType.kSignalStrength,
      RoutineType.kArcHttp,
      RoutineType.kArcPing,
      RoutineType.kArcDnsResolution,
    ];
  }

  // Implements SystemRoutineController.GetSupportedRoutines
  getSupportedRoutines(): Promise<{routines: RoutineType[]}> {
    return this.methods.resolveMethod('getSupportedRoutines');
  }

  // Sets the value that will be returned when calling getSupportedRoutines().
  setFakeSupportedRoutines(routines: RoutineType[]): void {
    this.methods.setResult('getSupportedRoutines', {routines: routines});
  }

  // Implements SystemRoutineController.RunRoutine.
  runRoutine(routineType: RoutineType, remoteRunner: RoutineRunnerInterface):
      void {
    this.resolver = new PromiseResolver();
    this.remote = remoteRunner;
    this.routineType = routineType;

    // If there is a positive or zero delay then setup a timer, otherwise
    // the routine will wait until resolveRoutineForTesting() is called.
    if (this.delayTimeMilliseconds >= 0) {
      setTimeout(() => {
        this.fireRemoteWithResult();
      }, this.delayTimeMilliseconds);
    }
  }

  // Setup method resolvers.
  registerMethods(): void {
    this.methods.register('getSupportedRoutines');
  }

  setFakeStandardRoutineResult(
      routineType: RoutineType, routineResult: StandardRoutineResult): void {
    this.routineResults.set(
        routineType, ({simpleResult: routineResult} as RoutineResult));
  }

  setFakePowerRoutineResult(
      routineType: RoutineType, routineResult: PowerRoutineResult): void {
    this.routineResults.set(
        routineType, ({powerResult: routineResult} as RoutineResult));
  }

  /**
   * Sets how long each routine waits before resolving. Setting to a value
   * >=0 will use a timer to delay resolution automatically. Setting to -1
   * will allow a caller to manually determine when each routine resolves.
   *
   * If set to -1, the caller can call resolveRoutineForTesting() and
   * isRoutineInProgressForTesting() to manually control in unit tests.
   */
  setDelayTimeInMillisecondsForTesting(delayMilliseconds: number): void {
    assert(delayMilliseconds >= -1);
    this.delayTimeMilliseconds = delayMilliseconds;
  }

  // Returns the pending run routine promise.
  getRunRoutinePromiseForTesting(): Promise<void> {
    assert(this.resolver != null);
    return this.resolver.promise;
  }

  /**
   * Resolves a routine that is in progress. The delay time must be set to
   * -1 or this method will assert. It will also assert if there is no
   * routine running. Use isRoutineInProgressForTesting() to determine if
   * a routing is currently in progress.
   */
  resolveRoutineForTesting(): Promise<void> {
    assert(this.delayTimeMilliseconds == -1);
    assert(this.resolver != null);
    const promise = this.resolver.promise;

    this.fireRemoteWithResult();
    return promise;
  }

  /**
   * Returns true if a routine is in progress. The delay time must be -1
   * otherwise this function will assert.
   */
  isRoutineInProgressForTesting(): boolean {
    assert(this.delayTimeMilliseconds == -1);
    return this.resolver != null;
  }

  // Returns the expected result for a running routine.
  private getResultInfo(): RoutineResultInfo {
    assert(this.routineType != null);
    let result = this.routineResults.get(this.routineType);
    if (result == undefined) {
      result = {simpleResult: StandardRoutineResult.kExecutionError} as
          RoutineResult;
    }

    const resultInfo: RoutineResultInfo = {
      type: this.routineType,
      result: result,
    };

    return resultInfo;
  }

  // Fires the remote callback with the expected result.
  private fireRemoteWithResult(): void {
    this.remote!.onRoutineResult(this.getResultInfo());
    this.resolver!.resolve(null);
    this.resolver = null;
    this.remote = null;
    this.routineType = null;
  }
}