I test the hot-reload system end-to-end against a real dependency.
HotReloadTest is a path dep under fixtures/ with a struct (Config) and a struct caller (Builder). Examples modify its source, verify the change took effect, then revert. Running examples leaves no traces.
All mutation examples go through with_reload/3 which handles
the write-compile-revert cycle. Each example only specifies what
to change and what to observe.
Summary
Functions
I add a module file OUTSIDE the bridge (plain File.write, as an external editor would) and verify the next save of any file announces it: mix's recompile builds the new file, and its regenerated .app on disk names the module even though no compiled or sibling list does. Message-driven, no sleeps. Returns the announced module's name.
I add a brand-new module and verify its beam lands in the app's ebin: a new module is in no app spec, so per-module app resolution skipped it and it existed only in memory until the next full mix compile. Reverting removes the module, which must delete the beam again. Returns the new module's beam exports.
I remove a module from a file and verify the reload purges it: the compile result is the file's full module set, so a module absent from it was deleted from the source. Message-driven - I wait on the broker's own events (no sleeps) and sync the projection with a get_state barrier before reading it. Returns the app's surviving module names.
Callback implementation for ExExample.Behaviour.copy/1.
Callback implementation for ExExample.Behaviour.rerun?/1.
Functions
I add a module file OUTSIDE the bridge (plain File.write, as an external editor would) and verify the next save of any file announces it: mix's recompile builds the new file, and its regenerated .app on disk names the module even though no compiled or sibling list does. Message-driven, no sleeps. Returns the announced module's name.
I add a brand-new module and verify its beam lands in the app's ebin: a new module is in no app spec, so per-module app resolution skipped it and it existed only in memory until the next full mix compile. Reverting removes the module, which must delete the beam again. Returns the new module's beam exports.
I remove a module from a file and verify the reload purges it: the compile result is the file's full module set, so a module absent from it was deleted from the source. Message-driven - I wait on the broker's own events (no sleeps) and sync the projection with a get_state barrier before reading it. Returns the app's surviving module names.
@spec add_function() :: [{atom(), non_neg_integer()}]
@spec beam_persisted() :: [{atom(), non_neg_integer()}]
Callback implementation for ExExample.Behaviour.copy/1.
@spec external_file_announced_on_recompile() :: String.t()
@spec modify_function() :: String.t()
@spec new_field_propagates() :: String.t()
@spec new_module_beam_persisted() :: [{atom(), non_neg_integer()}]
@spec removed_module_purged() :: [String.t()]
Callback implementation for ExExample.Behaviour.rerun?/1.
@spec revert_clean() :: [{atom(), non_neg_integer()}]
@spec standalone_module() :: keyword()