Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,7 @@ dotnet_diagnostic.VSTHRD003.severity = none
[tests/OpenCode.Sdk.Tests/Harness/Ownership/OwnedOperationDeadlineTests.cs]
dotnet_diagnostic.VSTHRD003.severity = none

[tests/OpenCode.Sdk.Tests/Support/SessionLogTranscriptTests.cs]
[tests/OpenCode.Sdk.Tests/Sessions/Support/SessionLogTranscriptTests.cs]
dotnet_diagnostic.VSTHRD003.severity = none

# The session-removal fault proof controls interrupt entry and expiry, then drains cancellation
Expand All @@ -970,7 +970,7 @@ dotnet_diagnostic.VSTHRD003.severity = none
# These diagnostic proofs control their own operation deadlines and retain supplied disposal
# tasks until finally releases and drains them. Winner: causal fixture/status ownership proofs;
# the rule remains enabled outside these exact files.
[tests/OpenCode.Sdk.Tests/Support/PtyFailureDiagnosticsTests.cs]
[tests/OpenCode.Sdk.Tests/Ptys/Support/PtyFailureDiagnosticsTests.cs]
dotnet_diagnostic.VSTHRD003.severity = none

[tests/OpenCode.Sdk.Tests/Support/ServerDiagnosticScenario.cs]
Expand Down Expand Up @@ -1155,7 +1155,7 @@ dotnet_diagnostic.MA0045.severity = none
# DriveManifestTests.cs documents above - and an async factory would make the arrangement
# awaitable without making it asynchronous. Winner: the cross-TFM arrangement shape; MA0045
# remains enabled for every other file.
[tests/OpenCode.Sdk.Tests/Support/PathCommandShim.cs]
[tests/OpenCode.Sdk.Tests/Hosting/Launcher/Support/PathCommandShim.cs]
dotnet_diagnostic.MA0045.severity = none

# Optional<T> is the tri-state carrier for a request property the document declares both optional
Expand Down
15 changes: 9 additions & 6 deletions docs/engineering/testing-style.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Testing Style β€” authoring tests

Date: 2026-10-16
Date: 2026-10-17

Binding authorship style for every test in this repository. `quality-gates.md` owns the
current assurance posture and completion gates; operational build-out state belongs in
Expand Down Expand Up @@ -64,9 +64,11 @@ sized to need:
like a BCL type shadows it there: tests under `Debug/` declare `OpenCode.Sdk.Tests.Debug` and
qualify `System.Diagnostics.Debug`. A guard test refuses two source types that project onto
one test path.
- A helper or fixture one slice uses lives in that test slice's `Support/` or `Fixtures/`;
helpers several slices share stay in the test project's root `Support/`. Live-server harness
code lives in `tests/OpenCode.Sdk.Tests/Harness/`. Files another test project links, and the
- In SDK.Tests, a helper or fixture used by one slice lives in that slice's `Support/` or
`Fixtures/`. Helpers and fixtures used by several slices stay in the test project's root
`Support/` and `Fixtures/`. Embedded fixtures retain their `Fixtures.` resource-name prefix
through explicit `LogicalName` metadata. Live-server harness code lives in
`tests/OpenCode.Sdk.Tests/Harness/`. Files another test project links, and the
embedded-fixture loader, stay in `tests/Shared/`, compiled through explicit links. There is no
separate test-support project.
- The mirror rule also applies to test infrastructure: `Harness/X.cs` is tested in
Expand Down Expand Up @@ -99,8 +101,9 @@ analyzer enforces it wherever an abstractions assembly is referenced β€” even te
**Raw JSON/XML/string-literal dumps pasted inline in test methods are forbidden.** A
test body states *intent*; the data it runs on lives in one of three sanctioned homes:

