Skip to content

Commit 86a9b99

Browse files
paulirwinclaude
andcommitted
Implement IsTestThread to fix flaky TestFlushExceptions
LuceneTestCase.IsTestThread was hardcoded to always return true, so the flush-failure injection in TestConcurrentMergeScheduler.TestFlushExceptions fired on background ConcurrentMergeScheduler merge threads (which also flush via CompressingStoredFieldsWriter.Flush()), not just the test thread. When a merge thread happened to be flushing during the SetDoFail()/Flush() window and Random.NextBoolean() returned true, the merge threw, surfacing as a flaky MergeException at teardown. Capture the test-case thread in SetUp() (cleared in TearDown()) in a [ThreadStatic] field and have IsTestThread compare against it, mirroring Java's ThreadAndTestNameRule.testCaseThread. Tests run with LevelOfParallelism(1), and NUnit's TimeoutCommand (active via the assembly-wide [Timeout]) runs SetUp, the test body, and TearDown on the same thread, so the capture lines up with the thread executing the test method. Background merge threads never run SetUp, so they correctly see a null test thread and are excluded. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 3dbdaef commit 86a9b99

1 file changed

Lines changed: 23 additions & 5 deletions

File tree

src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,11 @@ public virtual void SetUp()
896896
{
897897
// LUCENENET TODO: Not sure how to convert these
898898
//ParentChainCallRule.SetupCalled = true;
899+
900+
// LUCENENET specific: capture the thread running the test method so that
901+
// IsTestThread can distinguish it from background threads (e.g. ConcurrentMergeScheduler
902+
// merge threads). This mirrors Java's ThreadAndTestNameRule.testCaseThread.
903+
testCaseThread = Thread.CurrentThread;
899904
}
900905

901906
/// <summary>
@@ -983,6 +988,11 @@ System Properties
983988
RandomizedContext.PrintStackTrace(ex, NUnit.Framework.TestContext.Error);
984989
throw; // LUCENENET: Throw to preserve stack details of original throw.
985990
}
991+
finally
992+
{
993+
// LUCENENET specific: clear the captured test thread (see SetUp()).
994+
testCaseThread = null;
995+
}
986996
}
987997

988998
/// <summary>
@@ -1147,15 +1157,23 @@ public static SegmentReader GetOnlySegmentReader(DirectoryReader reader)
11471157
return (SegmentReader)r;
11481158
}
11491159

1160+
/// <summary>
1161+
/// LUCENENET specific: the thread that is executing the current test method, captured
1162+
/// in <see cref="SetUp()"/> and cleared in <see cref="TearDown()"/>. Used by
1163+
/// <see cref="IsTestThread"/>. This is <c>[ThreadStatic]</c> so that tests running in
1164+
/// parallel each track their own test thread, and background threads (which never run
1165+
/// <see cref="SetUp()"/>) see <c>null</c>.
1166+
/// </summary>
1167+
[ThreadStatic]
1168+
private static Thread testCaseThread;
1169+
11501170
/// <summary>
11511171
/// Returns true if and only if the calling thread is the primary thread
11521172
/// executing the test case.
1153-
/// <para/>
1154-
/// LUCENENET: Not Implemented - always returns true
11551173
/// </summary>
1156-
/*Assert.IsNotNull(ThreadAndTestNameRule.TestCaseThread, "Test case thread not set?");
1157-
return Thread.CurrentThread == ThreadAndTestNameRule.TestCaseThread;*/
1158-
internal static bool IsTestThread => true; // LUCENENET specific - changed from public to internal since there is no way to support it
1174+
// LUCENENET specific - changed from public to internal since IsTestThread is only meaningful
1175+
// within the test framework. Mirrors Java's check against ThreadAndTestNameRule.testCaseThread.
1176+
internal static bool IsTestThread => testCaseThread != null && Thread.CurrentThread == testCaseThread;
11591177

11601178
/// <summary>
11611179
/// Asserts that <see cref="FieldCacheSanityChecker"/> does not detect any

0 commit comments

Comments
 (0)