Skip to content

Commit 05f48b6

Browse files
committed
show virtual vs real clock in one example
1 parent 56fa952 commit 05f48b6

File tree

1 file changed

+132
-0
lines changed

1 file changed

+132
-0
lines changed
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
defmodule VirtualVsRealTimeSpeedTest do
2+
use ExUnit.Case, async: true
3+
4+
# A simple message sender for testing
5+
defmodule MessageSender do
6+
use VirtualTimeGenServer
7+
8+
def start_link(delay, test_pid, opts \\ []) do
9+
VirtualTimeGenServer.start_link(__MODULE__, {delay, test_pid}, opts)
10+
end
11+
12+
@impl true
13+
def init({delay, test_pid}) do
14+
# Send a message after the specified delay
15+
VirtualTimeGenServer.send_after(self(), :message, delay)
16+
{:ok, %{delay: delay, test_pid: test_pid}}
17+
end
18+
19+
@impl true
20+
def handle_info(:message, state) do
21+
# Send the message to the test process
22+
send(state.test_pid, :received_message)
23+
{:noreply, state}
24+
end
25+
end
26+
27+
describe "VirtualTimeGenServer as drop-in replacement" do
28+
test "production mode: works exactly like GenServer with real time" do
29+
# VirtualTimeGenServer is a drop-in replacement for GenServer
30+
# In production, it behaves exactly like GenServer with real time
31+
{:ok, sender} = MessageSender.start_link(100, self())
32+
33+
# Measure how long assert_receive takes with real time
34+
real_time_elapsed =
35+
measure_time(fn ->
36+
# This will actually wait for 100ms in real time (just like GenServer)
37+
assert_receive :received_message, 200
38+
end)
39+
40+
# With real time, this should take approximately 100ms (like GenServer)
41+
# At least 80ms (allowing some tolerance)
42+
assert real_time_elapsed >= 80
43+
# But not too much more than 100ms
44+
assert real_time_elapsed <= 150
45+
46+
# Clean up
47+
GenServer.stop(sender)
48+
end
49+
50+
test "testing mode: virtual time injection makes tests lightning fast" do
51+
# For testing, we can inject virtual time to make tests instant
52+
{:ok, clock} = VirtualClock.start_link()
53+
54+
# Pass the clock directly to this specific server (test-local)
55+
{:ok, sender} = MessageSender.start_link(100, self(), virtual_clock: clock)
56+
57+
# Advance the virtual clock to trigger the message instantly
58+
VirtualClock.advance(clock, 100)
59+
60+
# Measure how long assert_receive takes with virtual time
61+
virtual_time_elapsed =
62+
measure_time(fn ->
63+
# This should be instant with virtual time
64+
assert_receive :received_message, 200
65+
end)
66+
67+
# With virtual time, this should complete in milliseconds, not 100ms
68+
# Much faster than 100ms
69+
assert virtual_time_elapsed < 50
70+
71+
# Clean up
72+
GenServer.stop(sender)
73+
end
74+
75+
test "demonstrates drop-in replacement: same code, different performance" do
76+
# Same module, same code - but different performance based on time injection
77+
78+
# Test 1: Production mode (real time, like GenServer)
79+
{:ok, production_sender} = MessageSender.start_link(100, self())
80+
81+
production_elapsed =
82+
measure_time(fn ->
83+
# This will actually wait for 100ms in real time (like GenServer)
84+
assert_receive :received_message, 200
85+
end)
86+
87+
GenServer.stop(production_sender)
88+
89+
# Test 2: Testing mode (virtual time injection)
90+
{:ok, virtual_clock} = VirtualClock.start_link()
91+
92+
# Pass the clock directly to this specific server (test-local)
93+
{:ok, testing_sender} = MessageSender.start_link(100, self(), virtual_clock: virtual_clock)
94+
95+
# Advance the virtual clock to trigger the message instantly
96+
VirtualClock.advance(virtual_clock, 100)
97+
98+
testing_elapsed =
99+
measure_time(fn ->
100+
assert_receive :received_message, 200
101+
end)
102+
103+
GenServer.stop(testing_sender)
104+
105+
# Production mode should take real time
106+
# Testing mode should be instant
107+
# Real time: >= 80ms
108+
assert production_elapsed >= 80
109+
# Virtual time: < 50ms
110+
assert testing_elapsed < 50
111+
112+
# Calculate speedup (handle case where testing_elapsed might be 0)
113+
speedup =
114+
if testing_elapsed > 0, do: production_elapsed / testing_elapsed, else: production_elapsed
115+
116+
# At least 2x faster
117+
assert speedup > 2
118+
119+
IO.puts("\n🚀 Drop-in Replacement Performance:")
120+
IO.puts(" Production mode (real time): #{production_elapsed}ms")
121+
IO.puts(" Testing mode (virtual time): #{testing_elapsed}ms")
122+
IO.puts(" Speedup: #{speedup}x faster!")
123+
end
124+
end
125+
126+
# Helper function to measure elapsed time
127+
defp measure_time(callback) do
128+
start_time = System.monotonic_time(:millisecond)
129+
callback.()
130+
System.monotonic_time(:millisecond) - start_time
131+
end
132+
end

0 commit comments

Comments
 (0)