| 1 | // @effect-diagnostics nodeBuiltinImport:off - Compiles the native dependency regression directly. |
| 2 | import * as NodeChildProcess from "node:child_process"; |
| 3 | import * as NodeFS from "node:fs"; |
| 4 | import * as NodeModule from "node:module"; |
| 5 | import * as NodeOS from "node:os"; |
| 6 | import * as NodePath from "node:path"; |
| 7 | import { expect, it } from "vite-plus/test"; |
| 8 | |
| 9 | // oxlint-disable-next-line t3code/no-global-process-runtime -- This test compiles against the host Foundation framework. |
| 10 | it.skipIf(NodeOS.platform() !== "darwin")( |
| 11 | "registers and reads native permissions concurrently without corrupting the registry", |
| 12 | () => { |
| 13 | const directory = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-permissions-test-")); |
| 14 | try { |
| 15 | const require = NodeModule.createRequire(import.meta.url); |
| 16 | const core = NodePath.dirname( |
| 17 | NodeModule.createRequire(require.resolve("expo/package.json")).resolve( |
| 18 | "expo-modules-core/package.json", |
| 19 | ), |
| 20 | ); |
| 21 | const read = (relativePath: string) => |
| 22 | NodeFS.readFileSync(NodePath.join(core, relativePath), "utf8").replace( |
| 23 | /^#import .*$/gm, |
| 24 | "", |
| 25 | ); |
| 26 | const source = NodePath.join(directory, "regression.m"); |
| 27 | NodeFS.writeFileSync( |
| 28 | source, |
| 29 | [ |
| 30 | "#import <Foundation/Foundation.h>", |
| 31 | "#import <objc/runtime.h>", |
| 32 | "#include <assert.h>", |
| 33 | "#include <pthread.h>", |
| 34 | "typedef void (^EXPromiseResolveBlock)(id result);", |
| 35 | "typedef void (^EXPromiseRejectBlock)(NSString *, NSString *, NSError *);", |
| 36 | "#define RCTLogWarn(...)", |
| 37 | read("ios/Interfaces/Permissions/EXPermissionsInterface.h"), |
| 38 | read("ios/Legacy/Services/Permissions/EXPermissionsService.h"), |
| 39 | read("ios/Legacy/Services/Permissions/EXPermissionsService.m"), |
| 40 | NodeFS.readFileSync( |
| 41 | new URL("./fixtures/PermissionsServiceRegression.m", import.meta.url), |
| 42 | "utf8", |
| 43 | ), |
| 44 | ].join("\n"), |
| 45 | ); |
| 46 | const executable = NodePath.join(directory, "regression"); |
| 47 | NodeChildProcess.execFileSync( |
| 48 | "clang", |
| 49 | [ |
| 50 | "-fobjc-arc", |
| 51 | "-fblocks", |
| 52 | "-fsanitize=thread", |
| 53 | "-framework", |
| 54 | "Foundation", |
| 55 | source, |
| 56 | "-o", |
| 57 | executable, |
| 58 | ], |
| 59 | { encoding: "utf8", timeout: 30_000 }, |
| 60 | ); |
| 61 | expect( |
| 62 | NodeChildProcess.execFileSync(executable, { encoding: "utf8", timeout: 15_000 }).trim(), |
| 63 | ).toBe("passed"); |
| 64 | } finally { |
| 65 | NodeFS.rmSync(directory, { recursive: true, force: true }); |
| 66 | } |
| 67 | }, |
| 68 | ); |