This document tracks the differences between the original otplike test suite and the loom-otp compatibility test suite.
| Test File | Original Tests | Loom-OTP Tests | Extra Tests | Coverage |
|---|---|---|---|---|
| process_test.clj | 171 | 150 | 1 | 99% |
| gen_server_test.clj | 115 | 113 | 0 | 98% |
| timer_test.clj | 32 | 34 | 0 | 100%+ |
| supervisor_test.clj | 23 | 21 | 4 | 91% |
| gen_server_ns_test.clj | 1 | 1 | 0 | 100% |
Note: Tests that were added in loom-otp (not present in original otplike) have been moved to *_extra_test.clj files:
process_extra_test.clj- 1 test forregisteredfunctionsupervisor_extra_test.clj- 4 tests for dynamic child management and restart behavior
All implemented tests are now passing:
Ran 422 tests containing 933 assertions.
0 failures, 0 errors.
Status: Commented out with #_
Reason: This test expects process/async to execute concurrently (like core.async go blocks). In loom-otp, async executes synchronously and immediately on the calling thread. This is by design:
- With virtual threads, blocking is cheap - no need for parking/go blocks
- Sequential execution within a process is maintained
- The process mailbox can be accessed from within async blocks
The test was:
#_(deftest ^:parallel async-allows-parking
(let [done (promise)
done1 (promise)]
(process/async
(if (deref done 50 nil)
(deliver done1 true)
(is false "timeout")))
(deliver done true)
(await-completion!! done1 50)))This test assumed the async body would run concurrently, allowing (deliver done true) to execute before the async body checks done. With synchronous execution, the async body runs first and times out.
These tests are included as empty stubs from the original otplike. They represent edge cases that were never implemented in the original and are intentionally left as stubs in loom-otp for parity.
These test behavior when functions are called by an exiting process:
self-fails-when-process-is-exitingexit-fails-when-called-by-exiting-processflag-fails-when-called-by-exiting-processlink-fails-when-called-by-exiting-processmonitor-fails-when-called-by-exiting-processdemonitor-fails-when-called-by-exiting-processreceive!-fails-when-called-by-exiting-processselective-receive!-fails-when-called-by-exiting-processspawn-link-fails-when-called-by-exiting-processunlink-fails-when-called-by-exiting-process
These are property-based exhaustive tests that would require substantial helper functions:
test-one-for-onetest-one-for-alltest-rest-for-one
These were commented out in the original and remain as comments:
exit-self-reason-is-process-exit-reasonexit-kill-does-not-propagateprocess-killed-when-inbox-is-overflowedprocess-exit-reason-is-proc-fn-return-valuespawned-process-available-from-within-process-by-reg-namethere-are-no-residue-of-process-after-proc-fun-throws
The following tests were added in loom-otp and are NOT present in the original otplike test suite.
They have been moved to *_extra_test.clj files:
registered-returns-registered-names- Tests registered function behavior
one-for-one--child-restart- Tests supervisor restart behaviorstart-child--adds-child- Tests dynamic child additionterminate-child--stops-child- Tests child terminationrestart-child--restarts-stopped-child- Tests manual child restart
process-info- YESprocesses- YESregistered- YESawait!- YES (macro)await!!- YESawait?!- YES (macro)async- YES (executes synchronously, not concurrently like otplike)
start-ns!- YES
In the original otplike, async used core.async go blocks:
(defmacro async [& body]
`(->Async (go (ex-catch [:ok (do ~@body)])) nil []))In loom-otp, async executes immediately and synchronously:
(defmacro async [& body]
`(let [creator-pid# (core/try-self)
result# (try
[:value (do ~@body)]
(catch InterruptedException ie#
(throw ie#))
(catch Throwable t#
[:exception (pexit/ex->reason t#)]))]
(Async. (atom result#) [] creator-pid#)))This change was made because:
- Virtual threads make blocking cheap - no need for go block concurrency
- Sequential execution within a process must be maintained for mailbox access
- The gen_server implementation uses process operations inside async blocks
All timer functions have proper argument validation and process context checks.
Both receive! and selective-receive! validate that at least one message pattern is provided.
- Tests in loom-otp use
promiseinstead ofcore.async/chan - Tests in loom-otp use
mount.litefor fixtures - Virtual threads don't "park" like core.async go blocks - they block the virtual thread instead