From ba8e5dc1525ebb1d2f2bb6c9372633eafc2a7cf5 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Wed, 22 Jan 2025 12:32:33 +0700 Subject: [PATCH] chore(developer): report on filename collisions in kmc-copy Fixes: #12981 --- .../src/kmc-copy/src/KeymanProjectCopier.ts | 11 ++++- developer/src/kmc-copy/src/copier-messages.ts | 6 +++ ...ages.tests.ts => copier-messages.tests.ts} | 39 ++++-------------- .../fixtures/projects/collision/collision.kpj | 7 ++++ .../projects/collision/source/collided.ico | Bin 0 -> 1150 bytes .../projects/collision/source/collision.ico | Bin 0 -> 1150 bytes .../projects/collision/source/collision.kmn | 7 ++++ 7 files changed, 36 insertions(+), 34 deletions(-) rename developer/src/kmc-copy/test/{messages.tests.ts => copier-messages.tests.ts} (52%) create mode 100644 developer/src/kmc-copy/test/fixtures/projects/collision/collision.kpj create mode 100644 developer/src/kmc-copy/test/fixtures/projects/collision/source/collided.ico create mode 100644 developer/src/kmc-copy/test/fixtures/projects/collision/source/collision.ico create mode 100644 developer/src/kmc-copy/test/fixtures/projects/collision/source/collision.kmn diff --git a/developer/src/kmc-copy/src/KeymanProjectCopier.ts b/developer/src/kmc-copy/src/KeymanProjectCopier.ts index 7d39611f14..69eb723bfb 100644 --- a/developer/src/kmc-copy/src/KeymanProjectCopier.ts +++ b/developer/src/kmc-copy/src/KeymanProjectCopier.ts @@ -310,7 +310,6 @@ export class KeymanProjectCopier implements KeymanCompiler { }; private async copyFolder(inputPath: string, outputPath: string, ignorePatterns: (string | RegExp)[], result: CopierResult) { - // TODO-COPY: watch out for file collisions when copying project files -- after renames const files = await this.asyncCallbacks.fsAsync.readdir(inputPath); for(const {filename,type} of files) { const fullPath = this.normalizePath(this.callbacks.path.join(inputPath, filename)); @@ -320,9 +319,17 @@ export class KeymanProjectCopier implements KeymanCompiler { if(type == 'dir') { await this.copyFolder(fullPath, this.callbacks.path.join(outputPath, filename), ignorePatterns, result); } else if(!result.artifacts[fullPath]) { + const filename = this.generateNewFilename(fullPath, outputPath); + + for(const artifact of Object.keys(result.artifacts)) { + if(result.artifacts[artifact].filename == filename) { + this.callbacks.reportMessage(CopierMessages.Warn_FilenameCollides({filename})); + } + } + result.artifacts[fullPath] = { data: await this.asyncCallbacks.fsAsync.readFile(fullPath), - filename: this.generateNewFilename(fullPath, outputPath) + filename } } } diff --git a/developer/src/kmc-copy/src/copier-messages.ts b/developer/src/kmc-copy/src/copier-messages.ts index 4102170730..8afff79149 100644 --- a/developer/src/kmc-copy/src/copier-messages.ts +++ b/developer/src/kmc-copy/src/copier-messages.ts @@ -225,4 +225,10 @@ export class CopierMessages { `The specified lexical model id '${def(o.id)}' contains characters that are not permitted or does not match the required pattern of 'author.bcp47.uniq'.`, ); + static WARN_FilenameCollides = SevWarn | 0x0020; + static Warn_FilenameCollides = (o:{filename:string}) => m( + this.WARN_FilenameCollides, + `The output file '${def(o.filename)}' has two different possible source files.`, + ); + }; diff --git a/developer/src/kmc-copy/test/messages.tests.ts b/developer/src/kmc-copy/test/copier-messages.tests.ts similarity index 52% rename from developer/src/kmc-copy/test/messages.tests.ts rename to developer/src/kmc-copy/test/copier-messages.tests.ts index 0a35b8afde..0312910860 100644 --- a/developer/src/kmc-copy/test/messages.tests.ts +++ b/developer/src/kmc-copy/test/copier-messages.tests.ts @@ -44,36 +44,11 @@ describe('CopierMessages', function () { assert.isTrue(callbacks.hasMessage(CopierMessages.ERROR_InvalidLexicalModelId)); }); - // it('should generate ERROR_OutputPathAlreadyExists if output path already exists', async function () { - // const ag = new AbstractGenerator(); - // const callbacks = new TestCompilerCallbacks(); - // const options: GeneratorOptions = { - // id: 'ERROR_OutputPathAlreadyExists', - // outPath: path.dirname(fileURLToPath(import.meta.url)), - // }; - // assert(await ag.init(callbacks, options)); - // const dir = path.join(path.dirname(fileURLToPath(import.meta.url)), 'ERROR_OutputPathAlreadyExists'); - // if(!fs.existsSync(dir)) - // fs.mkdirSync(dir); - // assert.isFalse(await ag.write({})); - // assert.isTrue(callbacks.hasMessage(CopierMessages.ERROR_OutputPathAlreadyExists), - // `messageId ERROR_OutputPathAlreadyExists not generated, instead got: `+JSON.stringify(callbacks.messages,null,2)); - - // fs.rmdirSync(dir); - // }); - - // it('should generate ERROR_CannotWriteOutputFile if it cannot create a folder, e.g. invalid filename', async function () { - // const ag = new AbstractGenerator(); - // const callbacks = new TestCompilerCallbacks(); - // const options: GeneratorOptions = { - // id: 'ERROR_CannotWriteOutputFile', - // outPath: path.dirname(fileURLToPath(import.meta.url)), - // }; - // assert(await ag.init(callbacks, options)); - // assert.isFalse(await ag.write({ - // '.': {filename: '.', data: new Uint8Array([1,2,3])} - // })); - // assert.isTrue(callbacks.hasMessage(GeneratorMessages.ERROR_CannotWriteOutputFile), - // `messageId ERROR_CannotWriteOutputFile not generated, instead got: `+JSON.stringify(callbacks.messages,null,2)); - // }); + it('should generate WARN_FilenameCollision if a copied file will collide', async function() { + const copier = new KeymanProjectCopier(); + assert.isTrue(await copier.init(callbacks, {dryRun: true, outPath: 'collided'})) + const result = await copier.run(makePathToFixture('projects/collision/collision.kpj')); + assert.isNotNull(result); + assert.isTrue(callbacks.hasMessage(CopierMessages.WARN_FilenameCollides)); + }); }); diff --git a/developer/src/kmc-copy/test/fixtures/projects/collision/collision.kpj b/developer/src/kmc-copy/test/fixtures/projects/collision/collision.kpj new file mode 100644 index 0000000000..fffdca635e --- /dev/null +++ b/developer/src/kmc-copy/test/fixtures/projects/collision/collision.kpj @@ -0,0 +1,7 @@ + + + + keyboard + 2.0 + + diff --git a/developer/src/kmc-copy/test/fixtures/projects/collision/source/collided.ico b/developer/src/kmc-copy/test/fixtures/projects/collision/source/collided.ico new file mode 100644 index 0000000000000000000000000000000000000000..84b57c5aebc31098a75ea11ed69a4c62add2fc1a GIT binary patch literal 1150 zcmd6iOAdrE3`2u72k445%Q+hNzd%Wmo@7~#_^c>Oo!D_wB9{DG+hUyk7qKS(Gg`)$ zv7C1%srl)>$5%q%=5HFBbF-Qms?9jnHTOMuomUSzz0?Ca%2m1Vc;fY4cztHITY0?9 oRP8Fq%UHq)$ literal 0 HcmV?d00001 diff --git a/developer/src/kmc-copy/test/fixtures/projects/collision/source/collision.ico b/developer/src/kmc-copy/test/fixtures/projects/collision/source/collision.ico new file mode 100644 index 0000000000000000000000000000000000000000..84b57c5aebc31098a75ea11ed69a4c62add2fc1a GIT binary patch literal 1150 zcmd6iOAdrE3`2u72k445%Q+hNzd%Wmo@7~#_^c>Oo!D_wB9{DG+hUyk7qKS(Gg`)$ zv7C1%srl)>$5%q%=5HFBbF-Qms?9jnHTOMuomUSzz0?Ca%2m1Vc;fY4cztHITY0?9 oRP8Fq%UHq)$ literal 0 HcmV?d00001 diff --git a/developer/src/kmc-copy/test/fixtures/projects/collision/source/collision.kmn b/developer/src/kmc-copy/test/fixtures/projects/collision/source/collision.kmn new file mode 100644 index 0000000000..121e224d89 --- /dev/null +++ b/developer/src/kmc-copy/test/fixtures/projects/collision/source/collision.kmn @@ -0,0 +1,7 @@ +store(&TARGETS) 'any' +store(&NAME) 'Collision' +store(&BITMAP) 'collision.ico' + +begin Unicode > use(main) + +group(main) using keys