Skip to content

Commit

Permalink
fix: fix duplicate results for overlapping patterns due to a leading dot
Browse files Browse the repository at this point in the history
  • Loading branch information
mrmlnc committed May 27, 2023
1 parent 53cd80f commit 18aefc1
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 21 deletions.
29 changes: 29 additions & 0 deletions __snapshots__/unique.e2e.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
exports['Options Unique {"pattern":["./file.md","file.md","*"],"options":{"cwd":"fixtures","unique":false}} (sync) 1'] = [
"./file.md",
"file.md",
"file.md"
]

exports['Options Unique {"pattern":["./file.md","file.md","*"],"options":{"cwd":"fixtures","unique":false}} (async) 1'] = [
"./file.md",
"file.md",
"file.md"
]

exports['Options Unique {"pattern":["./file.md","file.md","*"],"options":{"cwd":"fixtures","unique":false}} (stream) 1'] = [
"./file.md",
"file.md",
"file.md"
]

exports['Options Unique {"pattern":["./file.md","file.md","*"],"options":{"cwd":"fixtures","unique":true}} (sync) 1'] = [
"./file.md"
]

exports['Options Unique {"pattern":["./file.md","file.md","*"],"options":{"cwd":"fixtures","unique":true}} (async) 1'] = [
"./file.md"
]

exports['Options Unique {"pattern":["./file.md","file.md","*"],"options":{"cwd":"fixtures","unique":true}} (stream) 1'] = [
"./file.md"
]
12 changes: 12 additions & 0 deletions src/providers/filters/entry.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,18 @@ describe('Providers → Filters → Entry', () => {
assert.ok(!actual);
});

it('should reject a duplicate entry when the two entries differ only by the leading dot segment', () => {
const first = tests.entry.builder().path('file.txt').file().build();
const second = tests.entry.builder().path('./file.txt').file().build();

const filter = getFilter({
positive: ['*', './file.txt']
});

assert.ok(filter(first));
assert.ok(!filter(second));
});

