Skip to content

Commit 840bc72

Browse files
committed
Some improvements to findCommonParentDir and some more unit tests
1 parent 96a3f31 commit 840bc72

File tree

2 files changed

+124
-16
lines changed

2 files changed

+124
-16
lines changed

extensions/ql-vscode/src/common/files.ts

+13-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { pathExists, stat, readdir, opendir } from "fs-extra";
2-
import { isAbsolute, join, relative, resolve, sep } from "path";
3-
import { tmpdir as osTmpdir } from "os";
2+
import { isAbsolute, join, parse, relative, resolve, sep } from "path";
3+
import { homedir, tmpdir as osTmpdir } from "os";
44

55
/**
66
* Recursively finds all .ql files in this set of Uris.
@@ -134,24 +134,27 @@ export function tmpdir(): string {
134134
}
135135

136136
/**
137-
* Finds the common parent directory of an arbitrary number of paths. If the paths are absolute,
138-
* the result is also absolute. If the paths are relative, the result is relative.
137+
* Finds the common parent directory of an arbitrary number of paths. The result
138+
* will be an absolute path.
139139
* @param paths The array of paths.
140140
* @returns The common parent directory of the paths.
141141
*/
142142
export function findCommonParentDir(...paths: string[]): string {
143+
const normalizedPaths = paths.map((path) => normalizePath(path));
144+
143145
// Split each path into its components
144-
const pathParts = paths.map((path) => path.split(sep));
146+
const pathParts = normalizedPaths.map((path) => path.split(sep));
147+
148+
// Start with the root directory
149+
let commonDir = parse(homedir()).root;
145150

146-
let commonDir = "";
147-
// Iterate over the components of the first path and checks if the same
151+
// Iterate over the components of the first path and check if the same
148152
// component exists at the same position in all the other paths. If it does,
149153
// add the component to the common directory. If it doesn't, stop the
150154
// iteration and returns the common directory found so far.
151-
for (let i = 0; i < pathParts[0].length; i++) {
152-
const part = pathParts[0][i];
155+
for (const [i, part] of pathParts[0].entries()) {
153156
if (pathParts.every((parts) => parts[i] === part)) {
154-
commonDir = `${commonDir}${part}${sep}`;
157+
commonDir = join(commonDir, part);
155158
} else {
156159
break;
157160
}

extensions/ql-vscode/test/unit-tests/common/files.test.ts

+111-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { join } from "path";
1+
import { join, parse } from "path";
22

33
import {
44
containsPath,
@@ -13,6 +13,7 @@ import type { DirResult } from "tmp";
1313
import { dirSync } from "tmp";
1414
import { ensureDirSync, symlinkSync, writeFileSync } from "fs-extra";
1515
import "../../matchers/toEqualPath";
16+
import { homedir } from "os";
1617

1718
describe("files", () => {
1819
const dataDir = join(__dirname, "../../data");
@@ -461,7 +462,11 @@ describe("walkDirectory", () => {
461462
});
462463

463464
describe("findCommonParentDir", () => {
464-
it("should find the common parent dir for multiple paths with common parent", () => {
465+
const dataDir = join(__dirname, "../../data");
466+
const rootDir = parse(homedir()).root;
467+
const sourceDir = join(dataDir, "../..");
468+
469+
it("should find the common parent dir for multiple relative paths with common parent", () => {
465470
const paths = [
466471
join("foo", "bar", "baz"),
467472
join("foo", "bar", "qux"),
@@ -473,7 +478,7 @@ describe("findCommonParentDir", () => {
473478
expect(commonDir).toEqualPath(join("foo", "bar"));
474479
});
475480

476-
it("should return the root if paths have no common parent other than root", () => {
481+
it("should return empty path if relative paths have no common parent", () => {
477482
const paths = [
478483
join("foo", "bar", "baz"),
479484
join("qux", "quux", "corge"),
@@ -485,15 +490,115 @@ describe("findCommonParentDir", () => {
485490
expect(commonDir).toEqualPath("");
486491
});
487492

488-
it("should return empty string for relative paths with no common parent", () => {
493+
it("should find the common parent dir for multiple absolute paths with common parent", () => {
494+
const paths = [
495+
join(dataDir, "foo", "bar", "baz"),
496+
join(dataDir, "foo", "bar", "qux"),
497+
join(dataDir, "foo", "bar", "quux"),
498+
];
499+
500+
const commonDir = findCommonParentDir(...paths);
501+
502+
expect(commonDir).toEqualPath(join(dataDir, "foo", "bar"));
503+
});
504+
505+
it("should return the root if absolute paths have no common parent other than root", () => {
506+
const paths = [
507+
join("/foo", "bar", "baz"),
508+
join("/qux", "quux", "corge"),
509+
join("/grault", "garply"),
510+
];
511+
512+
const commonDir = findCommonParentDir(...paths);
513+
514+
expect(commonDir).toEqualPath(rootDir);
515+
});
516+
517+
it("should handle a mix of absolute and relative paths", async () => {
518+
const paths = [
519+
join(dataDir, "foo", "bar", "baz"),
520+
join("foo", "bar", "qux"),
521+
join(dataDir, "foo", "bar", "quux"),
522+
];
523+
524+
const commonDir = findCommonParentDir(...paths);
525+
526+
expect(commonDir).toEqualPath(sourceDir);
527+
});
528+
529+
it("should handle a mix of dirs and files", async () => {
530+
const paths = [
531+
join(dataDir, "foo", "bar", "baz"),
532+
join(dataDir, "foo", "bar", "qux.ql"),
533+
join(dataDir, "foo", "bar", "quux"),
534+
];
535+
536+
const commonDir = findCommonParentDir(...paths);
537+
538+
expect(commonDir).toEqualPath(join(dataDir, "foo", "bar"));
539+
});
540+
541+
it("should handle dirs that have the same name", async () => {
542+
const paths = [
543+
join("foo", "foo", "bar"),
544+
join("foo", "foo", "baz"),
545+
join("foo", "foo"),
546+
];
547+
548+
const commonDir = findCommonParentDir(...paths);
549+
550+
expect(commonDir).toEqualPath(join("foo", "foo"));
551+
});
552+
553+
it("should handle dirs that have the same subdir structure but different base path", async () => {
554+
const paths = [
555+
join("foo", "bar"),
556+
join("bar", "foo", "bar"),
557+
join("foo", "foo", "bar"),
558+
];
559+
560+
const commonDir = findCommonParentDir(...paths);
561+
562+
expect(commonDir).toEqualPath("");
563+
});
564+
565+
it("should handle a single path", async () => {
566+
const paths = [join("foo", "bar", "baz")];
567+
568+
const commonDir = findCommonParentDir(...paths);
569+
570+
expect(commonDir).toEqualPath(join("foo", "bar", "baz"));
571+
});
572+
573+
it("should return the same path if all paths are identical", () => {
489574
const paths = [
490575
join("foo", "bar", "baz"),
491-
join("qux", "quux", "corge"),
492-
join("grault", "garply"),
576+
join("foo", "bar", "baz"),
577+
join("foo", "bar", "baz"),
493578
];
494579

495580
const commonDir = findCommonParentDir(...paths);
496581

582+
expect(commonDir).toEqualPath(join("foo", "bar", "baz"));
583+
});
584+
585+
it("should return the directory path if paths only differ by the file extension", () => {
586+
const paths = [
587+
join("foo", "bar", "baz.txt"),
588+
join("foo", "bar", "baz.jpg"),
589+
join("foo", "bar", "baz.pdf"),
590+
];
591+
592+
const commonDir = findCommonParentDir(...paths);
593+
594+
expect(commonDir).toEqualPath(join("foo", "bar"));
595+
});
596+
597+
it("should handle empty paths", () => {
598+
const paths = ["", "", ""];
599+
600+
const commonDir = findCommonParentDir(...paths);
601+
497602
expect(commonDir).toEqualPath("");
498603
});
499604
});

0 commit comments

Comments
 (0)