Files
zeekay 03964ca3b8 ci: add build+test workflow; run managed net8.0 unit suite
- Add .github/workflows/ci.yml (push/PR/dispatch, ubuntu, setup-dotnet@v4
  8.0.x). Builds the cross-platform libraries (runtime, generator, compiler,
  msbuild-generation) and runs the runtime test suite on net8.0.
- Zap.Net.Runtime.Tests: add a runnable net8.0 target alongside the retained
  legacy netcoreapp3.1/net471. net8.0 uses Test.Sdk 17.11.1 (16.x cannot host
  a net8.0 testhost) and MSTest 2.2.10 (keeps the non-generic Assert.AreEqual
  so existing assertions compile unchanged); RollForward=LatestMajor lets the
  testhost run on a newer shared runtime.
- Tag the I/O-bound integration classes (loopback-TCP RPC, anonymous-pipe
  frame pump) [TestCategory("Integration")] and exclude them from the default
  unit run; they hang/are slow in headless CI. TcpRpcInterop additionally
  skips on non-Windows (native ZapCompatTest.exe + kernel32 Job Objects).
- checkout fetch-depth: 0 so Nerdbank.GitVersioning can compute the version
  (it hard-fails on a shallow clone).

Local (net8.0): 301 passed / 0 failed (TestCategory!=Integration).
2026-06-20 22:51:47 -07:00

190 lines
5.3 KiB
C#

using System;
using System.Diagnostics;
using System.Net;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Zap.Rpc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Zap.Net.Runtime.Tests.GenImpls;
using Zap_test.Zap.Test;
using Microsoft.Extensions.Logging;
namespace Zap.Net.Runtime.Tests
{
[TestClass]
[TestCategory("Coverage")]
[TestCategory("Integration")] // Binds loopback TCP client/server pairs; excluded from the default unit CI run.
public class TcpRpcPorted: TestBase
{
[TestMethod]
public void Basic()
{
NewLocalhostTcpTestbed().RunTest(Testsuite.Basic);
}
[TestMethod]
public void Pipeline()
{
NewLocalhostTcpTestbed(TcpRpcTestOptions.ClientTracer).RunTest(Testsuite.Pipeline);
}
[TestMethod]
public void Release()
{
NewLocalhostTcpTestbed().RunTest(Testsuite.Release);
}
[TestMethod]
public void ReleaseOnCancel()
{
(var server, var client) = SetupClientServerPair();
using (server)
using (client)
{
//client.WhenConnected.Wait();
var counters = new Counters();
server.Main = new TestMoreStuffImpl(counters);
using (var main = client.GetMain<ITestMoreStuff>())
{
((Proxy)main).WhenResolved.WrappedTask.Wait(MediumNonDbgTimeout);
// Since we have a threaded model, there is no way to deterministically provoke the situation
// where Cancel and Finish message cross paths. Instead, we'll do a lot of such requests and
// later on verify that the handle count is 0.
for (int i = 0; i < 1000; i++)
{
var cts = new CancellationTokenSource();
var task = main.GetHandle(cts.Token);
cts.Cancel();
task.ContinueWith(t =>
{
try
{
t.Result.Dispose();
}
catch (AggregateException ex) when (ex.InnerException is TaskCanceledException)
{
}
cts.Dispose();
});
}
Thread.Sleep(ShortTimeout);
Assert.IsTrue(SpinWait.SpinUntil(() => counters.HandleCount == 0, MediumNonDbgTimeout));
}
}
}
[TestMethod]
public void TailCall()
{
NewLocalhostTcpTestbed().RunTest(Testsuite.TailCall);
}
[TestMethod]
public void Cancelation()
{
NewLocalhostTcpTestbed().RunTest(Testsuite.Cancelation);
}
[TestMethod]
public void PromiseResolve()
{
NewLocalhostTcpTestbed().RunTest(Testsuite.PromiseResolve);
}
[TestMethod]
public void RetainAndRelease()
{
NewLocalhostTcpTestbed().RunTest(Testsuite.RetainAndRelease);
}
[TestMethod]
public void Cancel()
{
NewLocalhostTcpTestbed().RunTest(Testsuite.Cancel);
}
[TestMethod]
public void SendTwice()
{
NewLocalhostTcpTestbed().RunTest(Testsuite.SendTwice);
}
[TestMethod]
public void Embargo()
{
NewLocalhostTcpTestbed().RunTest(Testsuite.EmbargoOnPromisedAnswer);
}
[TestMethod]
public void EmbargoError()
{
NewLocalhostTcpTestbed().RunTest(Testsuite.EmbargoError);
}
[TestMethod]
public void EmbargoNull()
{
NewLocalhostTcpTestbed().RunTest(Testsuite.EmbargoNull);
}
[TestMethod]
public void CallBrokenPromise()
{
NewLocalhostTcpTestbed().RunTest(Testsuite.CallBrokenPromise);
}
[TestMethod]
public void BootstrapReuse()
{
(var server, var client) = SetupClientServerPair();
var counters = new Counters();
var impl = new TestInterfaceImpl(counters);
using (server)
using (client)
{
//client.WhenConnected.Wait();
server.Main = impl;
for (int i = 0; i < 10; i++)
{
using (var main = client.GetMain<ITestMoreStuff>())
{
((Proxy)main).WhenResolved.WrappedTask.Wait(MediumNonDbgTimeout);
}
Assert.IsFalse(impl.IsDisposed);
}
}
Assert.IsTrue(impl.IsDisposed);
}
[TestMethod]
public void Ownership1()
{
NewLocalhostTcpTestbed().RunTest(Testsuite.Ownership1);
}
[TestMethod]
public void Ownership2()
{
NewLocalhostTcpTestbed().RunTest(Testsuite.Ownership2);
}
[TestMethod]
public void Ownership3()
{
NewLocalhostTcpTestbed().RunTest(Testsuite.Ownership3);
}
}
}