diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 5e9ea856426..237d9ea7977 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -389,7 +389,8 @@ To update the version and make a new release, increment the `VersionPrefix` in ` ### PR Protocol -1. Create a GitHub Issue (Enhancement / Bug / Task) -2. Branch naming: `/branch-name` -3. Include unit tests (see Test Structure above) -4. Link PR to the issue in the development section +1. Do not create an issue unless asked to do so. If such a request is made, create the issue with a clear and complete but concise description, assign it to the current user, and link the PR to the issue in the development section. Once an Issue has been created, do not update it until the PR is opened unless asked; when the PR is opened, update the Issue (again, completely but concisely) with any necessary changes. + a. Similarly, do not create a PR until asked. A PR may be created without an underlying Issue; the PR must completely and concisely state the problem. Once a PR has been created, update its description (again, completely but concisely) on subsequent commits with any necessary changes. +2. It is acceptable and encouraged to consolidate smaller changes into a single larger PR for reviewer convenience and reducing bookkeeping and process, as long as the PR description clearly outlines the different fixes or improvements being made in the work. +3. Branch naming: `/branch-name` +4. Include unit tests (see Test Structure above) diff --git a/libs/cluster/Server/Gossip/Gossip.cs b/libs/cluster/Server/Gossip/Gossip.cs index a08ff85dc17..9afc0c49aa4 100644 --- a/libs/cluster/Server/Gossip/Gossip.cs +++ b/libs/cluster/Server/Gossip/Gossip.cs @@ -355,12 +355,17 @@ async Task GossipMainAsync() else await GossipSampleSendAsync().ConfigureAwait(false); } - catch (Exception ex) when (!ctsGossip.Token.IsCancellationRequested) + catch (Exception ex) { // Nothing restarts this task, so letting a single failed round leave the loop would stop // this node gossiping for the rest of the process. Its view then stops being offered to // the rest of the cluster and no further configuration change can ever converge. - logger?.LogWarning("Gossip round failed {msg}", ex.Message); + // Every round failure is classified here rather than in an exception filter, so one that + // races shutdown is still handled as a round failure: the outer handlers recognize + // cancellation by exception type and treat anything else as terminal. Cancellation makes + // the failure shutdown noise, so the loop ends without logging it. + if (!ctsGossip.Token.IsCancellationRequested) + logger?.LogWarning("Gossip round failed {msg}", ex.Message); } await Task.Delay(gossipDelay, ctsGossip.Token).ConfigureAwait(false); diff --git a/libs/server/StoreWrapper.cs b/libs/server/StoreWrapper.cs index 82bb28e2ab8..bbe31cc8f62 100644 --- a/libs/server/StoreWrapper.cs +++ b/libs/server/StoreWrapper.cs @@ -736,9 +736,14 @@ internal static async Task RunMaintenanceLoopAsync(Func + /// Base class for tests — tracks currently running tests for diagnostics. + /// + public abstract class TestBase + { + // Thread-safe collection to store currently running tests + public static readonly ConcurrentDictionary RunningTests = new(); + + [SetUp] + public void TrackRunningTest() + { + // Add test to the running list + RunningTests[TestContext.CurrentContext.Test.Name] = true; + + if (TestContext.CurrentContext.CurrentRepeatCount > 0) + Debug.WriteLine($"*** Current test iteration {TestContext.CurrentContext.CurrentRepeatCount + 1}: {TestContext.CurrentContext.Test.Name} ***"); + } + + [TearDown] + public void RemoveRunningTest() + { + Assert.That(RunningTests.TryRemove(TestContext.CurrentContext.Test.Name, out _), Is.True, $"Could not find running test {TestContext.CurrentContext.Test.Name}"); + } + + /// + /// Installs process-wide handlers that dump the currently running tests when an exception escapes a + /// background thread or an unobserved task, which is otherwise reported with no indication of which + /// test was in flight. NUnit discovers a [SetUpFixture] only in the assembly under test, never + /// in a referenced one, so each test assembly calls this from its own fixture. + /// + public static void InstallUnhandledExceptionHandlers() + { + AppDomain.CurrentDomain.UnhandledException += (s, e) => + { + DumpTests(); + }; + + TaskScheduler.UnobservedTaskException += (s, e) => + { + DumpTests(); + e.SetObserved(); // Optionally mark observed so it doesn't escalate later + }; + + static void DumpTests() + { + if (RunningTests.Count == 0) + return; + var sb = new StringBuilder(); + _ = sb.AppendLine("*** CURRENTLY RUNNING TESTS ***:"); + foreach (var key in RunningTests.Keys) + _ = sb.AppendLine(key); + Console.WriteLine(sb.ToString()); + } + } + } +} \ No newline at end of file diff --git a/libs/storage/Tsavorite/cs/test/TestProjectSetup.cs b/libs/storage/Tsavorite/cs/test/TestProjectSetup.cs new file mode 100644 index 00000000000..994a34cb535 --- /dev/null +++ b/libs/storage/Tsavorite/cs/test/TestProjectSetup.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +using NUnit.Framework; + +namespace Tsavorite.test +{ + /// + /// Per-assembly setup. Installs the handlers that report which tests were in flight when an exception + /// escapes a background thread, which NUnit runs only for the assembly under test. + /// + [SetUpFixture] + public class TestProjectSetup + { + [OneTimeSetUp] + public void SetUpProject() => TestBase.InstallUnhandledExceptionHandlers(); + } +} \ No newline at end of file diff --git a/libs/storage/Tsavorite/cs/test/TryEnqueueBasicTests.cs b/libs/storage/Tsavorite/cs/test/TryEnqueueBasicTests.cs index a39dd037ba3..3798e789f1e 100644 --- a/libs/storage/Tsavorite/cs/test/TryEnqueueBasicTests.cs +++ b/libs/storage/Tsavorite/cs/test/TryEnqueueBasicTests.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/Tsavorite.test.csproj b/libs/storage/Tsavorite/cs/test/Tsavorite.test.csproj index bd67a567012..3c333bbefe7 100644 --- a/libs/storage/Tsavorite/cs/test/Tsavorite.test.csproj +++ b/libs/storage/Tsavorite/cs/test/Tsavorite.test.csproj @@ -11,7 +11,6 @@ - diff --git a/libs/storage/Tsavorite/cs/test/WaitForCommit.cs b/libs/storage/Tsavorite/cs/test/WaitForCommit.cs index 8d2fe7622c8..4ac72c12938 100644 --- a/libs/storage/Tsavorite/cs/test/WaitForCommit.cs +++ b/libs/storage/Tsavorite/cs/test/WaitForCommit.cs @@ -3,7 +3,6 @@ using System.IO; using System.Threading; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.epoch/helpers/EpochTestBase.cs b/libs/storage/Tsavorite/cs/test/test.epoch/helpers/EpochTestBase.cs index 222b318904d..f764a4ff295 100644 --- a/libs/storage/Tsavorite/cs/test/test.epoch/helpers/EpochTestBase.cs +++ b/libs/storage/Tsavorite/cs/test/test.epoch/helpers/EpochTestBase.cs @@ -9,10 +9,10 @@ namespace Tsavorite.test.epoch { /// - /// Base for every fixture here: the running-test tracking from , + /// Base for every fixture here: the running-test tracking from , /// a fresh per test, and the join helper they share. /// - public abstract class EpochTestBase : Garnet.test.TestBase + public abstract class EpochTestBase : TestBase { protected LightEpoch epoch; diff --git a/libs/storage/Tsavorite/cs/test/test.hlog/BasicStorageTests.cs b/libs/storage/Tsavorite/cs/test/test.hlog/BasicStorageTests.cs index e8f2bb494b0..ca92d8311a0 100644 --- a/libs/storage/Tsavorite/cs/test/test.hlog/BasicStorageTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.hlog/BasicStorageTests.cs @@ -5,7 +5,6 @@ using System.IO; using System.Runtime.InteropServices; using System.Threading; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.hlog/DeviceLogTests.cs b/libs/storage/Tsavorite/cs/test/test.hlog/DeviceLogTests.cs index 63be9b9df53..0c97082bf8e 100644 --- a/libs/storage/Tsavorite/cs/test/test.hlog/DeviceLogTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.hlog/DeviceLogTests.cs @@ -9,7 +9,6 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.hlog/DeviceTests.cs b/libs/storage/Tsavorite/cs/test/test.hlog/DeviceTests.cs index ec8f33df529..87b0d93f615 100644 --- a/libs/storage/Tsavorite/cs/test/test.hlog/DeviceTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.hlog/DeviceTests.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Threading; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.hlog/InsertAtTailSpanByteStressTests.cs b/libs/storage/Tsavorite/cs/test/test.hlog/InsertAtTailSpanByteStressTests.cs index 8ae89ed6f16..227a5f8f150 100644 --- a/libs/storage/Tsavorite/cs/test/test.hlog/InsertAtTailSpanByteStressTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.hlog/InsertAtTailSpanByteStressTests.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.hlog/LogAndDeviceConfigTests.cs b/libs/storage/Tsavorite/cs/test/test.hlog/LogAndDeviceConfigTests.cs index 9745de5a018..0b596d83fac 100644 --- a/libs/storage/Tsavorite/cs/test/test.hlog/LogAndDeviceConfigTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.hlog/LogAndDeviceConfigTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. using System.IO; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.hlog/LogReadAsyncTests.cs b/libs/storage/Tsavorite/cs/test/test.hlog/LogReadAsyncTests.cs index 9e98ca88b5e..c9046ceec4f 100644 --- a/libs/storage/Tsavorite/cs/test/test.hlog/LogReadAsyncTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.hlog/LogReadAsyncTests.cs @@ -4,7 +4,6 @@ using System.Buffers; using System.IO; using System.Threading; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.hlog/LogRecordTests.cs b/libs/storage/Tsavorite/cs/test/test.hlog/LogRecordTests.cs index 3b41b90ccf3..5b5b27153e6 100644 --- a/libs/storage/Tsavorite/cs/test/test.hlog/LogRecordTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.hlog/LogRecordTests.cs @@ -3,7 +3,6 @@ using System; using System.Runtime.InteropServices; -using Garnet.test; using NUnit.Framework; using Tsavorite.core; using static Tsavorite.test.TestUtils; diff --git a/libs/storage/Tsavorite/cs/test/test.hlog/LogRecoverReadOnlyTests.cs b/libs/storage/Tsavorite/cs/test/test.hlog/LogRecoverReadOnlyTests.cs index b10ebfd071b..4edc9db2fa9 100644 --- a/libs/storage/Tsavorite/cs/test/test.hlog/LogRecoverReadOnlyTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.hlog/LogRecoverReadOnlyTests.cs @@ -6,7 +6,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.hlog/LogScanTests.cs b/libs/storage/Tsavorite/cs/test/test.hlog/LogScanTests.cs index 621f822a51b..832f8ae72f4 100644 --- a/libs/storage/Tsavorite/cs/test/test.hlog/LogScanTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.hlog/LogScanTests.cs @@ -4,7 +4,6 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.hlog/LogTests.cs b/libs/storage/Tsavorite/cs/test/test.hlog/LogTests.cs index 1a4a33c2480..3e0fa2d4c17 100644 --- a/libs/storage/Tsavorite/cs/test/test.hlog/LogTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.hlog/LogTests.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.hlog/ManagedLocalStorageTests.cs b/libs/storage/Tsavorite/cs/test/test.hlog/ManagedLocalStorageTests.cs index d6b0283c281..2c3ff120bd8 100644 --- a/libs/storage/Tsavorite/cs/test/test.hlog/ManagedLocalStorageTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.hlog/ManagedLocalStorageTests.cs @@ -3,7 +3,6 @@ using System.IO; using System.Threading; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.hlog/MoreLogCompactionTests.cs b/libs/storage/Tsavorite/cs/test/test.hlog/MoreLogCompactionTests.cs index 84cf8611390..33635c74572 100644 --- a/libs/storage/Tsavorite/cs/test/test.hlog/MoreLogCompactionTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.hlog/MoreLogCompactionTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. using System.IO; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.hlog/NativeAllocatorStoreTests.cs b/libs/storage/Tsavorite/cs/test/test.hlog/NativeAllocatorStoreTests.cs index 55022fe2cc3..099d95c2dbf 100644 --- a/libs/storage/Tsavorite/cs/test/test.hlog/NativeAllocatorStoreTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.hlog/NativeAllocatorStoreTests.cs @@ -4,7 +4,6 @@ using System; using System.IO; using System.Runtime.InteropServices; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.hlog/NativeHashIndexTests.cs b/libs/storage/Tsavorite/cs/test/test.hlog/NativeHashIndexTests.cs index dbd05bcd77e..8e086588906 100644 --- a/libs/storage/Tsavorite/cs/test/test.hlog/NativeHashIndexTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.hlog/NativeHashIndexTests.cs @@ -5,7 +5,6 @@ using System.IO; using System.Runtime.InteropServices; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.hlog/SpanByteIterationTests.cs b/libs/storage/Tsavorite/cs/test/test.hlog/SpanByteIterationTests.cs index e02424c705e..15542c2cdb9 100644 --- a/libs/storage/Tsavorite/cs/test/test.hlog/SpanByteIterationTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.hlog/SpanByteIterationTests.cs @@ -6,7 +6,6 @@ using System.IO; using System.Runtime.InteropServices; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.hlog/SpanByteLogCompactionTests.cs b/libs/storage/Tsavorite/cs/test/test.hlog/SpanByteLogCompactionTests.cs index 6c61ee02e83..a5d205a9321 100644 --- a/libs/storage/Tsavorite/cs/test/test.hlog/SpanByteLogCompactionTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.hlog/SpanByteLogCompactionTests.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics; using System.IO; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.hlog/SpanByteLogScanTests.cs b/libs/storage/Tsavorite/cs/test/test.hlog/SpanByteLogScanTests.cs index a7b7d57015a..28377306072 100644 --- a/libs/storage/Tsavorite/cs/test/test.hlog/SpanByteLogScanTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.hlog/SpanByteLogScanTests.cs @@ -5,7 +5,6 @@ using System.IO; using System.Runtime.InteropServices; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.hlog/SpanByteTests.cs b/libs/storage/Tsavorite/cs/test/test.hlog/SpanByteTests.cs index 040a079582d..0e54ad6d8f6 100644 --- a/libs/storage/Tsavorite/cs/test/test.hlog/SpanByteTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.hlog/SpanByteTests.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.hlog/SpanByteVLVectorTests.cs b/libs/storage/Tsavorite/cs/test/test.hlog/SpanByteVLVectorTests.cs index 1b69a70b5e3..fa85222a9b7 100644 --- a/libs/storage/Tsavorite/cs/test/test.hlog/SpanByteVLVectorTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.hlog/SpanByteVLVectorTests.cs @@ -4,7 +4,6 @@ using System; using System.IO; using System.Runtime.InteropServices; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.hlog/TsavoriteLogAddressRangeTests.cs b/libs/storage/Tsavorite/cs/test/test.hlog/TsavoriteLogAddressRangeTests.cs index 13855499547..863a209fcc4 100644 --- a/libs/storage/Tsavorite/cs/test/test.hlog/TsavoriteLogAddressRangeTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.hlog/TsavoriteLogAddressRangeTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. using System.IO; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recordops/CopyToTailTests.cs b/libs/storage/Tsavorite/cs/test/test.recordops/CopyToTailTests.cs index b6b8fbdef60..280a254be4b 100644 --- a/libs/storage/Tsavorite/cs/test/test.recordops/CopyToTailTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recordops/CopyToTailTests.cs @@ -4,7 +4,6 @@ using System; using System.Buffers; using System.IO; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recordops/DeleteDisposeTests.cs b/libs/storage/Tsavorite/cs/test/test.recordops/DeleteDisposeTests.cs index 21be8e306cb..64c0c5e7c23 100644 --- a/libs/storage/Tsavorite/cs/test/test.recordops/DeleteDisposeTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recordops/DeleteDisposeTests.cs @@ -3,7 +3,6 @@ using System.IO; using System.Threading; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recordops/ModifiedBitTests.cs b/libs/storage/Tsavorite/cs/test/test.recordops/ModifiedBitTests.cs index a9f2f5b6891..f92c72c5566 100644 --- a/libs/storage/Tsavorite/cs/test/test.recordops/ModifiedBitTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recordops/ModifiedBitTests.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recordops/ObjectIdMapTests.cs b/libs/storage/Tsavorite/cs/test/test.recordops/ObjectIdMapTests.cs index 7e73ef9e4ba..1033c09eb46 100644 --- a/libs/storage/Tsavorite/cs/test/test.recordops/ObjectIdMapTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recordops/ObjectIdMapTests.cs @@ -8,7 +8,6 @@ using System.Threading; #endif using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using Tsavorite.core; using static Tsavorite.test.TestUtils; diff --git a/libs/storage/Tsavorite/cs/test/test.recordops/ObjectInlineTests.cs b/libs/storage/Tsavorite/cs/test/test.recordops/ObjectInlineTests.cs index 2f737838cfb..f033ebe5789 100644 --- a/libs/storage/Tsavorite/cs/test/test.recordops/ObjectInlineTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recordops/ObjectInlineTests.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using Garnet.test; using NUnit.Framework; using Tsavorite.core; using static Tsavorite.test.TestUtils; diff --git a/libs/storage/Tsavorite/cs/test/test.recordops/ObjectTests.cs b/libs/storage/Tsavorite/cs/test/test.recordops/ObjectTests.cs index f5dfaa2fa17..da87f9c6b5b 100644 --- a/libs/storage/Tsavorite/cs/test/test.recordops/ObjectTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recordops/ObjectTests.cs @@ -5,7 +5,6 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recordops/RecordLifecycleTests.cs b/libs/storage/Tsavorite/cs/test/test.recordops/RecordLifecycleTests.cs index 4dfd14bff38..4670a4ff8f6 100644 --- a/libs/storage/Tsavorite/cs/test/test.recordops/RecordLifecycleTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recordops/RecordLifecycleTests.cs @@ -5,7 +5,6 @@ using System.Collections.Concurrent; using System.IO; using System.Threading; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recordops/RevivificationTests.cs b/libs/storage/Tsavorite/cs/test/test.recordops/RevivificationTests.cs index c5028c0f7ab..c40425eb989 100644 --- a/libs/storage/Tsavorite/cs/test/test.recordops/RevivificationTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recordops/RevivificationTests.cs @@ -9,7 +9,6 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/CheckpointManagerTests.cs b/libs/storage/Tsavorite/cs/test/test.recovery/CheckpointManagerTests.cs index ea6fcb02f08..ec821444cad 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/CheckpointManagerTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/CheckpointManagerTests.cs @@ -6,7 +6,6 @@ using System.IO; using System.Linq; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/CheckpointSharedObjectMutationTests.cs b/libs/storage/Tsavorite/cs/test/test.recovery/CheckpointSharedObjectMutationTests.cs index 125233bdbe5..0864b6be6ef 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/CheckpointSharedObjectMutationTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/CheckpointSharedObjectMutationTests.cs @@ -6,7 +6,6 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using Tsavorite.core; using static Tsavorite.test.TestUtils; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/ComponentRecoveryTests.cs b/libs/storage/Tsavorite/cs/test/test.recovery/ComponentRecoveryTests.cs index 2ef4c98818d..0dd86038ef8 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/ComponentRecoveryTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/ComponentRecoveryTests.cs @@ -5,7 +5,6 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/IndexCheckpointIoTests.cs b/libs/storage/Tsavorite/cs/test/test.recovery/IndexCheckpointIoTests.cs index f7c2cf98a2a..156e22ce919 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/IndexCheckpointIoTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/IndexCheckpointIoTests.cs @@ -8,7 +8,6 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/LargeObjectTests.cs b/libs/storage/Tsavorite/cs/test/test.recovery/LargeObjectTests.cs index 452f103cc87..c80d5c55688 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/LargeObjectTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/LargeObjectTests.cs @@ -4,7 +4,6 @@ using System; using System.IO; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using Tsavorite.core; using static Tsavorite.test.TestUtils; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/LogResumeTests.cs b/libs/storage/Tsavorite/cs/test/test.recovery/LogResumeTests.cs index b9cd0303031..175c0c1be82 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/LogResumeTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/LogResumeTests.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/NativeSnapshotStressTests.cs b/libs/storage/Tsavorite/cs/test/test.recovery/NativeSnapshotStressTests.cs index b5307cae785..406c088963e 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/NativeSnapshotStressTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/NativeSnapshotStressTests.cs @@ -5,7 +5,6 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/NoHybridLogTests.cs b/libs/storage/Tsavorite/cs/test/test.recovery/NoHybridLogTests.cs index 974d4cb9662..730b7d5dd06 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/NoHybridLogTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/NoHybridLogTests.cs @@ -3,7 +3,6 @@ using System.IO; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectAllocatorDeviceFailureTests.cs b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectAllocatorDeviceFailureTests.cs index a433542f6c5..49a37a00df8 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectAllocatorDeviceFailureTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectAllocatorDeviceFailureTests.cs @@ -5,7 +5,6 @@ using System.IO; using System.Reflection; using System.Threading; -using Garnet.test; using NUnit.Framework; using Tsavorite.core; using static Tsavorite.core.Utility; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectDiskDeleteTests.cs b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectDiskDeleteTests.cs index cae907334e2..ad744ee5d20 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectDiskDeleteTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectDiskDeleteTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. using System.IO; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectIterationTests.cs b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectIterationTests.cs index 9abbac76026..ad82c7e01d6 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectIterationTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectIterationTests.cs @@ -6,7 +6,6 @@ using System.IO; using System.Runtime.InteropServices; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectLogCheckpointTransferTests.cs b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectLogCheckpointTransferTests.cs index 26e3c302f2c..b22c1021303 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectLogCheckpointTransferTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectLogCheckpointTransferTests.cs @@ -5,7 +5,6 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectLogCompactionTests.cs b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectLogCompactionTests.cs index 4a899e9127b..358263c79f2 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectLogCompactionTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectLogCompactionTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. using System.IO; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectLogScanTests.cs b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectLogScanTests.cs index 0a78fb37660..c46cc3683fa 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectLogScanTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectLogScanTests.cs @@ -5,7 +5,6 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectReadCacheTests.cs b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectReadCacheTests.cs index 246b8c3f474..8a87ca3fa53 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectReadCacheTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectReadCacheTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. using System.IO; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectRecoverySnapshotEvictionTests.cs b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectRecoverySnapshotEvictionTests.cs index 55e872a7cf5..6134cf2b642 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectRecoverySnapshotEvictionTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectRecoverySnapshotEvictionTests.cs @@ -3,7 +3,6 @@ using System.IO; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectRecoveryTest.cs b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectRecoveryTest.cs index 63e5d9cf6a8..fe000289189 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectRecoveryTest.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectRecoveryTest.cs @@ -4,7 +4,6 @@ using System; using System.IO; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using Tsavorite.core; using Tsavorite.test.recovery.sumstore; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectRecoveryTest2.cs b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectRecoveryTest2.cs index c77171d8be1..b1a4bc804ff 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectRecoveryTest2.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectRecoveryTest2.cs @@ -4,7 +4,6 @@ using System; using System.IO; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectRecoveryTest3.cs b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectRecoveryTest3.cs index 905834b63c0..0c2721be622 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/ObjectRecoveryTest3.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/ObjectRecoveryTest3.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/RecoverReadOnlyTest.cs b/libs/storage/Tsavorite/cs/test/test.recovery/RecoverReadOnlyTest.cs index 845716f15ee..79ee9e5f9d1 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/RecoverReadOnlyTest.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/RecoverReadOnlyTest.cs @@ -6,7 +6,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/RecoverToLowerMemoryTests.cs b/libs/storage/Tsavorite/cs/test/test.recovery/RecoverToLowerMemoryTests.cs index 56e22d32c3f..d2e92b41879 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/RecoverToLowerMemoryTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/RecoverToLowerMemoryTests.cs @@ -5,7 +5,6 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/RecoveryCheckTests.cs b/libs/storage/Tsavorite/cs/test/test.recovery/RecoveryCheckTests.cs index b84e93c1d80..37c7098f189 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/RecoveryCheckTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/RecoveryCheckTests.cs @@ -4,7 +4,6 @@ using System; using System.IO; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/RecoveryTests.cs b/libs/storage/Tsavorite/cs/test/test.recovery/RecoveryTests.cs index 378680bf65c..799f02ca305 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/RecoveryTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/RecoveryTests.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.recovery/SimpleRecoveryTest.cs b/libs/storage/Tsavorite/cs/test/test.recovery/SimpleRecoveryTest.cs index 9359b4dd650..a999bba73e0 100644 --- a/libs/storage/Tsavorite/cs/test/test.recovery/SimpleRecoveryTest.cs +++ b/libs/storage/Tsavorite/cs/test/test.recovery/SimpleRecoveryTest.cs @@ -7,7 +7,6 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using Garnet.test; using Microsoft.Extensions.Logging; using NUnit.Framework; using NUnit.Framework.Legacy; diff --git a/libs/storage/Tsavorite/cs/test/test.session.context/FunctionPerSessionTests.cs b/libs/storage/Tsavorite/cs/test/test.session.context/FunctionPerSessionTests.cs index 50257277c1d..51fc2e5869e 100644 --- a/libs/storage/Tsavorite/cs/test/test.session.context/FunctionPerSessionTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.session.context/FunctionPerSessionTests.cs @@ -4,7 +4,6 @@ using System.IO; using System.Runtime.InteropServices; using System.Threading; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.session.context/SessionTests.cs b/libs/storage/Tsavorite/cs/test/test.session.context/SessionTests.cs index 916b28cb4cd..4ef2b22860e 100644 --- a/libs/storage/Tsavorite/cs/test/test.session.context/SessionTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.session.context/SessionTests.cs @@ -3,7 +3,6 @@ using System.IO; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.session.context/TransactionalUnsafeContextTests.cs b/libs/storage/Tsavorite/cs/test/test.session.context/TransactionalUnsafeContextTests.cs index a5e19d05ec0..ccbcfa70b84 100644 --- a/libs/storage/Tsavorite/cs/test/test.session.context/TransactionalUnsafeContextTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.session.context/TransactionalUnsafeContextTests.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.session.context/UnsafeContextTests.cs b/libs/storage/Tsavorite/cs/test/test.session.context/UnsafeContextTests.cs index d2af1e96f9e..a410f21e4ee 100644 --- a/libs/storage/Tsavorite/cs/test/test.session.context/UnsafeContextTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.session.context/UnsafeContextTests.cs @@ -6,7 +6,6 @@ using System.IO; using System.Linq; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.session/NativeReadCacheTests.cs b/libs/storage/Tsavorite/cs/test/test.session/NativeReadCacheTests.cs index 6e000cb087a..0a64ed1d678 100644 --- a/libs/storage/Tsavorite/cs/test/test.session/NativeReadCacheTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.session/NativeReadCacheTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. using System.IO; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.session/RandomReadCacheTests.cs b/libs/storage/Tsavorite/cs/test/test.session/RandomReadCacheTests.cs index e53b12f1ddf..95dfe15a988 100644 --- a/libs/storage/Tsavorite/cs/test/test.session/RandomReadCacheTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.session/RandomReadCacheTests.cs @@ -7,7 +7,6 @@ using System.IO; using System.Runtime.InteropServices; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.session/ReadAddressTests.cs b/libs/storage/Tsavorite/cs/test/test.session/ReadAddressTests.cs index 6489e82c31d..efdfdd51fda 100644 --- a/libs/storage/Tsavorite/cs/test/test.session/ReadAddressTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.session/ReadAddressTests.cs @@ -6,7 +6,6 @@ using System.IO; using System.Runtime.InteropServices; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.session/ReadCacheChainTests.cs b/libs/storage/Tsavorite/cs/test/test.session/ReadCacheChainTests.cs index 7c298c4bc14..d2019b71085 100644 --- a/libs/storage/Tsavorite/cs/test/test.session/ReadCacheChainTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.session/ReadCacheChainTests.cs @@ -6,7 +6,6 @@ using System.IO; using System.Linq; using System.Threading.Tasks; -using Garnet.test; using NUnit.Framework; using NUnit.Framework.Legacy; using Tsavorite.core; diff --git a/libs/storage/Tsavorite/cs/test/test.stress/ReadCacheStressTests.cs b/libs/storage/Tsavorite/cs/test/test.stress/ReadCacheStressTests.cs index 5efbc154eee..d1b927c579e 100644 --- a/libs/storage/Tsavorite/cs/test/test.stress/ReadCacheStressTests.cs +++ b/libs/storage/Tsavorite/cs/test/test.stress/ReadCacheStressTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -using Garnet.test; using NUnit.Framework; using static Tsavorite.test.TestUtils; diff --git a/test/cluster/Directory.Build.props b/test/cluster/Directory.Build.props index 8df574cf7f0..33bb6af95a4 100644 --- a/test/cluster/Directory.Build.props +++ b/test/cluster/Directory.Build.props @@ -3,4 +3,10 @@ $(MSBuildThisFileDirectory)garnet-cluster.runsettings + + + + diff --git a/test/cluster/Garnet.test.cluster/Garnet.test.cluster.csproj b/test/cluster/Garnet.test.cluster/Garnet.test.cluster.csproj index 8a2aca2ad94..aae5dc4393b 100644 --- a/test/cluster/Garnet.test.cluster/Garnet.test.cluster.csproj +++ b/test/cluster/Garnet.test.cluster/Garnet.test.cluster.csproj @@ -8,8 +8,6 @@ 1701;1702;1591 - - $(DefineConstants);GARNET_TEST_UTILS @@ -28,7 +26,6 @@ - @@ -59,6 +56,7 @@ + diff --git a/test/cluster/Garnet.test.cluster/TestProjectSetup.cs b/test/cluster/Garnet.test.cluster/TestProjectSetup.cs new file mode 100644 index 00000000000..6623f5d2d4f --- /dev/null +++ b/test/cluster/Garnet.test.cluster/TestProjectSetup.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +using NUnit.Framework; + +namespace Garnet.test +{ + /// + /// Per-assembly setup. Forces the port slot to resolve before any test runs: this project uses the default + /// port assignment and so never calls , which is what + /// otherwise resolves the slot. It also installs the unhandled-exception handlers, because NUnit runs a + /// SetUpFixture only for the assembly under test and TestBase now lives in a referenced assembly. + /// The fixture sits in Garnet.test so that it covers the nested Garnet.test.cluster namespace + /// holding this project's tests. + /// + [SetUpFixture] + public class ClusterTestProjectSetup + { + [OneTimeSetUp] + public void SetUpProject() + { + TestBase.InstallUnhandledExceptionHandlers(); + TestUtils.EnsurePortSlotResolved(); + } + } +} \ No newline at end of file diff --git a/test/standalone/Directory.Build.props b/test/standalone/Directory.Build.props index be2ee3bdc8c..535547436a5 100644 --- a/test/standalone/Directory.Build.props +++ b/test/standalone/Directory.Build.props @@ -3,4 +3,10 @@ $(MSBuildThisFileDirectory)garnet.runsettings + + + + diff --git a/test/standalone/Garnet.test/Garnet.test.csproj b/test/standalone/Garnet.test/Garnet.test.csproj index abafb967d8f..5e1486add51 100644 --- a/test/standalone/Garnet.test/Garnet.test.csproj +++ b/test/standalone/Garnet.test/Garnet.test.csproj @@ -8,8 +8,6 @@ 1701;1702;1591 - - $(DefineConstants);GARNET_TEST_UTILS @@ -64,6 +62,7 @@ + diff --git a/test/standalone/Garnet.test/MaintenanceLoopTests.cs b/test/standalone/Garnet.test/MaintenanceLoopTests.cs index ead59906cbf..1a301da5bc4 100644 --- a/test/standalone/Garnet.test/MaintenanceLoopTests.cs +++ b/test/standalone/Garnet.test/MaintenanceLoopTests.cs @@ -133,6 +133,36 @@ public async Task ACycleCancelledDuringShutdownIsNotLoggedAsAFailure() Assert.That(logger.Entries, Is.Empty, "a cycle cancelled by shutdown must not be logged as a failure"); } + /// + /// A cycle can fail for its own reasons at the same moment shutdown begins. That is still an ordinary + /// cycle failure and must not be reported as the terminal, task-ending fault, which is reserved for a + /// fault in the loop itself. + /// + [Test] + public async Task ACycleFailingAsShutdownBeginsIsNotLoggedAsTerminal() + { + using var cts = new CancellationTokenSource(); + var logger = new RecordingLogger(); + + var loop = StoreWrapper.RunMaintenanceLoopAsync(async (_) => + { + // Cancel before throwing so cancellation is always already requested when the failure is + // classified, making the window deterministic rather than timing-dependent. + await cts.CancelAsync(); + throw new InvalidOperationException("injected failure concurrent with shutdown"); + }, Interval, "test task", logger, cts.Token); + + Assert.That(await Task.WhenAny(loop, Task.Delay(WaitTimeout)), Is.SameAs(loop), "the loop did not end after cancellation"); + await loop; + + Assert.Multiple(() => + { + Assert.That(loop.IsCompletedSuccessfully, Is.True, "cancellation must not fault the task"); + Assert.That(logger.Entries, Has.None.Matches(e => e.Level == LogLevel.Critical), + "a cycle failure must never be reported as terminal"); + }); + } + readonly record struct LogEntry(LogLevel Level, Exception Exception); sealed class RecordingLogger : ILogger diff --git a/test/standalone/Garnet.test/PortSlotTests.cs b/test/standalone/Garnet.test/PortSlotTests.cs index 9e4548039c6..b5f71de83d8 100644 --- a/test/standalone/Garnet.test/PortSlotTests.cs +++ b/test/standalone/Garnet.test/PortSlotTests.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using System.Net; +using System.Net.Sockets; using NUnit.Framework; using NUnit.Framework.Legacy; @@ -125,5 +126,161 @@ public void SlotStrideIsNotADistanceBetweenAssignments() } } } + + /// + /// A slot reserves one port per standalone assignment plus the cluster node range, so the probe must + /// enumerate all of them. This is the test that fails when a new assignment is added and the probe stops + /// covering the ports the projects actually bind. + /// + [Test] + public void SlotPortsCoverEveryReservedPort() + { + const int slot = TestUtils.MaxPortSlot; + var offset = slot * TestUtils.PortSlotStride; + var ports = TestUtils.SlotPorts(slot).ToHashSet(); + + foreach (var assignment in Enum.GetValues()) + { + Assert.That(ports, Does.Contain((int)assignment + offset), + $"Slot {slot} hands {assignment} a port the claim-time probe never checks, so a server " + + $"stranded on it would not stop the slot from being claimed."); + } + + for (var node = 0; node < TestUtils.MaxClusterNodesPerSubProject; node++) + { + Assert.That(ports, Does.Contain(TestUtils.ClusterPortBandBase + offset + node), + $"Cluster node {node} of slot {slot} is not probed, so a node stranded by a crashed run " + + $"would read as free."); + } + } + + /// + /// A server stranded on a non-default assignment has to stop auto from taking that slot, rather + /// than being discovered later when the owning project binds. GarnetTestAlternate is occupied because + /// this assembly is its only user and NUnit runs these fixtures sequentially, so the bind cannot + /// disturb a sibling project sharing the slot. + /// + [Test] + public void OccupiedNonDefaultAssignmentBlocksTheSlot() + => AssertOccupiedPortBlocksTheSlot(IPAddress.Loopback); + + /// + /// Tests bind every address returns, not just the loopbacks, + /// so a server stranded on one of those has to block the slot as well. + /// + [Test] + public void OccupiedNonLoopbackAddressBlocksTheSlot() + { + var address = NonLoopbackAddress(); + if (address is null) + Assert.Ignore("This host has no non-loopback IPv4 address to occupy."); + + AssertOccupiedPortBlocksTheSlot(address); + } + + /// + /// The IPv4 wildcard cannot see an IPv6 listener, so this is the case that fails when the probe stops + /// binding both families. Tests reach the IPv6 loopback through GarnetServerTcpTests and the + /// multi-endpoint configuration tests. + /// + [Test] + public void OccupiedIPv6LoopbackBlocksTheSlot() + { + if (!Socket.OSSupportsIPv6) + Assert.Ignore("This host has IPv6 disabled, so no test can strand a listener there."); + + AssertOccupiedPortBlocksTheSlot(IPAddress.IPv6Loopback); + } + + /// + /// A stranded server has to be seen wherever it listens, which is any of the four kinds of address the + /// tests bind. The port is one the OS hands out for this listener, so the case is exact and unaffected + /// by other test projects running in parallel. + /// + /// Which of the bound address kinds to strand the listener on. + [TestCase(BoundAddressKind.Loopback)] + [TestCase(BoundAddressKind.IPv6Loopback)] + [TestCase(BoundAddressKind.NonLoopback)] + [TestCase(BoundAddressKind.Wildcard)] + public void ListenerIsDetectedOnEveryBoundAddressKind(BoundAddressKind addressKind) + { + var address = ResolveAddress(addressKind); + if (address is null) + Assert.Ignore($"This host cannot bind {addressKind}."); + + var listener = new TcpListener(address, 0); + + try + { + listener.Start(); + var port = ((IPEndPoint)listener.LocalEndpoint).Port; + + Assert.That(TestUtils.IsPortFree(port), Is.False, + $"A listener on {address}:{port} reads as free, so a server stranded there would not stop " + + $"auto from claiming the slot that hands out that port."); + } + finally + { + listener.Stop(); + } + } + + /// + /// The kinds of address Garnet tests bind, which the probe has to cover. + /// + public enum BoundAddressKind + { + /// 127.0.0.1, the default behind . + Loopback, + + /// ::1, bound by GarnetServerTcpTests and the multi-endpoint configuration tests. + IPv6Loopback, + + /// A routable address, bound by the multi-endpoint configuration tests. + NonLoopback, + + /// 0.0.0.0, bound by the cluster tests. + Wildcard, + } + + private static IPAddress ResolveAddress(BoundAddressKind addressKind) => addressKind switch + { + BoundAddressKind.Loopback => IPAddress.Loopback, + BoundAddressKind.IPv6Loopback => Socket.OSSupportsIPv6 ? IPAddress.IPv6Loopback : null, + BoundAddressKind.NonLoopback => NonLoopbackAddress(), + _ => IPAddress.Any, + }; + + private static IPAddress NonLoopbackAddress() + => Dns.GetHostAddresses(TestUtils.GetHostName()) + .FirstOrDefault(a => !IPAddress.IsLoopback(a) && a.AddressFamily == AddressFamily.InterNetwork); + + /// + /// Occupies this slot's GarnetTestAlternate port on one address and asserts the slot stops reading as + /// free. The listener is always released, so a failed assertion cannot strand one on a shared machine. + /// + /// Address to hold the port on. + private static void AssertOccupiedPortBlocksTheSlot(IPAddress address) + { + var slot = TestUtils.PortOffset / TestUtils.PortSlotStride; + if (!TestUtils.ArePortsFree(slot)) + Assert.Ignore($"Slot {slot} already has ports in use, so blocking it proves nothing."); + + var port = TestUtils.GetTestPort(TestPortAssignment.GarnetTestAlternate); + var listener = new TcpListener(address, port); + + try + { + listener.Start(); + + Assert.That(TestUtils.ArePortsFree(slot), Is.False, + $"Port {port} ({TestPortAssignment.GarnetTestAlternate}) is occupied on {address}, but slot " + + $"{slot} still reads as free, so auto would claim a slot a project cannot bind."); + } + finally + { + listener.Stop(); + } + } } } \ No newline at end of file diff --git a/test/standalone/Garnet.test/TestBase.cs b/test/standalone/Garnet.test/TestBase.cs deleted file mode 100644 index 42099401d8e..00000000000 --- a/test/standalone/Garnet.test/TestBase.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using System; -using System.Collections.Concurrent; -using System.Diagnostics; -using System.Text; -using System.Threading.Tasks; -using NUnit.Framework; - -namespace Garnet.test -{ - /// - /// Base class for tests — tracks currently running tests for diagnostics. - /// - public abstract class TestBase - { - // Thread-safe collection to store currently running tests - public static readonly ConcurrentDictionary RunningTests = new(); - - [SetUp] - public void TrackRunningTest() - { - // Add test to the running list - RunningTests[TestContext.CurrentContext.Test.Name] = true; - - if (TestContext.CurrentContext.CurrentRepeatCount > 0) - Debug.WriteLine($"*** Current test iteration {TestContext.CurrentContext.CurrentRepeatCount + 1}: {TestContext.CurrentContext.Test.Name} ***"); - } - - [TearDown] - public void RemoveRunningTest() - { - Assert.That(RunningTests.TryRemove(TestContext.CurrentContext.Test.Name, out _), Is.True, $"Could not find running test {TestContext.CurrentContext.Test.Name}"); - } - } -} - -[SetUpFixture] -public sealed class GlobalUnhandledExceptionHandling -{ - [OneTimeSetUp] - public void Install() - { -#if GARNET_TEST_UTILS - // Resolve the port slot before any test runs. Garnet.test and Garnet.test.cluster use the static port - // defaults rather than a [SetUpFixture] of their own, so this is where they claim their slot. - // This file is also linked into Tsavorite.test, which does not compile TestUtils and does not bind - // Garnet ports, so the call is compiled only where TestUtils is available. - Garnet.test.TestUtils.EnsurePortSlotResolved(); -#endif - - AppDomain.CurrentDomain.UnhandledException += (s, e) => - { - DumpTests(); - }; - - TaskScheduler.UnobservedTaskException += (s, e) => - { - DumpTests(); - e.SetObserved(); // Optionally mark observed so it doesn't escalate later - }; - - static void DumpTests() - { - if (Garnet.test.TestBase.RunningTests.Count == 0) - return; - var sb = new StringBuilder(); - _ = sb.AppendLine("*** CURRENTLY RUNNING TESTS ***:"); - foreach (var key in Garnet.test.TestBase.RunningTests.Keys) - _ = sb.AppendLine(key); - Console.WriteLine(sb.ToString()); - } - } -} \ No newline at end of file diff --git a/test/standalone/Garnet.test/TestProjectSetup.cs b/test/standalone/Garnet.test/TestProjectSetup.cs new file mode 100644 index 00000000000..7cfd90d6d39 --- /dev/null +++ b/test/standalone/Garnet.test/TestProjectSetup.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +using NUnit.Framework; + +namespace Garnet.test +{ + /// + /// Per-assembly setup. Forces the port slot to resolve before any test runs: this project uses the default + /// port assignment and so never calls , which is what otherwise resolves + /// the slot. It also installs the unhandled-exception handlers, because NUnit runs a SetUpFixture only for + /// the assembly under test and TestBase now lives in a referenced assembly. + /// + [SetUpFixture] + public class TestProjectSetup + { + [OneTimeSetUp] + public void SetUpProject() + { + TestBase.InstallUnhandledExceptionHandlers(); + TestUtils.EnsurePortSlotResolved(); + } + } +} \ No newline at end of file diff --git a/test/standalone/Garnet.test/TestUtils.cs b/test/standalone/Garnet.test/TestUtils.cs index 9c8bfcd1969..92d8a4fd7c8 100644 --- a/test/standalone/Garnet.test/TestUtils.cs +++ b/test/standalone/Garnet.test/TestUtils.cs @@ -202,17 +202,27 @@ public static void SetTestPort(TestPortAssignment port) } /// - /// Forces the port slot to be resolved now. Projects that rely on the static port defaults have no - /// [SetUpFixture] calling , and the remaining members read only - /// constants, so without this the slot would be claimed lazily and any failure would surface from - /// whichever test first touched a port. See for what a failure does to the run. + /// Forces the port slot to be resolved now, at the point of this call. + /// + /// The slot is claimed by the field initializer, which the CLR runs as part of + /// this class's static constructor the first time anything touches the class. RunClassConstructor + /// is the mechanism: it runs that static constructor on demand, so + /// executes here rather than at some arbitrary later point. Nothing else pulls it forward — projects + /// that rely on the static port defaults have no [SetUpFixture] calling + /// , and the remaining members read only constants — so without this the slot + /// would be claimed lazily and any failure would surface from whichever test first touched a port. See + /// for what a failure does to the run. + /// + /// /// The underlying exception is rethrown in place of the type initializer wrapper, so the actionable /// message is the one reported rather than "the type initializer threw an exception". + /// /// internal static void EnsurePortSlotResolved() { try { + // Runs the static constructor, evaluating the PortOffset initializer and claiming the slot. RuntimeHelpers.RunClassConstructor(typeof(TestUtils).TypeHandle); } catch (TypeInitializationException e) when (e.InnerException is not null) @@ -386,28 +396,37 @@ private static int ClaimCheckoutPortSlot() } /// - /// Probes the ports a slot hands out. A server stranded by a crashed run holds no slot lock at all, so - /// binding is the authority on whether a slot is actually usable. The cluster band is probed across its - /// node range rather than at the base alone: a run can die leaving a non-base node listening while its - /// base node is already disposed, which a base-only probe would read as free. + /// The ports a slot reserves. Enumerated from rather than listed here, + /// so every assignment a project can bind is probed at claim time and a new one is covered without a + /// second edit: claim time and bind time have to see the same set of ports. + /// + /// Standalone assignments are probed at their base alone because no standalone project binds past it; + /// one that starts to must widen this. The cluster band is probed across its whole node range, because + /// a run can die leaving a non-base node listening while its base node is disposed, which a base-only + /// probe would read as free. + /// /// - /// Slot to probe. - /// True when every port probed for the slot is free. - private static bool ArePortsFree(int slot) + /// Slot whose ports to enumerate. + /// Every port the slot hands out. + internal static IEnumerable SlotPorts(int slot) { var offset = slot * PortSlotStride; - if (!IsPortFree((int)TestPortAssignment.GarnetTest + offset)) - return false; - for (var node = 0; node < MaxClusterNodesPerSubProject; node++) - { - if (!IsPortFree(ClusterPortBandBase + offset + node)) - return false; - } + foreach (var assignment in Enum.GetValues()) + yield return (int)assignment + offset; - return true; + for (var node = 0; node < MaxClusterNodesPerSubProject; node++) + yield return ClusterPortBandBase + offset + node; } + /// + /// Probes the ports a slot hands out. A server stranded by a crashed run holds no slot lock at all, so + /// binding is the authority on whether a slot is actually usable. + /// + /// Slot to probe. + /// True when every port the slot reserves is free. + internal static bool ArePortsFree(int slot) => SlotPorts(slot).All(IsPortFree); + /// /// Records the owning checkout and registers this process as a holder of the slot. /// @@ -555,15 +574,35 @@ private static string GetCheckoutKey() } /// - /// Reports whether a port can currently be bound. + /// Reports whether a port can currently be bound. Both wildcards are probed, and exclusively, so that a + /// listener is detected wherever it sits: tests bind the IPv4 and IPv6 loopbacks, every address + /// returns, and , and a wildcard + /// bind conflicts with a specific-address one only when it asks for exclusive use. + /// + /// Each family is bound separately rather than through one dual-mode socket, whose handling of + /// IPv4-mapped addresses is platform-dependent. covers IPv4 alone, so a + /// listener on needs the second bind to be seen. + /// /// /// Port to test. /// True when the port is free. - private static bool IsPortFree(int port) + internal static bool IsPortFree(int port) + => CanBindExclusively(IPAddress.Any, port) + && (!Socket.OSSupportsIPv6 || CanBindExclusively(IPAddress.IPv6Any, port)); + + /// + /// Binds one address exclusively and releases it again. Exclusivity is what makes this a probe: a shared + /// bind succeeds alongside a listener on a specific address under that wildcard and would report the + /// port free. A port in TIME_WAIT is still reported free, since no listener can be there. + /// + /// Address to bind, normally a wildcard. + /// Port to bind. + /// True when the bind succeeded. + private static bool CanBindExclusively(IPAddress address, int port) { try { - var listener = new TcpListener(IPAddress.Loopback, port); + var listener = new TcpListener(address, port) { ExclusiveAddressUse = true }; listener.Start(); listener.Stop(); return true;