UNPKG

@effect-ts/system

Version:

Effect-TS is a zero dependency set of libraries to write highly productive, purely functional TypeScript at scale.

329 lines (257 loc) 11.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Done = exports.Data = void 0; exports.Duration = Duration; exports.defaultTestClock = exports.TestClock = exports.Test = exports.Start = exports.Pending = void 0; exports.live = live; var _index = /*#__PURE__*/require("../../Case/index.js"); var ClockId = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../../Clock/id.js")); var Clock = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../../Clock/index.js")); var Chunk = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../../Collections/Immutable/Chunk/index.js")); var HashMap = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../../Collections/Immutable/HashMap/index.js")); var List = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../../Collections/Immutable/List/index.js")); var SortedSet = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../../Collections/Immutable/SortedSet/index.js")); var Tuple = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../../Collections/Immutable/Tuple/index.js")); var T = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../../Effect/index.js")); var Fiber = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../../Fiber/index.js")); var _index10 = /*#__PURE__*/require("../../Function/index.js"); var _index11 = /*#__PURE__*/require("../../Has/index.js"); var L = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../../Layer/index.js")); var M = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../../Managed/index.js")); var O = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../../Option/index.js")); var Ord = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../../Ord/index.js")); var Promise = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../../Promise/index.js")); var Ref = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../../Ref/index.js")); var RefM = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../../RefM/index.js")); var St = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../../Structural/index.js")); var _index20 = /*#__PURE__*/require("../Annotations/index.js"); var _index21 = /*#__PURE__*/require("../FiberSet/index.js"); var _index22 = /*#__PURE__*/require("../Live/index.js"); var _index23 = /*#__PURE__*/require("../TestAnnotation/index.js"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } // ets_tracing: off function Duration(n) { return n; } const TestClock = /*#__PURE__*/(0, _index11.tag)(Clock.ClockId); /** * `Data` represents the state of the `TestClock`, including the clock time */ exports.TestClock = TestClock; class Data extends (0, _index.Tagged)("Data") {} exports.Data = Data; class Start extends (0, _index.TaggedADT)()("Start") {} exports.Start = Start; class Done extends (0, _index.TaggedADT)()("Done") {} exports.Done = Done; class Pending extends (0, _index.TaggedADT)()("Pending") {} exports.Pending = Pending; class Test { constructor(clockState, live, annotations, warningState) { this.clockState = clockState; this.live = live; this.annotations = annotations; this.warningState = warningState; this.serviceId = ClockId.ClockId; /** * Increments the current clock time by the specified duration. Any * effects that were scheduled to occur on or before the new time will be * run in order. */ this.adjust = duration => { return T.zipRight_(this.warningDone, this.run(_ => Duration(_ + duration))); }; /** * Returns the current clock time. */ this.currentTime = T.map_(Ref.get(this.clockState), d => d.duration); /** * Saves the `TestClock`'s current state in an effect which, when run, * will restore the `TestClock` state to the saved state */ this.save = T.map_(T.bind_(T.do, "clockData", () => Ref.get(this.clockState)), ({ clockData }) => Ref.set_(this.clockState, clockData)); /** * Sets the current clock time to the specified time in terms of duration * since the epoch. Any effects that were scheduled to occur on or before * the new time will immediately be run in order. */ this.setTime = dateTime => T.zipRight_(this.warningDone, this.run(() => Duration(dateTime))); /** * Semantically blocks the current fiber until the clock time is equal * to or greater than the specified duration. Once the clock time is * adjusted to on or after the duration, the fiber will automatically be * resumed. */ this.sleep = duration => T.map_(T.tap_(T.bind_(T.bind_(T.do, "promise", () => Promise.make()), "shouldAwait", ({ promise }) => Ref.modify_(this.clockState, data => { const end = Duration(data.duration + duration); if (end > data.duration) { return Tuple.tuple(true, data.copy({ sleeps: List.prepend(Tuple.tuple(end, promise))(data.sleeps) })); } else { return Tuple.tuple(false, data); } })), ({ promise, shouldAwait }) => shouldAwait ? T.zipRight_(this.warningStart, Promise.await(promise)) : Promise.succeed_(promise, void 0)), () => void 0); /** * Returns a list of the times at which all queued effects are scheduled * to resume. */ this.sleeps = T.map_(Ref.get(this.clockState), d => List.map(_ => _.get(0))(d.sleeps)); /** * The warning message that will be displayed if a test is using time but * is not advancing the `TestClock`. */ this.warning = "Warning: A test is using time, but is not advancing the test clock, " + "which may result in the test hanging. Use TestClock.adjust to " + "manually advance the time."; /** * Forks a fiber that will display a warning message if a test is using * time but is not advancing the `TestClock`. */ this.warningStart = RefM.updateSome(_ => { switch (_._tag) { case "Start": { return O.some(T.map_(T.bind_(T.do, "fiber", () => this.live.provide(T.fork(T.interruptible(T.delay_(T.succeedWith(() => { console.log(this.warning); }), 5000))))), ({ fiber }) => Pending.make({ fiber }))); } default: return O.none; } })(this.warningState); /** * Cancels the warning message that is displayed if a test is using time * but is not advancing the `TestClock`. */ this.warningDone = RefM.updateSome(_ => { switch (_._tag) { case "Start": { return O.some(T.succeed(Done.make())); } case "Pending": { return O.some(T.as_(Fiber.interrupt(_.fiber), Done.make())); } default: return O.none; } })(this.warningState); /** * Returns a set of all fibers in this test. */ this.supervisedFibers = T.descriptorWith(d => T.chain_(this.annotations.get(_index23.fibers), fa => { switch (fa._tag) { case "Left": { return T.succeed(_index21.fiberSet); } case "Right": { return T.map_(T.map_(T.forEach_(fa.right, ref => T.succeedWith(() => ref.get)), Chunk.reduce(_index21.fiberSet, SortedSet.union_)), SortedSet.filter(_ => !St.equals(_.id, d.id))); } } })); /** * Captures a "snapshot" of the identifier and status of all fibers in * this test other than the current fiber. Fails with the `Unit` value if * any of these fibers are not done or suspended. Note that because we * cannot synchronize on the status of multiple fibers at the same time * this snapshot may not be fully consistent. */ this.freeze = T.chain_(this.supervisedFibers, T.reduce(HashMap.make(), (map, fiber) => T.chain_(fiber.status, status => { switch (status._tag) { case "Done": { return T.succeed(HashMap.set_(map, fiber.id, status)); } case "Suspended": { return T.succeed(HashMap.set_(map, fiber.id, status)); } default: return T.fail(void 0); } }))); /** * Delays for a short period of time. */ this.delay = this.live.provide(T.sleep(5)); /** * Returns whether all descendants of this fiber are done or suspended. */ this.suspended = T.chain_(T.zip_(this.freeze, T.zipRight_(this.delay, this.freeze)), ({ tuple: [first, last] }) => St.equals(first, last) ? T.succeed(first) : T.fail(void 0)); /** * Polls until all descendants of this fiber are done or suspended. */ this.awaitSuspended = T.asUnit(T.eventually(T.filterOrFail_(T.zipWith_(this.suspended, T.zipRight_(this.live.provide(T.sleep(10)), this.suspended), St.equals), _index10.identity, () => void 0))); /** * Runs all effects scheduled to occur on or before the specified * duration, which may depend on the current time, in order. */ this.run = f => T.zipRight_(this.awaitSuspended, T.chain_(Ref.modify_(this.clockState, data => { const end = f(data.duration); const sorted = List.sortWith_(data.sleeps, Ord.contramap_(Ord.number, _ => _.get(0))); if (!List.isEmpty(sorted)) { const { tuple: [duration, promise] } = List.unsafeFirst(sorted); const sleeps = List.tail(sorted); if (duration <= end) { return Tuple.tuple(O.some(Tuple.tuple(end, promise)), new Data({ duration, sleeps })); } } return Tuple.tuple(O.none, new Data({ duration: end, sleeps: data.sleeps })); }), o => { switch (o._tag) { case "None": { return T.unit; } case "Some": { return T.zipRight_(T.zipRight_(Promise.succeed_(o.value.get(1), void 0), T.yieldNow), this.run(() => o.value.get(0))); } } })); } } exports.Test = Test; function live(data) { return L.fromRawManaged(M.gen(function* (_) { const live = yield* _(_index22.Live); const annotations = yield* _(_index20.Annotations); const ref = yield* _(Ref.makeRef(data)); const refM = yield* _(RefM.makeRefM(Start.make())); const test = yield* _(M.make_(T.succeedWith(() => new Test(ref, live, annotations, refM)), _ => _.warningDone)); const testClock = TestClock.has(test); return testClock; })); } const defaultTestClock = /*#__PURE__*/live( /*#__PURE__*/new Data({ duration: /*#__PURE__*/Duration(0), sleeps: /*#__PURE__*/List.empty() })); exports.defaultTestClock = defaultTestClock; //# sourceMappingURL=index.js.map