@@ -8,9 +8,10 @@ import { Container } from '@theia/core/shared/inversify';
8
8
import { expect } from 'chai' ;
9
9
import { promises as fs } from 'node:fs' ;
10
10
import { basename , join } from 'node:path' ;
11
+ import { rejects } from 'node:assert/strict' ;
11
12
import { sync as rimrafSync } from 'rimraf' ;
12
13
import temp from 'temp' ;
13
- import { Sketch , SketchesService } from '../../common/protocol' ;
14
+ import { Sketch , SketchesError , SketchesService } from '../../common/protocol' ;
14
15
import {
15
16
isAccessibleSketchPath ,
16
17
SketchesServiceImpl ,
@@ -138,12 +139,31 @@ describe('sketches-service-impl', () => {
138
139
139
140
after ( ( ) => toDispose . dispose ( ) ) ;
140
141
141
- describe ( 'copy' , ( ) => {
142
- it ( 'should copy a sketch when the destination does not exist' , async function ( ) {
143
- this . timeout ( testTimeout ) ;
142
+ describe ( 'copy' , function ( ) {
143
+ this . timeout ( testTimeout ) ;
144
+ this . slow ( 250 ) ;
145
+
146
+ it ( 'should error when the destination sketch folder name is invalid' , async ( ) => {
147
+ const sketchesService =
148
+ container . get < SketchesServiceImpl > ( SketchesService ) ;
149
+ const tempDirPath = await sketchesService [ 'createTempFolder' ] ( ) ;
150
+ const destinationPath = join ( tempDirPath , 'invalid with spaces' ) ;
151
+ const sketch = await sketchesService . createNewSketch ( ) ;
152
+ toDispose . push ( disposeSketch ( sketch ) ) ;
153
+ await rejects (
154
+ sketchesService . copy ( sketch , {
155
+ destinationUri : FileUri . create ( destinationPath ) . toString ( ) ,
156
+ } ) ,
157
+ SketchesError . InvalidFolderName . is
158
+ ) ;
159
+ } ) ;
160
+
161
+ it ( 'should copy a sketch when the destination does not exist' , async ( ) => {
144
162
const sketchesService =
145
163
container . get < SketchesServiceImpl > ( SketchesService ) ;
146
- const destinationPath = await sketchesService [ 'createTempFolder' ] ( ) ;
164
+ const tempDirPath = await sketchesService [ 'createTempFolder' ] ( ) ;
165
+ const destinationPath = join ( tempDirPath , 'Does_Not_Exist_but_valid' ) ;
166
+ await rejects ( fs . readdir ( destinationPath ) , ErrnoException . isENOENT ) ;
147
167
let sketch = await sketchesService . createNewSketch ( ) ;
148
168
toDispose . push ( disposeSketch ( sketch ) ) ;
149
169
const sourcePath = FileUri . fsPath ( sketch . uri ) ;
@@ -187,11 +207,11 @@ describe('sketches-service-impl', () => {
187
207
) . to . be . true ;
188
208
} ) ;
189
209
190
- it ( "should copy only sketch files if 'onlySketchFiles' is true" , async function ( ) {
191
- this . timeout ( testTimeout ) ;
210
+ it ( "should copy only sketch files if 'onlySketchFiles' is true" , async ( ) => {
192
211
const sketchesService =
193
212
container . get < SketchesServiceImpl > ( SketchesService ) ;
194
- const destinationPath = await sketchesService [ 'createTempFolder' ] ( ) ;
213
+ const tempDirPath = await sketchesService [ 'createTempFolder' ] ( ) ;
214
+ const destinationPath = join ( tempDirPath , 'OnlySketchFiles' ) ;
195
215
let sketch = await sketchesService . createNewSketch ( ) ;
196
216
toDispose . push ( disposeSketch ( sketch ) ) ;
197
217
const sourcePath = FileUri . fsPath ( sketch . uri ) ;
@@ -207,11 +227,25 @@ describe('sketches-service-impl', () => {
207
227
const logContent = 'log file content' ;
208
228
const logPath = join ( sourcePath , logBasename ) ;
209
229
await fs . writeFile ( logPath , logContent , { encoding : 'utf8' } ) ;
230
+ const srcPath = join ( sourcePath , 'src' ) ;
231
+ await fs . mkdir ( srcPath , { recursive : true } ) ;
232
+ const libInSrcBasename = 'lib_in_src.cpp' ;
233
+ const libInSrcContent = 'lib in src content' ;
234
+ const libInSrcPath = join ( srcPath , libInSrcBasename ) ;
235
+ await fs . writeFile ( libInSrcPath , libInSrcContent , { encoding : 'utf8' } ) ;
236
+ const logInSrcBasename = 'inols-clangd-err_in_src.log' ;
237
+ const logInSrcContent = 'log file content in src' ;
238
+ const logInSrcPath = join ( srcPath , logInSrcBasename ) ;
239
+ await fs . writeFile ( logInSrcPath , logInSrcContent , { encoding : 'utf8' } ) ;
210
240
211
241
sketch = await sketchesService . loadSketch ( sketch . uri ) ;
212
242
expect ( Sketch . isInSketch ( FileUri . create ( libPath ) , sketch ) ) . to . be . true ;
213
243
expect ( Sketch . isInSketch ( FileUri . create ( headerPath ) , sketch ) ) . to . be . true ;
214
244
expect ( Sketch . isInSketch ( FileUri . create ( logPath ) , sketch ) ) . to . be . false ;
245
+ expect ( Sketch . isInSketch ( FileUri . create ( libInSrcPath ) , sketch ) ) . to . be
246
+ . true ;
247
+ expect ( Sketch . isInSketch ( FileUri . create ( logInSrcPath ) , sketch ) ) . to . be
248
+ . false ;
215
249
const reloadedLogContent = await fs . readFile ( logPath , {
216
250
encoding : 'utf8' ,
217
251
} ) ;
@@ -249,20 +283,25 @@ describe('sketches-service-impl', () => {
249
283
copied
250
284
)
251
285
) . to . be . false ;
252
- try {
253
- await fs . readFile ( join ( destinationPath , logBasename ) , {
254
- encoding : 'utf8' ,
255
- } ) ;
256
- expect . fail (
257
- 'Log file must not exist in the destination. Expected ENOENT when loading the log file.'
258
- ) ;
259
- } catch ( err ) {
260
- expect ( ErrnoException . isENOENT ( err ) ) . to . be . true ;
261
- }
286
+ expect (
287
+ Sketch . isInSketch (
288
+ FileUri . create ( join ( destinationPath , 'src' , libInSrcBasename ) ) ,
289
+ copied
290
+ )
291
+ ) . to . be . true ;
292
+ expect (
293
+ Sketch . isInSketch (
294
+ FileUri . create ( join ( destinationPath , 'src' , logInSrcBasename ) ) ,
295
+ copied
296
+ )
297
+ ) . to . be . false ;
298
+ await rejects (
299
+ fs . readFile ( join ( destinationPath , logBasename ) ) ,
300
+ ErrnoException . isENOENT
301
+ ) ;
262
302
} ) ;
263
303
264
- it ( 'should copy sketch inside the sketch folder' , async function ( ) {
265
- this . timeout ( testTimeout ) ;
304
+ it ( 'should copy sketch inside the sketch folder' , async ( ) => {
266
305
const sketchesService =
267
306
container . get < SketchesServiceImpl > ( SketchesService ) ;
268
307
let sketch = await sketchesService . createNewSketch ( ) ;
@@ -309,6 +348,55 @@ describe('sketches-service-impl', () => {
309
348
) . to . be . true ;
310
349
} ) ;
311
350
351
+ it ( 'should not modify the subfolder structure' , async ( ) => {
352
+ const sketchesService =
353
+ container . get < SketchesServiceImpl > ( SketchesService ) ;
354
+ const tempDirPath = await sketchesService [ 'createTempFolder' ] ( ) ;
355
+ const destinationPath = join ( tempDirPath , 'HasSubfolders_copy' ) ;
356
+ await fs . mkdir ( destinationPath , { recursive : true } ) ;
357
+ let sketch = await sketchesService . createNewSketch ( 'HasSubfolders' ) ;
358
+ toDispose . push ( disposeSketch ( sketch ) ) ;
359
+
360
+ const sourcePath = FileUri . fsPath ( sketch . uri ) ;
361
+ const srcPath = join ( sourcePath , 'src' ) ;
362
+ await fs . mkdir ( srcPath , { recursive : true } ) ;
363
+ const headerPath = join ( srcPath , 'FomSubfolder.h' ) ;
364
+ await fs . writeFile ( headerPath , '// empty' , { encoding : 'utf8' } ) ;
365
+
366
+ sketch = await sketchesService . loadSketch ( sketch . uri ) ;
367
+
368
+ expect ( sketch . mainFileUri ) . to . be . equal (
369
+ FileUri . create ( join ( sourcePath , 'HasSubfolders.ino' ) ) . toString ( )
370
+ ) ;
371
+ expect ( sketch . additionalFileUris ) . to . be . deep . equal ( [
372
+ FileUri . create ( join ( srcPath , 'FomSubfolder.h' ) ) . toString ( ) ,
373
+ ] ) ;
374
+ expect ( sketch . otherSketchFileUris ) . to . be . empty ;
375
+ expect ( sketch . rootFolderFileUris ) . to . be . empty ;
376
+
377
+ const destinationUri = FileUri . fsPath ( destinationPath ) . toString ( ) ;
378
+ const copySketch = await sketchesService . copy ( sketch , { destinationUri } ) ;
379
+ toDispose . push ( disposeSketch ( copySketch ) ) ;
380
+ expect ( copySketch . mainFileUri ) . to . be . equal (
381
+ FileUri . create (
382
+ join ( destinationPath , 'HasSubfolders_copy.ino' )
383
+ ) . toString ( )
384
+ ) ;
385
+ expect ( copySketch . additionalFileUris ) . to . be . deep . equal ( [
386
+ FileUri . create (
387
+ join ( destinationPath , 'src' , 'FomSubfolder.h' )
388
+ ) . toString ( ) ,
389
+ ] ) ;
390
+ expect ( copySketch . otherSketchFileUris ) . to . be . empty ;
391
+ expect ( copySketch . rootFolderFileUris ) . to . be . empty ;
392
+
393
+ const actualHeaderContent = await fs . readFile (
394
+ join ( destinationPath , 'src' , 'FomSubfolder.h' ) ,
395
+ { encoding : 'utf8' }
396
+ ) ;
397
+ expect ( actualHeaderContent ) . to . be . equal ( '// empty' ) ;
398
+ } ) ;
399
+
312
400
it ( 'should copy sketch with overwrite when source and destination sketch folder names are the same' , async function ( ) {
313
401
this . timeout ( testTimeout ) ;
314
402
const sketchesService =
@@ -346,7 +434,7 @@ describe('sketches-service-impl', () => {
346
434
[
347
435
'<' ,
348
436
'>' ,
349
- 'chevrons ' ,
437
+ 'lt+gt ' ,
350
438
{
351
439
predicate : ( ) => isWindows ,
352
440
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)' ,
0 commit comments