@@ -38,6 +38,16 @@ import std.string;
3838import std.uni ;
3939import std.conv ;
4040
41+ // / Ensures the Lifecycle singleton is initialized.
42+ // / Called from Expect to handle the case where assertions are used
43+ // / in shared static this() before the module static this() runs.
44+ private Lifecycle ensureLifecycle () @trusted {
45+ if (Lifecycle.instance is null ) {
46+ Lifecycle.instance = new Lifecycle();
47+ }
48+ return Lifecycle.instance;
49+ }
50+
4151// / Truncates a string value for display in assertion messages.
4252// / Only multiline strings are shortened to keep messages readable.
4353// / Long single-line values are kept intact to preserve type names and other identifiers.
@@ -87,7 +97,7 @@ string truncateForMessage(const(char)[] value) @trusted nothrow {
8797 // / Source parsing is deferred until assertion failure for performance.
8898 this (ValueEvaluation value) @trusted {
8999 _initialized = true ;
90- _evaluation.id = Lifecycle.instance .beginEvaluation(value);
100+ _evaluation.id = ensureLifecycle() .beginEvaluation(value);
91101 _evaluation.currentValue = value;
92102 _evaluation.source = SourceResult.create(value.fileName[].idup, value.line);
93103
@@ -139,7 +149,7 @@ string truncateForMessage(const(char)[] value) @trusted nothrow {
139149 _evaluation.result.addValue(truncateForMessage(_evaluation.expectedValue.strValue[]));
140150 }
141151
142- Lifecycle.instance .endEvaluation(_evaluation);
152+ ensureLifecycle() .endEvaluation(_evaluation);
143153 }
144154 }
145155
@@ -181,7 +191,7 @@ string truncateForMessage(const(char)[] value) @trusted nothrow {
181191
182192 // / Returns the throwable captured during evaluation.
183193 Throwable thrown () {
184- Lifecycle.instance .endEvaluation(_evaluation);
194+ ensureLifecycle() .endEvaluation(_evaluation);
185195 return _evaluation.throwable;
186196 }
187197
@@ -598,3 +608,20 @@ Expect expect(T)(lazy T testedValue, const string file = __FILE__, const size_t
598608 return result;
599609 }
600610}
611+
612+ // Issue #99: ensureLifecycle initializes Lifecycle when called before static this()
613+ @(" issue #99: ensureLifecycle creates instance when null" )
614+ unittest {
615+ // Save the current instance
616+ auto savedInstance = Lifecycle.instance;
617+ scope (exit) Lifecycle.instance = savedInstance;
618+
619+ // Simulate the condition where Lifecycle.instance is null
620+ // (as happens when expect() is called from shared static this())
621+ Lifecycle.instance = null ;
622+
623+ // ensureLifecycle should create a new instance
624+ auto lifecycle = ensureLifecycle();
625+ assert (lifecycle ! is null , " ensureLifecycle should create a Lifecycle instance" );
626+ assert (Lifecycle.instance is lifecycle, " ensureLifecycle should set Lifecycle.instance" );
627+ }
0 commit comments