1. **Embedded fixture files** β€” under the owning test project's `Fixtures/` folder,
loaded through a resource-loader helper by name. The default for wire-shaped data:
1. **Embedded fixture files** β€” in the `Fixtures/` folder of the slice that uses them, or the test
project's root `Fixtures/` when several slices share them, loaded through a resource-loader helper
by name. The default for wire-shaped data:
one small file per quirk, named for the construct it isolates, reviewable on its own.
2. **Typed builders** β€” for variation families where files would multiply: red tests
composing a valid base plus exactly one offending construct through the builder. The
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Net;
using System.Net.Sockets;
using OpenCode.Sdk.Internal;
using OpenCode.Sdk.Tests.Hosting.BackgroundService.Support;
using OpenCode.Sdk.Tests.Support;

namespace OpenCode.Sdk.Tests.Hosting.BackgroundService.Discovery;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Globalization;
using System.Net;
using OpenCode.Sdk.Tests.Harness;
using OpenCode.Sdk.Tests.Hosting.BackgroundService.Support;
using OpenCode.Sdk.Tests.Support;
using Testably.Abstractions;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using OpenCode.Sdk.Internal;
using OpenCode.Sdk.Tests.Harness;
using OpenCode.Sdk.Tests.Hosting.BackgroundService.Support;
using OpenCode.Sdk.Tests.Support;
using Testably.Abstractions;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Text;
using OpenCode.Sdk.Internal;
using OpenCode.Sdk.Tests.Hosting.BackgroundService.Registration;
using OpenCode.Sdk.Tests.Hosting.BackgroundService.Support;
using OpenCode.Sdk.Tests.Support;

namespace OpenCode.Sdk.Tests.Hosting.BackgroundService.Discovery;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Text;
using OpenCode.Sdk.Internal;
using OpenCode.Sdk.Tests.Harness;
using OpenCode.Sdk.Tests.Hosting.BackgroundService.Support;
using OpenCode.Sdk.Tests.Support;
using Testably.Abstractions;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Net;
using NSubstitute;
using OpenCode.Sdk.Internal;
using OpenCode.Sdk.Tests.Hosting.BackgroundService.Support;
using OpenCode.Sdk.Tests.Support;
using Testably.Abstractions.Testing;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Net.Sockets;

namespace OpenCode.Sdk.Tests.Support;
namespace OpenCode.Sdk.Tests.Hosting.BackgroundService.Support;

/// <summary>One connect the probe's loopback transport made itself, as <see cref="RecordingLoopbackConnector"/> saw it.</summary>
/// <param name="SynRetransmissionDisabled">Whether the loopback option took on the socket it connected.</param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using System.Net.Sockets;
using OpenCode.Sdk.Internal;

namespace OpenCode.Sdk.Tests.Support;
namespace OpenCode.Sdk.Tests.Hosting.BackgroundService.Support;

/// <summary>
/// The platform connect under the probe's loopback transport, recording each connect the transport
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#if !NET
using OpenCode.Sdk.Internal;

namespace OpenCode.Sdk.Tests.Support;
namespace OpenCode.Sdk.Tests.Hosting.BackgroundService.Support;

/// <summary>
/// A loopback connect that completes in a chosen order, with no socket traffic: it cancels the
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Globalization;

namespace OpenCode.Sdk.Tests.Support;
namespace OpenCode.Sdk.Tests.Hosting.BackgroundService.Support;

/// <summary>
/// The isolated discovery executable's output as a test reads it: the one stdout line for a found
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Globalization;
using OpenCode.Sdk.Internal;

namespace OpenCode.Sdk.Tests.Support;
namespace OpenCode.Sdk.Tests.Hosting.BackgroundService.Support;

/// <summary>
/// The probe bound of every background-service test about the probe's answer rather than its
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#if NET
using System.Diagnostics.Tracing;

namespace OpenCode.Sdk.Tests.Support;
namespace OpenCode.Sdk.Tests.Hosting.BackgroundService.Support;

