Skip to content

Commit 056e3bf

Browse files
committed
[@osd/cross-platform] Adds cross-platform helpers
Signed-off-by: Miki <amoo_miki@yahoo.com>
1 parent 887093d commit 056e3bf

File tree

33 files changed

+559
-168
lines changed

33 files changed

+559
-168
lines changed

packages/osd-config-schema/src/errors/__snapshots__/schema_error.test.ts.snap

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/osd-config-schema/src/errors/schema_error.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
import { relative, sep } from 'path';
3232
import { SchemaError } from '.';
3333

34-
import { standardize, PROCESS_WORKING_DIR } from '@osd/cross-platform';
34+
import { standardize, getRepoRoot } from '@osd/cross-platform';
3535

3636
/**
3737
* Make all paths in stacktrace relative.
@@ -48,7 +48,7 @@ export const cleanStack = (stack: string) =>
4848
}
4949

5050
const path = parts[1];
51-
const relativePath = standardize(relative(PROCESS_WORKING_DIR, path));
51+
const relativePath = standardize(relative(getRepoRoot(path) || '.', path));
5252

5353
return line.replace(path, relativePath);
5454
})

packages/osd-cross-platform/src/__snapshots__/path.test.ts.snap

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/osd-cross-platform/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55

66
export * from './path';
77
export * from './process';
8+
export * from './repo_root';
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import path from 'path';
7+
import fs from 'fs';
8+
import { access, rmdir, mkdir, writeFile, symlink } from 'fs/promises';
9+
10+
import {
11+
resolveToFullNameSync,
12+
resolveToFullPathSync,
13+
resolveToShortNameSync,
14+
resolveToShortPathSync,
15+
shortNamesSupportedSync,
16+
realPathSync,
17+
realShortPathSync,
18+
standardize,
19+
} from './path';
20+
21+
const tmpTestFolder = './__test_artifacts__';
22+
const longFolderName = '.long-folder-name';
23+
const longFileName = '.long-file-name.txt';
24+
const longSymlinkName = '.sym.link';
25+
const shortFolderName = 'LONG-F~1';
26+
const shortFileName = 'LONG-F~1.TXT';
27+
const dummyWindowsPath = 'C:\\a\\b\\c';
28+
const dummyWindowsPOSIXPath = 'C:/a/b/c';
29+
const dummyPOSIXPath = '/a/b/c';
30+
31+
const onWindows = process.platform === 'win32' ? describe : xdescribe;
32+
const onWindowsWithShortNames = shortNamesSupportedSync() ? describe : xdescribe;
33+
34+
// Save the real process.platform
35+
const realPlatform = Object.getOwnPropertyDescriptor(process, 'platform')!;
36+
37+
describe('Cross Platform', () => {
38+
describe('path', () => {
39+
onWindows('on Windows', () => {
40+
onWindowsWithShortNames('when 8.3 is supported', () => {
41+
beforeAll(async () => {
42+
// Cleanup
43+
try {
44+
// If leftover artifacts were found, get rid of them
45+
await access(tmpTestFolder);
46+
await rmdir(tmpTestFolder, { recursive: true });
47+
} catch (ex) {
48+
// Do nothing; if `rmdir` failed, let the `mkdir` below throw the error
49+
}
50+
51+
await mkdir(tmpTestFolder);
52+
await mkdir(path.resolve(tmpTestFolder, longFolderName));
53+
await writeFile(path.resolve(tmpTestFolder, longFolderName, longFileName), '');
54+
await symlink(
55+
path.resolve(tmpTestFolder, longFolderName),
56+
path.resolve(tmpTestFolder, longSymlinkName),
57+
'junction'
58+
);
59+
});
60+
61+
afterAll(async () => {
62+
try {
63+
await rmdir(tmpTestFolder, { recursive: true });
64+
} catch (ex) {
65+
// Do nothing
66+
}
67+
});
68+
69+
it('can synchronously extract full name of a folder', () => {
70+
const name = path.basename(
71+
resolveToFullPathSync(path.resolve(tmpTestFolder, shortFolderName))
72+
);
73+
expect(name).toBe(longFolderName);
74+
});
75+
76+
it('can synchronously extract full name of a file', () => {
77+
const name = path.basename(
78+
resolveToFullNameSync(path.resolve(tmpTestFolder, shortFolderName, shortFileName))
79+
);
80+
expect(name).toBe(longFileName);
81+
});
82+
83+
it('can synchronously extract short name of a folder', () => {
84+
const name = path.basename(
85+
resolveToShortPathSync(path.resolve(tmpTestFolder, longFolderName))
86+
);
87+
expect(name).toBe(shortFolderName);
88+
});
89+
90+
it('can synchronously extract short name of a file', () => {
91+
const name = path.basename(
92+
resolveToShortNameSync(path.resolve(tmpTestFolder, longFolderName, longFileName))
93+
);
94+
expect(name).toBe(shortFileName);
95+
});
96+
97+
it('can synchronously extract full name of a symbolic link', () => {
98+
const name = path.basename(realPathSync(path.resolve(tmpTestFolder, longSymlinkName)));
99+
expect(name).toBe(longFolderName);
100+
});
101+
102+
it('can synchronously extract short name of a symbolic link', () => {
103+
const name = path.basename(
104+
realShortPathSync(path.resolve(tmpTestFolder, longSymlinkName))
105+
);
106+
expect(name).toBe(shortFolderName);
107+
});
108+
});
109+
});
110+
111+
describe('on platforms other than Windows', () => {
112+
let mockPathNormalize: jest.SpyInstance<string, [p: string]>;
113+
let mockPathResolve: jest.SpyInstance<string, string[]>;
114+
let mockFSRealPathSync: jest.SpyInstance<string>;
115+
116+
beforeAll(() => {
117+
Object.defineProperty(process, 'platform', {
118+
...Object.getOwnPropertyDescriptor(process, 'property'),
119+
value: 'linux',
120+
});
121+
122+
mockPathNormalize = jest.spyOn(path, 'normalize').mockReturnValue(dummyPOSIXPath);
123+
mockPathResolve = jest.spyOn(path, 'resolve').mockReturnValue(dummyPOSIXPath);
124+
mockFSRealPathSync = jest
125+
.spyOn(fs, 'realpathSync')
126+
.mockReturnValue(dummyPOSIXPath) as jest.SpyInstance<string>;
127+
});
128+
129+
afterAll(() => {
130+
// Restore the real property value after each test
131+
Object.defineProperty(process, 'platform', realPlatform);
132+
mockPathNormalize.mockRestore();
133+
mockPathResolve.mockRestore();
134+
mockFSRealPathSync.mockRestore();
135+
});
136+
137+
it('all short and full name methods return just the normalized paths', () => {
138+
expect(shortNamesSupportedSync()).toBe(false);
139+
expect(resolveToFullPathSync(dummyPOSIXPath)).toBe(dummyPOSIXPath);
140+
expect(resolveToShortPathSync(dummyPOSIXPath)).toBe(dummyPOSIXPath);
141+
});
142+
});
143+
144+
describe('standardize', () => {
145+
describe('on Windows', () => {
146+
let mockPathNormalize: jest.SpyInstance<string, [p: string]>;
147+
148+
beforeAll(() => {
149+
Object.defineProperty(process, 'platform', {
150+
...Object.getOwnPropertyDescriptor(process, 'property'),
151+
value: 'win32',
152+
});
153+
154+
mockPathNormalize = jest.spyOn(path, 'normalize').mockReturnValue(dummyWindowsPath);
155+
});
156+
157+
afterAll(() => {
158+
// Restore the real property value after each test
159+
Object.defineProperty(process, 'platform', realPlatform);
160+
mockPathNormalize.mockRestore();
161+
});
162+
163+
it('produces a path in native format', () => {
164+
expect(standardize(dummyWindowsPath, false, false)).toMatchSnapshot();
165+
});
166+
167+
it('produces a path in native format even for POSIX input', () => {
168+
expect(standardize(dummyWindowsPOSIXPath, false, false)).toMatchSnapshot();
169+
});
170+
171+
it('produces a path in native format with escaped backslashes', () => {
172+
expect(standardize(dummyWindowsPath, false, true)).toMatchSnapshot();
173+
});
174+
175+
it('produces a path in POSIX format', () => {
176+
expect(standardize(dummyWindowsPath)).toMatchSnapshot();
177+
});
178+
});
179+
180+
describe('on POSIX-compatible platforms', () => {
181+
let mockPathNormalize: jest.SpyInstance<string, [p: string]>;
182+
183+
beforeAll(() => {
184+
Object.defineProperty(process, 'platform', {
185+
...Object.getOwnPropertyDescriptor(process, 'property'),
186+
value: 'linux',
187+
});
188+
189+
mockPathNormalize = jest.spyOn(path, 'normalize').mockReturnValue(dummyPOSIXPath);
190+
});
191+
192+
afterAll(() => {
193+
// Restore the real property value after each test
194+
Object.defineProperty(process, 'platform', realPlatform);
195+
mockPathNormalize.mockRestore();
196+
});
197+
198+
it('produces a path in POSIX format', () => {
199+
expect(standardize(dummyPOSIXPath)).toMatchSnapshot();
200+
});
201+
202+
it('ignores additional parameters', () => {
203+
expect(standardize(dummyPOSIXPath, false, true)).toMatchSnapshot();
204+
});
205+
});
206+
});
207+
});
208+
});

0 commit comments

Comments
 (0)