Skip to content

Commit 3f92f66

Browse files
committed
feat: Implement ensureLifecycle to initialize Lifecycle singleton and add unittest for its behavior
1 parent ece698f commit 3f92f66

File tree

1 file changed

+30
-3
lines changed

1 file changed

+30
-3
lines changed

source/fluentasserts/core/expect.d

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,16 @@ import std.string;
3838
import std.uni;
3939
import 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

Comments
 (0)