diff --git a/extension/src/data/index.ts b/extension/src/data/index.ts index 7277b1dd94..765a64f9a8 100644 --- a/extension/src/data/index.ts +++ b/extension/src/data/index.ts @@ -13,6 +13,7 @@ export abstract class BaseData< T extends { data: PlotsOutputOrError; revs: string[] } | ExpShowOutput > extends DeferredDisposable { public readonly onDidUpdate: Event + public readonly onDidChangeDvcYaml: Event protected readonly dvcRoot: string protected readonly processManager: ProcessManager @@ -25,6 +26,10 @@ export abstract class BaseData< new EventEmitter() ) + private readonly dvcYamlChanged: EventEmitter = this.dispose.track( + new EventEmitter() + ) + constructor( dvcRoot: string, internalCommands: InternalCommands, @@ -42,6 +47,8 @@ export abstract class BaseData< this.onDidUpdate = this.updated.event this.staticFiles = staticFiles + this.onDidChangeDvcYaml = this.dvcYamlChanged.event + this.watchFiles() this.waitForInitialData() @@ -80,6 +87,10 @@ export abstract class BaseData< ) { void this.managedUpdate(path) } + + if (path.endsWith('dvc.yaml')) { + this.dvcYamlChanged.fire() + } } ) } diff --git a/extension/src/experiments/checkpoints/collect.test.ts b/extension/src/experiments/checkpoints/collect.test.ts deleted file mode 100644 index 48f02afd32..0000000000 --- a/extension/src/experiments/checkpoints/collect.test.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { collectHasCheckpoints } from './collect' -import { PartialDvcYaml } from '../../fileSystem' - -describe('collectHasCheckpoints', () => { - it('should correctly identify that the demo project has checkpoints', () => { - const hasCheckpoints = collectHasCheckpoints({ - stages: { - train: { - cmd: 'python train.py', - deps: ['data/MNIST', 'train.py'], - live: { logs: { html: true, summary: true } }, - outs: [{ 'model.pt': { checkpoint: true } }], - params: ['seed', 'lr', 'weight_decay'], - plots: [ - 'plots', - { - 'predictions.json': { - cache: false, - template: 'confusion', - x: 'actual', - y: 'predicted' - } - } - ] - } - } - } as PartialDvcYaml) - expect(hasCheckpoints).toBe(true) - }) - - it('should correctly classify a dvc.yaml without checkpoint', () => { - const hasCheckpoints = collectHasCheckpoints({ - stages: { - extract: { - cmd: 'tar -xzf data/images.tar.gz --directory data', - deps: ['data/images.tar.gz'], - outs: [{ 'data/images/': { cache: false } }] - }, - train: { - cmd: 'python3 src/train.py', - deps: ['data/images/', 'src/train.py'], - live: { logs: { html: true, summary: true } }, - metrics: [{ 'metrics.json': { cache: false } }], - outs: ['models/model.h5'], - params: ['model.conv_units', 'train.epochs'], - plots: [{ 'logs.csv': { cache: false } }] - } - } - } as PartialDvcYaml) - - expect(hasCheckpoints).toBe(false) - }) - - it('should not fail if a train stage is not provided', () => { - const hasCheckpoints = collectHasCheckpoints({ - stages: { - extract: { - cmd: 'tar -xzf data/images.tar.gz --directory data', - deps: ['data/images.tar.gz'], - outs: [{ 'data/images/': { cache: false } }] - } - } - } as PartialDvcYaml) - - expect(hasCheckpoints).toBe(false) - }) - - it('should return true if any stage has checkpoints', () => { - const hasCheckpoints = collectHasCheckpoints({ - stages: { - extract: { - cmd: 'tar -xzf data/images.tar.gz --directory data', - deps: ['data/images.tar.gz'], - outs: [{ 'data/images/': { cache: false, checkpoint: true } }] - } - } - } as PartialDvcYaml) - - expect(hasCheckpoints).toBe(true) - }) - - it('should correctly classify a more complex dvc.yaml without checkpoint', () => { - const hasCheckpoints = collectHasCheckpoints({ - stages: { - evaluate: { - cmd: 'python src/evaluate.py model.pkl data/features scores.json prc.json roc.json', - deps: ['data/features', 'model.pkl', 'src/evaluate.py'], - metrics: [{ 'scores.json': { cache: false } }], - plots: [ - { 'prc.json': { cache: false, x: 'recall', y: 'precision' } }, - { 'roc.json': { cache: false, x: 'fpr', y: 'tpr' } } - ] - }, - featurize: { - cmd: 'python src/featurization.py data/prepared data/features', - deps: ['data/prepared', 'src/featurization.py'], - outs: ['data/features'], - params: ['featurize.max_features', 'featurize.ngrams'] - }, - prepare: { - cmd: 'python src/prepare.py data/data.xml', - deps: ['data/data.xml', 'src/prepare.py'], - outs: ['data/prepared'], - params: ['prepare.seed', 'prepare.split'] - }, - train: { - cmd: 'python src/train.py data/features model.pkl', - deps: ['data/features', 'src/train.py'], - outs: ['model.pkl'], - params: ['train.min_split', 'train.n_est', 'train.seed'] - } - } - } as PartialDvcYaml) - - expect(hasCheckpoints).toBe(false) - }) -}) diff --git a/extension/src/experiments/checkpoints/collect.ts b/extension/src/experiments/checkpoints/collect.ts deleted file mode 100644 index b1bcd850e2..0000000000 --- a/extension/src/experiments/checkpoints/collect.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Out, PartialDvcYaml } from '../../fileSystem' - -const stageHasCheckpoints = (outs: Out[] = []): boolean => { - for (const out of outs) { - if (typeof out === 'string') { - continue - } - if (Object.values(out).some(file => file?.checkpoint)) { - return true - } - } - return false -} - -export const collectHasCheckpoints = (yaml: PartialDvcYaml): boolean => { - for (const stage of Object.values(yaml?.stages || {})) { - if (stageHasCheckpoints(stage?.outs)) { - return true - } - } - return false -} diff --git a/extension/src/experiments/checkpoints/model.test.ts b/extension/src/experiments/checkpoints/model.test.ts deleted file mode 100644 index 8dc853be02..0000000000 --- a/extension/src/experiments/checkpoints/model.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { join } from 'path' -import { CheckpointsModel } from './model' -import { dvcDemoPath } from '../../test/util' - -describe('CheckpointsModel', () => { - it('should keep a record of yaml files that have checkpoints', () => { - const experimentCheckpointsModel = new CheckpointsModel() - expect(experimentCheckpointsModel.hasCheckpoints()).toBe(false) - - const rootYamlHasCheckpoints = { - path: join(dvcDemoPath, 'dvc.yaml'), - yaml: { - stages: { - train: { - outs: [{ 'model.pt': { checkpoint: true } }] - } - } - } - } - - const extraYamlHasCheckpoints = { - path: join(dvcDemoPath, 'extra', 'dvc.yaml'), - yaml: { - stages: { - train: { - outs: [{ 'extra-model.pt': { checkpoint: true } }] - } - } - } - } - - const rootYamlNoCheckpoints = { - path: join(dvcDemoPath, 'dvc.yaml'), - yaml: { - stages: { train: { outs: ['model.pt'] } } - } - } - - const extraYamlNoCheckpoints = { - path: join(dvcDemoPath, 'extra', 'dvc.yaml'), - yaml: { - stages: { train: { outs: ['extra-model.pt'] } } - } - } - - experimentCheckpointsModel.transformAndSet(rootYamlHasCheckpoints) - expect(experimentCheckpointsModel.hasCheckpoints()).toBe(true) - - experimentCheckpointsModel.transformAndSet(extraYamlHasCheckpoints) - expect(experimentCheckpointsModel.hasCheckpoints()).toBe(true) - - experimentCheckpointsModel.transformAndSet(rootYamlNoCheckpoints) - expect(experimentCheckpointsModel.hasCheckpoints()).toBe(true) - - experimentCheckpointsModel.transformAndSet(extraYamlNoCheckpoints) - - expect(experimentCheckpointsModel.hasCheckpoints()).toBe(false) - }) -}) diff --git a/extension/src/experiments/checkpoints/model.ts b/extension/src/experiments/checkpoints/model.ts deleted file mode 100644 index 0985f4d9ae..0000000000 --- a/extension/src/experiments/checkpoints/model.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { collectHasCheckpoints } from './collect' -import { PartialDvcYaml } from '../../fileSystem' -import { definedAndNonEmpty, uniqueValues } from '../../util/array' -import { Disposable } from '../../class/dispose' - -export class CheckpointsModel extends Disposable { - private yamlWithCheckpoints: string[] = [] - - public hasCheckpoints() { - return definedAndNonEmpty(this.yamlWithCheckpoints) - } - - public transformAndSet(data: { path: string; yaml: PartialDvcYaml }) { - const { path, yaml } = data - const hasCheckpoints = collectHasCheckpoints(yaml) - - this.yamlWithCheckpoints = hasCheckpoints - ? uniqueValues([...this.yamlWithCheckpoints, path]) - : this.yamlWithCheckpoints.filter(file => file !== path) - } -} diff --git a/extension/src/experiments/data/index.ts b/extension/src/experiments/data/index.ts index 0a6fc86464..d03d72058a 100644 --- a/extension/src/experiments/data/index.ts +++ b/extension/src/experiments/data/index.ts @@ -31,34 +31,21 @@ export class ExperimentsData extends BaseData { dvcRoot, internalCommands, updatesPaused, - [ - { - name: 'partialUpdate', - process: () => this.update(ExperimentFlag.NO_FETCH) - }, - { name: 'fullUpdate', process: () => this.update() } - ], + [{ name: 'update', process: () => this.update() }], ['dvc.lock', 'dvc.yaml', 'params.yaml', DOT_DVC] ) this.experiments = experiments void this.watchExpGitRefs() - void this.managedUpdate(QUEUED_EXPERIMENT_PATH) + void this.managedUpdate() } - public managedUpdate(path?: string) { - if ( - path?.includes(QUEUED_EXPERIMENT_PATH) || - this.processManager.isOngoingOrQueued('fullUpdate') - ) { - return this.processManager.run('fullUpdate') - } - - return this.processManager.run('partialUpdate') + public managedUpdate() { + return this.processManager.run('update') } - public async update(...args: (ExperimentFlag | string)[]): Promise { + public async update(): Promise { const flags = this.experiments.getIsBranchesView() ? [ExperimentFlag.ALL_BRANCHES] : [ @@ -68,8 +55,7 @@ export class ExperimentsData extends BaseData { const data = await this.internalCommands.executeCommand( AvailableCommands.EXP_SHOW, this.dvcRoot, - ...flags, - ...args + ...flags ) this.collectFiles(data) @@ -106,7 +92,7 @@ export class ExperimentsData extends BaseData { if ( watchedRelPaths.some(watchedRelPath => path.includes(watchedRelPath)) ) { - return this.managedUpdate(path) + return this.managedUpdate() } } ) diff --git a/extension/src/experiments/index.ts b/extension/src/experiments/index.ts index f31b9310b3..695ead685a 100644 --- a/extension/src/experiments/index.ts +++ b/extension/src/experiments/index.ts @@ -29,7 +29,6 @@ import { UNSELECTED } from './model/status' import { starredSort } from './model/sortBy/constants' import { pickSortsToRemove, pickSortToAdd } from './model/sortBy/quickPick' import { ColumnsModel } from './columns/model' -import { CheckpointsModel } from './checkpoints/model' import { ExperimentsData } from './data' import { Experiment, ColumnType, TableData } from './webview/contract' import { WebviewMessages } from './webview/messages' @@ -40,17 +39,13 @@ import { AvailableCommands, InternalCommands } from '../commands/internal' import { EXPERIMENT_WORKSPACE_ID, ExpShowOutput } from '../cli/dvc/contract' import { ViewKey } from '../webview/constants' import { BaseRepository } from '../webview/repository' -import { FileSystemData } from '../fileSystem/data' import { Title } from '../vscode/title' import { createTypedAccumulator } from '../util/object' import { pickPaths } from '../path/selection/quickPick' import { Toast } from '../vscode/toast' import { ConfigKey } from '../vscode/config' import { checkSignalFile, pollSignalFileForProcess } from '../fileSystem' -import { - DVCLIVE_ONLY_RUNNING_SIGNAL_FILE, - ExperimentFlag -} from '../cli/dvc/constants' +import { DVCLIVE_ONLY_RUNNING_SIGNAL_FILE } from '../cli/dvc/constants' export const ExperimentsScale = { ...omit(ColumnType, 'TIMESTAMP'), @@ -71,12 +66,10 @@ export class Experiments extends BaseRepository { public readonly viewKey = ViewKey.EXPERIMENTS - private readonly cliData: ExperimentsData - private readonly fileSystemData: FileSystemData + private readonly data: ExperimentsData private readonly experiments: ExperimentsModel private readonly columns: ColumnsModel - private readonly checkpoints: CheckpointsModel private readonly paramsFileFocused = this.dispose.track( new EventEmitter() @@ -114,8 +107,7 @@ export class Experiments extends BaseRepository { resourceLocator: ResourceLocator, workspaceState: Memento, addStage: () => Promise, - cliData?: ExperimentsData, - fileSystemData?: FileSystemData + data?: ExperimentsData ) { super(dvcRoot, resourceLocator.beaker) @@ -145,10 +137,8 @@ export class Experiments extends BaseRepository { ) ) - this.checkpoints = this.dispose.track(new CheckpointsModel()) - - this.cliData = this.dispose.track( - cliData || + this.data = this.dispose.track( + data || new ExperimentsData( dvcRoot, internalCommands, @@ -157,26 +147,17 @@ export class Experiments extends BaseRepository { ) ) - this.fileSystemData = this.dispose.track( - fileSystemData || new FileSystemData(dvcRoot) - ) - - this.dispose.track(this.cliData.onDidUpdate(data => this.setState(data))) + this.dispose.track(this.data.onDidUpdate(data => this.setState(data))) this.dispose.track( - this.fileSystemData.onDidUpdate(data => { - const hadCheckpoints = this.hasCheckpoints() - this.checkpoints.transformAndSet(data) - if (hadCheckpoints !== this.hasCheckpoints()) { - this.checkpointsChanged.fire() - } - void this.webviewMessages.changeHasConfig(true) - }) + this.data.onDidChangeDvcYaml(() => + this.webviewMessages.changeHasConfig(true) + ) ) this.dispose.track( workspace.onDidChangeConfiguration((event: ConfigurationChangeEvent) => { if (event.affectsConfiguration(ConfigKey.EXP_TABLE_HEAD_MAX_HEIGHT)) { - void this.cliData.update() + void this.data.update() } }) ) @@ -187,10 +168,11 @@ export class Experiments extends BaseRepository { } public update() { - return this.cliData.managedUpdate() + return this.data.managedUpdate() } public async setState(data: ExpShowOutput) { + const hadCheckpoints = this.hasCheckpoints() const dvcLiveOnly = await this.checkSignalFile() const commitsOutput = await this.getCommitOutput(data) await Promise.all([ @@ -198,11 +180,15 @@ export class Experiments extends BaseRepository { this.experiments.transformAndSet(data, dvcLiveOnly, commitsOutput) ]) + if (hadCheckpoints !== this.hasCheckpoints()) { + this.checkpointsChanged.fire() + } + return this.notifyChanged() } public hasCheckpoints() { - return this.checkpoints.hasCheckpoints() + return this.experiments.hasCheckpoints() } public getChildColumns(path?: string) { @@ -563,7 +549,6 @@ export class Experiments extends BaseRepository { this.dvcRoot, this.experiments, this.columns, - this.checkpoints, () => this.getWebview(), () => this.notifyChanged(), () => this.selectColumns(), @@ -584,7 +569,7 @@ export class Experiments extends BaseRepository { AvailableCommands.GIT_GET_NUM_COMMITS, this.dvcRoot ), - (...args: (ExperimentFlag | string)[]) => this.cliData.update(...args) + () => this.data.update() ) this.dispose.track( @@ -626,7 +611,7 @@ export class Experiments extends BaseRepository { void pollSignalFileForProcess(this.dvcLiveOnlySignalFile, () => { this.dvcLiveOnlyCleanupInitialized = false if (this.hasRunningExperiment()) { - void this.cliData.update() + void this.data.update() } }) } diff --git a/extension/src/experiments/model/collect.ts b/extension/src/experiments/model/collect.ts index 0ca940502f..938b8cabdb 100644 --- a/extension/src/experiments/model/collect.ts +++ b/extension/src/experiments/model/collect.ts @@ -43,6 +43,7 @@ export type ExperimentItem = { type ExperimentsAccumulator = { commits: Experiment[] experimentsByCommit: Map + hasCheckpoints: boolean runningExperiments: RunningExperiment[] workspace: Experiment } @@ -282,6 +283,20 @@ const setWorkspaceAsRunning = ( } } +const hasCheckpoints = (data: ExpShowOutput) => { + if (!data?.length) { + return false + } + + const [workspace] = data + + if (experimentHasError(workspace)) { + return false + } + + return !!workspace.data.meta.has_checkpoints +} + export const collectExperiments = ( output: ExpShowOutput, dvcLiveOnly: boolean, @@ -290,6 +305,7 @@ export const collectExperiments = ( const acc: ExperimentsAccumulator = { commits: [], experimentsByCommit: new Map(), + hasCheckpoints: hasCheckpoints(output), runningExperiments: [], workspace: { id: EXPERIMENT_WORKSPACE_ID, label: EXPERIMENT_WORKSPACE_ID } } diff --git a/extension/src/experiments/model/index.ts b/extension/src/experiments/model/index.ts index c4f4012413..1e580168b8 100644 --- a/extension/src/experiments/model/index.ts +++ b/extension/src/experiments/model/index.ts @@ -49,6 +49,7 @@ export class ExperimentsModel extends ModelWithPersistence { private workspace = {} as Experiment private commits: Experiment[] = [] private experimentsByCommit: Map = new Map() + private checkpoints = false private availableColors: Color[] private coloredStatus: ColoredStatus private starredExperiments: StarredExperiments @@ -104,12 +105,18 @@ export class ExperimentsModel extends ModelWithPersistence { dvcLiveOnly: boolean, commitsOutput: string | undefined ) { - const { workspace, commits, experimentsByCommit, runningExperiments } = - collectExperiments(data, dvcLiveOnly, commitsOutput) + const { + workspace, + commits, + experimentsByCommit, + runningExperiments, + hasCheckpoints + } = collectExperiments(data, dvcLiveOnly, commitsOutput) this.workspace = workspace this.commits = commits this.experimentsByCommit = experimentsByCommit + this.checkpoints = hasCheckpoints this.setColoredStatus(runningExperiments) } @@ -163,6 +170,10 @@ export class ExperimentsModel extends ModelWithPersistence { ) } + public hasCheckpoints() { + return this.checkpoints + } + public setRevisionCollected(revisions: string[]) { for (const { id } of this.getExperimentsAndQueued().filter(({ label }) => revisions.includes(label) diff --git a/extension/src/experiments/webview/messages.ts b/extension/src/experiments/webview/messages.ts index e7c7d5a2ae..8b057b526f 100644 --- a/extension/src/experiments/webview/messages.ts +++ b/extension/src/experiments/webview/messages.ts @@ -17,7 +17,6 @@ import { ColumnsModel } from '../columns/model' import { splitColumnPath } from '../columns/paths' import { ExperimentsModel } from '../model' import { SortDefinition } from '../model/sortBy' -import { CheckpointsModel } from '../checkpoints/model' import { getPositiveIntegerInput } from '../../vscode/inputBox' import { Title } from '../../vscode/title' import { ConfigKey, setConfigValue } from '../../vscode/config' @@ -32,7 +31,6 @@ export class WebviewMessages { private readonly experiments: ExperimentsModel private readonly columns: ColumnsModel - private readonly checkpoints: CheckpointsModel private readonly getWebview: () => BaseWebview | undefined private readonly notifyChanged: () => void @@ -57,7 +55,6 @@ export class WebviewMessages { dvcRoot: string, experiments: ExperimentsModel, columns: ColumnsModel, - checkpoints: CheckpointsModel, getWebview: () => BaseWebview | undefined, notifyChanged: () => void, selectColumns: () => Promise, @@ -73,7 +70,6 @@ export class WebviewMessages { this.dvcRoot = dvcRoot this.experiments = experiments this.columns = columns - this.checkpoints = checkpoints this.getWebview = getWebview this.notifyChanged = notifyChanged this.selectColumns = selectColumns @@ -269,7 +265,7 @@ export class WebviewMessages { columns: this.columns.getSelected(), filteredCount: this.experiments.getFilteredCount(), filters: this.experiments.getFilterPaths(), - hasCheckpoints: this.checkpoints.hasCheckpoints(), + hasCheckpoints: this.experiments.hasCheckpoints(), hasColumns: this.columns.hasNonDefaultColumns(), hasConfig: this.hasConfig, hasMoreCommits: this.hasMoreCommits, diff --git a/extension/src/fileSystem/data/index.ts b/extension/src/fileSystem/data/index.ts deleted file mode 100644 index 036e6d465f..0000000000 --- a/extension/src/fileSystem/data/index.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { join } from 'path' -import { Event, EventEmitter } from 'vscode' -import { isSameOrChild, loadYaml, PartialDvcYaml } from '..' -import { findFiles } from '../workspace' -import { getRelativePattern } from '../relativePattern' -import { createFileSystemWatcher } from '../watcher' -import { DeferredDisposable } from '../../class/deferred' - -export class FileSystemData extends DeferredDisposable { - public readonly onDidUpdate: Event<{ path: string; yaml: PartialDvcYaml }> - - private readonly dvcRoot: string - - private readonly updated = this.dispose.track( - new EventEmitter<{ path: string; yaml: PartialDvcYaml }>() - ) - - constructor(dvcRoot: string) { - super() - - this.dvcRoot = dvcRoot - this.onDidUpdate = this.updated.event - - this.watchDvcYaml() - void this.initialize() - } - - private async initialize() { - const files = await findFiles(join('**', 'dvc.yaml')) - const filesInRepo = files.filter(file => isSameOrChild(this.dvcRoot, file)) - - for (const path of filesInRepo) { - const yaml = loadYaml(path) - if (yaml) { - this.updated.fire({ path, yaml }) - } - } - - this.deferred.resolve() - } - - private watchDvcYaml() { - return createFileSystemWatcher( - disposable => this.dispose.track(disposable), - getRelativePattern(this.dvcRoot, join('**', 'dvc.yaml')), - path => { - if (!path) { - return - } - const yaml = loadYaml(path) - if (yaml) { - this.updated.fire({ path, yaml }) - } - } - ) - } -} diff --git a/extension/src/test/suite/experiments/data/index.test.ts b/extension/src/test/suite/experiments/data/index.test.ts index 7681acad0b..ba8bb8ae99 100644 --- a/extension/src/test/suite/experiments/data/index.test.ts +++ b/extension/src/test/suite/experiments/data/index.test.ts @@ -8,14 +8,10 @@ import { bypassProcessManagerDebounce, getArgOfCall, getMockNow, - getTimeSafeDisposer, - stubPrivateMemberMethod + getTimeSafeDisposer } from '../../util' import { dvcDemoPath, getTestWorkspaceFolder } from '../../../util' -import { - ExperimentsData, - QUEUED_EXPERIMENT_PATH -} from '../../../../experiments/data' +import { ExperimentsData } from '../../../../experiments/data' import * as Watcher from '../../../../fileSystem/watcher' import { AvailableCommands, @@ -23,10 +19,7 @@ import { InternalCommands } from '../../../../commands/internal' import { buildExperimentsData } from '../util' -import { - ExperimentFlag, - DEFAULT_NUM_OF_COMMITS_TO_SHOW -} from '../../../../cli/dvc/constants' +import { DEFAULT_NUM_OF_COMMITS_TO_SHOW } from '../../../../cli/dvc/constants' import { EXPERIMENTS_GIT_LOGS_REFS } from '../../../../experiments/data/constants' import { gitPath } from '../../../../cli/git/constants' import * as FileSystem from '../../../../fileSystem' @@ -181,62 +174,5 @@ suite('Experiments Data Test Suite', () => { expect(managedUpdateSpy).to.be.called }) - - it('should not use exp show to fetch git refs external to the workspace if the path is not from a temp workspace', async () => { - const mockNow = getMockNow() - const { data, mockExpShow } = buildExperimentsData(disposable) - - await data.isReady() - bypassProcessManagerDebounce(mockNow) - const mockIsOngoingOrQueued = stubPrivateMemberMethod( - data, - 'processManager', - 'isOngoingOrQueued' - ).returns(false) - - mockExpShow.resetHistory() - - await data.managedUpdate() - - expect(mockIsOngoingOrQueued).to.be.calledWith('fullUpdate') - expect(mockExpShow).to.be.calledOnce - expect(mockExpShow).to.be.calledWithExactly( - dvcDemoPath, - ExperimentFlag.NUM_COMMIT, - DEFAULT_NUM_OF_COMMITS_TO_SHOW.toString(), - ExperimentFlag.NO_FETCH - ) - - bypassProcessManagerDebounce(mockNow, 2) - mockExpShow.resetHistory() - - await data.managedUpdate(EXPERIMENTS_GIT_LOGS_REFS) - - expect(mockExpShow).to.be.calledOnce - expect(mockExpShow).to.be.calledWithExactly( - dvcDemoPath, - ExperimentFlag.NUM_COMMIT, - DEFAULT_NUM_OF_COMMITS_TO_SHOW.toString(), - ExperimentFlag.NO_FETCH - ) - }) - - it('should use exp show to fetch external git refs if the path to a temporary workspace (queued experiment) is provided', async () => { - const mockNow = getMockNow() - const { data, mockExpShow } = buildExperimentsData(disposable) - - await data.isReady() - bypassProcessManagerDebounce(mockNow) - mockExpShow.resetHistory() - - await data.managedUpdate(QUEUED_EXPERIMENT_PATH) - - expect(mockExpShow).to.be.calledOnce - expect(mockExpShow).to.be.calledWithExactly( - dvcDemoPath, - ExperimentFlag.NUM_COMMIT, - DEFAULT_NUM_OF_COMMITS_TO_SHOW.toString() - ) - }) }) }) diff --git a/extension/src/test/suite/experiments/index.test.ts b/extension/src/test/suite/experiments/index.test.ts index f772ae63bc..20f66993c2 100644 --- a/extension/src/test/suite/experiments/index.test.ts +++ b/extension/src/test/suite/experiments/index.test.ts @@ -33,7 +33,7 @@ import { } from '../../../experiments/webview/contract' import { buildInternalCommands, - buildMockData, + buildMockExperimentsData, closeAllEditors, configurationChangeEvent, experimentsUpdatedEvent, @@ -57,8 +57,6 @@ import { ColumnsModel } from '../../../experiments/columns/model' import { MessageFromWebviewType } from '../../../webview/contract' import { ExperimentsModel } from '../../../experiments/model' import { copyOriginalColors } from '../../../experiments/model/status/colors' -import { FileSystemData } from '../../../fileSystem/data' -import { ExperimentsData } from '../../../experiments/data' import { WEBVIEW_TEST_TIMEOUT } from '../timeouts' import * as Telemetry from '../../../telemetry' import { EventName } from '../../../telemetry/constants' @@ -1416,8 +1414,7 @@ suite('Experiments Test Suite', () => { resourceLocator, buildMockMemento(), () => Promise.resolve(true), - buildMockData(), - buildMockData() + buildMockExperimentsData() ) ) @@ -1655,8 +1652,7 @@ suite('Experiments Test Suite', () => { {} as ResourceLocator, mockMemento, () => Promise.resolve(true), - buildMockData(), - buildMockData() + buildMockExperimentsData() ) ) void testRepository.setState(expShowFixture) @@ -1811,8 +1807,7 @@ suite('Experiments Test Suite', () => { {} as ResourceLocator, mockMemento, () => Promise.resolve(true), - buildMockData(), - buildMockData() + buildMockExperimentsData() ) ) void testRepository.setState(expShowFixture) diff --git a/extension/src/test/suite/experiments/util.ts b/extension/src/test/suite/experiments/util.ts index 1b4a6618b6..337e0db522 100644 --- a/extension/src/test/suite/experiments/util.ts +++ b/extension/src/test/suite/experiments/util.ts @@ -8,37 +8,15 @@ import { buildMockMemento, dvcDemoPath } from '../../util' import { buildDependencies, buildInternalCommands, - buildMockData, + buildMockExperimentsData, SafeWatcherDisposer } from '../util' -import { ExpShowOutput, experimentHasError } from '../../../cli/dvc/contract' import { ExperimentsData } from '../../../experiments/data' -import { CheckpointsModel } from '../../../experiments/checkpoints/model' -import { FileSystemData } from '../../../fileSystem/data' import * as Watcher from '../../../fileSystem/watcher' import { ExperimentsModel } from '../../../experiments/model' import { ColumnsModel } from '../../../experiments/columns/model' import { DEFAULT_NUM_OF_COMMITS_TO_SHOW } from '../../../cli/dvc/constants' -const hasCheckpoints = (data: ExpShowOutput) => { - if (!data?.length) { - return false - } - - const [workspace] = data - - if (experimentHasError(workspace)) { - return false - } - - return !!workspace.data.meta.has_checkpoints -} - -export const mockHasCheckpoints = (data: ExpShowOutput) => - stub(CheckpointsModel.prototype, 'hasCheckpoints').returns( - hasCheckpoints(data) - ) - export const buildExperiments = ( disposer: Disposer, experimentShowData = expShowFixture, @@ -60,7 +38,7 @@ export const buildExperiments = ( } = buildDependencies(disposer, experimentShowData) const mockUpdateExperimentsData = stub() - const mockExperimentsData = buildMockData( + const mockExperimentsData = buildMockExperimentsData( mockUpdateExperimentsData ) const mockCheckOrAddPipeline = stub() @@ -73,13 +51,10 @@ export const buildExperiments = ( resourceLocator, buildMockMemento(), mockCheckOrAddPipeline, - mockExperimentsData, - buildMockData() + mockExperimentsData ) ) - mockHasCheckpoints(experimentShowData) // this can get removed too - void experiments.setState(experimentShowData) return { diff --git a/extension/src/test/suite/extension.test.ts b/extension/src/test/suite/extension.test.ts index 57462a6a3f..3aff799f9f 100644 --- a/extension/src/test/suite/extension.test.ts +++ b/extension/src/test/suite/extension.test.ts @@ -4,7 +4,6 @@ import { expect } from 'chai' import { stub, restore, spy } from 'sinon' import { commands, workspace } from 'vscode' import { closeAllEditors, mockDisposable, mockDuration } from './util' -import { mockHasCheckpoints } from './experiments/util' import { Disposable } from '../../extension' import * as Python from '../../extensions/python' import { DvcReader } from '../../cli/dvc/reader' @@ -92,7 +91,6 @@ suite('Extension Test Suite', () => { }) ) - mockHasCheckpoints(expShowFixture) const mockExpShow = stub(DvcReader.prototype, 'expShow') const mockDataStatus = stub(DvcReader.prototype, 'dataStatus') const mockPlotsDiff = stub(DvcReader.prototype, 'plotsDiff') diff --git a/extension/src/test/suite/fileSystem/data/index.test.ts b/extension/src/test/suite/fileSystem/data/index.test.ts deleted file mode 100644 index edcc53dce7..0000000000 --- a/extension/src/test/suite/fileSystem/data/index.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { afterEach, beforeEach, describe, it, suite } from 'mocha' -import { stub, restore } from 'sinon' -import { expect } from 'chai' -import { RelativePattern } from 'vscode' -import { FileSystemData } from '../../../../fileSystem/data' -import { dvcDemoPath, getTestWorkspaceFolder } from '../../../util' -import * as FileSystem from '../../../../fileSystem' -import * as Watcher from '../../../../fileSystem/watcher' -import { getArgOfCall, getTimeSafeDisposer } from '../../util' -import { join } from '../../../util/path' - -suite('File System Data Test Suite', () => { - const disposable = getTimeSafeDisposer() - - beforeEach(() => { - restore() - }) - - afterEach(() => { - return disposable.disposeAndFlush() - }) - - describe('FileSystemData', () => { - it('should read the dvc.yaml from the demo path and send an event containing the path and the yaml', async () => { - stub(Watcher, 'createFileSystemWatcher').returns(undefined) - const data = disposable.track(new FileSystemData(dvcDemoPath)) - - disposable.track( - data.onDidUpdate(({ path, yaml }) => { - expect(path).to.equal(dvcDemoPath) - expect(yaml.stages.train.outs).to.deep.equal([ - { 'model.pt': { checkpoint: true } } - ]) - }) - ) - - await data.isReady() - }) - - it('should create a watcher with the expected glob', async () => { - const mockCreateFileSystemWatcher = stub( - Watcher, - 'createFileSystemWatcher' - ).returns(undefined) - stub(FileSystem, 'loadYaml').returns(undefined) - const data = disposable.track(new FileSystemData(dvcDemoPath)) - - expect(mockCreateFileSystemWatcher).to.be.calledOnce - expect(getArgOfCall(mockCreateFileSystemWatcher, 0, 2)).to.deep.equal( - new RelativePattern(getTestWorkspaceFolder(), join('**', 'dvc.yaml')) - ) - - await data.isReady() - }) - }) -}) diff --git a/extension/src/test/suite/plots/util.ts b/extension/src/test/suite/plots/util.ts index ec6714418f..4538ada2a4 100644 --- a/extension/src/test/suite/plots/util.ts +++ b/extension/src/test/suite/plots/util.ts @@ -10,10 +10,7 @@ import { WorkspaceExperiments } from '../../../experiments/workspace' import { PlotsModel } from '../../../plots/model' import { PlotsData } from '../../../plots/data' import { Experiments } from '../../../experiments' -import { buildDependencies, buildMockData } from '../util' -import { FileSystemData } from '../../../fileSystem/data' -import { ExperimentsData } from '../../../experiments/data' -import { mockHasCheckpoints } from '../experiments/util' +import { buildDependencies, buildMockExperimentsData } from '../util' import { MOCK_IMAGE_MTIME } from '../../fixtures/plotsDiff' import { PathsModel } from '../../../plots/paths/model' import { BaseWorkspaceWebviews } from '../../../webview/workspace' @@ -49,8 +46,7 @@ export const buildPlots = async ( resourceLocator, buildMockMemento(), () => Promise.resolve(true), - buildMockData(), - buildMockData() + buildMockExperimentsData() ) ) const plots = disposer.track( @@ -64,7 +60,6 @@ export const buildPlots = async ( ) ) - mockHasCheckpoints(expShow) // eslint-disable-next-line @typescript-eslint/no-explicit-any const experimentsModel: ExperimentsModel = (experiments as any).experiments experimentsModel.setSelected([ diff --git a/extension/src/test/suite/util.ts b/extension/src/test/suite/util.ts index 05ed955c3b..85ae9954ea 100644 --- a/extension/src/test/suite/util.ts +++ b/extension/src/test/suite/util.ts @@ -25,7 +25,6 @@ import { BaseWebview } from '../../webview' import { ExperimentsData } from '../../experiments/data' import { ResourceLocator } from '../../resourceLocator' import { DEFAULT_DEBOUNCE_WINDOW_MS } from '../../process/manager' -import { FileSystemData } from '../../fileSystem/data' import * as FileSystem from '../../fileSystem' import * as Watcher from '../../fileSystem/watcher' import { MessageFromWebview } from '../../webview/contract' @@ -182,14 +181,14 @@ export const buildInternalCommands = (disposer: Disposer) => { } } -export const buildMockData = ( - update = stub() -) => +export const buildMockExperimentsData = (update = stub()) => ({ dispose: stub(), + dvcYamlChanged: stub(), + onDidChangeDvcYaml: stub(), onDidUpdate: stub(), update - } as unknown as T) + } as unknown as ExperimentsData) export const buildResourceLocator = (disposer: Disposer): ResourceLocator => disposer.track(new ResourceLocator(extensionUri))