test: observe landblock worker joins
This commit is contained in:
parent
fa4bdfe89f
commit
ad7ebe9425
3 changed files with 107 additions and 17 deletions
|
|
@ -53,8 +53,8 @@ inventory refreshed for Batch L reports:
|
|||
| Constant-truth assertion methods / sites | 0 / 0 |
|
||||
| Reviewed syntactic self-comparison methods / sites | 4 / 5 |
|
||||
| Reviewed diagnostic methods | 57 |
|
||||
| Methods directly using `Thread.Sleep` / `Task.Delay` | 15 / 19 |
|
||||
| Cancellable-infinite-only / elapsed-time methods | 5 / 29 |
|
||||
| Methods directly using `Thread.Sleep` / `Task.Delay` | 15 / 17 |
|
||||
| Cancellable-infinite-only / elapsed-time methods | 5 / 27 |
|
||||
| Methods directly reading environment variables | 47 |
|
||||
| Methods directly reading `.cs` source text | 63 |
|
||||
|
||||
|
|
@ -147,7 +147,7 @@ identifiable in the lane report.
|
|||
| T-016 historical test taxonomy | first descriptive-identity batch active in batch J | Remove opaque AP/R/J/K/Slice codes from current test/type names while preserving provenance in comments and ledgers. The 47 `Issue###` files remain an explicit user-decision set because their IDs still connect tests to retail evidence. |
|
||||
| T-017 Avalonia ownership | reopened and closed in batch D | The full gate exposed the same compositor ownership class between six newer `MainWindowViewTests` facts. Their six named assertion phases now run in one owned Avalonia application session; 25 fresh-process stress iterations and the complete gate pass. |
|
||||
| T-018 stderr observer race | resolved in R2 | Live reader shares writes/deletes; 25 focused repetitions plus the complete gate. |
|
||||
| T-019 remaining explicit waits | classified in batch H; four fixed-delay negative oracles resolved in batch M, five remain | Five methods are deterministic cancellation stubs, not wall-clock tests. Preserve intentional real-time contracts and bounded integration polling. The remaining cleanup owners are landblock streamer/pool disposal, headless late-subscriber replay, and two launcher installer/recovery contracts. |
|
||||
| T-019 remaining explicit waits | classified in batch H; six fixed-delay negative oracles resolved in batches M/N, three remain | Five methods are deterministic cancellation stubs, not wall-clock tests. Preserve intentional real-time contracts and bounded integration polling. The remaining cleanup owners are headless late-subscriber replay and two launcher installer/recovery contracts. |
|
||||
| T-020 exact duplicate bodies/data | resolved in batch K | Four redundant executions were removed. The remaining 11 body-equivalent groups are intentionally split theories with disjoint, meaningfully named datasets; the inventory enforces zero repeated rows within a theory or across body-equivalent theories. |
|
||||
| T-021 suspicious assertion shapes | resolved in batch L | The inventory enforces zero `Assert.True(true)` / `Assert.False(false)` sites and reports syntactic self-comparisons for review. All five current self-comparisons assert meaningful determinism or stable identity. |
|
||||
|
||||
|
|
@ -448,7 +448,7 @@ invocation instead of treating both method-level booleans as equivalent. The
|
|||
| Intentional real-time protocol/timeout contract | 4 | Keep with explicit bounds: two Live handshake race delays, the one-second net-probe cadence, and continuous unrelated shutdown drain. |
|
||||
| Cooperative yield while virtual-clock/background transport work drains | 8 | Retain for now; prefer an observable receiver/worker signal when that seam exists. The virtual behavioral oracle does not derive from the sleep duration. |
|
||||
| Bounded completion/readiness polling | 11 | Retain as integration polling with a terminal assertion and deadline; improve opportunistically with events, not by busy-spinning. |
|
||||
| Fixed delay used to prove another operation is still blocked | 9 originally; 5 remain | Batch M replaces four with observed thread wait state. Replace the remaining elapsed-time absence-of-completion checks with an observed wait state or test seam. |
|
||||
| Fixed delay used to prove another operation is still blocked | 9 originally; 3 remain | Batches M/N replace six with observed thread wait state. Replace the remaining elapsed-time absence-of-completion checks with an observed wait state or test seam. |
|
||||
| Positive completion timeout guard | 1 | Keep. The two-second `WhenAny` in `RuntimeCharacterStateTests` fails only if the operation does not complete; it does not delay a passing run. |
|
||||
|
||||
The original nine fixed-delay negative oracles are:
|
||||
|
|
@ -665,3 +665,34 @@ Batch M verification:
|
|||
Five fixed-delay negative oracles remain: the landblock streamer and pool
|
||||
disposal contracts, headless concurrent late-subscriber replay, and the two
|
||||
launcher installer/recovery contracts.
|
||||
|
||||
## Batch N observable landblock-worker joins
|
||||
|
||||
The two landblock disposal contracts used 50–100 ms delays to infer that
|
||||
`LandblockStreamer.Dispose` was waiting for its dedicated worker threads.
|
||||
Batch N replaces those delays with direct observation of the disposer threads:
|
||||
|
||||
- `LandblockStreamerTests.DisposeAndConcurrentDisposeWaitForInFlightLoad`
|
||||
observes the primary disposer blocked in a worker `Join`, then observes the
|
||||
concurrent disposer blocked behind the disposal monitor; and
|
||||
- `LandblockStreamerPoolTests.Dispose_JoinsEveryWorkerInThePool` observes the
|
||||
disposer blocked while all three controlled worker lanes remain in their
|
||||
loader callbacks.
|
||||
|
||||
Both tests then release the loaders, require every disposal thread to join
|
||||
within the existing bound, and retain their exception and post-disposal
|
||||
inertness checks. Production code is unchanged.
|
||||
|
||||
Batch N verification:
|
||||
|
||||
- both focused contracts pass;
|
||||
- 25 fresh-process iterations of each pass, for 50/50 observed-wait cases;
|
||||
- the refreshed inventory reduces direct `Task.Delay` methods from 19 to 17
|
||||
and elapsed-time methods from 29 to 27;
|
||||
- the complete 44-project Release build reports zero warnings and zero errors;
|
||||
and
|
||||
- the no-retry complete hermetic Release gate remains 14,382/14,382 with zero
|
||||
skips or failures across all 12 test assemblies.
|
||||
|
||||
Three fixed-delay negative oracles remain: headless concurrent late-subscriber
|
||||
replay and the two launcher installer/recovery contracts.
|
||||
|
|
|
|||
|
|
@ -377,7 +377,7 @@ public sealed class LandblockStreamerPoolTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Dispose_JoinsEveryWorkerInThePool()
|
||||
public void Dispose_JoinsEveryWorkerInThePool()
|
||||
{
|
||||
const int workerCount = 3;
|
||||
using var release = new ManualResetEventSlim();
|
||||
|
|
@ -408,13 +408,31 @@ public sealed class LandblockStreamerPoolTests
|
|||
Assert.True(entered.Wait(TimeSpan.FromSeconds(5)));
|
||||
Assert.Equal(workerCount, loaderThreads.Count);
|
||||
|
||||
Task dispose = Task.Run(streamer.Dispose);
|
||||
await Task.Delay(100);
|
||||
// Dispose must be blocked on the still-building workers.
|
||||
Assert.False(dispose.IsCompleted);
|
||||
Exception? disposeError = null;
|
||||
var disposeThread = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
streamer.Dispose();
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
disposeError = error;
|
||||
}
|
||||
})
|
||||
{
|
||||
IsBackground = true,
|
||||
Name = "LandblockStreamer pool dispose contract",
|
||||
};
|
||||
disposeThread.Start();
|
||||
Assert.True(SpinWait.SpinUntil(
|
||||
() => (disposeThread.ThreadState & ThreadState.WaitSleepJoin) != 0,
|
||||
TimeSpan.FromSeconds(5)),
|
||||
"dispose never waited for the still-building workers");
|
||||
|
||||
release.Set();
|
||||
await dispose.WaitAsync(TimeSpan.FromSeconds(5));
|
||||
Assert.True(disposeThread.Join(TimeSpan.FromSeconds(5)));
|
||||
Assert.Null(disposeError);
|
||||
|
||||
Assert.Throws<ObjectDisposedException>(
|
||||
() => streamer.EnqueueLoad(0x1234FFFFu, LandblockStreamJobKind.LoadFar));
|
||||
|
|
|
|||
|
|
@ -342,7 +342,7 @@ public class LandblockStreamerTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DisposeAndConcurrentDisposeWaitForInFlightLoad()
|
||||
public void DisposeAndConcurrentDisposeWaitForInFlightLoad()
|
||||
{
|
||||
using var entered = new ManualResetEventSlim();
|
||||
using var release = new ManualResetEventSlim();
|
||||
|
|
@ -359,14 +359,55 @@ public class LandblockStreamerTests
|
|||
streamer.EnqueueLoad(0x12340000u);
|
||||
Assert.True(entered.Wait(TimeSpan.FromSeconds(2)));
|
||||
|
||||
Task firstDispose = Task.Run(streamer.Dispose);
|
||||
Task secondDispose = Task.Run(streamer.Dispose);
|
||||
await Task.Delay(50);
|
||||
Assert.False(firstDispose.IsCompleted);
|
||||
Assert.False(secondDispose.IsCompleted);
|
||||
Exception? firstError = null;
|
||||
var firstDisposeThread = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
streamer.Dispose();
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
firstError = error;
|
||||
}
|
||||
})
|
||||
{
|
||||
IsBackground = true,
|
||||
Name = "LandblockStreamer primary dispose contract",
|
||||
};
|
||||
firstDisposeThread.Start();
|
||||
Assert.True(SpinWait.SpinUntil(
|
||||
() => (firstDisposeThread.ThreadState & ThreadState.WaitSleepJoin) != 0,
|
||||
TimeSpan.FromSeconds(2)),
|
||||
"the primary dispose never waited for the in-flight load");
|
||||
|
||||
Exception? secondError = null;
|
||||
var secondDisposeThread = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
streamer.Dispose();
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
secondError = error;
|
||||
}
|
||||
})
|
||||
{
|
||||
IsBackground = true,
|
||||
Name = "LandblockStreamer concurrent dispose contract",
|
||||
};
|
||||
secondDisposeThread.Start();
|
||||
Assert.True(SpinWait.SpinUntil(
|
||||
() => (secondDisposeThread.ThreadState & ThreadState.WaitSleepJoin) != 0,
|
||||
TimeSpan.FromSeconds(2)),
|
||||
"the concurrent dispose never waited for primary disposal");
|
||||
|
||||
release.Set();
|
||||
await Task.WhenAll(firstDispose, secondDispose).WaitAsync(TimeSpan.FromSeconds(2));
|
||||
Assert.True(firstDisposeThread.Join(TimeSpan.FromSeconds(2)));
|
||||
Assert.True(secondDisposeThread.Join(TimeSpan.FromSeconds(2)));
|
||||
Assert.Null(firstError);
|
||||
Assert.Null(secondError);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue