Skip to content

Commit 8384167

Browse files
authored
Ensure unique list of experiment passed to the rest of the app (#3925)
* make combined list of experiments unique * remove leftover method * deduplicate experiments in collection * add regression tests
1 parent d9aba68 commit 8384167

File tree

8 files changed

+179
-46
lines changed

8 files changed

+179
-46
lines changed

extension/src/experiments/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ export class Experiments extends BaseRepository<TableData> {
626626
}
627627

628628
return await pickExperiment(
629-
this.experiments.getCombinedList(),
629+
this.experiments.getUniqueList(),
630630
this.getFirstThreeColumnOrder(),
631631
Title.SELECT_BASE_EXPERIMENT
632632
)

extension/src/experiments/model/collect.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,47 @@ describe('collectExperiments', () => {
8585
tags: []
8686
})
8787
})
88+
89+
it('should not collect the same experiment twice', () => {
90+
const main = {
91+
experiments: [
92+
{
93+
name: 'campy-pall',
94+
rev: '0b4b001dfaa8f2c4cd2a62238699131ab2c679ea'
95+
},
96+
{
97+
name: 'shyer-stir',
98+
rev: '450e672f0d8913517ab2ab443f5d87b34f308290'
99+
}
100+
],
101+
name: 'main',
102+
rev: '61bed4ce8913eca7f73ca754d65bc5daad1520e2'
103+
}
104+
105+
const expShowWithDuplicateCommits = generateTestExpShowOutput(
106+
{},
107+
main,
108+
{
109+
name: 'branchOffMainWithCommit',
110+
rev: '351e42ace3cb6a3a853c65bef285e60748cc6341'
111+
},
112+
main
113+
)
114+
115+
const { experimentsByCommit, commits } = collectExperiments(
116+
expShowWithDuplicateCommits,
117+
false,
118+
''
119+
)
120+
121+
expect(commits.length).toStrictEqual(3)
122+
123+
const experiments = experimentsByCommit.get('main')
124+
125+
expect(experiments?.length).toStrictEqual(2)
126+
expect(experiments?.map(({ id }) => id).sort()).toStrictEqual([
127+
'campy-pall',
128+
'shyer-stir'
129+
])
130+
})
88131
})

