Skip to content

Commit e15a527

Browse files
Add arm64 dataconnect binary (#9423)
### Description * Add `arm64` binary details to `downloadableEmulatorInfo.json` * Update `downloadableEmulators.ts` to download arm64 binary when a `darwin` `arm64` environment is detected * Add unit tests to verify correct binary is chosen for certain environments * Edit `EmulatorDownloadDetails` to be generated on-demand to facilitate unit testing ### Scenarios Tested * `linux` environment --> `linux-amd64` binary * `win32` environment --> `windows-amd64` binary * `darwin (arm64)` environment --> `macos-arm64` binary * `darwin (non-arm64)` environment --> `macos-amd64` binary # commits: * add unit tests to ensure correct binary is chosen in certain environments * fix unit tests to properly clean up node process tampering * update unit tests to mock with sinon * address comments, make generateDownloadDetails private and switch existing uses to use getDownloadDetails
1 parent 0f280d2 commit e15a527

File tree

5 files changed

+184
-123
lines changed

5 files changed

+184
-123
lines changed

scripts/emulator-tests/unzipEmulators.spec.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import { expect } from "chai";
22
import * as fs from "fs";
33
import * as path from "path";
44
import { unzip } from "../../src/unzip";
5-
import { DownloadDetails } from "../../src/emulator/downloadableEmulators";
5+
import { getDownloadDetails } from "../../src/emulator/downloadableEmulators";
66
import { Client } from "../../src/apiv2";
77
import { tmpdir } from "os";
8+
import { Emulators } from "../../src/emulator/types";
89

910
describe("unzipEmulators", () => {
1011
let tempDir: string;
@@ -18,10 +19,8 @@ describe("unzipEmulators", () => {
1819
});
1920

2021
it("should unzip a ui emulator zip file", async () => {
21-
const [uiVersion, uiRemoteUrl] = [
22-
DownloadDetails.ui.version,
23-
DownloadDetails.ui.opts.remoteUrl,
24-
];
22+
const downloadDetails = getDownloadDetails(Emulators.UI);
23+
const [uiVersion, uiRemoteUrl] = [downloadDetails.version, downloadDetails.opts.remoteUrl];
2524

2625
const uiZipPath = path.join(tempDir, `ui-v${uiVersion}.zip`);
2726

@@ -44,9 +43,10 @@ describe("unzipEmulators", () => {
4443
}).timeout(10000);
4544

4645
it("should unzip a pubsub emulator zip file", async () => {
46+
const downloadDetails = getDownloadDetails(Emulators.PUBSUB);
4747
const [pubsubVersion, pubsubRemoteUrl] = [
48-
DownloadDetails.pubsub.version,
49-
DownloadDetails.pubsub.opts.remoteUrl,
48+
downloadDetails.version,
49+
downloadDetails.opts.remoteUrl,
5050
];
5151

5252
const pubsubZipPath = path.join(tempDir, `pubsub-emulator-v${pubsubVersion}.zip`);
@@ -97,7 +97,7 @@ async function downloadFile(url: string, targetPath: string): Promise<string> {
9797
}: ${await res.response.text()}`,
9898
{
9999
cause: new Error(
100-
`Object DownloadDetails from src${path.sep}emulator${path.sep}downloadableEmulators.ts contains invalid URL: ${url}`,
100+
`Object returned by getDownloadDetails() from src${path.sep}emulator${path.sep}downloadableEmulators.ts contains invalid URL: ${url}`,
101101
),
102102
},
103103
);

src/emulator/downloadableEmulatorInfo.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@
6161
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v2.16.0",
6262
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.16.0"
6363
},
64+
"darwin_arm64": {
65+
"version": "2.16.0",
66+
"expectedSize": 29442946,
67+
"expectedChecksum": "15a6ee63f48021197df5395455666f29",
68+
"expectedChecksumSHA256": "4a3933f55db380f20c478cd1a3cc4da8742da75bdcea589fc6e1f52b18943f48",
69+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v2.16.0",
70+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.16.0"
71+
},
6472
"win32": {
6573
"version": "2.16.0",
6674
"expectedSize": 30456320,

src/emulator/downloadableEmulators.spec.ts

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as fs from "fs-extra";
55

66
import * as downloadableEmulators from "./downloadableEmulators";
77
import { Emulators } from "./types";
8+
import * as emulatorUpdateDetails from "./downloadableEmulatorInfo.json";
89

910
type DownloadableEmulator = Emulators.FIRESTORE | Emulators.DATABASE | Emulators.PUBSUB;
1011

@@ -14,27 +15,16 @@ function checkDownloadPath(name: DownloadableEmulator): void {
1415
}
1516

1617
describe("downloadDetails", () => {
17-
const tempEnvVars: Record<DownloadableEmulator, string> = {
18-
firestore: "",
19-
database: "",
20-
pubsub: "",
21-
};
18+
let sandbox: sinon.SinonSandbox;
2219
let chmodStub: sinon.SinonStub;
2320
beforeEach(() => {
2421
chmodStub = sinon.stub(fs, "chmodSync").returns();
25-
tempEnvVars["firestore"] = process.env["FIRESTORE_EMULATOR_BINARY_PATH"] ?? "";
26-
tempEnvVars["database"] = process.env["DATABASE_EMULATOR_BINARY_PATH"] ?? "";
27-
tempEnvVars["pubsub"] = process.env["PUBSUB_EMULATOR_BINARY_PATH"] ?? "";
28-
delete process.env["FIRESTORE_EMULATOR_BINARY_PATH"];
29-
delete process.env["DATABASE_EMULATOR_BINARY_PATH"];
30-
delete process.env["PUBSUB_EMULATOR_BINARY_PATH"];
22+
sandbox = sinon.createSandbox();
3123
});
3224

3325
afterEach(() => {
3426
chmodStub.restore();
35-
process.env["FIRESTORE_EMULATOR_BINARY_PATH"] = tempEnvVars["firestore"];
36-
process.env["DATABASE_EMULATOR_BINARY_PATH"] = tempEnvVars["database"];
37-
process.env["PUBSUB_EMULATOR_BINARY_PATH"] = tempEnvVars["pubsub"];
27+
sandbox.restore();
3828
});
3929
it("should match the basename of remoteUrl", () => {
4030
checkDownloadPath(Emulators.FIRESTORE);
@@ -43,9 +33,13 @@ describe("downloadDetails", () => {
4333
});
4434

4535
it("should apply environment varable overrides", () => {
46-
process.env["FIRESTORE_EMULATOR_BINARY_PATH"] = "my/fake/firestore";
47-
process.env["DATABASE_EMULATOR_BINARY_PATH"] = "my/fake/database";
48-
process.env["PUBSUB_EMULATOR_BINARY_PATH"] = "my/fake/pubsub";
36+
sandbox.stub(process, "env").value({
37+
...process.env,
38+
FIRESTORE_EMULATOR_BINARY_PATH: "my/fake/firestore",
39+
DATABASE_EMULATOR_BINARY_PATH: "my/fake/database",
40+
PUBSUB_EMULATOR_BINARY_PATH: "my/fake/pubsub",
41+
DATACONNECT_EMULATOR_BINARY_PATH: "my/fake/dataconnect",
42+
});
4943

5044
expect(downloadableEmulators.getDownloadDetails(Emulators.FIRESTORE).binaryPath).to.equal(
5145
"my/fake/firestore",
@@ -56,6 +50,37 @@ describe("downloadDetails", () => {
5650
expect(downloadableEmulators.getDownloadDetails(Emulators.PUBSUB).binaryPath).to.equal(
5751
"my/fake/pubsub",
5852
);
59-
expect(chmodStub.callCount).to.equal(3);
53+
expect(downloadableEmulators.getDownloadDetails(Emulators.DATACONNECT).binaryPath).to.equal(
54+
"my/fake/dataconnect",
55+
);
56+
expect(chmodStub.callCount).to.equal(4);
57+
});
58+
59+
it("should select the right binary for the host environment", () => {
60+
let downloadDetails;
61+
sandbox.stub(process, "platform").value("linux");
62+
downloadDetails = downloadableEmulators.getDownloadDetails(Emulators.DATACONNECT);
63+
expect(downloadDetails.opts.remoteUrl).to.equal(
64+
emulatorUpdateDetails.dataconnect.linux.remoteUrl,
65+
);
66+
67+
sandbox.stub(process, "platform").value("win32");
68+
downloadDetails = downloadableEmulators.getDownloadDetails(Emulators.DATACONNECT);
69+
expect(downloadDetails.opts.remoteUrl).to.equal(
70+
emulatorUpdateDetails.dataconnect.win32.remoteUrl,
71+
);
72+
73+
sandbox.stub(process, "platform").value("darwin");
74+
sandbox.stub(process, "arch").value("x64");
75+
downloadDetails = downloadableEmulators.getDownloadDetails(Emulators.DATACONNECT);
76+
expect(downloadDetails.opts.remoteUrl).to.equal(
77+
emulatorUpdateDetails.dataconnect.darwin.remoteUrl,
78+
);
79+
80+
sandbox.stub(process, "arch").value("arm64");
81+
downloadDetails = downloadableEmulators.getDownloadDetails(Emulators.DATACONNECT);
82+
expect(downloadDetails.opts.remoteUrl).to.equal(
83+
emulatorUpdateDetails.dataconnect.darwin_arm64.remoteUrl,
84+
);
6085
});
6186
});

src/emulator/downloadableEmulators.ts

Lines changed: 111 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -40,97 +40,121 @@ const EMULATOR_UPDATE_DETAILS: {
4040
};
4141
dataconnect: {
4242
darwin: EmulatorUpdateDetails;
43+
darwin_arm64: EmulatorUpdateDetails;
4344
win32: EmulatorUpdateDetails;
4445
linux: EmulatorUpdateDetails;
4546
};
4647
} = emulatorUpdateDetails;
4748

48-
const emulatorUiDetails = experiments.isEnabled("emulatoruisnapshot")
49-
? EMULATOR_UPDATE_DETAILS.ui.snapshot
50-
: EMULATOR_UPDATE_DETAILS.ui.main;
51-
const dataconnectDetails =
52-
process.platform === "darwin"
53-
? EMULATOR_UPDATE_DETAILS.dataconnect.darwin
54-
: process.platform === "win32"
55-
? EMULATOR_UPDATE_DETAILS.dataconnect.win32
56-
: EMULATOR_UPDATE_DETAILS.dataconnect.linux;
57-
export const DownloadDetails: { [s in DownloadableEmulators]: EmulatorDownloadDetails } = {
58-
database: {
59-
downloadPath: path.join(
60-
CACHE_DIR,
61-
EMULATOR_UPDATE_DETAILS.database.downloadPathRelativeToCacheDir,
62-
),
63-
version: EMULATOR_UPDATE_DETAILS.database.version,
64-
opts: {
65-
...EMULATOR_UPDATE_DETAILS.database,
66-
cacheDir: CACHE_DIR,
67-
namePrefix: "firebase-database-emulator",
68-
},
69-
},
70-
firestore: {
71-
downloadPath: path.join(
72-
CACHE_DIR,
73-
EMULATOR_UPDATE_DETAILS.firestore.downloadPathRelativeToCacheDir,
74-
),
75-
version: EMULATOR_UPDATE_DETAILS.firestore.version,
76-
opts: {
77-
...EMULATOR_UPDATE_DETAILS.firestore,
78-
cacheDir: CACHE_DIR,
79-
namePrefix: "cloud-firestore-emulator",
80-
},
81-
},
82-
storage: {
83-
downloadPath: path.join(
84-
CACHE_DIR,
85-
EMULATOR_UPDATE_DETAILS.storage.downloadPathRelativeToCacheDir,
86-
),
87-
version: EMULATOR_UPDATE_DETAILS.storage.version,
88-
opts: {
89-
...EMULATOR_UPDATE_DETAILS.storage,
90-
cacheDir: CACHE_DIR,
91-
namePrefix: "cloud-storage-rules-emulator",
92-
},
93-
},
94-
ui: {
95-
version: emulatorUiDetails.version,
96-
downloadPath: path.join(CACHE_DIR, emulatorUiDetails.downloadPathRelativeToCacheDir),
97-
unzipDir: path.join(CACHE_DIR, `ui-v${emulatorUiDetails.version}`),
98-
binaryPath: path.join(CACHE_DIR, emulatorUiDetails.binaryPathRelativeToCacheDir!),
99-
opts: {
100-
...emulatorUiDetails,
101-
cacheDir: CACHE_DIR,
102-
skipCache: experiments.isEnabled("emulatoruisnapshot"),
103-
skipChecksumAndSize: experiments.isEnabled("emulatoruisnapshot"),
104-
namePrefix: "ui",
105-
},
106-
},
107-
pubsub: {
108-
downloadPath: path.join(
109-
CACHE_DIR,
110-
EMULATOR_UPDATE_DETAILS.pubsub.downloadPathRelativeToCacheDir,
111-
),
112-
version: EMULATOR_UPDATE_DETAILS.pubsub.version,
113-
unzipDir: path.join(CACHE_DIR, `pubsub-emulator-${EMULATOR_UPDATE_DETAILS.pubsub.version}`),
114-
binaryPath: path.join(CACHE_DIR, EMULATOR_UPDATE_DETAILS.pubsub.binaryPathRelativeToCacheDir!),
115-
opts: {
116-
...EMULATOR_UPDATE_DETAILS.pubsub,
117-
cacheDir: CACHE_DIR,
118-
namePrefix: "pubsub-emulator",
119-
},
120-
},
121-
dataconnect: {
122-
downloadPath: path.join(CACHE_DIR, dataconnectDetails.downloadPathRelativeToCacheDir),
123-
version: dataconnectDetails.version,
124-
binaryPath: path.join(CACHE_DIR, dataconnectDetails.downloadPathRelativeToCacheDir),
125-
opts: {
126-
...dataconnectDetails,
127-
cacheDir: CACHE_DIR,
128-
skipChecksumAndSize: false,
129-
namePrefix: "dataconnect-emulator",
130-
auth: false,
131-
},
132-
},
133-
};
49+
/**
50+
* generate download details for a single emulator, based on the host environment.
51+
* pulls data from `downloadableEmulatorInfo.json`.
52+
* @param emulator The name of the downloadable emulator to get details for.
53+
* @returns The download details for the specified emulator.
54+
*/
55+
function generateDownloadDetails(emulator: DownloadableEmulators): EmulatorDownloadDetails {
56+
const emulatorUiDetails = experiments.isEnabled("emulatoruisnapshot")
57+
? EMULATOR_UPDATE_DETAILS.ui.snapshot
58+
: EMULATOR_UPDATE_DETAILS.ui.main;
59+
60+
const dataconnectDetails =
61+
process.platform === "darwin"
62+
? process.arch === "arm64"
63+
? EMULATOR_UPDATE_DETAILS.dataconnect.darwin_arm64
64+
: EMULATOR_UPDATE_DETAILS.dataconnect.darwin
65+
: process.platform === "win32"
66+
? EMULATOR_UPDATE_DETAILS.dataconnect.win32
67+
: EMULATOR_UPDATE_DETAILS.dataconnect.linux;
68+
69+
switch (emulator) {
70+
case "database":
71+
return {
72+
downloadPath: path.join(
73+
CACHE_DIR,
74+
EMULATOR_UPDATE_DETAILS.database.downloadPathRelativeToCacheDir,
75+
),
76+
version: EMULATOR_UPDATE_DETAILS.database.version,
77+
opts: {
78+
...EMULATOR_UPDATE_DETAILS.database,
79+
cacheDir: CACHE_DIR,
80+
namePrefix: "firebase-database-emulator",
81+
},
82+
};
83+
case "firestore":
84+
return {
85+
downloadPath: path.join(
86+
CACHE_DIR,
87+
EMULATOR_UPDATE_DETAILS.firestore.downloadPathRelativeToCacheDir,
88+
),
89+
version: EMULATOR_UPDATE_DETAILS.firestore.version,
90+
opts: {
91+
...EMULATOR_UPDATE_DETAILS.firestore,
92+
cacheDir: CACHE_DIR,
93+
namePrefix: "cloud-firestore-emulator",
94+
},
95+
};
96+
case "storage":
97+
return {
98+
downloadPath: path.join(
99+
CACHE_DIR,
100+
EMULATOR_UPDATE_DETAILS.storage.downloadPathRelativeToCacheDir,
101+
),
102+
version: EMULATOR_UPDATE_DETAILS.storage.version,
103+
opts: {
104+
...EMULATOR_UPDATE_DETAILS.storage,
105+
cacheDir: CACHE_DIR,
106+
namePrefix: "cloud-storage-rules-emulator",
107+
},
108+
};
109+
case "ui":
110+
return {
111+
version: emulatorUiDetails.version,
112+
downloadPath: path.join(CACHE_DIR, emulatorUiDetails.downloadPathRelativeToCacheDir),
113+
unzipDir: path.join(CACHE_DIR, `ui-v${emulatorUiDetails.version}`),
114+
binaryPath: path.join(CACHE_DIR, emulatorUiDetails.binaryPathRelativeToCacheDir!),
115+
opts: {
116+
...emulatorUiDetails,
117+
cacheDir: CACHE_DIR,
118+
skipCache: experiments.isEnabled("emulatoruisnapshot"),
119+
skipChecksumAndSize: experiments.isEnabled("emulatoruisnapshot"),
120+
namePrefix: "ui",
121+
},
122+
};
123+
case "pubsub":
124+
return {
125+
downloadPath: path.join(
126+
CACHE_DIR,
127+
EMULATOR_UPDATE_DETAILS.pubsub.downloadPathRelativeToCacheDir,
128+
),
129+
version: EMULATOR_UPDATE_DETAILS.pubsub.version,
130+
unzipDir: path.join(CACHE_DIR, `pubsub-emulator-${EMULATOR_UPDATE_DETAILS.pubsub.version}`),
131+
binaryPath: path.join(
132+
CACHE_DIR,
133+
EMULATOR_UPDATE_DETAILS.pubsub.binaryPathRelativeToCacheDir!,
134+
),
135+
opts: {
136+
...EMULATOR_UPDATE_DETAILS.pubsub,
137+
cacheDir: CACHE_DIR,
138+
namePrefix: "pubsub-emulator",
139+
},
140+
};
141+
case "dataconnect":
142+
return {
143+
downloadPath: path.join(CACHE_DIR, dataconnectDetails.downloadPathRelativeToCacheDir),
144+
version: dataconnectDetails.version,
145+
binaryPath: path.join(CACHE_DIR, dataconnectDetails.downloadPathRelativeToCacheDir),
146+
opts: {
147+
...dataconnectDetails,
148+
cacheDir: CACHE_DIR,
149+
skipChecksumAndSize: false,
150+
namePrefix: "dataconnect-emulator",
151+
auth: false,
152+
},
153+
};
154+
default:
155+
throw new Error(`Invalid downloadable emulator: ${emulator}`);
156+
}
157+
}
134158

135159
const EmulatorDetails: { [s in DownloadableEmulators]: DownloadableEmulatorDetails } = {
136160
database: {
@@ -480,7 +504,7 @@ async function _runBinary(
480504
* @param emulator
481505
*/
482506
export function getDownloadDetails(emulator: DownloadableEmulators): EmulatorDownloadDetails {
483-
const details = DownloadDetails[emulator];
507+
const details = generateDownloadDetails(emulator);
484508
const pathOverride = process.env[`${emulator.toUpperCase()}_EMULATOR_BINARY_PATH`];
485509
if (pathOverride) {
486510
const logger = EmulatorLogger.forEmulator(emulator);

0 commit comments

Comments
 (0)