Skip to content

Commit 6bf19eb

Browse files
committed
Use case-insensitive path comparison on Windows
1 parent 477b326 commit 6bf19eb

File tree

5 files changed

+176
-11
lines changed

5 files changed

+176
-11
lines changed

extensions/ql-vscode/src/databases.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { DisposableObject } from "./pure/disposable-object";
2121
import { Logger, extLogger } from "./common";
2222
import { getErrorMessage } from "./pure/helpers-pure";
2323
import { QueryRunner } from "./queryRunner";
24+
import { pathsEqual } from "./pure/files";
2425

2526
/**
2627
* databases.ts
@@ -523,7 +524,11 @@ export class DatabaseItemImpl implements DatabaseItem {
523524
// database for /one/two/three/test.ql is at /one/two/three/three.testproj
524525
const testdir = dirname(testPath);
525526
const testdirbase = basename(testdir);
526-
return databasePath == join(testdir, `${testdirbase}.testproj`);
527+
return pathsEqual(
528+
databasePath,
529+
join(testdir, `${testdirbase}.testproj`),
530+
process.platform,
531+
);
527532
}
528533
} catch {
529534
// No information available for test path - assume database is unaffected.

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { pathExists, stat, readdir } from "fs-extra";
2-
import { join } from "path";
2+
import { join, resolve } from "path";
33

44
/**
55
* Recursively finds all .ql files in this set of Uris.
@@ -50,3 +50,20 @@ export async function getDirectoryNamesInsidePath(
5050

5151
return dirNames;
5252
}
53+
54+
export function pathsEqual(
55+
path1: string,
56+
path2: string,
57+
platform: NodeJS.Platform,
58+
): boolean {
59+
// On Windows, "C:/", "C:\", and "c:/" are all equivalent. We need
60+
// to normalize the paths to ensure they all get resolved to the
61+
// same format. On Windows, we also need to do the comparison
62+
// case-insensitively.
63+
path1 = resolve(path1);
64+
path2 = resolve(path2);
65+
if (platform === "win32") {
66+
return path1.toLowerCase() === path2.toLowerCase();
67+
}
68+
return path1 === path2;
69+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { expect } from "@jest/globals";
2+
import type { MatcherFunction } from "expect";
3+
import { pathsEqual } from "../../src/pure/files";
4+
5+
// eslint-disable-next-line func-style -- We need to have access to this and specify the type of the function
6+
const toEqualPath: MatcherFunction<[expectedPath: unknown]> = function (
7+
actual,
8+
expectedPath,
9+
) {
10+
if (typeof actual !== "string" || typeof expectedPath !== "string") {
11+
throw new Error("These must be of type string!");
12+
}
13+
14+
const pass = pathsEqual(actual, expectedPath, process.platform);
15+
if (pass) {
16+
return {
17+
message: () =>
18+
// eslint-disable-next-line @typescript-eslint/no-invalid-this
19+
`expected ${this.utils.printReceived(
20+
actual,
21+
// eslint-disable-next-line @typescript-eslint/no-invalid-this
22+
)} to equal path ${this.utils.printExpected(expectedPath)}`,
23+
pass: true,
24+
};
25+
} else {
26+
return {
27+
message: () =>
28+
// eslint-disable-next-line @typescript-eslint/no-invalid-this
29+
`expected ${this.utils.printReceived(
30+
actual,
31+
// eslint-disable-next-line @typescript-eslint/no-invalid-this
32+
)} to equal path ${this.utils.printExpected(expectedPath)}`,
33+
pass: false,
34+
};
35+
}
36+
};
37+
38+
expect.extend({
39+
toEqualPath,
40+
});
41+
42+
declare global {
43+
// eslint-disable-next-line @typescript-eslint/no-namespace -- We need to extend this global declaration
44+
namespace jest {
45+
interface AsymmetricMatchers {
46+
toEqualPath(expectedPath: string): void;
47+
}
48+
49+
interface Matchers<R> {
50+
toEqualPath(expectedPath: string): R;
51+
}
52+
}
53+
}

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

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { join } from "path";
33
import {
44
gatherQlFiles,
55
getDirectoryNamesInsidePath,
6+
pathsEqual,
67
} from "../../../src/pure/files";
78

89
describe("files", () => {
@@ -100,3 +101,90 @@ describe("files", () => {
100101
});
101102
});
102103
});
104+
105+
describe("pathsEqual", () => {
106+
const testCases: Array<{
107+
path1: string;
108+
path2: string;
109+
platform: NodeJS.Platform;
110+
expected: boolean;
111+
}> = [
112+
{
113+
path1:
114+
"/home/github/projects/vscode-codeql-starter/codeql-custom-queries-javascript/example.ql",
115+
path2:
116+
"/home/github/projects/vscode-codeql-starter/codeql-custom-queries-javascript/example.ql",
117+
platform: "linux",
118+
expected: true,
119+
},
120+
{
121+
path1:
122+
"/HOME/github/projects/vscode-codeql-starter/codeql-custom-queries-javascript/example.ql",
123+
path2:
124+
"/home/github/projects/vscode-codeql-starter/codeql-custom-queries-javascript/example.ql",
125+
platform: "linux",
126+
expected: false,
127+
},
128+
{
129+
path1:
130+
"/home/github/projects/vscode-codeql-starter/codeql-custom-queries-javascript/example.ql",
131+
path2:
132+
"\\home\\github\\projects\\vscode-codeql-starter\\codeql-custom-queries-javascript\\example.ql",
133+
platform: "linux",
134+
expected: false,
135+
},
136+
{
137+
path1:
138+
"C:/Users/github/projects/vscode-codeql-starter/codeql-custom-queries-javascript/example.ql",
139+
path2:
140+
"C:/Users/github/projects/vscode-codeql-starter/codeql-custom-queries-javascript/example.ql",
141+
platform: "win32",
142+
expected: true,
143+
},
144+
{
145+
path1:
146+
"C:/Users/github/projects/vscode-codeql-starter/codeql-custom-queries-javascript/example.ql",
147+
path2:
148+
"c:/Users/github/projects/vscode-codeql-starter/codeql-custom-queries-javascript/example.ql",
149+
platform: "win32",
150+
expected: true,
151+
},
152+
{
153+
path1:
154+
"C:/Users/github/projects/vscode-codeql-starter/codeql-custom-queries-javascript/example.ql",
155+
path2:
156+
"D:/Users/github/projects/vscode-codeql-starter/codeql-custom-queries-javascript/example.ql",
157+
platform: "win32",
158+
expected: false,
159+
},
160+
{
161+
path1:
162+
"C:/Users/github/projects/vscode-codeql-starter/codeql-custom-queries-javascript/example.ql",
163+
path2:
164+
"C:\\Users\\github\\projects\\vscode-codeql-starter\\codeql-custom-queries-javascript\\example.ql",
165+
platform: "win32",
166+
expected: true,
167+
},
168+
{
169+
path1:
170+
"C:/Users/github/projects/vscode-codeql-starter/codeql-custom-queries-javascript/example.ql",
171+
path2:
172+
"D:\\Users\\github\\projects\\vscode-codeql-starter\\codeql-custom-queries-javascript\\example.ql",
173+
platform: "win32",
174+
expected: false,
175+
},
176+
];
177+
178+
test.each(testCases)(
179+
"$path1 and $path2 are equal on $platform = $expected",
180+
({ path1, path2, platform, expected }) => {
181+
if (platform !== process.platform) {
182+
// We're using the platform-specific path.resolve, so we can't really run
183+
// these tests on all platforms.
184+
return;
185+
}
186+
187+
expect(pathsEqual(path1, path2, platform)).toEqual(expected);
188+
},
189+
);
190+
});

extensions/ql-vscode/test/vscode-tests/minimal-workspace/qltest-discovery.test.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { QLTestDiscovery } from "../../../src/qltest-discovery";
66
import { DirectoryResult } from "tmp-promise";
77
import * as tmp from "tmp-promise";
88

9+
import "../../matchers/toEqualPath";
10+
911
describe("qltest-discovery", () => {
1012
describe("discoverTests", () => {
1113
let directory: DirectoryResult;
@@ -50,39 +52,39 @@ describe("qltest-discovery", () => {
5052

5153
it("should run discovery", async () => {
5254
const result = await (qlTestDiscover as any).discover();
53-
expect(result.watchPath).toBe(baseDir);
54-
expect(result.testDirectory.path).toBe(baseDir);
55+
expect(result.watchPath).toEqualPath(baseDir);
56+
expect(result.testDirectory.path).toEqualPath(baseDir);
5557
expect(result.testDirectory.name).toBe("My tests");
5658

5759
let children = result.testDirectory.children;
5860
expect(children.length).toBe(1);
5961

60-
expect(children[0].path).toBe(cDir);
62+
expect(children[0].path).toEqualPath(cDir);
6163
expect(children[0].name).toBe("c");
6264

6365
children = children[0].children;
6466
expect(children.length).toBe(3);
6567

66-
expect(children[0].path).toBe(dFile);
68+
expect(children[0].path).toEqualPath(dFile);
6769
expect(children[0].name).toBe("d.ql");
68-
expect(children[1].path).toBe(eFile);
70+
expect(children[1].path).toEqualPath(eFile);
6971
expect(children[1].name).toBe("e.ql");
7072

7173
// A merged foler
72-
expect(children[2].path).toBe(hDir);
74+
expect(children[2].path).toEqualPath(hDir);
7375
expect(children[2].name).toBe("f / g / h");
7476

7577
children = children[2].children;
76-
expect(children[0].path).toBe(iFile);
78+
expect(children[0].path).toEqualPath(iFile);
7779
expect(children[0].name).toBe("i.ql");
7880
});
7981

8082
it("should avoid discovery if a folder does not exist", async () => {
8183
await fs.remove(baseDir);
8284

8385
const result = await (qlTestDiscover as any).discover();
84-
expect(result.watchPath).toBe(baseDir);
85-
expect(result.testDirectory.path).toBe(baseDir);
86+
expect(result.watchPath).toEqualPath(baseDir);
87+
expect(result.testDirectory.path).toEqualPath(baseDir);
8688
expect(result.testDirectory.name).toBe("My tests");
8789

8890
expect(result.testDirectory.children.length).toBe(0);

0 commit comments

Comments
 (0)