|
2 | 2 | import { describe, it } from "@effect/vitest"; |
3 | 3 | import { afterAll, expect } from "vite-plus/test"; |
4 | 4 | import * as NodeChildProcess from "node:child_process"; |
| 5 | +import * as Deferred from "effect/Deferred"; |
5 | 6 | import * as Duration from "effect/Duration"; |
6 | 7 | import * as Effect from "effect/Effect"; |
7 | 8 | import * as Fiber from "effect/Fiber"; |
| 9 | +import * as FileSystem from "effect/FileSystem"; |
8 | 10 | import * as Layer from "effect/Layer"; |
| 11 | +import * as Option from "effect/Option"; |
| 12 | +import * as Path from "effect/Path"; |
9 | 13 | import * as Sink from "effect/Sink"; |
10 | 14 | import * as Stream from "effect/Stream"; |
11 | 15 | import * as TestClock from "effect/testing/TestClock"; |
12 | 16 | import { ChildProcessSpawner } from "effect/process"; |
13 | 17 |
|
| 18 | +import * as DesktopConfig from "../app/DesktopConfig.ts"; |
| 19 | +import * as DesktopEnvironment from "../app/DesktopEnvironment.ts"; |
14 | 20 | import { |
15 | 21 | buildWslRuntimeInstallScript, |
16 | 22 | buildWslRuntimeInvalidateScript, |
17 | 23 | buildWslRuntimePruneScript, |
18 | 24 | buildWslRuntimeProbeScript, |
19 | 25 | DesktopWslDistroListError, |
| 26 | + DesktopWslEnvironment, |
20 | 27 | formatMissingToolsReason, |
| 28 | + layer as wslEnvironmentLayer, |
21 | 29 | parseNodePath, |
22 | 30 | parseNodeVersion, |
23 | 31 | parseResolvedPath, |
@@ -158,6 +166,102 @@ describe("probeWslDistros", () => { |
158 | 166 | }); |
159 | 167 | }); |
160 | 168 |
|
| 169 | +describe("WSL command deadlines", () => { |
| 170 | + const makeControlledEnvironment = Effect.gen(function* () { |
| 171 | + const started = yield* Deferred.make<void>(); |
| 172 | + const exited = yield* Deferred.make<ChildProcessSpawner.ExitCode>(); |
| 173 | + const released = yield* Deferred.make<void>(); |
| 174 | + const spawner = ChildProcessSpawner.make(() => |
| 175 | + Effect.acquireRelease( |
| 176 | + Deferred.succeed(started, undefined).pipe( |
| 177 | + Effect.as( |
| 178 | + ChildProcessSpawner.makeHandle({ |
| 179 | + pid: ChildProcessSpawner.ProcessId(1), |
| 180 | + exitCode: Deferred.await(exited), |
| 181 | + isRunning: Deferred.isDone(exited).pipe(Effect.map((done) => !done)), |
| 182 | + kill: () => Effect.void, |
| 183 | + unref: Effect.succeed(Effect.void), |
| 184 | + stdin: Sink.drain, |
| 185 | + stdout: Stream.empty, |
| 186 | + stderr: Stream.empty, |
| 187 | + all: Stream.empty, |
| 188 | + getInputFd: () => Sink.drain, |
| 189 | + getOutputFd: () => Stream.empty, |
| 190 | + }), |
| 191 | + ), |
| 192 | + ), |
| 193 | + () => Deferred.succeed(released, undefined), |
| 194 | + ), |
| 195 | + ); |
| 196 | + const environment = yield* DesktopWslEnvironment.pipe( |
| 197 | + Effect.provide( |
| 198 | + wslEnvironmentLayer.pipe( |
| 199 | + Layer.provide([ |
| 200 | + DesktopEnvironment.layer({ |
| 201 | + dirname: "/repo/apps/desktop", |
| 202 | + homeDirectory: "/home/test", |
| 203 | + platform: "win32", |
| 204 | + processArch: "x64", |
| 205 | + appVersion: "1.2.3", |
| 206 | + appPath: "/repo", |
| 207 | + isPackaged: true, |
| 208 | + resourcesPath: "/repo/resources", |
| 209 | + runningUnderArm64Translation: false, |
| 210 | + }).pipe(Layer.provide([Path.layer, DesktopConfig.layerTest({})])), |
| 211 | + FileSystem.layerNoop({ exists: () => Effect.succeed(true) }), |
| 212 | + Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner), |
| 213 | + ]), |
| 214 | + ), |
| 215 | + ), |
| 216 | + ); |
| 217 | + return { environment, started, exited, released }; |
| 218 | + }); |
| 219 | + |
| 220 | + it.effect("waits for a cold distro to finish starting after 15 seconds", () => |
| 221 | + Effect.gen(function* () { |
| 222 | + const { environment, started, exited, released } = yield* makeControlledEnvironment; |
| 223 | + const fiber = yield* environment.preWarm("Ubuntu").pipe(Effect.forkScoped); |
| 224 | + yield* Deferred.await(started); |
| 225 | + yield* TestClock.adjust(Duration.seconds(15)); |
| 226 | + expect(yield* Deferred.isDone(released)).toBe(false); |
| 227 | + |
| 228 | + yield* Deferred.succeed(exited, ChildProcessSpawner.ExitCode(0)); |
| 229 | + yield* Fiber.join(fiber); |
| 230 | + expect(yield* Deferred.isDone(released)).toBe(true); |
| 231 | + }), |
| 232 | + ); |
| 233 | + |
| 234 | + it.effect("releases a stalled warmup process at the 30-second deadline", () => |
| 235 | + Effect.gen(function* () { |
| 236 | + const { environment, started, released } = yield* makeControlledEnvironment; |
| 237 | + const fiber = yield* environment.preWarm("Ubuntu").pipe(Effect.forkScoped); |
| 238 | + yield* Deferred.await(started); |
| 239 | + yield* TestClock.adjust(Duration.seconds(29)); |
| 240 | + expect(yield* Deferred.isDone(released)).toBe(false); |
| 241 | + |
| 242 | + yield* TestClock.adjust(Duration.seconds(1)); |
| 243 | + yield* Fiber.join(fiber); |
| 244 | + expect(yield* Deferred.isDone(released)).toBe(true); |
| 245 | + }), |
| 246 | + ); |
| 247 | + |
| 248 | + it.effect("still gives path conversion only 10 seconds and releases its process", () => |
| 249 | + Effect.gen(function* () { |
| 250 | + const { environment, started, released } = yield* makeControlledEnvironment; |
| 251 | + const fiber = yield* environment |
| 252 | + .windowsToWslPath("Ubuntu", "C:\\project") |
| 253 | + .pipe(Effect.forkScoped); |
| 254 | + yield* Deferred.await(started); |
| 255 | + yield* TestClock.adjust(Duration.seconds(9)); |
| 256 | + expect(yield* Deferred.isDone(released)).toBe(false); |
| 257 | + |
| 258 | + yield* TestClock.adjust(Duration.seconds(1)); |
| 259 | + expect(yield* Fiber.join(fiber)).toEqual(Option.none()); |
| 260 | + expect(yield* Deferred.isDone(released)).toBe(true); |
| 261 | + }), |
| 262 | + ); |
| 263 | +}); |
| 264 | + |
161 | 265 | describe("WSL runtime cache", () => { |
162 | 266 | it.each([ |
163 | 267 | [ |
|
0 commit comments