Merge commit '36c66dd290d3ce6eb1ccd310d0c658d4a32bb8eb' as 'repos/effect'
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,214 @@
|
||||
import * as NodeFileSystem from "@effect/platform-node-shared/NodeFileSystem"
|
||||
import { assert, describe, expect, it } from "@effect/vitest"
|
||||
import { Array } from "effect"
|
||||
import * as Effect from "effect/Effect"
|
||||
import * as Fs from "effect/FileSystem"
|
||||
import * as Stream from "effect/Stream"
|
||||
|
||||
const runPromise = <E, A>(self: Effect.Effect<A, E, Fs.FileSystem>) =>
|
||||
Effect.runPromise(
|
||||
Effect.provide(self, NodeFileSystem.layer)
|
||||
)
|
||||
|
||||
describe("FileSystem", () => {
|
||||
it("readFile", () =>
|
||||
runPromise(Effect.gen(function*() {
|
||||
const fs = yield* Fs.FileSystem
|
||||
const data = yield* fs.readFile(`${__dirname}/fixtures/text.txt`)
|
||||
const text = new TextDecoder().decode(data)
|
||||
expect(text.trim()).toEqual("lorem ipsum dolar sit amet")
|
||||
})))
|
||||
|
||||
it("makeTempDirectory", () =>
|
||||
runPromise(Effect.gen(function*() {
|
||||
const fs = yield* Fs.FileSystem
|
||||
let dir = ""
|
||||
yield* Effect.scoped(Effect.gen(function*() {
|
||||
dir = yield* fs.makeTempDirectory()
|
||||
const stat = yield* fs.stat(dir)
|
||||
expect(stat.type).toEqual("Directory")
|
||||
}))
|
||||
const stat = yield* fs.stat(dir)
|
||||
expect(stat.type).toEqual("Directory")
|
||||
})))
|
||||
|
||||
it("makeTempDirectoryScoped", () =>
|
||||
runPromise(Effect.gen(function*() {
|
||||
const fs = yield* Fs.FileSystem
|
||||
let dir = ""
|
||||
yield* Effect.scoped(
|
||||
Effect.gen(function*() {
|
||||
dir = yield* fs.makeTempDirectoryScoped()
|
||||
const stat = yield* fs.stat(dir)
|
||||
expect(stat.type).toEqual("Directory")
|
||||
})
|
||||
)
|
||||
const error = yield* Effect.flip(fs.stat(dir))
|
||||
assert(error.reason._tag === "NotFound")
|
||||
})))
|
||||
|
||||
it("truncate", () =>
|
||||
runPromise(Effect.gen(function*() {
|
||||
const fs = yield* Fs.FileSystem
|
||||
const file = yield* fs.makeTempFile()
|
||||
|
||||
const text = "hello world"
|
||||
yield* fs.writeFile(file, new TextEncoder().encode(text))
|
||||
|
||||
const before = yield* Effect.map(fs.readFile(file), (_) => new TextDecoder().decode(_))
|
||||
expect(before).toEqual(text)
|
||||
|
||||
yield* fs.truncate(file)
|
||||
|
||||
const after = yield* Effect.map(fs.readFile(file), (_) => new TextDecoder().decode(_))
|
||||
expect(after).toEqual("")
|
||||
})))
|
||||
|
||||
it("should track the cursor position when reading", () =>
|
||||
runPromise(Effect.gen(function*() {
|
||||
const fs = yield* Fs.FileSystem
|
||||
|
||||
yield* Effect.gen(function*() {
|
||||
let text: string
|
||||
const file = yield* fs.open(`${__dirname}/fixtures/text.txt`)
|
||||
|
||||
text = yield* file.readAlloc(Fs.Size(5)).pipe(
|
||||
Effect.flatMap(Effect.fromOption),
|
||||
Effect.map((_) => new TextDecoder().decode(_))
|
||||
)
|
||||
expect(text).toBe("lorem")
|
||||
|
||||
yield* file.seek(Fs.Size(7), "current")
|
||||
text = yield* file.readAlloc(Fs.Size(5)).pipe(
|
||||
Effect.flatMap(Effect.fromOption),
|
||||
Effect.map((_) => new TextDecoder().decode(_))
|
||||
)
|
||||
expect(text).toBe("dolar")
|
||||
|
||||
yield* file.seek(Fs.Size(1), "current")
|
||||
text = yield* file.readAlloc(Fs.Size(8)).pipe(
|
||||
Effect.flatMap(Effect.fromOption),
|
||||
Effect.map((_) => new TextDecoder().decode(_))
|
||||
)
|
||||
expect(text).toBe("sit amet")
|
||||
|
||||
yield* file.seek(Fs.Size(0), "start")
|
||||
text = yield* file.readAlloc(Fs.Size(11)).pipe(
|
||||
Effect.flatMap(Effect.fromOption),
|
||||
Effect.map((_) => new TextDecoder().decode(_))
|
||||
)
|
||||
expect(text).toBe("lorem ipsum")
|
||||
|
||||
text = yield* fs.stream(`${__dirname}/fixtures/text.txt`, { offset: Fs.Size(6), bytesToRead: Fs.Size(5) }).pipe(
|
||||
Stream.map((_) => new TextDecoder().decode(_)),
|
||||
Stream.runCollect,
|
||||
Effect.map(Array.join(""))
|
||||
)
|
||||
expect(text).toBe("ipsum")
|
||||
}).pipe(
|
||||
Effect.scoped
|
||||
)
|
||||
})))
|
||||
|
||||
it("should track the cursor position when writing", () =>
|
||||
runPromise(Effect.gen(function*() {
|
||||
const fs = yield* Fs.FileSystem
|
||||
|
||||
yield* Effect.gen(function*() {
|
||||
let text: string
|
||||
const path = yield* fs.makeTempFileScoped()
|
||||
const file = yield* fs.open(path, { flag: "w+" })
|
||||
|
||||
yield* file.write(new TextEncoder().encode("lorem ipsum"))
|
||||
yield* file.write(new TextEncoder().encode(" "))
|
||||
yield* file.write(new TextEncoder().encode("dolor sit amet"))
|
||||
text = yield* fs.readFileString(path)
|
||||
expect(text).toBe("lorem ipsum dolor sit amet")
|
||||
|
||||
yield* file.seek(Fs.Size(-4), "current")
|
||||
yield* file.write(new TextEncoder().encode("hello world"))
|
||||
text = yield* fs.readFileString(path)
|
||||
expect(text).toBe("lorem ipsum dolor sit hello world")
|
||||
|
||||
yield* file.seek(Fs.Size(6), "start")
|
||||
yield* file.write(new TextEncoder().encode("blabl"))
|
||||
text = yield* fs.readFileString(path)
|
||||
expect(text).toBe("lorem blabl dolor sit hello world")
|
||||
}).pipe(
|
||||
Effect.scoped
|
||||
)
|
||||
})))
|
||||
|
||||
it("should maintain a read cursor in append mode", () =>
|
||||
runPromise(Effect.gen(function*() {
|
||||
const fs = yield* Fs.FileSystem
|
||||
|
||||
yield* Effect.gen(function*() {
|
||||
let text: string
|
||||
const path = yield* fs.makeTempFileScoped()
|
||||
const file = yield* fs.open(path, { flag: "a+" })
|
||||
|
||||
yield* file.write(new TextEncoder().encode("foo"))
|
||||
yield* file.seek(Fs.Size(0), "start")
|
||||
|
||||
yield* file.write(new TextEncoder().encode("bar"))
|
||||
text = yield* fs.readFileString(path)
|
||||
expect(text).toBe("foobar")
|
||||
|
||||
text = yield* file.readAlloc(Fs.Size(3)).pipe(
|
||||
Effect.flatMap(Effect.fromOption),
|
||||
Effect.map((_) => new TextDecoder().decode(_))
|
||||
)
|
||||
expect(text).toBe("foo")
|
||||
|
||||
yield* file.write(new TextEncoder().encode("baz"))
|
||||
text = yield* fs.readFileString(path)
|
||||
expect(text).toBe("foobarbaz")
|
||||
|
||||
text = yield* file.readAlloc(Fs.Size(6)).pipe(
|
||||
Effect.flatMap(Effect.fromOption),
|
||||
Effect.map((_) => new TextDecoder().decode(_))
|
||||
)
|
||||
expect(text).toBe("barbaz")
|
||||
}).pipe(
|
||||
Effect.scoped
|
||||
)
|
||||
})))
|
||||
|
||||
it("should keep the current cursor if truncating doesn't affect it", () =>
|
||||
runPromise(Effect.gen(function*() {
|
||||
const fs = yield* Fs.FileSystem
|
||||
|
||||
yield* Effect.gen(function*() {
|
||||
const path = yield* fs.makeTempFileScoped()
|
||||
const file = yield* fs.open(path, { flag: "w+" })
|
||||
|
||||
yield* file.write(new TextEncoder().encode("lorem ipsum dolor sit amet"))
|
||||
yield* file.seek(Fs.Size(6), "start")
|
||||
yield* file.truncate(Fs.Size(11))
|
||||
|
||||
const cursor = yield* file.seek(Fs.Size(0), "current")
|
||||
expect(cursor).toBe(Fs.Size(6))
|
||||
}).pipe(
|
||||
Effect.scoped
|
||||
)
|
||||
})))
|
||||
|
||||
it("should update the current cursor if truncating affects it", () =>
|
||||
runPromise(Effect.gen(function*() {
|
||||
const fs = yield* Fs.FileSystem
|
||||
|
||||
yield* Effect.gen(function*() {
|
||||
const path = yield* fs.makeTempFileScoped()
|
||||
const file = yield* fs.open(path, { flag: "w+" })
|
||||
|
||||
yield* file.write(new TextEncoder().encode("lorem ipsum dolor sit amet"))
|
||||
yield* file.truncate(Fs.Size(11))
|
||||
|
||||
const cursor = yield* file.seek(Fs.Size(0), "current")
|
||||
expect(cursor).toBe(Fs.Size(11))
|
||||
}).pipe(
|
||||
Effect.scoped
|
||||
)
|
||||
})))
|
||||
})
|
||||
153
repos/effect/packages/platform-node-shared/test/NodeSink.test.ts
Normal file
153
repos/effect/packages/platform-node-shared/test/NodeSink.test.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import * as NodeSink from "@effect/platform-node-shared/NodeSink"
|
||||
import * as NodeStream from "@effect/platform-node-shared/NodeStream"
|
||||
import { assert, describe, it } from "@effect/vitest"
|
||||
import { Effect } from "effect"
|
||||
import * as Data from "effect/Data"
|
||||
import * as Latch from "effect/Latch"
|
||||
import * as Queue from "effect/Queue"
|
||||
import * as Stream from "effect/Stream"
|
||||
import { createReadStream } from "fs"
|
||||
import { join } from "path"
|
||||
import { Writable } from "stream"
|
||||
import * as Tar from "tar"
|
||||
|
||||
const TEST_TARBALL = join(__dirname, "fixtures", "helloworld.tar.gz")
|
||||
|
||||
describe("Sink", () => {
|
||||
it.effect("should write to a stream", () =>
|
||||
Effect.gen(function*() {
|
||||
const items: Array<string> = []
|
||||
const destroyLatch = yield* Latch.make()
|
||||
yield* Stream.make("a", "b", "c").pipe(
|
||||
Stream.run(NodeSink.fromWritable({
|
||||
evaluate: () =>
|
||||
new Writable({
|
||||
construct(callback) {
|
||||
callback()
|
||||
},
|
||||
write(chunk, _encoding, callback) {
|
||||
items.push(chunk.toString())
|
||||
callback()
|
||||
},
|
||||
destroy(_error, callback) {
|
||||
destroyLatch.openUnsafe()
|
||||
callback(null)
|
||||
}
|
||||
}),
|
||||
onError: () => "error"
|
||||
}))
|
||||
)
|
||||
assert.deepEqual(items, ["a", "b", "c"])
|
||||
yield* destroyLatch.await
|
||||
}))
|
||||
|
||||
it.effect("write error", () =>
|
||||
Effect.gen(function*() {
|
||||
const items: Array<string> = []
|
||||
const sink = NodeSink.fromWritable({
|
||||
evaluate: () =>
|
||||
new Writable({
|
||||
construct(callback) {
|
||||
callback()
|
||||
},
|
||||
write(chunk, _encoding, callback) {
|
||||
items.push(chunk.toString())
|
||||
callback()
|
||||
},
|
||||
destroy(_error, callback) {
|
||||
callback(null)
|
||||
}
|
||||
}),
|
||||
onError: () => "error"
|
||||
})
|
||||
const result = yield* Stream.fail("a").pipe(Stream.run(sink), Effect.flip)
|
||||
assert.deepEqual(items, [])
|
||||
assert.strictEqual(result, "a")
|
||||
}))
|
||||
|
||||
it.live("endOnClose false", () =>
|
||||
Effect.gen(function*() {
|
||||
const items: Array<string> = []
|
||||
let destroyed = false
|
||||
const sink = NodeSink.fromWritable({
|
||||
evaluate: () =>
|
||||
new Writable({
|
||||
construct(callback) {
|
||||
callback()
|
||||
},
|
||||
write(chunk, _encoding, callback) {
|
||||
items.push(chunk.toString())
|
||||
callback()
|
||||
},
|
||||
destroy(_error, callback) {
|
||||
destroyed = true
|
||||
callback(null)
|
||||
}
|
||||
}),
|
||||
onError: () => "error",
|
||||
endOnDone: false
|
||||
})
|
||||
yield* Stream.run(Stream.make("a", "b", "c"), sink)
|
||||
yield* Effect.sleep(10)
|
||||
assert.deepEqual(items, ["a", "b", "c"])
|
||||
assert.strictEqual(destroyed, false)
|
||||
}))
|
||||
|
||||
it.effect("should handle non-compliant node streams", () =>
|
||||
Effect.gen(function*() {
|
||||
const stream = NodeStream.fromReadable<Uint8Array, "error">({
|
||||
evaluate: () => createReadStream(TEST_TARBALL),
|
||||
onError: () => "error"
|
||||
})
|
||||
const items = yield* entries(stream).pipe(
|
||||
Stream.flatMap((entry) =>
|
||||
NodeStream.fromReadable({
|
||||
evaluate: () => (entry as any),
|
||||
onError: (error) => new TarError({ error })
|
||||
}).pipe(
|
||||
Stream.map((content) => ({
|
||||
path: entry.path,
|
||||
content: Buffer.from(content).toString("utf-8")
|
||||
}))
|
||||
)
|
||||
),
|
||||
Stream.runCollect
|
||||
)
|
||||
assert.deepEqual(items, [
|
||||
{ path: "./tar/world.txt", content: "world\n" },
|
||||
{ path: "./tar/hello.txt", content: "hello\n" }
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
class TarError extends Data.TaggedError("TarError")<{
|
||||
readonly error: unknown
|
||||
}> {}
|
||||
|
||||
const entries = <R, E>(
|
||||
input: Stream.Stream<Uint8Array, E, R>
|
||||
): Stream.Stream<Tar.ReadEntry, TarError | E, R> =>
|
||||
Effect.gen(function*() {
|
||||
const parser = new Tar.Parser()
|
||||
|
||||
yield* input.pipe(
|
||||
Stream.run(
|
||||
NodeSink.fromWritable({
|
||||
evaluate: () => parser,
|
||||
onError: (error) => new TarError({ error })
|
||||
})
|
||||
),
|
||||
Effect.forkScoped
|
||||
)
|
||||
|
||||
return Stream.callback<Tar.ReadEntry, TarError>((queue) =>
|
||||
Effect.sync(() => {
|
||||
parser.on("entry", (entry) => {
|
||||
Queue.offerUnsafe(queue, entry)
|
||||
})
|
||||
parser.on("close", () => {
|
||||
Queue.endUnsafe(queue)
|
||||
})
|
||||
})
|
||||
)
|
||||
}).pipe(Stream.unwrap)
|
||||
@@ -0,0 +1,173 @@
|
||||
import * as NodeStream from "@effect/platform-node-shared/NodeStream"
|
||||
import { assert, describe, it } from "@effect/vitest"
|
||||
import { Effect } from "effect"
|
||||
import * as Array from "effect/Array"
|
||||
import * as Channel from "effect/Channel"
|
||||
import * as Console from "effect/Console"
|
||||
import * as Stream from "effect/Stream"
|
||||
import { Duplex, Readable, Transform } from "node:stream"
|
||||
import * as Zlib from "node:zlib"
|
||||
|
||||
describe("Stream", () => {
|
||||
it.effect("should read a stream", () =>
|
||||
Effect.gen(function*() {
|
||||
const stream = NodeStream.fromReadable<"error", string>({
|
||||
evaluate: () => Readable.from(["a", "b", "c"]),
|
||||
onError: () => "error"
|
||||
})
|
||||
const items = yield* Stream.runCollect(stream)
|
||||
assert.deepEqual(items, ["a", "b", "c"])
|
||||
}))
|
||||
|
||||
it.effect("fromDuplex", () =>
|
||||
Effect.gen(function*() {
|
||||
const result = yield* Stream.fromArray(["a", "b", "c"]).pipe(
|
||||
Stream.pipeThroughChannelOrFail(NodeStream.fromDuplex({
|
||||
evaluate: () =>
|
||||
new Transform({
|
||||
transform(chunk, _encoding, callback) {
|
||||
callback(null, chunk.toString().toUpperCase())
|
||||
}
|
||||
}),
|
||||
onError: () => "error" as const
|
||||
})),
|
||||
Stream.decodeText(),
|
||||
Stream.mkString
|
||||
)
|
||||
|
||||
assert.strictEqual(result, "ABC")
|
||||
}))
|
||||
|
||||
it.effect("fromDuplex failure", () =>
|
||||
Effect.gen(function*() {
|
||||
const result = yield* Stream.fromArray(["a", "b", "c"]).pipe(
|
||||
Stream.pipeThroughChannelOrFail(NodeStream.fromDuplex({
|
||||
evaluate: () =>
|
||||
new Transform({
|
||||
transform(_chunk, _encoding, callback) {
|
||||
callback(new Error())
|
||||
}
|
||||
}),
|
||||
onError: () => "error" as const
|
||||
})),
|
||||
Stream.runDrain,
|
||||
Effect.flip
|
||||
)
|
||||
|
||||
assert.strictEqual(result, "error")
|
||||
}))
|
||||
|
||||
it.effect("pipeThroughDuplex", () =>
|
||||
Effect.gen(function*() {
|
||||
const result = yield* Stream.fromArray(["a", "b", "c"]).pipe(
|
||||
NodeStream.pipeThroughDuplex({
|
||||
evaluate: () =>
|
||||
new Transform({
|
||||
transform(chunk, _encoding, callback) {
|
||||
callback(null, chunk.toString().toUpperCase())
|
||||
}
|
||||
}),
|
||||
onError: () => "error" as const
|
||||
}),
|
||||
Stream.decodeText(),
|
||||
Stream.mkString
|
||||
)
|
||||
|
||||
assert.strictEqual(result, "ABC")
|
||||
}))
|
||||
|
||||
it.effect("pipeThroughDuplex write error", () =>
|
||||
Effect.gen(function*() {
|
||||
const result = yield* Stream.fromArray(["a", "b", "c"]).pipe(
|
||||
NodeStream.pipeThroughDuplex({
|
||||
evaluate: () =>
|
||||
new Duplex({
|
||||
read() {},
|
||||
write(_chunk, _encoding, callback) {
|
||||
callback(new Error())
|
||||
}
|
||||
}),
|
||||
onError: () => "error" as const
|
||||
}),
|
||||
Stream.runDrain,
|
||||
Effect.flip
|
||||
)
|
||||
assert.strictEqual(result, "error")
|
||||
}))
|
||||
|
||||
it.effect("pipeThroughSimple", () =>
|
||||
Effect.gen(function*() {
|
||||
const result = yield* Stream.fromArray(["a", Buffer.from("b"), "c"]).pipe(
|
||||
NodeStream.pipeThroughSimple(
|
||||
() =>
|
||||
new Transform({
|
||||
transform(chunk, _encoding, callback) {
|
||||
callback(null, chunk.toString().toUpperCase())
|
||||
}
|
||||
})
|
||||
),
|
||||
Stream.decodeText(),
|
||||
Stream.mkString
|
||||
)
|
||||
|
||||
assert.strictEqual(result, "ABC")
|
||||
}))
|
||||
|
||||
it.effect("fromDuplex should work with node:zlib", () =>
|
||||
Effect.gen(function*() {
|
||||
const text = "abcdefg1234567890"
|
||||
const encoder = new TextEncoder()
|
||||
const input = encoder.encode(text)
|
||||
const stream = NodeStream.fromReadable<Uint8Array, "error">({
|
||||
evaluate: () => Readable.from([input]),
|
||||
onError: () => "error"
|
||||
})
|
||||
const deflate = NodeStream.fromDuplex({
|
||||
evaluate: () => Zlib.createGzip(),
|
||||
onError: () => "error" as const
|
||||
})
|
||||
const inflate = NodeStream.fromDuplex<never, Uint8Array, Uint8Array, "error">({
|
||||
evaluate: () => Zlib.createUnzip(),
|
||||
onError: () => "error" as const
|
||||
})
|
||||
const channel = Channel.pipeToOrFail(deflate, inflate)
|
||||
const result = yield* stream.pipe(
|
||||
Stream.pipeThroughChannelOrFail(channel),
|
||||
Stream.decodeText(),
|
||||
Stream.mkString
|
||||
)
|
||||
assert.strictEqual(result, text)
|
||||
}))
|
||||
|
||||
it.effect("toReadable roundtrip", () =>
|
||||
Effect.gen(function*() {
|
||||
const stream = Stream.range(0, 10000).pipe(
|
||||
Stream.map((n) => String(n))
|
||||
)
|
||||
const readable = yield* NodeStream.toReadable(stream)
|
||||
const outStream = NodeStream.fromReadable({
|
||||
evaluate: () => readable,
|
||||
onError: () => "error" as const
|
||||
})
|
||||
const items = yield* outStream.pipe(
|
||||
Stream.decodeText(),
|
||||
Stream.runCollect
|
||||
)
|
||||
assert.strictEqual(items.join(""), Array.range(0, 10000).join(""))
|
||||
}))
|
||||
|
||||
it.effect("toReadable with error", () =>
|
||||
Effect.gen(function*() {
|
||||
const stream = Stream.fail("error")
|
||||
const readable = yield* NodeStream.toReadable(stream)
|
||||
const outStream = NodeStream.fromReadable({
|
||||
evaluate: () => readable
|
||||
})
|
||||
const error = yield* outStream.pipe(
|
||||
Stream.runCollect,
|
||||
Effect.tapError((_) => Console.log(_)),
|
||||
Effect.flip
|
||||
)
|
||||
assert.deepEqual(error.cause, "error")
|
||||
}))
|
||||
})
|
||||
3
repos/effect/packages/platform-node-shared/test/fixtures/bash/no-permissions.sh
vendored
Normal file
3
repos/effect/packages/platform-node-shared/test/fixtures/bash/no-permissions.sh
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "this should not run because it doesn't have execute permissions"
|
||||
31
repos/effect/packages/platform-node-shared/test/fixtures/bash/parent-exits-early.sh
vendored
Executable file
31
repos/effect/packages/platform-node-shared/test/fixtures/bash/parent-exits-early.sh
vendored
Executable file
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# This script spawns child processes and then exits early
|
||||
echo "Parent process started with PID $$"
|
||||
|
||||
# Spawn multiple child processes that will outlive the parent
|
||||
for i in {1..3}; do
|
||||
(
|
||||
# Child process
|
||||
child_pid=$BASHPID
|
||||
echo "Child $i started with PID $child_pid"
|
||||
|
||||
# Spawn a grandchild that runs for a long time
|
||||
(
|
||||
grandchild_pid=$BASHPID
|
||||
echo "Grandchild of child $i started with PID $grandchild_pid"
|
||||
# Keep running for 30 seconds
|
||||
sleep 30
|
||||
) &
|
||||
|
||||
# Keep the child running
|
||||
sleep 30
|
||||
) &
|
||||
done
|
||||
|
||||
# Give children time to start
|
||||
sleep 0.5
|
||||
|
||||
# Exit early (simulating a crash or early termination)
|
||||
echo "Parent exiting early with status 1..."
|
||||
exit 1
|
||||
32
repos/effect/packages/platform-node-shared/test/fixtures/bash/spawn-children.sh
vendored
Executable file
32
repos/effect/packages/platform-node-shared/test/fixtures/bash/spawn-children.sh
vendored
Executable file
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# This script spawns child processes to test process group cleanup
|
||||
echo "Parent process started with PID $$"
|
||||
|
||||
# Spawn multiple child processes
|
||||
for i in {1..3}; do
|
||||
(
|
||||
# Child process
|
||||
child_pid=$BASHPID
|
||||
echo "Child $i started with PID $child_pid"
|
||||
|
||||
# Spawn a grandchild that runs for a long time
|
||||
(
|
||||
grandchild_pid=$BASHPID
|
||||
echo "Grandchild of child $i started with PID $grandchild_pid"
|
||||
# Keep running for 60 seconds
|
||||
for j in {1..60}; do
|
||||
sleep 1
|
||||
done
|
||||
) &
|
||||
|
||||
# Keep the child running
|
||||
for j in {1..60}; do
|
||||
sleep 1
|
||||
done
|
||||
) &
|
||||
done
|
||||
|
||||
# Keep the parent running
|
||||
echo "Parent process waiting..."
|
||||
wait
|
||||
1
repos/effect/packages/platform-node-shared/test/fixtures/config/SHOUTING
vendored
Normal file
1
repos/effect/packages/platform-node-shared/test/fixtures/config/SHOUTING
vendored
Normal file
@@ -0,0 +1 @@
|
||||
value
|
||||
1
repos/effect/packages/platform-node-shared/test/fixtures/config/integer
vendored
Normal file
1
repos/effect/packages/platform-node-shared/test/fixtures/config/integer
vendored
Normal file
@@ -0,0 +1 @@
|
||||
123
|
||||
1
repos/effect/packages/platform-node-shared/test/fixtures/config/nested/config
vendored
Normal file
1
repos/effect/packages/platform-node-shared/test/fixtures/config/nested/config
vendored
Normal file
@@ -0,0 +1 @@
|
||||
hello
|
||||
1
repos/effect/packages/platform-node-shared/test/fixtures/config/secret
vendored
Normal file
1
repos/effect/packages/platform-node-shared/test/fixtures/config/secret
vendored
Normal file
@@ -0,0 +1 @@
|
||||
keepitsafe
|
||||
BIN
repos/effect/packages/platform-node-shared/test/fixtures/helloworld.tar.gz
vendored
Normal file
BIN
repos/effect/packages/platform-node-shared/test/fixtures/helloworld.tar.gz
vendored
Normal file
Binary file not shown.
1
repos/effect/packages/platform-node-shared/test/fixtures/text.txt
vendored
Normal file
1
repos/effect/packages/platform-node-shared/test/fixtures/text.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
lorem ipsum dolar sit amet
|
||||
Reference in New Issue
Block a user