diff --git a/src/IKVM.OpenJDK.Tests/jdk/test/ExcludeList.txt b/src/IKVM.OpenJDK.Tests/jdk/test/ExcludeList.txt
index 24f416cb1d..2c3bdfbf85 100644
--- a/src/IKVM.OpenJDK.Tests/jdk/test/ExcludeList.txt
+++ b/src/IKVM.OpenJDK.Tests/jdk/test/ExcludeList.txt
@@ -3617,7 +3617,7 @@ java/awt/Focus/ModalDialogInitialFocusTest/ModalDialogInitialFocusTest.html
java/awt/Focus/ModalExcludedWindowClickTest/ModalExcludedWindowClickTest.html macosx-all
java/awt/Focus/MouseClickRequestFocusRaceTest/MouseClickRequestFocusRaceTest.html linux-all,macosx-all
java/awt/Focus/NonFocusableBlockedOwnerTest/NonFocusableBlockedOwnerTest.html macosx-all
-java/awt/Focus/WindowUpdateFocusabilityTest/WindowUpdateFocusabilityTest.html macosx-all
+java/awt/Focus/WindowUpdateFocusabilityTest/WindowUpdateFocusabilityTest.html macosx-all,windows-all
java/awt/font/GlyphVector/TestLayoutFlags.java macosx-all
java/awt/font/Underline/UnderlineTest.java macosx-all
java/awt/FontClass/HeadlessFont.java macosx-all
@@ -4259,7 +4259,7 @@ java/awt/Focus/MouseClickRequestFocusRaceTest/MouseClickRequestFocusRaceTest.jav
java/awt/Focus/NonFocusableBlockedOwnerTest/NonFocusableBlockedOwnerTest.java macosx-all
java/awt/Focus/ToFrontFocusTest/ToFrontFocus.java generic-all
java/awt/Focus/WindowInitialFocusTest/WindowInitialFocusTest.java generic-all
-java/awt/Focus/WindowUpdateFocusabilityTest/WindowUpdateFocusabilityTest.java macosx-all
+java/awt/Focus/WindowUpdateFocusabilityTest/WindowUpdateFocusabilityTest.java macosx-all,windows-all
java/awt/FontClass/CreateFont/bigfont.java generic-all
java/awt/Frame/DisposeStressTest/DisposeStressTest.java generic-all
java/awt/Frame/NonEDT_GUI_DeadlockTest/NonEDT_GUI_Deadlock.java macosx-all
@@ -4315,3 +4315,24 @@ java/awt/Focus/6401036/InputVerifierTest2.java
javax/swing/DefaultButtonModel/DefaultButtonModelCrashTest.java generic-all
javax/swing/JFileChooser/FileSystemView/Win32FolderSort.java generic-all
lib/property/CheckWindowsProperty.java generic-all
+
+# SetFullScreenTest arrived with jdk8u504-b01; it does not exist in 8u482. It
+# goes full screen, then asks Robot for the colour of the centre pixel and
+# expects the red frame it just showed. On the runner it reads 12,12,12, the
+# desktop background, 300ms after waitForIdle. Every other test in
+# java/awt/FullScreen is already excluded for the same reason: a runner with no
+# interactive desktop session does not put a window on screen the way a real
+# one does. It failed on net472 win-x64 and passed elsewhere in the same run,
+# so it is intermittent rather than broken, but it is intermittent for the
+# reason its whole directory is excluded.
+java/awt/FullScreen/SetFullScreenTest.java generic-all
+
+# VerifyCACerts checks every root in the shipped cacerts bundle and errors on
+# any that expires within 90 days. "entrustevca [jdk]" expires 2026-11-27
+# 20:53:42 UTC, so the check began failing at 2026-08-29 20:53:42 UTC and fails
+# on every run from then on, on all platforms at once. It is not intermittent:
+# a run of partition 13 that finished before that instant passed and the next
+# one failed. The real fix is upstream refreshing the bundle, which jdk8u does
+# periodically; drop this entry when the next update removes or replaces that
+# root, rather than leaving it excluded indefinitely.
+sun/security/lib/cacerts/VerifyCACerts.java generic-all
diff --git a/src/IKVM.Tests.Util/DotNetSdkResolver.cs b/src/IKVM.Tests.Util/DotNetSdkResolver.cs
index 0e97a2cddc..eca0777b29 100644
--- a/src/IKVM.Tests.Util/DotNetSdkResolver.cs
+++ b/src/IKVM.Tests.Util/DotNetSdkResolver.cs
@@ -15,9 +15,33 @@ namespace IKVM.Tests.Util
public static class DotNetSdkResolver
{
+ ///
+ /// Cache of previously resolved base paths, keyed by the 'dotnet' executable they were resolved from. The
+ /// installed SDK cannot change while the test run is in progress, and the callers are test initializers that
+ /// ask for the same path over and over, so the 'dotnet --info' process is only worth running once.
+ ///
+ static readonly Dictionary cache = new Dictionary();
+
public static string ResolvePath(string dotnetExePath)
{
- var output = GetInfo(dotnetExePath ?? "dotnet");
+ var key = dotnetExePath ?? "dotnet";
+
+ // the lock also keeps concurrent tests from spawning 'dotnet --info' at the same time, which is part of
+ // what made the call slow enough to time out on a loaded machine in the first place
+ lock (cache)
+ {
+ if (cache.TryGetValue(key, out var cached))
+ return cached;
+
+ var path = ResolvePathCore(key);
+ cache[key] = path;
+ return path;
+ }
+ }
+
+ static string ResolvePathCore(string dotnetExePath)
+ {
+ var output = GetInfo(dotnetExePath);
if (output == null || output.Count == 0)
return null;
@@ -47,12 +71,15 @@ static IList GetInfo(string dotnetExePath)
["MSBuildExtensionsPath"] = null,
};
+ // 'dotnet --info' enumerates every installed SDK and runtime, and CI runners have several of each and are
+ // busy running tests besides. A short budget here does not fail the call, it kills the process and throws
+ // OperationCanceledException out of whichever test initializer happened to ask, so give it room.
var info = new List();
var task = (Cli.Wrap(dotnetExePath)
.WithArguments("--info")
.WithEnvironmentVariables(envv)
| info.Add)
- .ExecuteAsync(new CancellationTokenSource(2000).Token);
+ .ExecuteAsync(new CancellationTokenSource(TimeSpan.FromMinutes(2)).Token);
task.GetAwaiter().GetResult();
return info;
diff --git a/src/IKVM.Tests/Java/java/nio/channels/ServerSocketChannelTests.cs b/src/IKVM.Tests/Java/java/nio/channels/ServerSocketChannelTests.cs
index 3567b7c790..8d163af35a 100644
--- a/src/IKVM.Tests/Java/java/nio/channels/ServerSocketChannelTests.cs
+++ b/src/IKVM.Tests/Java/java/nio/channels/ServerSocketChannelTests.cs
@@ -66,15 +66,18 @@ public async Task CanReceiveBlocking()
var cancellationTokenSource = new CancellationTokenSource();
var receive = ByteBuffer.allocate(sizeof(int) * 4);
+ // bind on the test thread rather than inside the server task: port 0 asks the OS for a
+ // free port instead of claiming a fixed one that something else on the machine may hold,
+ // and returning from bind establishes the listen backlog, so the client below can connect
+ // immediately instead of sleeping and hoping the task got there first
+ using var server = ServerSocketChannel.open();
+ server.bind(new InetSocketAddress(0));
+ server.configureBlocking(true);
+ var port = server.socket().getLocalPort();
+
// server receives messages until cancelled
var serverTask = Task.Run(() =>
{
- // initialize server
- using var server = ServerSocketChannel.open();
- var serverAddr = new InetSocketAddress(42341);
- server.bind(serverAddr);
- server.configureBlocking(true);
-
// accept the first socket
var c = server.accept();
@@ -83,9 +86,8 @@ public async Task CanReceiveBlocking()
continue;
});
- // wait a second and write some messages to the server
- await Task.Delay(1000);
- using (var c = SocketChannel.open(new InetSocketAddress("127.0.0.1", 42341)))
+ // write some messages to the server
+ using (var c = SocketChannel.open(new InetSocketAddress("127.0.0.1", port)))
{
foreach (var i in new[] { 1, 2, 3, 4 })
{
@@ -120,18 +122,18 @@ public async Task CanReceiveNonBlocking()
{
var cancellationTokenSource = new CancellationTokenSource();
var receive = ByteBuffer.allocate(sizeof(int) * 4);
- int port = 0;
+
+ // bind on the test thread rather than inside the server task. The port was previously
+ // read back from a field the task assigned, a second after starting it, so a task that
+ // had not been scheduled yet left the client connecting to port 0
+ using var server = ServerSocketChannel.open();
+ server.bind(new InetSocketAddress(0));
+ server.configureBlocking(false);
+ var port = server.socket().getLocalPort();
// server receives messages until cancelled
var serverTask = Task.Run(() =>
{
- // initialize server
- using var server = ServerSocketChannel.open();
- var serverAddr = new InetSocketAddress(port);
- server.bind(serverAddr);
- server.configureBlocking(false);
- port = server.socket().getLocalPort();
-
// begin selector
var selector = Selector.open();
var serverKey = server.register(selector, server.validOps(), null);