extension/src/experiments/model/collect.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,16 +251,25 @@ const collectExpRange = (
251251
const expState = revs[0]
252252

253253
const { name, rev } = expState
254-
const { branch, id } = baseline
254+
const { id: baselineId } = baseline
255255

256256
const label =
257257
rev === EXPERIMENT_WORKSPACE_ID
258258
? EXPERIMENT_WORKSPACE_ID
259259
: shortenForLabel(rev)
260260

261+
const experimentId = name || label
262+
263+
if (
264+
acc.experimentsByCommit
265+
.get(baselineId)
266+
?.find(({ id }) => id === experimentId)
267+
) {
268+
return
269+
}
270+
261271
const experiment = transformExpState(
262272
{
263-
branch,
264273
id: name || label,
265274
label
266275
},
@@ -275,7 +284,7 @@ const collectExpRange = (
275284
collectExecutorInfo(experiment, executor)
276285
collectRunningExperiment(acc, experiment)
277286

278-
addToMapArray(acc.experimentsByCommit, id, experiment)
287+
addToMapArray(acc.experimentsByCommit, baselineId, experiment)
279288
}
280289

281290
const setWorkspaceAsRunning = (

extension/src/experiments/model/index.test.ts

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ describe('ExperimentsModel', () => {
141141

142142
model.transformAndSet(data, false, '')
143143

144-
const experiments = model.getCombinedList()
144+
const experiments = model.getUniqueList()
145145

146146
const changed: string[] = []
147147
for (const { deps, sha } of experiments) {
@@ -273,7 +273,7 @@ describe('ExperimentsModel', () => {
273273
model.setSelected([])
274274
expect(model.getSelectedRevisions().map(({ id }) => id)).toStrictEqual([])
275275

276-
model.setSelected(model.getCombinedList())
276+
model.setSelected(model.getUniqueList())
277277
expect(model.getSelectedRevisions().map(({ id }) => id)).toStrictEqual([
278278
EXPERIMENT_WORKSPACE_ID,
279279
'testBranch',
@@ -358,4 +358,60 @@ describe('ExperimentsModel', () => {
358358
'three'
359359
])
360360
})
361+
362+
it('should handle duplicate commits being returned in the data', () => {
363+
const main = {
364+
experiments: [
365+
{
366+
name: 'campy-pall',
367+
rev: '0b4b001dfaa8f2c4cd2a62238699131ab2c679ea'
368+
},
369+
{
370+
name: 'shyer-stir',
371+
rev: '450e672f0d8913517ab2ab443f5d87b34f308290'
372+
}
373+
],
374+
name: 'main',
375+
rev: '61bed4ce8913eca7f73ca754d65bc5daad1520e2'
376+
}
377+
378+
const expShowWithDuplicateCommits = generateTestExpShowOutput(
379+
{},
380+
main,
381+
{
382+
name: 'branchOffMainWithCommit',
383+
rev: '351e42ace3cb6a3a853c65bef285e60748cc6341'
384+
},
385+
main
386+
)
387+
388+
const model = new ExperimentsModel('', buildMockMemento())
389+
model.transformAndSet(expShowWithDuplicateCommits, false, '')
390+
const distinctTreeItems = model.getWorkspaceAndCommits()
391+
expect(distinctTreeItems).toHaveLength(3)
392+
393+
const tableRows = model.getRowData()
394+
expect(tableRows).toHaveLength(4)
395+
expect(tableRows.map(({ id }) => id)).toStrictEqual([
396+
EXPERIMENT_WORKSPACE_ID,
397+
'main',
398+
'branchOffMainWithCommit',
399+
'main'
400+
])
401+
402+
const tableExperimentRows = (
403+
tableRows[1] as unknown as { subRows: Experiment[] }
404+
).subRows
405+
406+
const duplicateTableExperimentRows = (
407+
tableRows[3] as unknown as { subRows: Experiment[] }
408+
).subRows
409+
410+
expect(tableExperimentRows).toHaveLength(2)
411+
expect(tableExperimentRows.map(({ id }) => id)).toStrictEqual([
412+
'campy-pall',
413+
'shyer-stir'
414+
])
415+
expect(tableExperimentRows).toStrictEqual(duplicateTableExperimentRows)
416+
})
361417
})

extension/src/experiments/model/index.ts

Lines changed: 61 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -248,11 +248,25 @@ export class ExperimentsModel extends ModelWithPersistence {
248248
}
249249

250250
public getRevisionIds() {
251-
return this.getCombinedList().map(({ id }) => id)
251+
return this.getUniqueList().map(({ id }) => id)
252252
}
253253

254254
public getSelectedRevisions() {
255-
return this.getSelectedFromList(() => this.getCombinedList())
255+
const acc: SelectedExperimentWithColor[] = []
256+
257+
for (const experiment of this.getUniqueList()) {
258+
const { id } = experiment
259+
const displayColor = this.coloredStatus[id]
260+
if (displayColor) {
261+
acc.push({ ...experiment, displayColor } as SelectedExperimentWithColor)
262+
}
263+
}
264+
265+
return copyOriginalColors()
266+
.flatMap(orderedItem =>
267+
acc.filter(item => item.displayColor === orderedItem)
268+
)
269+
.filter(Boolean)
256270
}
257271

258272
public setSelected(selectedExperiments: Experiment[]) {
@@ -273,7 +287,7 @@ export class ExperimentsModel extends ModelWithPersistence {
273287
}
274288

275289
public getLabels() {
276-
return this.getCombinedList().map(({ label }) => label)
290+
return this.getUniqueList().map(({ label }) => label)
277291
}
278292

279293
public getLabelsToDecorate() {
@@ -283,7 +297,7 @@ export class ExperimentsModel extends ModelWithPersistence {
283297
}
284298

285299
public getWorkspaceAndCommits() {
286-
return [
300+
const experiments = [
287301
{
288302
...this.addDetails(this.workspace),
289303
hasChildren: false,
@@ -292,15 +306,18 @@ export class ExperimentsModel extends ModelWithPersistence {
292306
)
293307
? ExperimentType.RUNNING
294308
: ExperimentType.WORKSPACE
295-
},
296-
...this.commits.map(commit => {
297-
return {
298-
...this.addDetails(commit),
299-
hasChildren: !!this.experimentsByCommit.get(commit.id),
300-
type: ExperimentType.COMMIT
301-
}
302-
})
309+
}
303310
]
311+
312+
for (const commit of this.getCommits()) {
313+
experiments.push({
314+
...this.addDetails(commit),
315+
hasChildren: !!this.experimentsByCommit.get(commit.id),
316+
type: ExperimentType.COMMIT
317+
})
318+
}
319+
320+
return experiments
304321
}
305322

306323
public getWorkspaceCommitsAndExperiments() {
@@ -309,14 +326,14 @@ export class ExperimentsModel extends ModelWithPersistence {
309326

310327
public getErrors() {
311328
return new Set(
312-
this.getCombinedList()
329+
this.getUniqueList()
313330
.filter(({ error }) => error)
314331
.map(({ label }) => label)
315332
)
316333
}
317334

318335
public getExperimentParams(id: string) {
319-
const params = this.getCombinedList().find(
336+
const params = this.getUniqueList().find(
320337
experiment => experiment.id === id
321338
)?.params
322339

@@ -330,7 +347,7 @@ export class ExperimentsModel extends ModelWithPersistence {
330347
}
331348

332349
public getCommitsAndExperiments() {
333-
return collectOrderedCommitsAndExperiments(this.commits, commit =>
350+
return collectOrderedCommitsAndExperiments(this.getCommits(), commit =>
334351
this.getExperimentsByCommit(commit)
335352
)
336353
}
@@ -387,16 +404,24 @@ export class ExperimentsModel extends ModelWithPersistence {
387404
}
388405

389406
public getExperimentCount() {
390-
return sum([this.getExperimentsAndQueued().length, this.commits.length, 1])
407+
return sum([
408+
this.getExperimentsAndQueued().length,
409+
this.getCommits().length,
410+
1
411+
])
391412
}
392413

393414
public getFilteredCount() {
394415
const filtered = this.getFilteredExperiments()
395416
return filtered.length
396417
}
397418

398-
public getCombinedList() {
399-
return [this.workspace, ...this.commits, ...this.getExperimentsAndQueued()]
419+
public getUniqueList() {
420+
return [
421+
this.workspace,
422+
...this.getCommits(),
423+
...this.getExperimentsAndQueued()
424+
]
400425
}
401426

402427
public getExperimentsByCommitForTree(commit: Experiment) {
@@ -452,6 +477,20 @@ export class ExperimentsModel extends ModelWithPersistence {
452477
return this.currentSorts.findIndex(({ path }) => path === pathToRemove)
453478
}
454479

480+
private getCommits() {
481+
const ids = new Set<string>()
482+
const commits: Experiment[] = []
483+
for (const commit of this.commits) {
484+
const { id } = commit
485+
if (ids.has(id)) {
486+
continue
487+
}
488+
commits.push(commit)
489+
ids.add(id)
490+
}
491+
return commits
492+
}
493+
455494
private getFilteredExperiments() {
456495
const acc: Experiment[] = []
457496

@@ -467,7 +506,10 @@ export class ExperimentsModel extends ModelWithPersistence {
467506
private getExperimentsByCommit(commit: Experiment) {
468507
const experiments = this.experimentsByCommit
469508
.get(commit.id)
470-
?.map(experiment => this.addDetails(experiment))
509+
?.map(experiment => ({
510+
...this.addDetails(experiment),
511+
branch: commit.branch
512+
}))
471513
if (!experiments) {
472514
return
473515
}
@@ -607,23 +649,6 @@ export class ExperimentsModel extends ModelWithPersistence {
607649
return color
608650
}
609651

610-
private getSelectedFromList(getList: () => Experiment[]) {
611-
const acc: SelectedExperimentWithColor[] = []
612-
613-
for (const experiment of getList()) {
614-
const displayColor = this.coloredStatus[experiment.id]
615-
if (displayColor) {
616-
acc.push({ ...experiment, displayColor } as SelectedExperimentWithColor)
617-
}
618-
}
619-
620-
return copyOriginalColors()
621-
.flatMap(orderedItem =>
622-
acc.filter(item => item.displayColor === orderedItem)
623-
)
624-
.filter(Boolean)
625-
}
626-
627652
private reviveColoredStatus() {
628653
const uniqueStatus: ColoredStatus = {}
629654
const colors = new Set<Color>()

extension/src/experiments/webview/contract.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export type Experiment = {
4646
starred?: boolean
4747
status?: ExperimentStatus
4848
timestamp?: string | null
49-
branch: string | undefined
49+
branch?: string
5050
}
5151

5252
export const isRunning = (status: ExperimentStatus | undefined): boolean =>

extension/src/experiments/webview/messages.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ export class WebviewMessages {
407407

408408
private setSelectedExperiments(ids: string[]) {
409409
const experiments = this.experiments
410-
.getCombinedList()
410+
.getUniqueList()
411411
.filter(({ id }) => ids.includes(id))
412412

413413
this.experiments.setSelected(experiments)

extension/src/test/suite/experiments/index.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -873,7 +873,7 @@ suite('Experiments Test Suite', () => {
873873
const queuedId = '90aea7f'
874874

875875
const isExperimentSelected = (expId: string): boolean =>
876-
!!experimentsModel.getCombinedList().find(({ id }) => id === expId)
876+
!!experimentsModel.getUniqueList().find(({ id }) => id === expId)
877877
?.selected
878878

879879
expect(
@@ -1101,7 +1101,7 @@ suite('Experiments Test Suite', () => {
11011101
const areExperimentsStarred = (expIds: string[]): boolean =>
11021102
expIds
11031103
.map(expId =>
1104-
experimentsModel.getCombinedList().find(({ id }) => id === expId)
1104+
experimentsModel.getUniqueList().find(({ id }) => id === expId)
11051105
)
11061106
.every(exp => exp?.starred)
11071107

0 commit comments

Comments
 (0)