it('should accept a duplicate entry when an option is disabled', () => {
const filter = getFilter({
positive: ['**/*'],
Expand Down
22 changes: 11 additions & 11 deletions src/providers/filters/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,35 +18,37 @@ export default class EntryFilter {
}

private _filter(entry: Entry, positiveRe: PatternRe[], negativeRe: PatternRe[]): boolean {
if (this._settings.unique && this._isDuplicateEntry(entry)) {
const filepath = utils.path.removeLeadingDotSegment(entry.path);

if (this._settings.unique && this._isDuplicateEntry(filepath)) {
return false;
}

if (this._onlyFileFilter(entry) || this._onlyDirectoryFilter(entry)) {
return false;
}

if (this._isSkippedByAbsoluteNegativePatterns(entry.path, negativeRe)) {
if (this._isSkippedByAbsoluteNegativePatterns(filepath, negativeRe)) {
return false;
}

const isDirectory = entry.dirent.isDirectory();

const isMatched = this._isMatchToPatterns(entry.path, positiveRe, isDirectory) && !this._isMatchToPatterns(entry.path, negativeRe, isDirectory);
const isMatched = this._isMatchToPatterns(filepath, positiveRe, isDirectory) && !this._isMatchToPatterns(filepath, negativeRe, isDirectory);

if (this._settings.unique && isMatched) {
this._createIndexRecord(entry);
this._createIndexRecord(filepath);
}

return isMatched;
}

private _isDuplicateEntry(entry: Entry): boolean {
return this.index.has(entry.path);
private _isDuplicateEntry(filepath: string): boolean {
return this.index.has(filepath);
}

private _createIndexRecord(entry: Entry): void {
this.index.set(entry.path, undefined);
private _createIndexRecord(filepath: string): void {
this.index.set(filepath, undefined);
}

private _onlyFileFilter(entry: Entry): boolean {
Expand All @@ -67,9 +69,7 @@ export default class EntryFilter {
return utils.pattern.matchAny(fullpath, patternsRe);
}

private _isMatchToPatterns(entryPath: string, patternsRe: PatternRe[], isDirectory: boolean): boolean {
const filepath = utils.path.removeLeadingDotSegment(entryPath);

private _isMatchToPatterns(filepath: string, patternsRe: PatternRe[], isDirectory: boolean): boolean {
// Trying to match files and directories by patterns.
const isMatched = utils.pattern.matchAny(filepath, patternsRe);

Expand Down
20 changes: 20 additions & 0 deletions src/tests/e2e/options/unique.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as runner from '../runner';

runner.suite('Options Unique', {
tests: [
{
pattern: ['./file.md', 'file.md', '*'],
options: {
cwd: 'fixtures',
unique: false
}
},
{
pattern: ['./file.md', 'file.md', '*'],
options: {
cwd: 'fixtures',
unique: true
}
}
]
});
25 changes: 15 additions & 10 deletions src/tests/e2e/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type Suite = {
};

type Test = {
pattern: Pattern;
pattern: Pattern | Pattern[];
options?: fg.Options;
/**
* Allow to run only one test case with debug information.
Expand All @@ -48,10 +48,11 @@ export function suite(name: string, suite: Suite): void {
const title = getTestTitle(test);
const definition = getTestMochaDefinition(suite, test);
const transformers = getResultTransformers(suite, test);
const patterns = getTestPatterns(test);
const options = getFastGlobOptions(suite, test);

definition(`${title} (sync)`, () => {
let actual = getFastGlobEntriesSync(test.pattern, options);
let actual = getFastGlobEntriesSync(patterns, options);

actual = transform(actual, transformers);

Expand All @@ -60,7 +61,7 @@ export function suite(name: string, suite: Suite): void {
});

definition(`${title} (async)`, async () => {
let actual = await getFastGlobEntriesAsync(test.pattern, options);
let actual = await getFastGlobEntriesAsync(patterns, options);

actual = transform(actual, transformers);

Expand All @@ -69,7 +70,7 @@ export function suite(name: string, suite: Suite): void {
});

definition(`${title} (stream)`, async () => {
let actual = await getFastGlobEntriesStream(test.pattern, options);
let actual = await getFastGlobEntriesStream(patterns, options);

actual = transform(actual, transformers);

Expand All @@ -84,6 +85,10 @@ function getSuiteTests(tests: Test[] | Test[][]): Test[] {
return ([] as Test[]).concat(...tests);
}

function getTestPatterns(test: Test): Pattern[] {
return ([] as Pattern[]).concat(test.pattern);
}

function getTestTitle(test: Test): string {
// Replacing placeholders to hide absolute paths from snapshots.
const replacements = {
Expand Down Expand Up @@ -143,18 +148,18 @@ function getResultTransformers(suite: Suite, test: Test): TransformFunction[] {
return transformers;
}

function getFastGlobEntriesSync(pattern: Pattern, options?: fg.Options): string[] {
return fg.sync(pattern, options);
function getFastGlobEntriesSync(patterns: Pattern[], options?: fg.Options): string[] {
return fg.sync(patterns, options);
}

async function getFastGlobEntriesAsync(pattern: Pattern, options?: fg.Options): Promise<string[]> {
return fg(pattern, options);
async function getFastGlobEntriesAsync(patterns: Pattern[], options?: fg.Options): Promise<string[]> {
return fg(patterns, options);
}

async function getFastGlobEntriesStream(pattern: Pattern, options?: fg.Options): Promise<string[]> {
async function getFastGlobEntriesStream(patterns: Pattern[], options?: fg.Options): Promise<string[]> {
const entries: string[] = [];

const stream = fg.stream(pattern, options);
const stream = fg.stream(patterns, options);

await new Promise((resolve, reject) => {
stream.on('data', (entry: string) => entries.push(entry));
Expand Down

0 comments on commit 18aefc1

Please sign in to comment.