From dd5e5199db93cf671825ba86cb28ef257ec86916 Mon Sep 17 00:00:00 2001 From: Christian Emmer <10749361+emmercm@users.noreply.github.com> Date: Thu, 8 Feb 2024 17:44:49 -0800 Subject: [PATCH 1/4] Feature: prefer-regex option --- docs/roms/filtering-preferences.md | 21 ++++++++-- src/modules/argumentsParser.ts | 8 ++++ src/modules/candidatePreferer.ts | 56 ++++++++++++++++---------- src/types/options.ts | 10 ++++- test/modules/argumentsParser.test.ts | 21 ++++++++++ test/modules/candidatePreferer.test.ts | 34 ++++++++++++++++ 6 files changed, 124 insertions(+), 26 deletions(-) diff --git a/docs/roms/filtering-preferences.md b/docs/roms/filtering-preferences.md index dca503bb2..3442a2081 100644 --- a/docs/roms/filtering-preferences.md +++ b/docs/roms/filtering-preferences.md @@ -8,13 +8,13 @@ ROM filters cut down the list of games desired for a set, and any games filtered Multiple filter options can be specified at once. -### Game name regex filter +### Game name filter ```text --filter-regex , --filter-regex-exclude ``` -Only include, or exclude games based on their DAT name (or filename if not using DATs). +Only include, or exclude games based if their DAT name (or filename if not using DATs) matches a regular expression. Regex flags can be optionally provided in the form `//`, for example: @@ -363,7 +363,22 @@ Micro Machines Military - It's a Blast! (E) [x] The `--single` option is required for all `--prefer-*` options, otherwise there would be no effect. -Multiple `--prefer-*` options can be specified at once, and they will be applied in the following order of importance (most to least). +Multiple `--prefer-*` options can be specified at once, and they will be applied in the following order of importance (most important to least important). + +### Prefer game names + +```text +--prefer-regex +``` + +Prefer games if their DAT name (or filename if not using DATs) matches a regular expression. + +Regex flags can be optionally provided in the form `//`, for example: + +```text +.*case sensitive.* +/.*case insensitive.*/i +``` ### Prefer verified diff --git a/src/modules/argumentsParser.ts b/src/modules/argumentsParser.ts index 9e9e9cbc6..fd016fbf1 100644 --- a/src/modules/argumentsParser.ts +++ b/src/modules/argumentsParser.ts @@ -575,6 +575,14 @@ export default class ArgumentsParser { description: 'Output only a single game per parent (1G1R) (required for all options below, requires DATs with parent/clone information)', type: 'boolean', }) + .option('prefer-regex', { + group: groupRomPriority, + description: 'Regular expression of game names to prefer', + type: 'string', + coerce: ArgumentsParser.readRegexFile, + requiresArg: true, + implies: 'single', + }) .option('prefer-verified', { group: groupRomPriority, description: 'Prefer verified ROM dumps over unverified', diff --git a/src/modules/candidatePreferer.ts b/src/modules/candidatePreferer.ts index 777d43d93..d13a024c6 100644 --- a/src/modules/candidatePreferer.ts +++ b/src/modules/candidatePreferer.ts @@ -103,7 +103,8 @@ export default class CandidatePreferer extends Module { */ private sort(a: ReleaseCandidate, b: ReleaseCandidate): number { - return this.preferVerifiedSort(a, b) + return this.preferRegexSort(a, b) + || this.preferVerifiedSort(a, b) || this.preferGoodSort(a, b) || this.preferLanguagesSort(a, b) || this.preferRegionsSort(a, b) @@ -114,18 +115,29 @@ export default class CandidatePreferer extends Module { || this.preferParentSort(a, b); } + private preferRegexSort(a: ReleaseCandidate, b: ReleaseCandidate): number { + const preferRegex = this.options.getPreferRegex(); + if (preferRegex === undefined || preferRegex.length === 0) { + return 0; + } + + const aMatched = preferRegex.some((regex) => regex.test(a.getGame().getName())) ? 0 : 1; + const bMatched = preferRegex.some((regex) => regex.test(b.getGame().getName())) ? 0 : 1; + return aMatched - bMatched; + } + private preferVerifiedSort(a: ReleaseCandidate, b: ReleaseCandidate): number { - if (this.options.getPreferVerified()) { - return (a.getGame().isVerified() ? 0 : 1) - (b.getGame().isVerified() ? 0 : 1); + if (!this.options.getPreferVerified()) { + return 0; } - return 0; + return (a.getGame().isVerified() ? 0 : 1) - (b.getGame().isVerified() ? 0 : 1); } private preferGoodSort(a: ReleaseCandidate, b: ReleaseCandidate): number { - if (this.options.getPreferGood()) { - return (b.getGame().isBad() ? 0 : 1) - (a.getGame().isBad() ? 0 : 1); + if (!this.options.getPreferGood()) { + return 0; } - return 0; + return (b.getGame().isBad() ? 0 : 1) - (a.getGame().isBad() ? 0 : 1); } private preferLanguagesSort(a: ReleaseCandidate, b: ReleaseCandidate): number { @@ -149,10 +161,10 @@ export default class CandidatePreferer extends Module { } private preferRegionsSort(a: ReleaseCandidate, b: ReleaseCandidate): number { - if (this.options.getPreferRegions().length > 0) { - return this.preferRegionSortValue(a) - this.preferRegionSortValue(b); + if (this.options.getPreferRegions().length === 0) { + return 0; } - return 0; + return this.preferRegionSortValue(a) - this.preferRegionSortValue(b); } private preferRegionSortValue(releaseCandidate: ReleaseCandidate): number { @@ -175,30 +187,30 @@ export default class CandidatePreferer extends Module { } private preferRetailSort(a: ReleaseCandidate, b: ReleaseCandidate): number { - if (this.options.getPreferRetail()) { - return (a.getGame().isRetail() ? 0 : 1) - (b.getGame().isRetail() ? 0 : 1); + if (!this.options.getPreferRetail()) { + return 0; } - return 0; + return (a.getGame().isRetail() ? 0 : 1) - (b.getGame().isRetail() ? 0 : 1); } private preferNTSCSort(a: ReleaseCandidate, b: ReleaseCandidate): number { - if (this.options.getPreferNTSC()) { - return (a.getGame().isNTSC() ? 0 : 1) - (b.getGame().isNTSC() ? 0 : 1); + if (!this.options.getPreferNTSC()) { + return 0; } - return 0; + return (a.getGame().isNTSC() ? 0 : 1) - (b.getGame().isNTSC() ? 0 : 1); } private preferPALSort(a: ReleaseCandidate, b: ReleaseCandidate): number { - if (this.options.getPreferPAL()) { - return (a.getGame().isPAL() ? 0 : 1) - (b.getGame().isPAL() ? 0 : 1); + if (!this.options.getPreferPAL()) { + return 0; } - return 0; + return (a.getGame().isPAL() ? 0 : 1) - (b.getGame().isPAL() ? 0 : 1); } private preferParentSort(a: ReleaseCandidate, b: ReleaseCandidate): number { - if (this.options.getPreferParent()) { - return (a.getGame().isParent() ? 0 : 1) - (b.getGame().isParent() ? 0 : 1); + if (!this.options.getPreferParent()) { + return 0; } - return 0; + return (a.getGame().isParent() ? 0 : 1) - (b.getGame().isParent() ? 0 : 1); } } diff --git a/src/types/options.ts b/src/types/options.ts index 75d777bab..3f411121d 100644 --- a/src/types/options.ts +++ b/src/types/options.ts @@ -122,6 +122,7 @@ export interface OptionsProps { readonly onlyBad?: boolean, readonly single?: boolean, + readonly preferRegex?: string, readonly preferVerified?: boolean, readonly preferGood?: boolean, readonly preferLanguage?: string[], @@ -283,7 +284,9 @@ export default class Options implements OptionsProps { readonly onlyBad: boolean; - readonly single: boolean = false; + readonly single: boolean; + + readonly preferRegex: string; readonly preferVerified: boolean; @@ -401,6 +404,7 @@ export default class Options implements OptionsProps { this.onlyBad = options?.onlyBad ?? false; this.single = options?.single ?? false; + this.preferRegex = options?.preferRegex ?? ''; this.preferVerified = options?.preferVerified ?? false; this.preferGood = options?.preferGood ?? false; this.preferLanguage = options?.preferLanguage ?? []; @@ -1046,6 +1050,10 @@ export default class Options implements OptionsProps { return this.single; } + getPreferRegex(): RegExp[] | undefined { + return Options.getRegex(this.preferRegex); + } + getPreferVerified(): boolean { return this.preferVerified; } diff --git a/test/modules/argumentsParser.test.ts b/test/modules/argumentsParser.test.ts index 53bf6f155..9753b3544 100644 --- a/test/modules/argumentsParser.test.ts +++ b/test/modules/argumentsParser.test.ts @@ -162,6 +162,7 @@ describe('options', () => { expect(options.getOnlyBad()).toEqual(false); expect(options.getSingle()).toEqual(false); + expect(options.getPreferRegex()).toBeUndefined(); expect(options.getPreferVerified()).toEqual(false); expect(options.getPreferGood()).toEqual(false); expect(options.getPreferLanguages()).toHaveLength(0); @@ -554,6 +555,26 @@ describe('options', () => { expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, 'zip', '--remove-headers', 'lnx,.LNX']).canRemoveHeader(dat, '.LnX')).toEqual(true); }); + it('should parse "prefer-regex"', async () => { + expect(() => argumentsParser.parse([...dummyCommandAndRequiredArgs, '--prefer-regex', '[a-z]'])).toThrow(/dependent|implication/i); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-regex', '[a-z]']).getPreferRegex()?.some((regex) => regex.test('lower'))).toEqual(true); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-regex', '[a-z]']).getPreferRegex()?.some((regex) => regex.test('UPPER'))).toEqual(false); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-regex', '/[a-z]/i']).getPreferRegex()?.some((regex) => regex.test('UPPER'))).toEqual(true); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-regex', '/[a-z]/i', '--prefer-regex', '[0-9]']).getPreferRegex()?.some((regex) => regex.test('UPPER'))).toEqual(false); + + const tempFile = await FsPoly.mktemp(path.join(Constants.GLOBAL_TEMP_DIR, 'temp')); + try { + await util.promisify(fs.writeFile)(tempFile, '\n/[a-z]/i\r\n[0-9]\n\n'); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-regex', tempFile]).getPreferRegex()?.some((regex) => regex.test(''))).toEqual(false); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-regex', tempFile]).getPreferRegex()?.some((regex) => regex.test('lower'))).toEqual(true); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-regex', tempFile]).getPreferRegex()?.some((regex) => regex.test('UPPER'))).toEqual(true); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-regex', tempFile]).getPreferRegex()?.some((regex) => regex.test('007'))).toEqual(true); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-regex', tempFile]).getPreferRegex()?.some((regex) => regex.test('@!#?@!'))).toEqual(false); + } finally { + await FsPoly.rm(tempFile); + } + }); + it('should parse "prefer-verified"', () => { expect(() => argumentsParser.parse([...dummyCommandAndRequiredArgs, '--prefer-verified'])).toThrow(/dependent|implication/i); expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--prefer-verified', '--single']).getPreferVerified()).toEqual(true); diff --git a/test/modules/candidatePreferer.test.ts b/test/modules/candidatePreferer.test.ts index 1d9f71f41..4d9f3915e 100644 --- a/test/modules/candidatePreferer.test.ts +++ b/test/modules/candidatePreferer.test.ts @@ -165,6 +165,40 @@ describe('sort', () => { }); }); + describe('prefer regex', () => { + it('should return the first candidate when option is empty', async () => { + await expectPreferredCandidates({ preferRegex: undefined, single: true }, [ + await buildReleaseCandidatesWithRegionLanguage(['one'], [], 'EN'), + await buildReleaseCandidatesWithRegionLanguage(['two', 'three'], [], 'EN'), + await buildReleaseCandidatesWithRegionLanguage(['four', 'five', 'six'], [], 'EN'), + ], ['one', 'two', 'four']); + }); + + it('should return the first candidate when none matching', async () => { + await expectPreferredCandidates({ preferRegex: 'NINE', single: true }, [ + await buildReleaseCandidatesWithRegionLanguage(['one'], [], 'EN'), + await buildReleaseCandidatesWithRegionLanguage(['two', 'three'], [], 'EN'), + await buildReleaseCandidatesWithRegionLanguage(['four', 'five', 'six'], [], 'EN'), + ], ['one', 'two', 'four']); + }); + + it('should return the first matching candidate when some matching', async () => { + await expectPreferredCandidates({ preferRegex: '/THREE|five/i', single: true }, [ + await buildReleaseCandidatesWithRegionLanguage(['one'], [], 'EN'), + await buildReleaseCandidatesWithRegionLanguage(['two', 'three'], [], 'EN'), + await buildReleaseCandidatesWithRegionLanguage(['four', 'five', 'six'], [], 'EN'), + ], ['one', 'three', 'five']); + }); + + it('should return the first candidate when all matching', async () => { + await expectPreferredCandidates({ preferRegex: 'one|two|three|four|five|six', single: true }, [ + await buildReleaseCandidatesWithRegionLanguage(['one'], [], 'EN'), + await buildReleaseCandidatesWithRegionLanguage(['two', 'three'], [], 'EN'), + await buildReleaseCandidatesWithRegionLanguage(['four', 'five', 'six'], [], 'EN'), + ], ['one', 'two', 'four']); + }); + }); + describe('prefer good', () => { it('should return the first candidate when option is false', async () => { await expectPreferredCandidates({ preferGood: false, single: true }, [ From a7ced31f21f0e5bd68655a067d5f31d7d513aa53 Mon Sep 17 00:00:00 2001 From: Christian Emmer <10749361+emmercm@users.noreply.github.com> Date: Thu, 8 Feb 2024 18:17:57 -0800 Subject: [PATCH 2/4] Rename --- docs/roms/filtering-preferences.md | 2 +- src/modules/argumentsParser.ts | 10 +++++++++- src/modules/candidatePreferer.ts | 2 +- src/types/options.ts | 10 +++++----- test/modules/argumentsParser.test.ts | 24 ++++++++++++------------ test/modules/candidatePreferer.test.ts | 8 ++++---- 6 files changed, 32 insertions(+), 24 deletions(-) diff --git a/docs/roms/filtering-preferences.md b/docs/roms/filtering-preferences.md index 3442a2081..1f751e70b 100644 --- a/docs/roms/filtering-preferences.md +++ b/docs/roms/filtering-preferences.md @@ -368,7 +368,7 @@ Multiple `--prefer-*` options can be specified at once, and they will be applied ### Prefer game names ```text ---prefer-regex +--prefer-game-regex ``` Prefer games if their DAT name (or filename if not using DATs) matches a regular expression. diff --git a/src/modules/argumentsParser.ts b/src/modules/argumentsParser.ts index 37472bfe1..da5ee5c54 100644 --- a/src/modules/argumentsParser.ts +++ b/src/modules/argumentsParser.ts @@ -575,7 +575,7 @@ export default class ArgumentsParser { description: 'Output only a single game per parent (1G1R) (required for all options below, requires DATs with parent/clone information)', type: 'boolean', }) - .option('prefer-regex', { + .option('prefer-game-regex', { group: groupRomPriority, description: 'Regular expression of game names to prefer', type: 'string', @@ -583,6 +583,14 @@ export default class ArgumentsParser { requiresArg: true, implies: 'single', }) + .option('prefer-rom-regex', { + group: groupRomPriority, + description: 'Regular expression of ROM filenames to prefer', + type: 'string', + coerce: ArgumentsParser.readRegexFile, + requiresArg: true, + implies: 'single', + }) .option('prefer-verified', { group: groupRomPriority, description: 'Prefer verified ROM dumps over unverified', diff --git a/src/modules/candidatePreferer.ts b/src/modules/candidatePreferer.ts index d13a024c6..495d33530 100644 --- a/src/modules/candidatePreferer.ts +++ b/src/modules/candidatePreferer.ts @@ -116,7 +116,7 @@ export default class CandidatePreferer extends Module { } private preferRegexSort(a: ReleaseCandidate, b: ReleaseCandidate): number { - const preferRegex = this.options.getPreferRegex(); + const preferRegex = this.options.getPreferGameRegex(); if (preferRegex === undefined || preferRegex.length === 0) { return 0; } diff --git a/src/types/options.ts b/src/types/options.ts index 3f411121d..1fb18e796 100644 --- a/src/types/options.ts +++ b/src/types/options.ts @@ -122,7 +122,7 @@ export interface OptionsProps { readonly onlyBad?: boolean, readonly single?: boolean, - readonly preferRegex?: string, + readonly preferGameRegex?: string, readonly preferVerified?: boolean, readonly preferGood?: boolean, readonly preferLanguage?: string[], @@ -286,7 +286,7 @@ export default class Options implements OptionsProps { readonly single: boolean; - readonly preferRegex: string; + readonly preferGameRegex: string; readonly preferVerified: boolean; @@ -404,7 +404,7 @@ export default class Options implements OptionsProps { this.onlyBad = options?.onlyBad ?? false; this.single = options?.single ?? false; - this.preferRegex = options?.preferRegex ?? ''; + this.preferGameRegex = options?.preferGameRegex ?? ''; this.preferVerified = options?.preferVerified ?? false; this.preferGood = options?.preferGood ?? false; this.preferLanguage = options?.preferLanguage ?? []; @@ -1050,8 +1050,8 @@ export default class Options implements OptionsProps { return this.single; } - getPreferRegex(): RegExp[] | undefined { - return Options.getRegex(this.preferRegex); + getPreferGameRegex(): RegExp[] | undefined { + return Options.getRegex(this.preferGameRegex); } getPreferVerified(): boolean { diff --git a/test/modules/argumentsParser.test.ts b/test/modules/argumentsParser.test.ts index 9753b3544..ef855a5e9 100644 --- a/test/modules/argumentsParser.test.ts +++ b/test/modules/argumentsParser.test.ts @@ -162,7 +162,7 @@ describe('options', () => { expect(options.getOnlyBad()).toEqual(false); expect(options.getSingle()).toEqual(false); - expect(options.getPreferRegex()).toBeUndefined(); + expect(options.getPreferGameRegex()).toBeUndefined(); expect(options.getPreferVerified()).toEqual(false); expect(options.getPreferGood()).toEqual(false); expect(options.getPreferLanguages()).toHaveLength(0); @@ -555,21 +555,21 @@ describe('options', () => { expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, 'zip', '--remove-headers', 'lnx,.LNX']).canRemoveHeader(dat, '.LnX')).toEqual(true); }); - it('should parse "prefer-regex"', async () => { - expect(() => argumentsParser.parse([...dummyCommandAndRequiredArgs, '--prefer-regex', '[a-z]'])).toThrow(/dependent|implication/i); - expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-regex', '[a-z]']).getPreferRegex()?.some((regex) => regex.test('lower'))).toEqual(true); - expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-regex', '[a-z]']).getPreferRegex()?.some((regex) => regex.test('UPPER'))).toEqual(false); - expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-regex', '/[a-z]/i']).getPreferRegex()?.some((regex) => regex.test('UPPER'))).toEqual(true); - expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-regex', '/[a-z]/i', '--prefer-regex', '[0-9]']).getPreferRegex()?.some((regex) => regex.test('UPPER'))).toEqual(false); + it('should parse "prefer-game-regex"', async () => { + expect(() => argumentsParser.parse([...dummyCommandAndRequiredArgs, '--prefer-game-regex', '[a-z]'])).toThrow(/dependent|implication/i); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-game-regex', '[a-z]']).getPreferGameRegex()?.some((regex) => regex.test('lower'))).toEqual(true); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-game-regex', '[a-z]']).getPreferGameRegex()?.some((regex) => regex.test('UPPER'))).toEqual(false); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-game-regex', '/[a-z]/i']).getPreferGameRegex()?.some((regex) => regex.test('UPPER'))).toEqual(true); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-game-regex', '/[a-z]/i', '--prefer-game-regex', '[0-9]']).getPreferGameRegex()?.some((regex) => regex.test('UPPER'))).toEqual(false); const tempFile = await FsPoly.mktemp(path.join(Constants.GLOBAL_TEMP_DIR, 'temp')); try { await util.promisify(fs.writeFile)(tempFile, '\n/[a-z]/i\r\n[0-9]\n\n'); - expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-regex', tempFile]).getPreferRegex()?.some((regex) => regex.test(''))).toEqual(false); - expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-regex', tempFile]).getPreferRegex()?.some((regex) => regex.test('lower'))).toEqual(true); - expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-regex', tempFile]).getPreferRegex()?.some((regex) => regex.test('UPPER'))).toEqual(true); - expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-regex', tempFile]).getPreferRegex()?.some((regex) => regex.test('007'))).toEqual(true); - expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-regex', tempFile]).getPreferRegex()?.some((regex) => regex.test('@!#?@!'))).toEqual(false); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-game-regex', tempFile]).getPreferGameRegex()?.some((regex) => regex.test(''))).toEqual(false); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-game-regex', tempFile]).getPreferGameRegex()?.some((regex) => regex.test('lower'))).toEqual(true); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-game-regex', tempFile]).getPreferGameRegex()?.some((regex) => regex.test('UPPER'))).toEqual(true); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-game-regex', tempFile]).getPreferGameRegex()?.some((regex) => regex.test('007'))).toEqual(true); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-game-regex', tempFile]).getPreferGameRegex()?.some((regex) => regex.test('@!#?@!'))).toEqual(false); } finally { await FsPoly.rm(tempFile); } diff --git a/test/modules/candidatePreferer.test.ts b/test/modules/candidatePreferer.test.ts index 4d9f3915e..22b598ab9 100644 --- a/test/modules/candidatePreferer.test.ts +++ b/test/modules/candidatePreferer.test.ts @@ -167,7 +167,7 @@ describe('sort', () => { describe('prefer regex', () => { it('should return the first candidate when option is empty', async () => { - await expectPreferredCandidates({ preferRegex: undefined, single: true }, [ + await expectPreferredCandidates({ preferGameRegex: undefined, single: true }, [ await buildReleaseCandidatesWithRegionLanguage(['one'], [], 'EN'), await buildReleaseCandidatesWithRegionLanguage(['two', 'three'], [], 'EN'), await buildReleaseCandidatesWithRegionLanguage(['four', 'five', 'six'], [], 'EN'), @@ -175,7 +175,7 @@ describe('sort', () => { }); it('should return the first candidate when none matching', async () => { - await expectPreferredCandidates({ preferRegex: 'NINE', single: true }, [ + await expectPreferredCandidates({ preferGameRegex: 'NINE', single: true }, [ await buildReleaseCandidatesWithRegionLanguage(['one'], [], 'EN'), await buildReleaseCandidatesWithRegionLanguage(['two', 'three'], [], 'EN'), await buildReleaseCandidatesWithRegionLanguage(['four', 'five', 'six'], [], 'EN'), @@ -183,7 +183,7 @@ describe('sort', () => { }); it('should return the first matching candidate when some matching', async () => { - await expectPreferredCandidates({ preferRegex: '/THREE|five/i', single: true }, [ + await expectPreferredCandidates({ preferGameRegex: '/THREE|five/i', single: true }, [ await buildReleaseCandidatesWithRegionLanguage(['one'], [], 'EN'), await buildReleaseCandidatesWithRegionLanguage(['two', 'three'], [], 'EN'), await buildReleaseCandidatesWithRegionLanguage(['four', 'five', 'six'], [], 'EN'), @@ -191,7 +191,7 @@ describe('sort', () => { }); it('should return the first candidate when all matching', async () => { - await expectPreferredCandidates({ preferRegex: 'one|two|three|four|five|six', single: true }, [ + await expectPreferredCandidates({ preferGameRegex: 'one|two|three|four|five|six', single: true }, [ await buildReleaseCandidatesWithRegionLanguage(['one'], [], 'EN'), await buildReleaseCandidatesWithRegionLanguage(['two', 'three'], [], 'EN'), await buildReleaseCandidatesWithRegionLanguage(['four', 'five', 'six'], [], 'EN'), From 2b7c517cf7902e0099a5ea7a40da9185635976d1 Mon Sep 17 00:00:00 2001 From: Christian Emmer <10749361+emmercm@users.noreply.github.com> Date: Thu, 8 Feb 2024 18:32:29 -0800 Subject: [PATCH 3/4] --prefer-rom-regex --- docs/roms/filtering-preferences.md | 19 ++++- src/modules/candidatePreferer.ts | 26 +++++-- src/types/options.ts | 8 +++ test/modules/argumentsParser.test.ts | 20 ++++++ test/modules/candidatePreferer.test.ts | 99 ++++++++++++++++++-------- 5 files changed, 133 insertions(+), 39 deletions(-) diff --git a/docs/roms/filtering-preferences.md b/docs/roms/filtering-preferences.md index 1f751e70b..ddb295c2c 100644 --- a/docs/roms/filtering-preferences.md +++ b/docs/roms/filtering-preferences.md @@ -376,8 +376,23 @@ Prefer games if their DAT name (or filename if not using DATs) matches a regular Regex flags can be optionally provided in the form `//`, for example: ```text -.*case sensitive.* -/.*case insensitive.*/i +.*Mario.* +/.*mario.*/i +``` + +### Prefer ROM filenames + +```text +--prefer-rom-regex +``` + +Prefer games if any of their ROM filenames matches a regular expression. + +Regex flags can be optionally provided in the form `//`, for example: + +```text +Mario.*\\.gb$ +/mario.*\\.gb$/i ``` ### Prefer verified diff --git a/src/modules/candidatePreferer.ts b/src/modules/candidatePreferer.ts index 495d33530..577634a12 100644 --- a/src/modules/candidatePreferer.ts +++ b/src/modules/candidatePreferer.ts @@ -103,7 +103,8 @@ export default class CandidatePreferer extends Module { */ private sort(a: ReleaseCandidate, b: ReleaseCandidate): number { - return this.preferRegexSort(a, b) + return this.preferGameRegexSort(a, b) + || this.preferRomRegexSort(a, b) || this.preferVerifiedSort(a, b) || this.preferGoodSort(a, b) || this.preferLanguagesSort(a, b) @@ -115,14 +116,27 @@ export default class CandidatePreferer extends Module { || this.preferParentSort(a, b); } - private preferRegexSort(a: ReleaseCandidate, b: ReleaseCandidate): number { - const preferRegex = this.options.getPreferGameRegex(); - if (preferRegex === undefined || preferRegex.length === 0) { + private preferGameRegexSort(a: ReleaseCandidate, b: ReleaseCandidate): number { + const gameRegex = this.options.getPreferGameRegex(); + if (gameRegex === undefined || gameRegex.length === 0) { return 0; } - const aMatched = preferRegex.some((regex) => regex.test(a.getGame().getName())) ? 0 : 1; - const bMatched = preferRegex.some((regex) => regex.test(b.getGame().getName())) ? 0 : 1; + const aMatched = gameRegex.some((regex) => regex.test(a.getGame().getName())) ? 0 : 1; + const bMatched = gameRegex.some((regex) => regex.test(b.getGame().getName())) ? 0 : 1; + return aMatched - bMatched; + } + + private preferRomRegexSort(a: ReleaseCandidate, b: ReleaseCandidate): number { + const romRegex = this.options.getPreferRomRegex(); + if (romRegex === undefined || romRegex.length === 0) { + return 0; + } + + const aMatched = romRegex + .some((regex) => a.getGame().getRoms().some((rom) => regex.test(rom.getName()))) ? 0 : 1; + const bMatched = romRegex + .some((regex) => b.getGame().getRoms().some((rom) => regex.test(rom.getName()))) ? 0 : 1; return aMatched - bMatched; } diff --git a/src/types/options.ts b/src/types/options.ts index 1fb18e796..40102dc99 100644 --- a/src/types/options.ts +++ b/src/types/options.ts @@ -123,6 +123,7 @@ export interface OptionsProps { readonly single?: boolean, readonly preferGameRegex?: string, + readonly preferRomRegex?: string, readonly preferVerified?: boolean, readonly preferGood?: boolean, readonly preferLanguage?: string[], @@ -288,6 +289,8 @@ export default class Options implements OptionsProps { readonly preferGameRegex: string; + readonly preferRomRegex: string; + readonly preferVerified: boolean; readonly preferGood: boolean; @@ -405,6 +408,7 @@ export default class Options implements OptionsProps { this.single = options?.single ?? false; this.preferGameRegex = options?.preferGameRegex ?? ''; + this.preferRomRegex = options?.preferRomRegex ?? ''; this.preferVerified = options?.preferVerified ?? false; this.preferGood = options?.preferGood ?? false; this.preferLanguage = options?.preferLanguage ?? []; @@ -1054,6 +1058,10 @@ export default class Options implements OptionsProps { return Options.getRegex(this.preferGameRegex); } + getPreferRomRegex(): RegExp[] | undefined { + return Options.getRegex(this.preferRomRegex); + } + getPreferVerified(): boolean { return this.preferVerified; } diff --git a/test/modules/argumentsParser.test.ts b/test/modules/argumentsParser.test.ts index ef855a5e9..b1e8ead0d 100644 --- a/test/modules/argumentsParser.test.ts +++ b/test/modules/argumentsParser.test.ts @@ -575,6 +575,26 @@ describe('options', () => { } }); + it('should parse "prefer-rom-regex"', async () => { + expect(() => argumentsParser.parse([...dummyCommandAndRequiredArgs, '--prefer-rom-regex', '[a-z]'])).toThrow(/dependent|implication/i); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', '[a-z]']).getPreferGameRegex()?.some((regex) => regex.test('lower'))).toEqual(true); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', '[a-z]']).getPreferGameRegex()?.some((regex) => regex.test('UPPER'))).toEqual(false); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', '/[a-z]/i']).getPreferGameRegex()?.some((regex) => regex.test('UPPER'))).toEqual(true); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', '/[a-z]/i', '--prefer-rom-regex', '[0-9]']).getPreferGameRegex()?.some((regex) => regex.test('UPPER'))).toEqual(false); + + const tempFile = await FsPoly.mktemp(path.join(Constants.GLOBAL_TEMP_DIR, 'temp')); + try { + await util.promisify(fs.writeFile)(tempFile, '\n/[a-z]/i\r\n[0-9]\n\n'); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', tempFile]).getPreferGameRegex()?.some((regex) => regex.test(''))).toEqual(false); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', tempFile]).getPreferGameRegex()?.some((regex) => regex.test('lower'))).toEqual(true); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', tempFile]).getPreferGameRegex()?.some((regex) => regex.test('UPPER'))).toEqual(true); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', tempFile]).getPreferGameRegex()?.some((regex) => regex.test('007'))).toEqual(true); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', tempFile]).getPreferGameRegex()?.some((regex) => regex.test('@!#?@!'))).toEqual(false); + } finally { + await FsPoly.rm(tempFile); + } + }); + it('should parse "prefer-verified"', () => { expect(() => argumentsParser.parse([...dummyCommandAndRequiredArgs, '--prefer-verified'])).toThrow(/dependent|implication/i); expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--prefer-verified', '--single']).getPreferVerified()).toEqual(true); diff --git a/test/modules/candidatePreferer.test.ts b/test/modules/candidatePreferer.test.ts index 22b598ab9..ac2dbd284 100644 --- a/test/modules/candidatePreferer.test.ts +++ b/test/modules/candidatePreferer.test.ts @@ -66,12 +66,12 @@ function arrayCoerce(val: T | T[] | undefined): T[] { } async function buildReleaseCandidatesWithRegionLanguage( - names: string | string[], + gameNames: string | string[], regions?: string | string[], languages?: string | string[], gameOptions?: GameProps | GameProps[], ): Promise<[Parent, ReleaseCandidate[]]> { - const namesArr = arrayCoerce(names); + const gameNamesArr = arrayCoerce(gameNames); const regionsArr = arrayCoerce(regions); const languagesArr = Array.isArray(languages) ? languages : [languages]; const gameOptionsArr = arrayCoerce(gameOptions); @@ -79,12 +79,12 @@ async function buildReleaseCandidatesWithRegionLanguage( // Every different name+language combo is a different ROM+Game const games: Game[] = []; const releaseCandidates: ReleaseCandidate[] = []; - for (const [idx, romName] of namesArr.entries()) { + for (const [idx, gameName] of gameNamesArr.entries()) { for (const language of languagesArr) { // Every region is a different Release+ReleaseCandidate const releases: Release[] = []; for (const region of regionsArr) { - let releaseName = romName; + let releaseName = gameName; if (region) { releaseName += ` (${region})`; } @@ -94,9 +94,12 @@ async function buildReleaseCandidatesWithRegionLanguage( releases.push(new Release(releaseName, region, language)); } - const rom = new ROM({ name: `${romName}.rom`, size: 0, crc: '00000000' }); + const rom = new ROM({ name: `${gameName}.rom`, size: 0, crc: '00000000' }); const game = new Game({ - name: romName, rom: [rom], release: releases, ...gameOptionsArr[idx], + name: gameName, + rom: [rom], + release: releases, + ...gameOptionsArr[idx], }); games.push(game); @@ -116,7 +119,7 @@ async function buildReleaseCandidatesWithRegionLanguage( } } - const parent = new Parent(namesArr[0], games); + const parent = new Parent(gameNamesArr[0], games); return [parent, releaseCandidates]; } @@ -131,43 +134,43 @@ it('should return nothing if no parent has release candidates', async () => { }); describe('sort', () => { - describe('prefer verified', () => { - it('should return the first candidate when option is false', async () => { - await expectPreferredCandidates({ preferVerified: false, single: true }, [ + describe('prefer game regex', () => { + it('should return the first candidate when option is empty', async () => { + await expectPreferredCandidates({ preferGameRegex: undefined, single: true }, [ await buildReleaseCandidatesWithRegionLanguage(['one'], [], 'EN'), - await buildReleaseCandidatesWithRegionLanguage(['two', 'two [!]'], [], 'EN'), - await buildReleaseCandidatesWithRegionLanguage(['three [!]', 'three'], [], 'EN'), - ], ['one', 'two', 'three [!]']); + await buildReleaseCandidatesWithRegionLanguage(['two', 'three'], [], 'EN'), + await buildReleaseCandidatesWithRegionLanguage(['four', 'five', 'six'], [], 'EN'), + ], ['one', 'two', 'four']); }); it('should return the first candidate when none matching', async () => { - await expectPreferredCandidates({ preferVerified: true, single: true }, [ + await expectPreferredCandidates({ preferGameRegex: 'NINE', single: true }, [ await buildReleaseCandidatesWithRegionLanguage(['one'], [], 'EN'), - await buildReleaseCandidatesWithRegionLanguage(['two', 'two two'], [], 'EN'), - await buildReleaseCandidatesWithRegionLanguage(['three', 'three three'], [], 'EN'), - ], ['one', 'two', 'three']); + await buildReleaseCandidatesWithRegionLanguage(['two', 'three'], [], 'EN'), + await buildReleaseCandidatesWithRegionLanguage(['four', 'five', 'six'], [], 'EN'), + ], ['one', 'two', 'four']); }); it('should return the first matching candidate when some matching', async () => { - await expectPreferredCandidates({ preferVerified: true, single: true }, [ + await expectPreferredCandidates({ preferGameRegex: '/THREE|five/i', single: true }, [ await buildReleaseCandidatesWithRegionLanguage(['one'], [], 'EN'), - await buildReleaseCandidatesWithRegionLanguage(['two', 'two [!]'], [], 'EN'), - await buildReleaseCandidatesWithRegionLanguage(['three [!]', 'three'], [], 'EN'), - ], ['one', 'two [!]', 'three [!]']); + await buildReleaseCandidatesWithRegionLanguage(['two', 'three'], [], 'EN'), + await buildReleaseCandidatesWithRegionLanguage(['four', 'five', 'six'], [], 'EN'), + ], ['one', 'three', 'five']); }); it('should return the first candidate when all matching', async () => { - await expectPreferredCandidates({ preferVerified: true, single: true }, [ - await buildReleaseCandidatesWithRegionLanguage(['one [!]'], [], 'EN'), - await buildReleaseCandidatesWithRegionLanguage(['two [!]', 'two two [!]'], [], 'EN'), - await buildReleaseCandidatesWithRegionLanguage(['three [!]', 'three three [!]'], [], 'EN'), - ], ['one [!]', 'two [!]', 'three [!]']); + await expectPreferredCandidates({ preferGameRegex: '[aeiou]', single: true }, [ + await buildReleaseCandidatesWithRegionLanguage(['one'], [], 'EN'), + await buildReleaseCandidatesWithRegionLanguage(['two', 'three'], [], 'EN'), + await buildReleaseCandidatesWithRegionLanguage(['four', 'five', 'six'], [], 'EN'), + ], ['one', 'two', 'four']); }); }); - describe('prefer regex', () => { + describe('prefer rom regex', () => { it('should return the first candidate when option is empty', async () => { - await expectPreferredCandidates({ preferGameRegex: undefined, single: true }, [ + await expectPreferredCandidates({ preferRomRegex: undefined, single: true }, [ await buildReleaseCandidatesWithRegionLanguage(['one'], [], 'EN'), await buildReleaseCandidatesWithRegionLanguage(['two', 'three'], [], 'EN'), await buildReleaseCandidatesWithRegionLanguage(['four', 'five', 'six'], [], 'EN'), @@ -175,7 +178,7 @@ describe('sort', () => { }); it('should return the first candidate when none matching', async () => { - await expectPreferredCandidates({ preferGameRegex: 'NINE', single: true }, [ + await expectPreferredCandidates({ preferRomRegex: '/five\\.nes/i', single: true }, [ await buildReleaseCandidatesWithRegionLanguage(['one'], [], 'EN'), await buildReleaseCandidatesWithRegionLanguage(['two', 'three'], [], 'EN'), await buildReleaseCandidatesWithRegionLanguage(['four', 'five', 'six'], [], 'EN'), @@ -183,7 +186,7 @@ describe('sort', () => { }); it('should return the first matching candidate when some matching', async () => { - await expectPreferredCandidates({ preferGameRegex: '/THREE|five/i', single: true }, [ + await expectPreferredCandidates({ preferRomRegex: '/THREE|five\\.rom/i', single: true }, [ await buildReleaseCandidatesWithRegionLanguage(['one'], [], 'EN'), await buildReleaseCandidatesWithRegionLanguage(['two', 'three'], [], 'EN'), await buildReleaseCandidatesWithRegionLanguage(['four', 'five', 'six'], [], 'EN'), @@ -191,7 +194,7 @@ describe('sort', () => { }); it('should return the first candidate when all matching', async () => { - await expectPreferredCandidates({ preferGameRegex: 'one|two|three|four|five|six', single: true }, [ + await expectPreferredCandidates({ preferRomRegex: '[aeiou]', single: true }, [ await buildReleaseCandidatesWithRegionLanguage(['one'], [], 'EN'), await buildReleaseCandidatesWithRegionLanguage(['two', 'three'], [], 'EN'), await buildReleaseCandidatesWithRegionLanguage(['four', 'five', 'six'], [], 'EN'), @@ -199,6 +202,40 @@ describe('sort', () => { }); }); + describe('prefer verified', () => { + it('should return the first candidate when option is false', async () => { + await expectPreferredCandidates({ preferVerified: false, single: true }, [ + await buildReleaseCandidatesWithRegionLanguage(['one'], [], 'EN'), + await buildReleaseCandidatesWithRegionLanguage(['two', 'two [!]'], [], 'EN'), + await buildReleaseCandidatesWithRegionLanguage(['three [!]', 'three'], [], 'EN'), + ], ['one', 'two', 'three [!]']); + }); + + it('should return the first candidate when none matching', async () => { + await expectPreferredCandidates({ preferVerified: true, single: true }, [ + await buildReleaseCandidatesWithRegionLanguage(['one'], [], 'EN'), + await buildReleaseCandidatesWithRegionLanguage(['two', 'two two'], [], 'EN'), + await buildReleaseCandidatesWithRegionLanguage(['three', 'three three'], [], 'EN'), + ], ['one', 'two', 'three']); + }); + + it('should return the first matching candidate when some matching', async () => { + await expectPreferredCandidates({ preferVerified: true, single: true }, [ + await buildReleaseCandidatesWithRegionLanguage(['one'], [], 'EN'), + await buildReleaseCandidatesWithRegionLanguage(['two', 'two [!]'], [], 'EN'), + await buildReleaseCandidatesWithRegionLanguage(['three [!]', 'three'], [], 'EN'), + ], ['one', 'two [!]', 'three [!]']); + }); + + it('should return the first candidate when all matching', async () => { + await expectPreferredCandidates({ preferVerified: true, single: true }, [ + await buildReleaseCandidatesWithRegionLanguage(['one [!]'], [], 'EN'), + await buildReleaseCandidatesWithRegionLanguage(['two [!]', 'two two [!]'], [], 'EN'), + await buildReleaseCandidatesWithRegionLanguage(['three [!]', 'three three [!]'], [], 'EN'), + ], ['one [!]', 'two [!]', 'three [!]']); + }); + }); + describe('prefer good', () => { it('should return the first candidate when option is false', async () => { await expectPreferredCandidates({ preferGood: false, single: true }, [ From 512ab8185832e9aa240c159fbad7be5e2d1027af Mon Sep 17 00:00:00 2001 From: Christian Emmer <10749361+emmercm@users.noreply.github.com> Date: Thu, 8 Feb 2024 18:42:30 -0800 Subject: [PATCH 4/4] Tests fix --- test/modules/argumentsParser.test.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/test/modules/argumentsParser.test.ts b/test/modules/argumentsParser.test.ts index b1e8ead0d..4bf4a446a 100644 --- a/test/modules/argumentsParser.test.ts +++ b/test/modules/argumentsParser.test.ts @@ -163,6 +163,7 @@ describe('options', () => { expect(options.getSingle()).toEqual(false); expect(options.getPreferGameRegex()).toBeUndefined(); + expect(options.getPreferRomRegex()).toBeUndefined(); expect(options.getPreferVerified()).toEqual(false); expect(options.getPreferGood()).toEqual(false); expect(options.getPreferLanguages()).toHaveLength(0); @@ -577,19 +578,19 @@ describe('options', () => { it('should parse "prefer-rom-regex"', async () => { expect(() => argumentsParser.parse([...dummyCommandAndRequiredArgs, '--prefer-rom-regex', '[a-z]'])).toThrow(/dependent|implication/i); - expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', '[a-z]']).getPreferGameRegex()?.some((regex) => regex.test('lower'))).toEqual(true); - expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', '[a-z]']).getPreferGameRegex()?.some((regex) => regex.test('UPPER'))).toEqual(false); - expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', '/[a-z]/i']).getPreferGameRegex()?.some((regex) => regex.test('UPPER'))).toEqual(true); - expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', '/[a-z]/i', '--prefer-rom-regex', '[0-9]']).getPreferGameRegex()?.some((regex) => regex.test('UPPER'))).toEqual(false); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', '[a-z]']).getPreferRomRegex()?.some((regex) => regex.test('lower'))).toEqual(true); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', '[a-z]']).getPreferRomRegex()?.some((regex) => regex.test('UPPER'))).toEqual(false); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', '/[a-z]/i']).getPreferRomRegex()?.some((regex) => regex.test('UPPER'))).toEqual(true); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', '/[a-z]/i', '--prefer-rom-regex', '[0-9]']).getPreferRomRegex()?.some((regex) => regex.test('UPPER'))).toEqual(false); const tempFile = await FsPoly.mktemp(path.join(Constants.GLOBAL_TEMP_DIR, 'temp')); try { await util.promisify(fs.writeFile)(tempFile, '\n/[a-z]/i\r\n[0-9]\n\n'); - expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', tempFile]).getPreferGameRegex()?.some((regex) => regex.test(''))).toEqual(false); - expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', tempFile]).getPreferGameRegex()?.some((regex) => regex.test('lower'))).toEqual(true); - expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', tempFile]).getPreferGameRegex()?.some((regex) => regex.test('UPPER'))).toEqual(true); - expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', tempFile]).getPreferGameRegex()?.some((regex) => regex.test('007'))).toEqual(true); - expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', tempFile]).getPreferGameRegex()?.some((regex) => regex.test('@!#?@!'))).toEqual(false); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', tempFile]).getPreferRomRegex()?.some((regex) => regex.test(''))).toEqual(false); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', tempFile]).getPreferRomRegex()?.some((regex) => regex.test('lower'))).toEqual(true); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', tempFile]).getPreferRomRegex()?.some((regex) => regex.test('UPPER'))).toEqual(true); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', tempFile]).getPreferRomRegex()?.some((regex) => regex.test('007'))).toEqual(true); + expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--single', '--prefer-rom-regex', tempFile]).getPreferRomRegex()?.some((regex) => regex.test('@!#?@!'))).toEqual(false); } finally { await FsPoly.rm(tempFile); }