Current section

Files

Jump to
gtempo src tempo@mock.erl
Raw

src/tempo@mock.erl

-module(tempo@mock).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([freeze_time/1, unfreeze_time/0, set_time_with_speedup/2, set_time/1, unset_time/0, enable_sleep_warp/0, disable_sleep_warp/0, warp_time/1, reset_warp_time/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Provides functions to mock the system time as seen by the tempo \n"
" package for testing purposes.\n"
" \n"
" There are four main ways to mock time for testing in this package:\n"
" \n"
" ## Freezing the system time\n"
" By freezing the system time to a specific time so that calls to the current\n"
" system time will return a known value, you can reliably test code that \n"
" gets the current system time once. Frozen time can also be warped, allowing\n"
" for fine-grained control over the system time. More on that below.\n"
" \n"
" ```gleam\n"
" import tempo\n"
" import tempo/mock\n"
" import gleeunit/should\n"
" \n"
" pub fn format_system_time() {\n"
" tempo.format_utc(tempo.Custom(\"dddd @ HH:mm:ss\"))\n"
" }\n"
" \n"
" pub fn format_system_time_test() {\n"
" // We can test that this function returns the expected value by first\n"
" // freezing the system time to a specific, known time.\n"
" mock.freeze_time(datetime.literal(\"2024-06-21T11:10:35.000Z\"))\n"
" \n"
" format_system_time()\n"
" |> should.equal(\"Friday @ 11:10:35\")\n"
" \n"
" mock.unfreeze_time()\n"
" }\n"
" ```\n"
" \n"
" ## Setting the current system time to a reference time\n"
" By setting the current system time to a specific reference time and \n"
" letting it progress forward from there, you can reliably test code\n"
" that may get the system time multiple times but executes different \n"
" logic depending on the date or time of day.\n"
" \n"
" An example of this would be complex, so the implementation is left out.\n"
" \n"
" ```gleam\n"
" import app\n"
" import tempo\n"
" import tempo/mock\n"
" import gleeunit/should\n"
" \n"
" pub fn thursday_run_test() {\n"
" // We can test that this function returns the expected value on Thursdays\n"
" // by first setting the current system time to a reference time.\n"
" mock.set_time(datetime.literal(\"2024-06-20T11:10:35.000Z\"))\n"
" \n"
" app.run()\n"
" |> should.equal(42)\n"
" \n"
" mock.unset_time()\n"
" }\n"
" ```\n"
" \n"
" ## Warping system time instead of sleeping\n"
" By changing sleep operations to time warps, you can instantly test any \n"
" function with sleeps in it. Sleeps will warp frozen time when this is \n"
" enabled.\n"
" \n"
" ```gleam\n"
" import app\n"
" import tempo\n"
" import tempo/instant\n"
" import tempo/mock\n"
" import gleeunit/should\n"
" \n"
" pub fn do_logic_after_sleep() {\n"
" tempo.sleep(duration.seconds(10))\n"
" app.do_logic()\n"
" }\n"
" \n"
" pub fn do_logic_after_sleep_test() {\n"
" tempo.enable_sleep_warp()\n"
" \n"
" let start = instant.now()\n"
" do_logic_after_sleep()\n"
" \n"
" // This test executes immediately, but it appears to have taken 10\n"
" // seconds to this package.\n"
" instant.since(start) |> duration.as_seconds |> should.equal(10)\n"
" \n"
" tempo.disable_sleep_warp()\n"
" }\n"
" ```\n"
" \n"
" ## Manually warping system time\n"
" By manually warping the system time by a specific duration, you can instantly\n"
" run logic after exact durations. You can also warp time when it is frozen\n"
" to manually progress it.\n"
" \n"
" ```gleam\n"
" import app\n"
" import tempo\n"
" import tempo/mock\n"
" \n"
" pub fn test_spaced_calls() {\n"
" mock.freeze_time(datetime.literal(\"2024-06-21T00:10:00.000Z\"))\n"
" \n"
" app.do_side_effect()\n"
" |> should.equal(Ok(Nil))\n"
" \n"
" mock.warp_time(duration.minutes(30))\n"
"\n"
" app.do_side_effect()\n"
" |> should.equal(Ok(Nil))\n"
" \n"
" mock.unfreeze_time()\n"
" mock.reset_warp_time()\n"
" }\n"
).
-file("src/tempo/mock.gleam", 134).
?DOC(
" Freezes the current system time and local offset (as seen by this package) \n"
" to the provided datetime. Time will not progress until the 'unfreeze_time'\n"
" function is called.\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" mock.freeze_time(datetime.literal(\"2024-06-21T00:10:00.000Z\"))\n"
" tempo.format_local(tempo.ISO8601Seconds)\n"
" // -> \"2024-06-21T00:10:00Z\"\n"
" process.sleep(duration.seconds(10))\n"
" tempo.format_utc(tempo.ISO8601Seconds)\n"
" // -> \"2024-06-21T00:10:00Z\"\n"
" ```\n"
).
-spec freeze_time(tempo:date_time()) -> nil.
freeze_time(Datetime) ->
_pipe = tempo:datetime_to_unix_micro(Datetime),
tempo_ffi:freeze_time(
_pipe,
begin
_pipe@1 = erlang:element(4, Datetime),
_pipe@2 = tempo@offset:to_duration(_pipe@1),
tempo@duration:as_minutes(_pipe@2)
end
).
-file("src/tempo/mock.gleam", 156).
?DOC(
" Unfreezes the current system time (as seen by this package) back to the\n"
" real system time.\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" mock.freeze_time(datetime.literal(\"2024-06-21T00:10:00.000Z\"))\n"
" tempo.format_utc(tempo.ISO8601Seconds)\n"
" // -> \"2024-06-21T00:10:00Z\"\n"
" mock.unfreeze_time()\n"
" tempo.format_utc(tempo.ISO8601Seconds)\n"
" // -> \"2025-01-31T22:48:00.000Z\"\n"
" ```\n"
).
-spec unfreeze_time() -> nil.
unfreeze_time() ->
tempo_ffi:unfreeze_time().
-file("src/tempo/mock.gleam", 203).
?DOC(false).
-spec set_time_with_speedup(tempo:date_time(), float()) -> nil.
set_time_with_speedup(Datetime, Speedup) ->
_pipe = tempo:datetime_to_unix_micro(Datetime),
tempo_ffi:set_reference_time(
_pipe,
begin
_pipe@1 = erlang:element(4, Datetime),
_pipe@2 = tempo@offset:to_duration(_pipe@1),
tempo@duration:as_minutes(_pipe@2)
end,
Speedup
).
-file("src/tempo/mock.gleam", 196).
?DOC(
" Sets the current system time (as seen by this package) to the provided\n"
" datetime, allowing time to progress after with a speedup factor. If the \n"
" speedup is 1.0, then time will progress at the normal rate after being set.\n"
" If the speedup is less than 1.0, time will progress slower than normal. If\n"
" the speedup is greater than 1.0, time will progress faster than normal. This\n"
" can be useful for quickly testing logic that may periodically wait for some\n"
" amount of time.\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" mock.set_time(datetime.literal(\"2024-06-21T00:10:00.000Z\"), 1.0)\n"
" tempo.format_utc(tempo.ISO8601Seconds)\n"
" // -> \"2024-06-21T00:00:00Z\"\n"
" process.sleep(duration.seconds(10))\n"
" tempo.format_utc(tempo.ISO8601Seconds)\n"
" // -> \"2024-06-21T00:00:10Z\"\n"
" ```\n"
" \n"
" ```gleam\n"
" mock.set_time(datetime.literal(\"2024-06-21T00:10:00.000Z\"), 0.5)\n"
" tempo.format_utc(tempo.ISO8601Seconds)\n"
" // -> \"2024-06-21T00:00:00Z\"\n"
" process.sleep(duration.seconds(10))\n"
" tempo.format_utc(tempo.ISO8601Seconds)\n"
" // -> \"2024-06-21T00:00:05Z\"\n"
" ```\n"
" \n"
" ```gleam\n"
" mock.set_time(datetime.literal(\"2024-06-21T00:10:00.000Z\"), 3.0)\n"
" tempo.format_utc(tempo.ISO8601Seconds)\n"
" // -> \"2024-06-21T00:00:00Z\"\n"
" process.sleep(duration.seconds(10))\n"
" tempo.format_utc(tempo.ISO8601Seconds)\n"
" // -> \"2024-06-21T00:00:30Z\"\n"
" ```\n"
).
-spec set_time(tempo:date_time()) -> nil.
set_time(Datetime) ->
set_time_with_speedup(Datetime, 1.0).
-file("src/tempo/mock.gleam", 229).
?DOC(
" Resets the current system time (as seen by this package) back to the real\n"
" system time.\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" mock.set_time(datetime.literal(\"2024-06-21T00:10:00.000Z\"), 1.0)\n"
" tempo.format_utc(tempo.ISO8601Seconds)\n"
" // -> \"2024-06-21T00:00:00Z\"\n"
" mock.unset_time()\n"
" tempo.format_utc(tempo.ISO8601Seconds)\n"
" // -> \"2025-02-01T09:12:00Z\"\n"
" ```\n"
).
-spec unset_time() -> nil.
unset_time() ->
tempo_ffi:unset_reference_time().
-file("src/tempo/mock.gleam", 245).
?DOC(
" Enables warping of the system time (as seen by this package) instead of\n"
" sleeping when calls to `tempo.sleep` are made. This is useful for instantly\n"
" testing code that may have sleeps in it.\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" mock.enable_sleep_warp()\n"
" tempo.sleep(duration.seconds(10))\n"
" // -> This will warp perceived system time to 10 seconds in the future \n"
" // instead of waiting for 10 real seconds.\n"
" ```\n"
).
-spec enable_sleep_warp() -> nil.
enable_sleep_warp() ->
tempo_ffi:set_sleep_warp(true).
-file("src/tempo/mock.gleam", 259).
?DOC(
" Disables warping of the system time (as seen by this package) instead of\n"
" sleeping when calls to `tempo.sleep` are made.\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" mock.enable_sleep_warp()\n"
" do_some_sleepy_test()\n"
" mock.disable_sleep_warp()\n"
" ```\n"
).
-spec disable_sleep_warp() -> nil.
disable_sleep_warp() ->
tempo_ffi:set_sleep_warp(false).
-file("src/tempo/mock.gleam", 265).
?DOC(
" Warps the current system time (as seen by this package) by the provided\n"
" duration. This is useful for instantly testing code after a precise duration.\n"
).
-spec warp_time(gleam@time@duration:duration()) -> nil.
warp_time(Duration) ->
_pipe = tempo@duration:as_microseconds(Duration),
tempo_ffi:add_warp_time(_pipe).
-file("src/tempo/mock.gleam", 284).
?DOC(
" Resets the warp time (as seen by this package) back to the real system time.\n"
" This will clear any warp time added by either `mock.warp_time` or \n"
" `mock.enable_sleep_warp` with subsequent calls to `tempo.sleep`.\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" mock.enable_sleep_warp()\n"
" tempo.sleep(duration.seconds(10))\n"
" tempo.format_utc(tempo.ISO8601Seconds)\n"
" // -> \"2024-06-21T00:10:00Z\"\n"
" mock.reset_warp_time()\n"
" tempo.format_utc(tempo.ISO8601Seconds)\n"
" // -> \"2024-06-21T00:00:00Z\"\n"
" ```\n"
).
-spec reset_warp_time() -> nil.
reset_warp_time() ->
tempo_ffi:reset_warp_time().