/// <summary>
/// Records this process's socket connect events while it lives, so a test can read that its own
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using OpenCode.Sdk.Internal;
using OpenCode.Sdk.Tests.Harness;
using OpenCode.Sdk.Tests.Hosting.Launcher.Support;
using OpenCode.Sdk.Tests.Support;
using Testably.Abstractions;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Globalization;
using OpenCode.Sdk.Internal;
using OpenCode.Sdk.Tests.Harness;
using OpenCode.Sdk.Tests.Hosting.Launcher.Support;
using OpenCode.Sdk.Tests.Support;
using Testably.Abstractions;
using TUnit.Assertions.Enums;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using OpenCode.Sdk.Internal;
using OpenCode.Sdk.Tests.Harness;
using OpenCode.Sdk.Tests.Hosting.Launcher.Support;
using OpenCode.Sdk.Tests.Support;
using Testably.Abstractions;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.Win32.SafeHandles;
using OpenCode.Sdk.Internal;
using OpenCode.Sdk.Tests.Harness;
using OpenCode.Sdk.Tests.Hosting.Launcher.Support;
using OpenCode.Sdk.Tests.Support;
using Testably.Abstractions;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using NSubstitute;
using OpenCode.Sdk.Internal;
using OpenCode.Sdk.Tests.Harness;
using OpenCode.Sdk.Tests.Hosting.Launcher.Support;
using OpenCode.Sdk.Tests.Support;
using Testably.Abstractions;
using TUnit.Assertions.Enums;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Diagnostics;
using System.Globalization;
using OpenCode.Sdk.Tests.Harness;
using OpenCode.Sdk.Tests.Hosting.Launcher.Support;
using OpenCode.Sdk.Tests.Support;
using Testably.Abstractions;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using NSubstitute;
using OpenCode.Sdk.Internal;
using OpenCode.Sdk.Tests.Harness;
using OpenCode.Sdk.Tests.Hosting.Launcher.Support;
using OpenCode.Sdk.Tests.Support;
using Testably.Abstractions;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using NSubstitute;
using OpenCode.Sdk.Internal;
using OpenCode.Sdk.Tests.Hosting.Launcher.Support;
using OpenCode.Sdk.Tests.Support;
using SdkTestSupport = OpenCode.Sdk.Tests.Support;

namespace OpenCode.Sdk.Tests.Hosting.Launcher;

Expand Down Expand Up @@ -149,7 +151,7 @@ public Task StartAsync_Should_Keep_A_Recorded_Exit_When_The_Caller_Cancels_Durin
AssertRecordedAtTimeoutAsync(ExitCommand, EarlyExitDiagnostic, cancellationToken, static async (time, arbitrate, caller) =>
{
_ = arbitrate.TrySetResult();
await time.WaitForArmedAsync(OwnedServerChild.EarlyEndBound, Support.HangGuard.Bound, count: 2);
await time.WaitForArmedAsync(OwnedServerChild.EarlyEndBound, SdkTestSupport.HangGuard.Bound, count: 2);
await caller.CancelOnWorkerAsync();
});

