Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a3bee4d

Browse files
committedMay 29, 2023
Add deletion patch.
1 parent 8ff9647 commit a3bee4d

File tree

3 files changed

+352
-215
lines changed

3 files changed

+352
-215
lines changed
 

‎patches/store-socket.diff

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ To test this:
99

1010
It should open in your existing code-server instance.
1111

12+
Delete session from session registry on extension host termination.
13+
1214
Index: code-server/lib/vscode/src/vs/workbench/api/node/extHostExtensionService.ts
1315
===================================================================
1416
--- code-server.orig/lib/vscode/src/vs/workbench/api/node/extHostExtensionService.ts
@@ -53,7 +55,7 @@ Index: code-server/lib/vscode/src/vs/workbench/api/node/extHostExtensionService.
5355
+ const codeServerSocketPath = path.join(os.tmpdir(), 'code-server-ipc.sock');
5456
+ await new Promise<void>((resolve, reject) => {
5557
+ const opts: _http.RequestOptions = {
56-
+ path: '/session',
58+
+ path: '/add-session',
5759
+ socketPath: codeServerSocketPath,
5860
+ method: 'POST',
5961
+ headers: {
@@ -92,3 +94,48 @@ Index: code-server/lib/vscode/src/vs/workbench/api/node/extHostExtensionService.
9294
// Do this when extension service exists, but extensions are not being activated yet.
9395
const configProvider = await this._extHostConfiguration.getConfigProvider();
9496
await connectProxyResolver(this._extHostWorkspace, configProvider, this, this._logService, this._mainThreadTelemetryProxy, this._initData);
97+
Index: code-server/lib/vscode/src/vs/workbench/api/node/extensionHostProcess.ts
98+
===================================================================
99+
--- code-server.orig/lib/vscode/src/vs/workbench/api/node/extensionHostProcess.ts
100+
+++ code-server/lib/vscode/src/vs/workbench/api/node/extensionHostProcess.ts
101+
@@ -3,6 +3,9 @@
102+
* Licensed under the MIT License. See License.txt in the project root for license information.
103+
*--------------------------------------------------------------------------------------------*/
104+
105+
+import * as os from 'os';
106+
+import * as _http from 'http';
107+
+import * as path from 'vs/base/common/path';
108+
import * as nativeWatchdog from 'native-watchdog';
109+
import * as net from 'net';
110+
import * as minimist from 'minimist';
111+
@@ -400,7 +403,29 @@ async function startExtensionHostProcess
112+
);
113+
114+
// rewrite onTerminate-function to be a proper shutdown
115+
- onTerminate = (reason: string) => extensionHostMain.terminate(reason);
116+
+ onTerminate = (reason: string) => {
117+
+ extensionHostMain.terminate(reason);
118+
+ (() => {
119+
+ const socketPath = process.env['VSCODE_IPC_HOOK_CLI'];
120+
+ if (!socketPath) {
121+
+ return;
122+
+ }
123+
+ const message = JSON.stringify({socketPath});
124+
+ const codeServerSocketPath = path.join(os.tmpdir(), 'code-server-ipc.sock');
125+
+ const opts: _http.RequestOptions = {
126+
+ path: '/delete-session',
127+
+ socketPath: codeServerSocketPath,
128+
+ method: 'POST',
129+
+ headers: {
130+
+ 'content-type': 'application/json',
131+
+ 'accept': 'application/json'
132+
+ }
133+
+ };
134+
+ const req = _http.request(opts);
135+
+ req.write(message);
136+
+ req.end();
137+
+ })();
138+
+ };
139+
}
140+
141+
startExtensionHostProcess().catch((err) => console.log(err));

‎src/node/vscodeSocket.ts

Lines changed: 68 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ export interface EditorSessionEntry {
2424
socketPath: string
2525
}
2626

27-
// interface DeleteSessionRequest {
28-
// socketPath: string
29-
// }
27+
interface DeleteSessionRequest {
28+
socketPath: string
29+
}
3030

31-
interface RegisterSessionRequest {
31+
interface AddSessionRequest {
3232
entry: EditorSessionEntry
3333
}
3434

@@ -37,52 +37,15 @@ interface GetSessionResponse {
3737
socketPath?: string
3838
}
3939

40-
export class EditorSessionManagerClient {
41-
constructor(private codeServerSocketPath: string) {}
42-
43-
async canConnect() {
44-
return canConnect(this.codeServerSocketPath)
45-
}
46-
47-
async getConnectedSocketPath(filePath: string): Promise<string | undefined> {
48-
const response = await new Promise<GetSessionResponse>((resolve, reject) => {
49-
const opts = {
50-
path: "/session?filePath=" + encodeURIComponent(filePath),
51-
socketPath: this.codeServerSocketPath,
52-
}
53-
const req = http.get(opts, (res) => {
54-
let rawData = ""
55-
res.setEncoding("utf8")
56-
res.on("data", (chunk) => {
57-
rawData += chunk
58-
})
59-
res.on("end", () => {
60-
try {
61-
const obj = JSON.parse(rawData)
62-
if (res.statusCode === 200) {
63-
resolve(obj)
64-
} else {
65-
reject(new Error("Unexpected status code: " + res.statusCode))
66-
}
67-
} catch (e: unknown) {
68-
reject(e)
69-
}
70-
})
71-
})
72-
req.on("error", reject)
73-
req.end()
74-
})
75-
return response.socketPath
76-
}
77-
}
78-
7940
export async function makeEditorSessionManagerServer(
8041
codeServerSocketPath: string,
8142
editorSessionManager: EditorSessionManager,
8243
): Promise<http.Server> {
8344
const router = express()
45+
8446
// eslint-disable-next-line import/no-named-as-default-member
8547
router.use(express.json())
48+
8649
router.get("/session", async (req, res) => {
8750
const filePath = req.query.filePath as string
8851
if (!filePath) {
@@ -97,17 +60,25 @@ export async function makeEditorSessionManagerServer(
9760
res.status(500).send(error)
9861
}
9962
})
100-
router.post("/session", async (req, res) => {
101-
const request = req.body as RegisterSessionRequest
63+
64+
router.post("/add-session", async (req, res) => {
65+
const request = req.body as AddSessionRequest
10266
if (!request.entry) {
10367
res.status(400).send("entry is required")
10468
}
105-
editorSessionManager.registerSession(request.entry)
69+
editorSessionManager.addSession(request.entry)
10670
res.status(200).send()
10771
})
108-
router.delete("/session", async (req, res) => {
109-
throw new Error("Not implemented")
72+
73+
router.post("/delete-session", async (req, res) => {
74+
const request = req.body as DeleteSessionRequest
75+
if (!request.socketPath) {
76+
res.status(400).send("socketPath is required")
77+
}
78+
editorSessionManager.deleteSession(request.socketPath)
79+
res.status(200).send()
11080
})
81+
11182
const server = http.createServer(router)
11283
try {
11384
await fs.unlink(codeServerSocketPath)
@@ -133,18 +104,19 @@ export class EditorSessionManager {
133104
// Map from socket path to EditorSessionEntry.
134105
private entries = new Map<string, EditorSessionEntry>()
135106

136-
registerSession(entry: EditorSessionEntry): void {
107+
addSession(entry: EditorSessionEntry): void {
108+
logger.debug(`Adding session to session registry: ${entry.socketPath}`)
137109
this.entries.set(entry.socketPath, entry)
138110
}
139111

140-
getCandidatesForFile(path: string): EditorSessionEntry[] {
112+
getCandidatesForFile(filePath: string): EditorSessionEntry[] {
141113
const matchCheckResults = new Map<string, boolean>()
142114

143115
const checkMatch = (entry: EditorSessionEntry): boolean => {
144116
if (matchCheckResults.has(entry.socketPath)) {
145117
return matchCheckResults.get(entry.socketPath)!
146118
}
147-
const result = this.containsPath(entry, path)
119+
const result = entry.workspace.folders.some((folder) => filePath.startsWith(folder.uri.path + path.sep))
148120
matchCheckResults.set(entry.socketPath, result)
149121
return result
150122
}
@@ -165,17 +137,17 @@ export class EditorSessionManager {
165137
})
166138
}
167139

168-
private containsPath(entry: EditorSessionEntry, path: string): boolean {
169-
return entry.workspace.folders.some((folder) => path.startsWith(folder.uri.path))
140+
async deleteSession(socketPath: string): Promise<void> {
141+
logger.debug(`Deleting session from session registry: ${socketPath}`)
142+
this.entries.delete(socketPath)
170143
}
171144

172145
/**
173146
* Returns the best socket path that we can connect to.
174-
* See getSocketPaths for the order of preference.
175147
* We also delete any sockets that we can't connect to.
176148
*/
177-
async getConnectedSocketPath(path: string): Promise<string | undefined> {
178-
const candidates = this.getCandidatesForFile(path)
149+
async getConnectedSocketPath(filePath: string): Promise<string | undefined> {
150+
const candidates = this.getCandidatesForFile(filePath)
179151
let match: EditorSessionEntry | undefined = undefined
180152

181153
const toDelete = new Set<EditorSessionEntry>()
@@ -189,11 +161,49 @@ export class EditorSessionManager {
189161

190162
if (toDelete.size > 0) {
191163
for (const candidate of toDelete) {
192-
logger.debug(`Deleting stale socket from socket registry: ${candidate.socketPath}`)
193-
this.entries.delete(candidate.socketPath)
164+
this.deleteSession(candidate.socketPath)
194165
}
195166
}
196167

197168
return match?.socketPath
198169
}
199170
}
171+
172+
export class EditorSessionManagerClient {
173+
constructor(private codeServerSocketPath: string) {}
174+
175+
async canConnect() {
176+
return canConnect(this.codeServerSocketPath)
177+
}
178+
179+
async getConnectedSocketPath(filePath: string): Promise<string | undefined> {
180+
const response = await new Promise<GetSessionResponse>((resolve, reject) => {
181+
const opts = {
182+
path: "/session?filePath=" + encodeURIComponent(filePath),
183+
socketPath: this.codeServerSocketPath,
184+
}
185+
const req = http.get(opts, (res) => {
186+
let rawData = ""
187+
res.setEncoding("utf8")
188+
res.on("data", (chunk) => {
189+
rawData += chunk
190+
})
191+
res.on("end", () => {
192+
try {
193+
const obj = JSON.parse(rawData)
194+
if (res.statusCode === 200) {
195+
resolve(obj)
196+
} else {
197+
reject(new Error("Unexpected status code: " + res.statusCode))
198+
}
199+
} catch (e: unknown) {
200+
reject(e)
201+
}
202+
})
203+
})
204+
req.on("error", reject)
205+
req.end()
206+
})
207+
return response.socketPath
208+
}
209+
}

‎test/unit/node/vscodeSocket.test.ts

Lines changed: 236 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -1,192 +1,272 @@
1-
import { EditorSessionEntry, EditorSessionManager } from "../../../src/node/vscodeSocket"
2-
import { clean, tmpdir } from "../../utils/helpers"
3-
import * as path from "path"
41
import * as net from "net"
5-
import { promises as fs } from "fs"
6-
7-
const makeTestEntries = (tmpDirPath: string): EditorSessionEntry[] => {
8-
return [
9-
{
10-
workspace: {
11-
id: "ws1",
12-
folders: [
13-
{
14-
uri: {
15-
path: "/foo",
16-
},
17-
},
18-
{
19-
uri: {
20-
path: "/bar",
21-
},
22-
},
23-
],
24-
},
25-
socketPath: `${tmpDirPath}/vscode-ipc-1-old.sock`,
26-
},
27-
{
28-
workspace: {
29-
id: "ws1",
30-
folders: [
31-
{
32-
uri: {
33-
path: "/foo",
34-
},
35-
},
36-
{
37-
uri: {
38-
path: "/bar",
39-
},
40-
},
41-
],
42-
},
43-
socketPath: `${tmpDirPath}/vscode-ipc-1-new.sock`,
44-
},
45-
{
46-
workspace: {
47-
id: "ws2",
48-
folders: [
49-
{
50-
uri: {
51-
path: "/foo",
52-
},
53-
},
54-
{
55-
uri: {
56-
path: "/baz",
57-
},
58-
},
59-
],
60-
},
61-
socketPath: `${tmpDirPath}/vscode-ipc-2.sock`,
62-
},
63-
]
64-
}
2+
import { EditorSessionManager } from "../../../src/node/vscodeSocket"
3+
import { clean, tmpdir } from "../../utils/helpers"
654

66-
describe("VscodeSocketResolver", () => {
5+
describe("EditorSessionManager", () => {
676
let tmpDirPath: string
68-
let tmpFilePath: string
69-
let entries: EditorSessionEntry[] = []
70-
let fileContents = ""
717

72-
const testName = "readSocketPath"
8+
const testName = "esm"
9+
7310
beforeAll(async () => {
7411
await clean(testName)
7512
})
7613

7714
beforeEach(async () => {
7815
tmpDirPath = await tmpdir(testName)
79-
tmpFilePath = path.join(tmpDirPath, "readSocketPath.txt")
80-
entries = makeTestEntries(tmpDirPath)
81-
fileContents = entries.map((entry) => JSON.stringify(entry)).join("\n")
82-
await fs.writeFile(tmpFilePath, fileContents)
8316
})
8417

85-
it("should throw an error on load if the socket registry file is a directory", async () => {
86-
const resolver = new EditorSessionManager(tmpDirPath)
87-
expect(() => resolver.load()).rejects.toThrow("EISDIR")
88-
})
89-
90-
describe("getSocketPaths", () => {
91-
it("should return empty list if it can't read the socket registry file", async () => {
92-
const resolver = new EditorSessionManager(path.join(tmpDirPath, "not-a-file"))
93-
await resolver.load()
94-
const socketPaths = resolver.getSocketPaths()
95-
expect(socketPaths).toEqual([])
96-
})
97-
it("should prefer the last added recent socket path for a matching path", async () => {
98-
const resolver = new EditorSessionManager(tmpFilePath)
99-
await resolver.load()
100-
const socketPaths = resolver.getSocketPaths(["/bar/README.md:1:1"])
101-
expect(socketPaths).toEqual([
18+
describe("getCandidatesForFile", () => {
19+
it("should prefer the last added socket path for a matching path", async () => {
20+
const manager = new EditorSessionManager()
21+
manager.addSession({
22+
workspace: {
23+
id: "aaa",
24+
folders: [
25+
{
26+
uri: {
27+
path: "/aaa",
28+
},
29+
},
30+
],
31+
},
32+
socketPath: `${tmpDirPath}/vscode-ipc-aaa-1.sock`,
33+
})
34+
manager.addSession({
35+
workspace: {
36+
id: "aaa",
37+
folders: [
38+
{
39+
uri: {
40+
path: "/aaa",
41+
},
42+
},
43+
],
44+
},
45+
socketPath: `${tmpDirPath}/vscode-ipc-aaa-2.sock`,
46+
})
47+
manager.addSession({
48+
workspace: {
49+
id: "bbb",
50+
folders: [
51+
{
52+
uri: {
53+
path: "/bbb",
54+
},
55+
},
56+
],
57+
},
58+
socketPath: `${tmpDirPath}/vscode-ipc-bbb.sock`,
59+
})
60+
const socketPaths = manager.getCandidatesForFile("/aaa/some-file:1:1")
61+
expect(socketPaths.map((x) => x.socketPath)).toEqual([
10262
// Matches
103-
`${tmpDirPath}/vscode-ipc-1-new.sock`,
104-
`${tmpDirPath}/vscode-ipc-1-old.sock`,
63+
`${tmpDirPath}/vscode-ipc-aaa-2.sock`,
64+
`${tmpDirPath}/vscode-ipc-aaa-1.sock`,
10565
// Non-matches
106-
`${tmpDirPath}/vscode-ipc-2.sock`,
107-
])
108-
})
109-
it("should prefer the last added socket path for a matching path, even across workspaces", async () => {
110-
const resolver = new EditorSessionManager(tmpFilePath)
111-
await resolver.load()
112-
const socketPaths = resolver.getSocketPaths(["/foo/README.md:1:1"])
113-
expect(socketPaths).toEqual([
114-
// Matches
115-
`${tmpDirPath}/vscode-ipc-2.sock`,
116-
`${tmpDirPath}/vscode-ipc-1-new.sock`,
117-
`${tmpDirPath}/vscode-ipc-1-old.sock`,
66+
`${tmpDirPath}/vscode-ipc-bbb.sock`,
11867
])
11968
})
120-
it("should return the last added socketPath if there are no matches", async () => {
121-
const resolver = new EditorSessionManager(tmpFilePath)
122-
await resolver.load()
12369

124-
const socketPaths = resolver.getSocketPaths()
125-
expect(socketPaths).toEqual([
126-
`${tmpDirPath}/vscode-ipc-2.sock`,
127-
`${tmpDirPath}/vscode-ipc-1-new.sock`,
128-
`${tmpDirPath}/vscode-ipc-1-old.sock`,
70+
it("should return the last added socketPath if there are no matches", async () => {
71+
const manager = new EditorSessionManager()
72+
manager.addSession({
73+
workspace: {
74+
id: "aaa",
75+
folders: [
76+
{
77+
uri: {
78+
path: "/aaa",
79+
},
80+
},
81+
],
82+
},
83+
socketPath: `${tmpDirPath}/vscode-ipc-aaa.sock`,
84+
})
85+
manager.addSession({
86+
workspace: {
87+
id: "bbb",
88+
folders: [
89+
{
90+
uri: {
91+
path: "/bbb",
92+
},
93+
},
94+
],
95+
},
96+
socketPath: `${tmpDirPath}/vscode-ipc-bbb.sock`,
97+
})
98+
const socketPaths = manager.getCandidatesForFile("/ccc/some-file:1:1")
99+
expect(socketPaths.map((x) => x.socketPath)).toEqual([
100+
`${tmpDirPath}/vscode-ipc-bbb.sock`,
101+
`${tmpDirPath}/vscode-ipc-aaa.sock`,
129102
])
130103
})
131-
})
132-
133-
describe("save", () => {
134-
it("should write back to the socket registry file", async () => {
135-
jest.spyOn(fs, "writeFile")
136-
expect(fs.writeFile).not.toHaveBeenCalled()
137104

138-
const resolver = new EditorSessionManager(tmpFilePath)
139-
await resolver.load()
140-
await resolver.save()
141-
expect(fs.writeFile).toHaveBeenCalledWith(tmpFilePath, fileContents)
105+
it("does not just directly do a substring match", async () => {
106+
const manager = new EditorSessionManager()
107+
manager.addSession({
108+
workspace: {
109+
id: "aaa",
110+
folders: [
111+
{
112+
uri: {
113+
path: "/aaa",
114+
},
115+
},
116+
],
117+
},
118+
socketPath: `${tmpDirPath}/vscode-ipc-aaa.sock`,
119+
})
120+
manager.addSession({
121+
workspace: {
122+
id: "bbb",
123+
folders: [
124+
{
125+
uri: {
126+
path: "/bbb",
127+
},
128+
},
129+
],
130+
},
131+
socketPath: `${tmpDirPath}/vscode-ipc-bbb.sock`,
132+
})
133+
const entries = manager.getCandidatesForFile("/aaaxxx/some-file:1:1")
134+
expect(entries.map((x) => x.socketPath)).toEqual([
135+
`${tmpDirPath}/vscode-ipc-bbb.sock`,
136+
`${tmpDirPath}/vscode-ipc-aaa.sock`,
137+
])
142138
})
143139
})
144140

145141
describe("getConnectedSocketPath", () => {
146142
it("should return socket path if socket is active", async () => {
147-
const resolver = new EditorSessionManager(tmpFilePath)
148-
await resolver.load()
149-
const connectedPromise = new Promise((resolve) => {
150-
const server = net.createServer(() => {
151-
// Close after getting the first connection.
152-
server.close()
153-
})
154-
server.once("listening", () => resolve(server))
155-
server.listen(`${tmpDirPath}/vscode-ipc-1-new.sock`)
143+
listenOn(`${tmpDirPath}/vscode-ipc-aaa.sock`)
144+
const manager = new EditorSessionManager()
145+
manager.addSession({
146+
workspace: {
147+
id: "aaa",
148+
folders: [
149+
{
150+
uri: {
151+
path: "/aaa",
152+
},
153+
},
154+
],
155+
},
156+
socketPath: `${tmpDirPath}/vscode-ipc-aaa.sock`,
156157
})
157-
const socketPathPromise = resolver.getConnectedSocketPath(["/bar/README.md:1:1"])
158-
const [socketPath] = await Promise.all([socketPathPromise, connectedPromise])
159-
expect(socketPath).toBe(`${tmpDirPath}/vscode-ipc-1-new.sock`)
158+
const socketPath = await manager.getConnectedSocketPath("/aaa/some-file:1:1")
159+
expect(socketPath).toBe(`${tmpDirPath}/vscode-ipc-aaa.sock`)
160160
})
161+
161162
it("should return undefined if socket is inactive", async () => {
162-
const resolver = new EditorSessionManager(tmpFilePath)
163-
await resolver.load()
164-
const socketPath = await resolver.getConnectedSocketPath(["/bar/README.md:1:1"])
163+
const manager = new EditorSessionManager()
164+
manager.addSession({
165+
workspace: {
166+
id: "aaa",
167+
folders: [
168+
{
169+
uri: {
170+
path: "/aaa",
171+
},
172+
},
173+
],
174+
},
175+
socketPath: `${tmpDirPath}/vscode-ipc-aaa.sock`,
176+
})
177+
const socketPath = await manager.getConnectedSocketPath("/aaa/some-file:1:1")
178+
expect(socketPath).toBeUndefined()
179+
})
180+
181+
it("should return undefined given no matching active sockets", async () => {
182+
const servers = listenOn(`${tmpDirPath}/vscode-ipc-bbb.sock`)
183+
const manager = new EditorSessionManager()
184+
manager.addSession({
185+
workspace: {
186+
id: "aaa",
187+
folders: [
188+
{
189+
uri: {
190+
path: "/aaa",
191+
},
192+
},
193+
],
194+
},
195+
socketPath: `${tmpDirPath}/vscode-ipc-aaa.sock`,
196+
})
197+
const socketPath = await manager.getConnectedSocketPath("/aaa/some-file:1:1")
165198
expect(socketPath).toBeUndefined()
199+
servers.close()
166200
})
201+
202+
it("should return undefined if there are no entries", async () => {
203+
const manager = new EditorSessionManager()
204+
const socketPath = await manager.getConnectedSocketPath("/aaa/some-file:1:1")
205+
expect(socketPath).toBeUndefined()
206+
})
207+
167208
it("should return most recently used socket path available", async () => {
168-
jest.spyOn(fs, "writeFile")
169-
const resolver = new EditorSessionManager(tmpFilePath)
170-
jest.spyOn(resolver, "save")
171-
await resolver.load()
172-
const connectedPromise = new Promise((resolve) => {
173-
const server = net.createServer(() => {
174-
// Close after getting the first connection.
175-
server.close()
176-
})
177-
server.once("listening", () => resolve(server))
178-
server.listen(`${tmpDirPath}/vscode-ipc-1-old.sock`)
209+
listenOn(`${tmpDirPath}/vscode-ipc-aaa-1.sock`)
210+
const manager = new EditorSessionManager()
211+
manager.addSession({
212+
workspace: {
213+
id: "aaa",
214+
folders: [
215+
{
216+
uri: {
217+
path: "/aaa",
218+
},
219+
},
220+
],
221+
},
222+
socketPath: `${tmpDirPath}/vscode-ipc-aaa-1.sock`,
223+
})
224+
manager.addSession({
225+
workspace: {
226+
id: "aaa",
227+
folders: [
228+
{
229+
uri: {
230+
path: "/aaa",
231+
},
232+
},
233+
],
234+
},
235+
socketPath: `${tmpDirPath}/vscode-ipc-aaa-2.sock`,
179236
})
180-
const socketPathPromise = resolver.getConnectedSocketPath(["/bar/README.md:1:1"])
181-
const [socketPath] = await Promise.all([socketPathPromise, connectedPromise])
182-
expect(socketPath).toBe(`${tmpDirPath}/vscode-ipc-1-old.sock`)
183237

184-
// Should clear socket paths that failed to connect.
185-
await (resolver.save as unknown as jest.SpiedFunction<typeof resolver.save>).mock.results[0].value
186-
expect(fs.writeFile).toHaveBeenCalledWith(
187-
tmpFilePath,
188-
[JSON.stringify(entries[0]), JSON.stringify(entries[2])].join("\n"),
189-
)
238+
const socketPath = await manager.getConnectedSocketPath("/aaa/some-file:1:1")
239+
expect(socketPath).toBe(`${tmpDirPath}/vscode-ipc-aaa-1.sock`)
240+
// Failed sockets should be removed from the entries.
241+
expect((manager as any).entries.has(`${tmpDirPath}/vscode-ipc-aaa-2.sock`)).toBe(false)
190242
})
191243
})
192244
})
245+
246+
function listenOn(...socketPaths: string[]) {
247+
const servers = socketPaths.map((socketPath) => {
248+
const server = net.createServer(() => {
249+
close()
250+
})
251+
server.listen(socketPath)
252+
return server
253+
})
254+
255+
async function close() {
256+
await Promise.all(
257+
servers.map(
258+
(server) =>
259+
new Promise<void>((resolve, reject) => {
260+
server.close((err) => {
261+
if (err) {
262+
reject(err)
263+
return
264+
}
265+
resolve()
266+
})
267+
}),
268+
),
269+
)
270+
}
271+
return { close }
272+
}

0 commit comments

Comments
 (0)
Please sign in to comment.