1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
'use strict';
const assert = require('node:assert/strict');
const { test } = require('node:test');
const { spawnSync } = require('node:child_process');
const { buildRunCommand, registerRunner } = require('../src/runner');
test('commands quote paths and only run after changing directory', () => {
assert.equal(buildRunCommand('/tools/my cpsc', '/work/my file.cps', '/work/my project', 'posix'),
"cd '/work/my project' && '/tools/my cpsc' '/work/my file.cps' -r");
assert.equal(buildRunCommand('C:\\My Tools\\cpsc.exe', 'D:\\My Files\\test.cps', 'D:\\My Files', 'cmd'),
'cd /d "D:\\My Files" && "C:\\My Tools\\cpsc.exe" "D:\\My Files\\test.cps" -r');
assert.equal(buildRunCommand('C:\\My Tools\\cpsc.exe', "D:\\Pat's Files\\test.cps", "D:\\Pat's Files", 'powershell'),
"Set-Location -LiteralPath 'D:\\Pat''s Files'; if ($?) { & 'C:\\My Tools\\cpsc.exe' 'D:\\Pat''s Files\\test.cps' -r }");
assert.equal(buildRunCommand('cpsc', 'Pat\u2019s \u2018x\u201B.cps', '.', 'powershell'),
"Set-Location -LiteralPath '.'; if ($?) { & 'cpsc' 'Pat\u2019\u2019s \u2018\u2018x\u201B\u201B.cps' -r }");
assert.throws(() => buildRunCommand('cpsc\nother', 'test.cps', '.', 'posix'), /line breaks/);
if (process.platform !== 'win32') {
const file = "/tmp/Pat's file $(echo unexpected) `echo unexpected`.cps";
const result = spawnSync('/bin/sh', ['-c', buildRunCommand('/usr/bin/printf', file, '/tmp', 'posix')], { encoding: 'utf8' });
assert.equal(result.status, 0);
assert.equal(result.stdout, file);
const failed = spawnSync('/bin/sh', ['-c', buildRunCommand('/usr/bin/printf', file, '/missing-cie-run-directory', 'posix')], { encoding: 'utf8' });
assert.notEqual(failed.status, 0);
assert.equal(failed.stdout, '');
}
});
function harness(platform = 'linux') {
let run, close;
const calls = { saved: 0, terminals: [], errors: [], settings: [] };
const document = {
languageId: 'cie-pseudocode', isUntitled: false,
uri: { scheme: 'file', fsPath: '/project/sub/my file.cps' },
async save() { calls.saved++; return true; }
};
const vscode = {
env: { shell: 'C:\\Windows\\System32\\cmd.exe' },
commands: { registerCommand(id, callback) { assert.equal(id, 'cie-pseudocode.runFile'); run = callback; return { dispose() {} }; } },
workspace: {
async openTextDocument() { return document; },
getWorkspaceFolder() { return { uri: { fsPath: '/project' } }; },
getConfiguration(section, uri) {
calls.settings.push({ section, uri });
return { get(key, fallback) { return section === 'cie-pseudocode' ? '/tools/my cpsc' : fallback; } };
}
},
window: {
activeTextEditor: { document },
async showTextDocument(doc) { this.activeTextEditor = { document: doc }; },
showErrorMessage(message) { calls.errors.push(message); },
onDidCloseTerminal(callback) { close = callback; return { dispose() {} }; },
createTerminal(name) {
const terminal = { name, sent: [], show(focus) { this.preserveFocus = focus; }, sendText(text) { this.sent.push(text); }, dispose() {} };
calls.terminals.push(terminal);
return terminal;
}
}
};
registerRunner(vscode, { subscriptions: [] }, platform);
return { vscode, document, calls, run: uri => run(uri), close: terminal => close(terminal) };
}
test('save before running the whole file, scope settings, reuse and recreate the terminal', async () => {
const h = harness();
await h.run(h.document.uri);
assert.equal(h.calls.saved, 1);
assert.equal(h.calls.terminals[0].preserveFocus, false);
assert.equal(h.calls.terminals[0].sent[0], "cd '/project' && '/tools/my cpsc' '/project/sub/my file.cps' -r");
assert.equal(h.calls.settings[0].uri, h.document.uri);
h.close({});
await h.run();
assert.equal(h.calls.terminals.length, 1);
h.close(h.calls.terminals[0]);
h.vscode.workspace.getWorkspaceFolder = () => undefined;
await h.run();
assert.equal(h.calls.terminals.length, 2);
assert.match(h.calls.terminals[1].sent[0], /^cd '\/project\/sub'/);
assert.deepEqual(h.calls.errors, []);
});
test('save cancellation and failure do not create a terminal; untitled files use the saved URI', async () => {
const h = harness();
h.document.isUntitled = true;
h.document.uri = { scheme: 'untitled', fsPath: 'Untitled-1' };
h.document.save = async () => false;
await h.run();
assert.equal(h.calls.terminals.length, 0);
h.document.save = async () => { throw new Error('Save failed'); };
await h.run();
assert.match(h.calls.errors[0], /Save failed/);
assert.equal(h.calls.terminals.length, 0);
h.document.save = async () => {
h.vscode.window.activeTextEditor = { document: { ...h.document, isUntitled: false,
uri: { scheme: 'file', fsPath: '/project/new file.cps' } } };
return true;
};
await h.run();
assert.match(h.calls.terminals[0].sent[0], /'\/project\/new file.cps' -r$/);
});
test('Windows recognizes custom pwsh and cmd profiles and retains the reused terminal shell', async () => {
const h = harness('win32');
let profile = 'My Shell';
h.vscode.workspace.getConfiguration = section => ({ get(key, fallback) {
if (section === 'cie-pseudocode') return 'cpsc';
if (key === 'integrated.defaultProfile.windows') return profile;
if (key === 'integrated.profiles.windows') return { 'My Shell': { path: 'C:\\Program Files\\PowerShell\\7\\pwsh.exe' } };
return fallback;
} });
await h.run();
assert.match(h.calls.terminals[0].sent[0], /^Set-Location.*& 'cpsc'/);
profile = 'Command Prompt';
await h.run();
assert.match(h.calls.terminals[0].sent[1], /^Set-Location/);
h.close(h.calls.terminals[0]);
await h.run();
assert.match(h.calls.terminals[1].sent[0], /^cd \/d .*&& "cpsc"/);
});
|