// ***************************************************************************** // Copyright (C) 2023 STMicroelectronics and others. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0. // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse // Public License v. 2.0 are satisfied: GNU General Public License, version 2 // with the GNU Classpath Exception which is available at // https://www.gnu.org/software/classpath/license.html. // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { TestController, TestFailure, TestOutputItem, TestRun, TestService, TestState, TestStateChangedEvent } from '../test-service'; import { Disposable, Emitter, Event } from '@theia/core'; import { TestContextKeyService } from './test-context-key-service'; export interface ActiveRunEvent { controller: TestController; activeRun: TestRun | undefined } export interface TestOutputSource { readonly output: readonly TestOutputItem[]; onDidAddTestOutput: Event; readonly noOutputMessage?: string; } export interface ActiveTestStateChangedEvent { controller: TestController; testRun: TestRun; statedDelta: TestStateChangedEvent[]; } interface ActiveTestRunInfo { run: TestRun; toDispose: Disposable; } @injectable() export class TestOutputUIModel { @inject(TestContextKeyService) protected readonly testContextKeys: TestContextKeyService; @inject(TestService) protected testService: TestService; protected readonly activeRuns = new Map(); protected readonly controllerListeners = new Map(); private _selectedOutputSource: TestOutputSource | undefined; private _selectedTestState: TestState | undefined; @postConstruct() init(): void { this.testService.getControllers().forEach(controller => this.addController(controller)); this.testService.onControllersChanged(deltas => { deltas.added?.forEach(controller => this.addController(controller)); deltas.removed?.forEach(controller => this.removeController(controller)); }); } protected removeController(id: string): void { this.controllerListeners.get(id)?.dispose(); if (this.activeRuns.has(id)) { this.activeRuns.delete(id); } } protected addController(controller: TestController): void { this.controllerListeners.set(controller.id, controller.onRunsChanged(delta => { if (delta.added) { const currentRun = controller.testRuns[controller.testRuns.length - 1]; if (currentRun) { this.setActiveTestRun(currentRun); } } else { delta.removed?.forEach(run => { if (run === this.getActiveTestRun(controller)) { const currentRun = controller.testRuns[controller.testRuns.length - 1]; this.doSetActiveRun(controller, currentRun); } }); } })); } getActiveTestRun(controller: TestController): TestRun | undefined { return this.activeRuns.get(controller.id)?.run; } protected readonly onDidChangeActiveTestRunEmitter = new Emitter(); onDidChangeActiveTestRun: Event = this.onDidChangeActiveTestRunEmitter.event; setActiveTestRun(run: TestRun): void { this.doSetActiveRun(run.controller, run); } doSetActiveRun(controller: TestController, run: TestRun | undefined): void { const old = this.activeRuns.get(controller.id); if (old !== run) { if (old) { old.toDispose.dispose(); } if (run) { const toDispose = run.onDidChangeTestState(e => { this.onDidChangeActiveTestStateEmitter.fire({ controller, testRun: run, statedDelta: e }); }); this.activeRuns.set(controller.id, { run, toDispose }); } else { this.activeRuns.delete(controller.id); } this.onDidChangeActiveTestRunEmitter.fire({ activeRun: run, controller: controller }); } } private onDidChangeActiveTestStateEmitter: Emitter = new Emitter(); onDidChangeActiveTestState: Event = this.onDidChangeActiveTestStateEmitter.event; get selectedOutputSource(): TestOutputSource | undefined { return this._selectedOutputSource; } set selectedOutputSource(element: TestOutputSource | undefined) { if (element !== this._selectedOutputSource) { this._selectedOutputSource = element; this.onDidChangeSelectedOutputSourceEmitter.fire(element); } } protected readonly onDidChangeSelectedOutputSourceEmitter = new Emitter(); readonly onDidChangeSelectedOutputSource: Event = this.onDidChangeSelectedOutputSourceEmitter.event; get selectedTestState(): TestState | undefined { return this._selectedTestState; } set selectedTestState(element: TestState | undefined) { if (element !== this._selectedTestState) { this._selectedTestState = element; if (this._selectedTestState && TestFailure.is(this._selectedTestState.state)) { const message = this._selectedTestState.state.messages[0]; this.testContextKeys.contextValue.set(message.contextValue); } else { this.testContextKeys.contextValue.reset(); } this.onDidChangeSelectedTestStateEmitter.fire(element); } } protected readonly onDidChangeSelectedTestStateEmitter = new Emitter(); readonly onDidChangeSelectedTestState: Event = this.onDidChangeSelectedTestStateEmitter.event; }