Skip to content

Commit c07cde0

Browse files
authored
fix(shared-ini-file-loader): cache value returned by os.homedir() (#903)
1 parent b0392a7 commit c07cde0

File tree

3 files changed

+104
-2
lines changed

3 files changed

+104
-2
lines changed

.changeset/unlucky-tigers-raise.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@smithy/shared-ini-file-loader": patch
3+
---
4+
5+
Cache value returned by os.homedir()

packages/shared-ini-file-loader/src/getHomeDir.spec.ts

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { getHomeDir } from "./getHomeDir";
66
jest.mock("os");
77

88
describe(getHomeDir.name, () => {
9+
const mockUid = 1;
910
const mockHOME = "mockHOME";
1011
const mockUSERPROFILE = "mockUSERPROFILE";
1112
const mockHOMEPATH = "mockHOMEPATH";
@@ -28,23 +29,28 @@ describe(getHomeDir.name, () => {
2829
afterEach(() => {
2930
process.env = OLD_ENV;
3031
jest.clearAllMocks();
31-
jest.resetModules();
3232
});
3333

3434
it("returns value in process.env.HOME first", () => {
3535
expect(getHomeDir()).toEqual(mockHOME);
36+
expect(homedir).not.toHaveBeenCalled();
3637
});
3738

3839
it("returns value in process.env.USERPROFILE second", () => {
3940
process.env = { ...process.env, HOME: undefined };
4041
expect(getHomeDir()).toEqual(mockUSERPROFILE);
42+
expect(homedir).not.toHaveBeenCalled();
4143
});
4244

4345
describe("returns value in HOMEPATH third", () => {
4446
beforeEach(() => {
4547
process.env = { ...process.env, HOME: undefined, USERPROFILE: undefined };
4648
});
4749

50+
afterEach(() => {
51+
expect(homedir).not.toHaveBeenCalled();
52+
});
53+
4854
it("uses value in process.env.HOMEDRIVE if it's set", () => {
4955
expect(getHomeDir()).toEqual(`${mockHOMEDRIVE}${mockHOMEPATH}`);
5056
});
@@ -56,7 +62,84 @@ describe(getHomeDir.name, () => {
5662
});
5763

5864
it("returns value from homedir fourth", () => {
65+
const processGeteuidSpy = jest.spyOn(process, "geteuid").mockReturnValue(mockUid);
5966
process.env = { ...process.env, HOME: undefined, USERPROFILE: undefined, HOMEPATH: undefined };
6067
expect(getHomeDir()).toEqual(mockHomeDir);
68+
expect(homedir).toHaveBeenCalledTimes(1);
69+
expect(processGeteuidSpy).toHaveBeenCalledTimes(1);
70+
});
71+
72+
describe("makes one homedir call irrespective of getHomeDir calls", () => {
73+
const testSingleHomeDirCall = (num: number) => {
74+
jest.isolateModules(() => {
75+
const { getHomeDir } = require("./getHomeDir");
76+
process.env = { ...process.env, HOME: undefined, USERPROFILE: undefined, HOMEPATH: undefined };
77+
78+
expect(homedir).not.toHaveBeenCalled();
79+
const homeDirArr = Array(num)
80+
.fill(num)
81+
.map(() => getHomeDir());
82+
expect(homeDirArr).toStrictEqual(Array(num).fill(mockHomeDir));
83+
84+
// There is one homedir call even through getHomeDir is called num times.
85+
expect(homedir).toHaveBeenCalledTimes(1);
86+
});
87+
};
88+
89+
describe("when geteuid is available", () => {
90+
it.each([10, 100, 1000, 10000])("calls: %d ", (num: number) => {
91+
const processGeteuidSpy = jest.spyOn(process, "geteuid").mockReturnValue(mockUid);
92+
expect(processGeteuidSpy).not.toHaveBeenCalled();
93+
testSingleHomeDirCall(num);
94+
expect(processGeteuidSpy).toHaveBeenCalledTimes(num);
95+
});
96+
});
97+
98+
describe("when geteuid is not available", () => {
99+
const OLD_GETEUID = process.geteuid;
100+
101+
beforeAll(() => {
102+
// @ts-ignore Type 'undefined' is not assignable to type '() => number'.
103+
process.geteuid = undefined;
104+
});
105+
106+
afterAll(() => {
107+
process.geteuid = OLD_GETEUID;
108+
});
109+
110+
it.each([10, 100, 1000, 10000])("calls: %d ", testSingleHomeDirCall);
111+
});
112+
});
113+
114+
describe("makes multiple homedir calls with based on UIDs", () => {
115+
it.each([2, 10, 100])("calls: %d ", (num: number) => {
116+
jest.isolateModules(() => {
117+
const { getHomeDir } = require("./getHomeDir");
118+
const processGeteuidSpy = jest.spyOn(process, "geteuid").mockReturnValue(mockUid);
119+
for (let i = 0; i < num; i++) {
120+
jest.spyOn(process, "geteuid").mockReturnValueOnce(mockUid + i);
121+
}
122+
process.env = { ...process.env, HOME: undefined, USERPROFILE: undefined, HOMEPATH: undefined };
123+
124+
expect(homedir).not.toHaveBeenCalled();
125+
expect(processGeteuidSpy).not.toHaveBeenCalled();
126+
const homeDirArr = Array(num)
127+
.fill(num)
128+
.map(() => getHomeDir());
129+
expect(homeDirArr).toStrictEqual(Array(num).fill(mockHomeDir));
130+
131+
// There is num homedir calls as each call returns different UID
132+
expect(homedir).toHaveBeenCalledTimes(num);
133+
expect(processGeteuidSpy).toHaveBeenCalledTimes(num);
134+
135+
const homeDir = getHomeDir();
136+
expect(homeDir).toStrictEqual(mockHomeDir);
137+
138+
// No extra calls made to homedir, as mockUid is same as the first call.
139+
expect(homedir).toHaveBeenCalledTimes(num);
140+
// Extra call was made to geteuid to get the same UID as the first call.
141+
expect(processGeteuidSpy).toHaveBeenCalledTimes(num + 1);
142+
});
143+
});
61144
});
62145
});

packages/shared-ini-file-loader/src/getHomeDir.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
import { homedir } from "os";
22
import { sep } from "path";
3+
import { geteuid } from "process";
4+
5+
const homeDirCache: Record<string, string> = {};
6+
7+
const getHomeDirCacheKey = (): string => {
8+
// geteuid is only available on POSIX platforms (i.e. not Windows or Android).
9+
if (geteuid) {
10+
return `${geteuid()}`;
11+
}
12+
return "DEFAULT";
13+
};
314

415
/**
516
* Get the HOME directory for the current runtime.
@@ -13,5 +24,8 @@ export const getHomeDir = (): string => {
1324
if (USERPROFILE) return USERPROFILE;
1425
if (HOMEPATH) return `${HOMEDRIVE}${HOMEPATH}`;
1526

16-
return homedir();
27+
const homeDirCacheKey = getHomeDirCacheKey();
28+
if (!homeDirCache[homeDirCacheKey]) homeDirCache[homeDirCacheKey] = homedir();
29+
30+
return homeDirCache[homeDirCacheKey];
1731
};

0 commit comments

Comments
 (0)