diff --git a/Remoting.sln b/Remoting.sln index df83dca..0494eb9 100644 --- a/Remoting.sln +++ b/Remoting.sln @@ -19,6 +19,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution LICENSE = LICENSE NuGet.Config = NuGet.Config README.md = README.md + testEnvironments.json = testEnvironments.json .github\workflows\windows.yml = .github\workflows\windows.yml EndProjectSection EndProject @@ -32,7 +33,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Menees.Remoting.Tests", "te EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Menees.Remoting.TestHost", "tests\Menees.Remoting.TestHost\Menees.Remoting.TestHost.csproj", "{A454B747-CAAA-4F97-89D7-996381D5E25C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Menees.Remoting.TestClient", "tests\Menees.Remoting.TestClient\Menees.Remoting.TestClient.csproj", "{A1DEDBCB-2AA9-4288-8427-9EF357BAF663}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Menees.Remoting.TestClient", "tests\Menees.Remoting.TestClient\Menees.Remoting.TestClient.csproj", "{A1DEDBCB-2AA9-4288-8427-9EF357BAF663}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/Directory.Build.props b/src/Directory.Build.props index d8fa513..a8f6c4d 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -16,7 +16,8 @@ - net48 + + net8.0 $(MeneesTargetNetCoreBase);$(MeneesTargetNetFramework) @@ -81,7 +82,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Menees.Remoting/ClientProxy.cs b/src/Menees.Remoting/ClientProxy.cs index 0067db2..a0a2fa7 100644 --- a/src/Menees.Remoting/ClientProxy.cs +++ b/src/Menees.Remoting/ClientProxy.cs @@ -74,10 +74,7 @@ internal void Initialize(RmiClient client) throw new InvalidOperationException("Client proxy was not initialized."); } - if (targetMethod == null) - { - throw new ArgumentNullException(nameof(targetMethod)); - } + ArgumentNullException.ThrowIfNull(targetMethod); // This requires a synchronous call from the client to avoid deadlocks since DispatchProxy.Invoke is synchronous. object? result = this.client.Invoke(targetMethod, args ?? []); diff --git a/src/Menees.Remoting/Menees.Remoting.csproj b/src/Menees.Remoting/Menees.Remoting.csproj index 9b740a9..1752c80 100644 --- a/src/Menees.Remoting/Menees.Remoting.csproj +++ b/src/Menees.Remoting/Menees.Remoting.csproj @@ -16,8 +16,8 @@ - - + + diff --git a/src/Menees.Remoting/MessageClient.cs b/src/Menees.Remoting/MessageClient.cs index 0b281fa..018043e 100644 --- a/src/Menees.Remoting/MessageClient.cs +++ b/src/Menees.Remoting/MessageClient.cs @@ -51,10 +51,7 @@ public MessageClient( public MessageClient(ClientSettings settings) : base(settings) { - if (settings == null) - { - throw new ArgumentNullException(nameof(settings)); - } + ArgumentNullException.ThrowIfNull(settings); this.ConnectTimeout = settings.ConnectTimeout; this.pipe = new(settings.ServerPath, settings.ServerHost, this, (PipeClientSecurity?)settings.Security); diff --git a/src/Menees.Remoting/MessageServer.cs b/src/Menees.Remoting/MessageServer.cs index 77bc6b5..90509f7 100644 --- a/src/Menees.Remoting/MessageServer.cs +++ b/src/Menees.Remoting/MessageServer.cs @@ -105,10 +105,7 @@ public MessageServer(Func> requestHandler, ServerSettings settin public MessageServer(Func> requestHandler, ServerSettings settings) : base(settings) { - if (settings == null) - { - throw new ArgumentNullException(nameof(settings)); - } + ArgumentNullException.ThrowIfNull(settings); this.requestHandler = requestHandler ?? throw new ArgumentNullException(nameof(requestHandler)); diff --git a/src/Menees.Remoting/Node.cs b/src/Menees.Remoting/Node.cs index 03f8b15..0a00189 100644 --- a/src/Menees.Remoting/Node.cs +++ b/src/Menees.Remoting/Node.cs @@ -117,6 +117,7 @@ protected virtual void Dispose(bool disposing) #region Private Types private sealed class ScopedLogger : ILogger + where TScope : notnull { #region Private Data Members @@ -137,7 +138,9 @@ public ScopedLogger(ILogger logger, TScope scope) #region Public Methods - public IDisposable BeginScope(TState state) => this.logger.BeginScope(state); + public IDisposable? BeginScope(TState state) + where TState : notnull + => this.logger.BeginScope(state); public bool IsEnabled(LogLevel logLevel) => this.logger.IsEnabled(logLevel); @@ -150,7 +153,7 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except // call context wrapped around one or more local ILogger.Log calls with no intervening Task.Runs or awaits. // To avoid having to do BeginScope around all our Log calls, it's easier to make this wrapper class that does it. // https://stackoverflow.com/questions/63851259/since-iloggert-is-a-singleton-how-different-threads-can-use-beginscope-with#comment128022925_63852241 - using IDisposable logScope = this.BeginScope(this.scope); + using IDisposable? logScope = this.BeginScope(this.scope); this.logger.Log(logLevel, eventId, state, exception, formatter); } diff --git a/src/Menees.Remoting/RmiClient.cs b/src/Menees.Remoting/RmiClient.cs index f29d976..740dc5b 100644 --- a/src/Menees.Remoting/RmiClient.cs +++ b/src/Menees.Remoting/RmiClient.cs @@ -51,10 +51,7 @@ public RmiClient( public RmiClient(ClientSettings settings) : base(settings) { - if (settings == null) - { - throw new ArgumentNullException(nameof(settings)); - } + ArgumentNullException.ThrowIfNull(settings); this.ConnectTimeout = settings.ConnectTimeout; this.pipe = new(settings.ServerPath, settings.ServerHost, this, (PipeClientSecurity?)settings.Security); diff --git a/src/Menees.Remoting/RmiServer.cs b/src/Menees.Remoting/RmiServer.cs index ca99eac..26cd291 100644 --- a/src/Menees.Remoting/RmiServer.cs +++ b/src/Menees.Remoting/RmiServer.cs @@ -63,10 +63,7 @@ public RmiServer( public RmiServer(TServiceInterface serviceInstance, ServerSettings settings) : base(settings) { - if (settings == null) - { - throw new ArgumentNullException(nameof(settings)); - } + ArgumentNullException.ThrowIfNull(settings); this.serviceInstance = serviceInstance ?? throw new ArgumentNullException(nameof(serviceInstance)); diff --git a/src/Menees.Remoting/ServerHost.cs b/src/Menees.Remoting/ServerHost.cs index 5383b3b..4b44ad7 100644 --- a/src/Menees.Remoting/ServerHost.cs +++ b/src/Menees.Remoting/ServerHost.cs @@ -94,10 +94,7 @@ public void Dispose() /// If has started already. public void Add(IServer server) { - if (server == null) - { - throw new ArgumentNullException(nameof(server)); - } + ArgumentNullException.ThrowIfNull(server); this.EnsureReady(); @@ -209,10 +206,7 @@ private void Dispose(bool disposing) private void EnsureReady([CallerMemberName] string? callerMemberName = null) { - if (this.isDisposed) - { - throw new ObjectDisposedException(nameof(ServerHost)); - } + ObjectDisposedException.ThrowIf(this.isDisposed, this); if (this.isExiting) { diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index aa7e7a1..ada3dfa 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -14,13 +14,11 @@ - - - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + \ No newline at end of file diff --git a/tests/Menees.Remoting.TestHost/Menees.Remoting.TestHost.csproj b/tests/Menees.Remoting.TestHost/Menees.Remoting.TestHost.csproj index 3046401..f1604f3 100644 --- a/tests/Menees.Remoting.TestHost/Menees.Remoting.TestHost.csproj +++ b/tests/Menees.Remoting.TestHost/Menees.Remoting.TestHost.csproj @@ -11,8 +11,8 @@ - - - + + + diff --git a/tests/Menees.Remoting.Tests/BaseTests.cs b/tests/Menees.Remoting.Tests/BaseTests.cs index e7c53c8..452a77d 100644 --- a/tests/Menees.Remoting.Tests/BaseTests.cs +++ b/tests/Menees.Remoting.Tests/BaseTests.cs @@ -11,7 +11,9 @@ #endregion [TestClass] +#pragma warning disable MSTEST0016 // Test class should have test method. This is used as a base class with init and cleanup. public class BaseTests +#pragma warning restore MSTEST0016 // Test class should have test method { #region Private Data Members @@ -70,10 +72,7 @@ protected static Task TestCrossProcessClientAsync(int clientCount, string server protected string GenerateServerPath([CallerMemberName] string? callerMemberName = null) { - if (callerMemberName == null) - { - throw new ArgumentNullException(nameof(callerMemberName)); - } + ArgumentNullException.ThrowIfNull(callerMemberName); string result = $"{this.GetType().FullName}.{callerMemberName}"; return result; diff --git a/tests/Menees.Remoting.Tests/RmiServerTests.cs b/tests/Menees.Remoting.Tests/RmiServerTests.cs index 138a3ce..261208a 100644 --- a/tests/Menees.Remoting.Tests/RmiServerTests.cs +++ b/tests/Menees.Remoting.Tests/RmiServerTests.cs @@ -66,6 +66,13 @@ public void CloneString() actual.ShouldBe(expected); } + [TestMethod] + public void OneShot() + { + // Run 1 client with a single server listener. + this.TestClient(1, 1, 1); + } + [TestMethod] public void SingleServer() { @@ -222,7 +229,7 @@ private void TestClient( // Make sure all the servers have completely exited in case another TestClient // starts up immediately using the same serverPath. We don't want a new client - // to race in an grab an old server listener just as its shutting down. + // to race in and grab an old server listener just as its shutting down. ((IServerHost)host).Exit(); host.WaitForExit(); } @@ -230,9 +237,11 @@ private void TestClient( private void TestClient(int clientCount, string serverPath, ClientSecurity? clientSecurity = null) { TimeSpan timeout = Debugger.IsAttached ? Timeout.InfiniteTimeSpan : ClientSettings.DefaultConnectTimeout; + ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = Math.Min(clientCount, 8 * Environment.ProcessorCount) }; + int[] source = [.. Enumerable.Range(1, clientCount)]; Parallel.ForEach( - Enumerable.Range(1, clientCount), - new ParallelOptions { MaxDegreeOfParallelism = Math.Min(clientCount, 8 * Environment.ProcessorCount) }, + source, + parallelOptions, item => { ClientSettings clientSettings = new(serverPath) @@ -242,6 +251,7 @@ private void TestClient(int clientCount, string serverPath, ClientSecurity? clie Security = clientSecurity, }; + Debug.WriteLine($"Testing client {item}"); using RmiClient client = new(clientSettings); ITester proxy = client.CreateProxy(); TestProxy(proxy, item, isSingleClient: clientCount == 1);