1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132

ash / webui / diagnostics_ui / resources / routine_group.ts [blame]

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

import {RoutineProperties} from './diagnostics_types.js';
import {ExecutionProgress, ResultStatusItem} from './routine_list_executor.js';
import {getSimpleResult} from './routine_result_entry.js';
import {RoutineResult, RoutineType, StandardRoutineResult} from './system_routine_controller.mojom-webui.js';

function isBlockingRoutine(routineProp: RoutineProperties): boolean {
  return routineProp.blocking;
}

function getNonBlockingRoutines(routines: RoutineProperties[]): RoutineType[] {
  return routines.filter(r => !isBlockingRoutine(r)).map(getRoutine);
}

function getRoutine(routineProp: RoutineProperties): RoutineType {
  return routineProp.routine;
}

/**
 * @fileoverview
 * Used to aggregate individual tests into a shared category (Wi-Fi).
 */
export class RoutineGroup {
  routineProperties: RoutineProperties[];
  nonBlockingRoutines: Set<RoutineType>;
  routines: RoutineType[];
  groupName: string;
  progress: ExecutionProgress;
  failedTest: RoutineType|null = null;
  inWarningState: boolean = false;
  constructor(routines: RoutineProperties[], groupName: string) {
    /**
     * Store routine properties array for calls to |clone|.
     */
    this.routineProperties = this.routineProperties || routines;
    this.nonBlockingRoutines = new Set(getNonBlockingRoutines(routines));
    this.routines = routines.map(getRoutine);
    this.groupName = groupName;
    this.progress = ExecutionProgress.NOT_STARTED;
    /**
     * Used to track the first test failure in the group of tests.
     */
    this.failedTest = null;
    this.inWarningState = false;
  }

  /**
   * Determine overall status of routine group. Report test status as "running"
   * as long as the current routine is not the last routine in the group.
   */
  setStatus(status: ResultStatusItem): void {
    if (status.progress !== ExecutionProgress.COMPLETED) {
      // Prevent 'WARNING' badge from being overwritten by a subsequent routine.
      this.progress = this.inWarningState ? this.progress : status.progress;
      return;
    }

    const isLastRoutine = this.isLastRoutine(status.routine);
    if (status.result && this.testFailed(status.result)) {
      // Prevent 1st failed test from being overwritten.
      this.failedTest = this.failedTest || status.routine;

      const isBlocking = !this.nonBlockingRoutines.has(status.routine);
      this.inWarningState = this.inWarningState || !isBlocking;

      // We've encountered a blocking failure.
      if (this.failedTest && isBlocking) {
        this.progress = ExecutionProgress.COMPLETED;
        return;
      }
    }

    // Set status to "completed" only when all routines in this group are
    // finished running. Otherwise, check if we're in the warning state
    // before setting the progress to running.
    this.progress = isLastRoutine ? ExecutionProgress.COMPLETED :
        this.inWarningState       ? ExecutionProgress.WARNING :
                                    ExecutionProgress.RUNNING;

    return;
  }

  private testFailed(result: RoutineResult): boolean {
    return getSimpleResult(result) === StandardRoutineResult.kTestFailed;
  }

  private isLastRoutine(routine: RoutineType): boolean {
    return routine === this.routines[this.routines.length - 1];
  }

  /**
   * Used to add new routines after initialization for this instance is done.
   * Ex: Routines that are added only when a flag is enabled.
   * */
  addRoutine(routineProps: RoutineProperties): void {
    this.routines.push(routineProps.routine);
    if (!isBlockingRoutine(routineProps)) {
      this.nonBlockingRoutines.add(routineProps.routine);
    }
    this.routineProperties = [...this.routineProperties, routineProps];
  }

  /**
   * Whether we should skip the remaining routines (true when a blocking
   * routine fails) or not.
   */
  hasBlockingFailure(): boolean {
    if (!this.failedTest) {
      return false;
    }

    // Skip remaining tests if this is a blocking test failure and we're not
    // in a warning state.
    return !this.nonBlockingRoutines.has(this.failedTest) &&
        !this.inWarningState;
  }

  /**
   * Clones properties of RoutineGroup and returns new RoutineGroup
   */
  clone(): RoutineGroup {
    const clonedRoutineGroup =
        new RoutineGroup(this.routineProperties, this.groupName);
    clonedRoutineGroup.progress = this.progress;
    clonedRoutineGroup.failedTest = this.failedTest;
    clonedRoutineGroup.inWarningState = this.inWarningState;
    return clonedRoutineGroup;
  }
}