Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/test/base/simulationOutcome.ts
13389 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
import * as fs from 'fs';
6
import * as path from 'path';
7
import { SimpleRPC } from '../../src/extension/onboardDebug/node/copilotDebugWorker/rpc';
8
import { createServiceIdentifier } from '../../src/util/common/services';
9
import { ITestRunResult } from '../testExecutor';
10
import { SimulationTest, toDirname } from './stest';
11
12
13
export const ISimulationOutcome = createServiceIdentifier<ISimulationOutcome>('ISimulationOutcome');
14
15
export interface ISimulationOutcome {
16
readonly _serviceBrand: undefined;
17
get(test: SimulationTest): Promise<OutcomeEntry | undefined>;
18
set(test: SimulationTest, results: ITestRunResult[]): Promise<void>;
19
}
20
21
type TestSubset = Pick<SimulationTest, 'outcomeCategory' | 'fullName'>;
22
23
export class ProxiedSimulationOutcome implements ISimulationOutcome {
24
25
declare readonly _serviceBrand: undefined;
26
27
public static registerTo(instance: ISimulationOutcome, rpc: SimpleRPC): ISimulationOutcome {
28
rpc.registerMethod('ProxiedSimulationOutcome.get', (test) => instance.get(test));
29
rpc.registerMethod('ProxiedSimulationOutcome.set', ({ test, results }) => instance.set(test, results));
30
return instance;
31
}
32
33
constructor(
34
private readonly rpc: SimpleRPC,
35
) {
36
}
37
38
get(test: TestSubset): Promise<OutcomeEntry | undefined> {
39
return this.rpc.callMethod('ProxiedSimulationOutcome.get', { fullName: test.fullName, outcomeCategory: test.outcomeCategory } satisfies TestSubset);
40
}
41
42
set(test: TestSubset, results: ITestRunResult[]): Promise<void> {
43
return this.rpc.callMethod('ProxiedSimulationOutcome.set', { test: { fullName: test.fullName, outcomeCategory: test.outcomeCategory } satisfies TestSubset, results });
44
}
45
}
46
47
export const outcomePath = path.join(__dirname, '../test/outcome');
48
49
export class SimulationOutcomeImpl implements ISimulationOutcome {
50
51
declare readonly _serviceBrand: undefined;
52
53
private readonly outcome: Map<string, OutcomeEntry[]> = new Map();
54
55
constructor(
56
private readonly _runningAllTests: boolean
57
) {
58
}
59
60
async get(test: TestSubset): Promise<OutcomeEntry | undefined> {
61
const filePath = path.join(outcomePath, this._getCategoryFilename(test.outcomeCategory));
62
const entriesBuffer = await fs.promises.readFile(filePath, 'utf8');
63
const entries = JSON.parse(entriesBuffer) as OutcomeEntry[];
64
return entries.find(entry => entry.name === test.fullName);
65
}
66
67
set(test: TestSubset, results: ITestRunResult[]): Promise<void> {
68
const requestsSet = new Set<string>();
69
for (const testRun of results) {
70
for (const cacheInfo of testRun.cacheInfo) {
71
if (cacheInfo.type === 'request') {
72
requestsSet.add(cacheInfo.key);
73
}
74
}
75
}
76
77
const requests = Array.from(requestsSet).sort();
78
79
let entries: OutcomeEntry[];
80
if (!this.outcome.has(test.outcomeCategory)) {
81
entries = [];
82
this.outcome.set(test.outcomeCategory, entries);
83
} else {
84
entries = this.outcome.get(test.outcomeCategory)!;
85
}
86
87
entries.push({
88
name: test.fullName,
89
requests
90
});
91
92
return Promise.resolve();
93
}
94
95
public async write(): Promise<void> {
96
for (const [category, entries] of this.outcome) {
97
// When running a subset of tests, we will copy over the old existing test results for tests that were not executed
98
const filePath = path.join(outcomePath, this._getCategoryFilename(category));
99
100
if (!this._runningAllTests) {
101
let prevEntriesBuffer: string | undefined;
102
try {
103
prevEntriesBuffer = await fs.promises.readFile(filePath, 'utf8');
104
} catch (err) {
105
}
106
if (prevEntriesBuffer) {
107
try {
108
const prevEntries: OutcomeEntry[] = JSON.parse(prevEntriesBuffer);
109
const currentEntries = new Set<string>(entries.map(el => el.name));
110
for (const prevEntry of prevEntries) {
111
if (!currentEntries.has(prevEntry.name)) {
112
entries.push(prevEntry);
113
}
114
}
115
} catch (err) {
116
console.error(err);
117
}
118
}
119
}
120
121
entries.sort((a, b) => a.name.localeCompare(b.name));
122
await fs.promises.writeFile(filePath, JSON.stringify(entries, undefined, '\t'));
123
}
124
// console.log(this.outcome);
125
}
126
127
private _getCategoryFilename(category: string): string {
128
return `${toDirname(category.toLowerCase())}.json`;
129
}
130
131
public async cleanFolder(): Promise<void> {
132
// Clean the outcome folder
133
const names = await fs.promises.readdir(outcomePath);
134
const entries = new Set(names.filter(name => name.endsWith('.json')));
135
for (const [category, _] of this.outcome) {
136
entries.delete(this._getCategoryFilename(category));
137
}
138
if (entries.size > 0) {
139
await Promise.all(
140
Array.from(entries.values()).map(entry => fs.promises.unlink(path.join(outcomePath, entry)))
141
);
142
}
143
}
144
}
145
146
interface OutcomeEntry {
147
name: string;
148
requests: string[];
149
}
150
151