Expand Down Expand Up @@ -180,10 +182,10 @@ private static async Task AssertRecordedAtTimeoutAsync(
var starting = OpenCodeServer.StartWithSeamsAsync(RecordedOptions(publish), seams, caller.Token);
try
{
var recorded = await entered.Task.WaitAsync(Support.HangGuard.Bound, cancellationToken);
await time.WaitForArmedAsync(ReadinessBound, Support.HangGuard.Bound);
var recorded = await entered.Task.WaitAsync(SdkTestSupport.HangGuard.Bound, cancellationToken);
await time.WaitForArmedAsync(ReadinessBound, SdkTestSupport.HangGuard.Bound);
time.Advance(ReadinessBound);
await canceled.Task.WaitAsync(Support.HangGuard.Bound, cancellationToken);
await canceled.Task.WaitAsync(SdkTestSupport.HangGuard.Bound, cancellationToken);
// The spawn supplied stdin before the owner entered its wait.
await PublishRecordedOutcomeAsync(recorded, input!, closingOutput, cancellationToken);
if (closingOutput is not null)
Expand Down Expand Up @@ -263,11 +265,11 @@ private static async Task AssertRecordedAtTimeoutAsync(
private static async Task PublishRecordedOutcomeAsync(
Task recorded, Stream input, GatedStream? closingOutput, CancellationToken cancellationToken)
{
await input.WriteAsync(new byte[] { 1 }, cancellationToken).AsTask().WaitAsync(Support.HangGuard.Bound, cancellationToken);
await input.FlushAsync(cancellationToken).WaitAsync(Support.HangGuard.Bound, cancellationToken);
await input.WriteAsync(new byte[] { 1 }, cancellationToken).AsTask().WaitAsync(SdkTestSupport.HangGuard.Bound, cancellationToken);
await input.FlushAsync(cancellationToken).WaitAsync(SdkTestSupport.HangGuard.Bound, cancellationToken);
closingOutput?.Release();
Task[] recordedTasks = [recorded];
await Task.WhenAll(recordedTasks).WaitAsync(Support.HangGuard.Bound, cancellationToken);
await Task.WhenAll(recordedTasks).WaitAsync(SdkTestSupport.HangGuard.Bound, cancellationToken);
}

private static async Task AssertCallerCancellationAsync(
Expand All @@ -289,11 +291,11 @@ private static async Task AdvanceEarlyEndAsync(
ManualTimeProvider time, Task starting, int count, CancellationToken cancellationToken)
{
// A timeout can finish cleanup without a drain; a recorded early end drains first.
var draining = time.WaitForArmedAsync(OwnedServerChild.EarlyEndBound, Support.HangGuard.Bound, count);
var draining = time.WaitForArmedAsync(OwnedServerChild.EarlyEndBound, SdkTestSupport.HangGuard.Bound, count);
Task[] startingTasks = [starting];
if (await Task.WhenAny(Task.WhenAll(startingTasks), draining).WaitAsync(Support.HangGuard.Bound, cancellationToken) == draining)
if (await Task.WhenAny(Task.WhenAll(startingTasks), draining).WaitAsync(SdkTestSupport.HangGuard.Bound, cancellationToken) == draining)
{
await draining.WaitAsync(Support.HangGuard.Bound, cancellationToken);
await draining.WaitAsync(SdkTestSupport.HangGuard.Bound, cancellationToken);
time.Advance(OwnedServerChild.EarlyEndBound);
}
}
Expand Down Expand Up @@ -340,13 +342,13 @@ private static Func<Task, CancellationToken, Task> HoldCanceledWait(
try
{
Task[] pendingTasks = [pending];
await Task.WhenAll(pendingTasks).WaitAsync(token).WaitAsync(Support.HangGuard.Bound, cancellationToken);
await Task.WhenAll(pendingTasks).WaitAsync(token).WaitAsync(SdkTestSupport.HangGuard.Bound, cancellationToken);
}
catch (OperationCanceledException) when (token.IsCancellationRequested)
{
_ = canceled.TrySetResult();
Task[] arbitrationTasks = [arbitrate.Task];
await Task.WhenAll(arbitrationTasks).WaitAsync(Support.HangGuard.Bound, cancellationToken);
await Task.WhenAll(arbitrationTasks).WaitAsync(SdkTestSupport.HangGuard.Bound, cancellationToken);
throw;
}
};
Expand All @@ -364,7 +366,7 @@ private static async Task AssertStartOutcomeAsync(
}
else
{
await Assert.That(async () => await Task.WhenAll(startingTasks).WaitAsync(Support.HangGuard.Bound, cancellationToken)).ThrowsNothing();
await Assert.That(async () => await Task.WhenAll(startingTasks).WaitAsync(SdkTestSupport.HangGuard.Bound, cancellationToken)).ThrowsNothing();
var server = await startingTasks[0];
await Assert.That(server.Endpoint).IsEqualTo(new Uri(ReadyEndpoint));
await Assert.That(server.ChildExited.IsCompleted).IsFalse();
Expand Down Expand Up @@ -397,7 +399,7 @@ await starting.ContinueWith(static completed => _ = completed.Exception, Cancell
{
if (originalOutput is not null)
{
await originalOutput.DisposeAsync().AsTask().WaitAsync(Support.HangGuard.Bound);
await originalOutput.DisposeAsync().AsTask().WaitAsync(SdkTestSupport.HangGuard.Bound);
}
}
finally
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Globalization;
using OpenCode.Sdk.Internal;
using OpenCode.Sdk.Tests.Harness;
using OpenCode.Sdk.Tests.Hosting.Launcher.Support;
using OpenCode.Sdk.Tests.Support;
using Testably.Abstractions;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Globalization;
using OpenCode.Sdk.Internal;
using OpenCode.Sdk.Tests.Harness;
using OpenCode.Sdk.Tests.Hosting.Launcher.Support;
using OpenCode.Sdk.Tests.Support;
using Testably.Abstractions;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Text;
using NSubstitute;
using OpenCode.Sdk.Internal;
using OpenCode.Sdk.Tests.Hosting.Launcher.Support;
using OpenCode.Sdk.Tests.Support;
using TUnit.Assertions.Enums;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using System.Globalization;
using OpenCode.Sdk.Internal;
using OpenCode.Sdk.Tests.Harness;
using OpenCode.Sdk.Tests.Support;

namespace OpenCode.Sdk.Tests.Support;
namespace OpenCode.Sdk.Tests.Hosting.Launcher.Support;

/// <summary>
/// The <c>ladder-tree.js</c> server stand-in: a root with the children a flag list asks for, each
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
using Microsoft.Win32.SafeHandles;
using OpenCode.Sdk.Internal;
using OpenCode.Sdk.Tests.Harness;
using OpenCode.Sdk.Tests.Support;
using Testably.Abstractions;

namespace OpenCode.Sdk.Tests.Support;
namespace OpenCode.Sdk.Tests.Hosting.Launcher.Support;

/// <summary>
/// The service fixture's <c>launcher-host</c> mode: a separate .NET process that owns one
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace OpenCode.Sdk.Tests.Support;
namespace OpenCode.Sdk.Tests.Hosting.Launcher.Support;

/// <summary>The signal a <see cref="LauncherHost"/> starts with ignored, inherited the way a real host inherits it.</summary>
internal enum LauncherHostIgnores
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using OpenCode.Sdk.Internal;

namespace OpenCode.Sdk.Tests.Support;
namespace OpenCode.Sdk.Tests.Hosting.Launcher.Support;

/// <summary>
/// A C library without <c>posix_spawn_file_actions_addchdir_np</c>, as glibc before 2.29 is: the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using System.Text;
using OpenCode.Sdk.Tests.Harness;

namespace OpenCode.Sdk.Tests.Support;
namespace OpenCode.Sdk.Tests.Hosting.Launcher.Support;

/// <summary>
/// An npm-shaped command shim on the process PATH: a temporary directory holding one Windows
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using OpenCode.Sdk.Internal;

namespace OpenCode.Sdk.Tests.Support;
namespace OpenCode.Sdk.Tests.Hosting.Launcher.Support;

/// <summary>
/// The real exit-status seam, reporting that the kernel reaps this process's children itself, as
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using System.Diagnostics;
using OpenCode.Sdk.Internal;

namespace OpenCode.Sdk.Tests.Support;
namespace OpenCode.Sdk.Tests.Hosting.Launcher.Support;

/// <summary>
/// The shipped Windows tree kill behind a recorder: every run is recorded with the time since the
Expand Down
Loading
Loading