Skip to content

Commit dd06688

Browse files
author
Akos Kitta
committed
fix: copy when punctuation marks in sketch path
Changed the `source` and `cwd` args to avoid accidentally creating an invalid `glob` patterns when doing the brace expansion by `cpy`. Closes #2043 Signed-off-by: Akos Kitta <[email protected]>
1 parent 51f69f6 commit dd06688

File tree

2 files changed

+87
-7
lines changed

2 files changed

+87
-7
lines changed

Diff for: arduino-ide-extension/src/node/sketches-service-impl.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ export class SketchesServiceImpl
444444
* For example, on Windows, instead of getting an [8.3 filename](https://en.wikipedia.org/wiki/8.3_filename), callers will get a fully resolved path.
445445
* `C:\\Users\\KITTAA~1\\AppData\\Local\\Temp\\.arduinoIDE-unsaved2022615-21100-iahybb.yyvh\\sketch_jul15a` will be `C:\\Users\\kittaakos\\AppData\\Local\\Temp\\.arduinoIDE-unsaved2022615-21100-iahybb.yyvh\\sketch_jul15a`
446446
*/
447-
createTempFolder(): Promise<string> {
447+
private createTempFolder(): Promise<string> {
448448
return new Promise<string>((resolve, reject) => {
449449
temp.mkdir({ prefix: TempSketchPrefix }, (createError, dirPath) => {
450450
if (createError) {
@@ -523,13 +523,14 @@ export class SketchesServiceImpl
523523
} else {
524524
filter = () => true;
525525
}
526-
await cpy(source, destination, {
526+
await cpy(sourceFolderBasename, destination, {
527527
rename: (basename) =>
528528
sourceFolderBasename !== destinationFolderBasename &&
529529
basename === `${sourceFolderBasename}.ino`
530530
? `${destinationFolderBasename}.ino`
531531
: basename,
532532
filter,
533+
cwd: path.dirname(source),
533534
});
534535
const copiedSketch = await this.doLoadSketch(destinationUri, false);
535536
return copiedSketch;

Diff for: arduino-ide-extension/src/test/node/sketches-service-impl.slow-test.ts

+84-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
Disposable,
33
DisposableCollection,
44
} from '@theia/core/lib/common/disposable';
5+
import { isWindows } from '@theia/core/lib/common/os';
56
import { FileUri } from '@theia/core/lib/node/file-uri';
67
import { Container } from '@theia/core/shared/inversify';
78
import { expect } from 'chai';
@@ -226,16 +227,94 @@ describe('sketches-service-impl', () => {
226227
expect(mainFileContentOneAfterCopy).to.be.equal(contentOne);
227228
expect(mainFileContentTwoAfterCopy).to.be.equal(contentOne);
228229
});
230+
231+
(
232+
[
233+
['(', ')', 'parentheses'],
234+
['[', ']', 'brackets'],
235+
['{', '}', 'braces'],
236+
[
237+
'<',
238+
'>',
239+
'chevrons',
240+
{
241+
predicate: () => isWindows,
242+
why: '< (less than) and > (greater than) are reserved characters on Windows (https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions)',
243+
},
244+
],
245+
] as [
246+
open: string,
247+
close: string,
248+
name: string,
249+
skip?: { predicate: () => boolean; why: string }
250+
][]
251+
).map(([open, close, name, skip]) =>
252+
it(`should copy a sketch when the path contains ${name} in the sketch folder path: '${open},${close}'`, async function () {
253+
if (skip) {
254+
const { predicate, why } = skip;
255+
if (predicate()) {
256+
console.info(why);
257+
return this.skip();
258+
}
259+
}
260+
this.timeout(testTimeout);
261+
const sketchesService =
262+
container.get<SketchesServiceImpl>(SketchesService);
263+
const content = `// special content when ${name} are in the path`;
264+
const tempRoot = await sketchesService['createTempFolder']();
265+
toDispose.push(disposeFolder(tempRoot));
266+
const sketch = await sketchesService.createNewSketch(
267+
'punctuation_marks',
268+
content
269+
);
270+
toDispose.push(disposeSketch(sketch));
271+
272+
// the destination path contains punctuation marks
273+
const tempRootUri = FileUri.create(tempRoot);
274+
const testSegment = `path segment with ${open}${name}${close}`;
275+
const firstDestinationUri = tempRootUri
276+
.resolve(testSegment)
277+
.resolve('first')
278+
.resolve(sketch.name);
279+
280+
const firstSketchCopy = await sketchesService.copy(sketch, {
281+
destinationUri: firstDestinationUri.toString(),
282+
});
283+
expect(firstSketchCopy).to.be.not.undefined;
284+
expect(firstSketchCopy.mainFileUri).to.be.equal(
285+
firstDestinationUri.resolve(`${sketch.name}.ino`).toString()
286+
);
287+
const firstCopyContent = await mainFileContentOf(firstSketchCopy);
288+
expect(firstCopyContent).to.be.equal(content);
289+
290+
// the source path contains punctuation marks. yes, the target too, but it does not matter
291+
const secondDestinationUri = tempRootUri
292+
.resolve(testSegment)
293+
.resolve('second')
294+
.resolve(sketch.name);
295+
const secondSketchCopy = await sketchesService.copy(firstSketchCopy, {
296+
destinationUri: secondDestinationUri.toString(),
297+
});
298+
expect(secondSketchCopy).to.be.not.undefined;
299+
expect(secondSketchCopy.mainFileUri).to.be.equal(
300+
secondDestinationUri.resolve(`${sketch.name}.ino`).toString()
301+
);
302+
const secondCopyContent = await mainFileContentOf(secondSketchCopy);
303+
expect(secondCopyContent).to.be.equal(content);
304+
})
305+
);
229306
});
230307
});
231308

232309
function disposeSketch(...sketch: Sketch[]): Disposable {
310+
return disposeFolder(...sketch.map(({ uri }) => FileUri.fsPath(uri)));
311+
}
312+
313+
function disposeFolder(...paths: string[]): Disposable {
233314
return new DisposableCollection(
234-
...sketch
235-
.map(({ uri }) => FileUri.fsPath(uri))
236-
.map((path) =>
237-
Disposable.create(() => rimrafSync(path, { maxBusyTries: 5 }))
238-
)
315+
...paths.map((path) =>
316+
Disposable.create(() => rimrafSync(path, { maxBusyTries: 5 }))
317+
)
239318
);
240319
}
241320

0 commit comments

Comments
 (0)