From 42f6c89b34dadfbfe6fa2840a7e16c8d1b9173bc Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 5 Aug 2026 19:20:25 +0000
Subject: [PATCH 1/3] Initial plan
From a7880ac4885221f8ed616c60571c9a947c58bac5 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 5 Aug 2026 20:24:54 +0000
Subject: [PATCH 2/3] Achieve InProcess/out-of-process Java SDK test parity
Co-authored-by: edburns <75821+edburns@users.noreply.github.com>
---
java/sdk/pom.xml | 13 +++---
.../github/copilot/ClientOptionsE2ETest.java | 5 ++
.../com/github/copilot/CopilotClientTest.java | 9 ++++
.../com/github/copilot/E2ETestContext.java | 46 ++++++++++++++++---
.../github/copilot/PerSessionAuthTest.java | 3 ++
.../github/copilot/RpcServerMiscE2ETest.java | 3 ++
.../com/github/copilot/SlashCommandsIT.java | 3 ++
.../github/copilot/e2e/RequireInProcess.java | 2 +-
.../com/github/copilot/e2e/SkipInProcess.java | 2 +-
9 files changed, 72 insertions(+), 14 deletions(-)
diff --git a/java/sdk/pom.xml b/java/sdk/pom.xml
index 9bfe76a4b8..7f11023d63 100644
--- a/java/sdk/pom.xml
+++ b/java/sdk/pom.xml
@@ -265,7 +265,7 @@
alphabetical
- ${testExecutionAgentArgs} ${surefire.jvm.args}
+ ${testExecutionAgentArgs} ${surefire.jvm.args} --add-opens com.github.copilot.java/com.github.copilot.e2e=ALL-UNNAMED
false
+ 0
+ none
+
+ inprocess
+
org.apache.maven.plugins
maven-failsafe-plugin
- 1
+ 0
none
-
- **/InProcessTransportIT.java
-
${copilot.inprocess.cli.path}
inprocess
diff --git a/java/sdk/src/test/java/com/github/copilot/ClientOptionsE2ETest.java b/java/sdk/src/test/java/com/github/copilot/ClientOptionsE2ETest.java
index 45056afdb4..f6ff46a144 100644
--- a/java/sdk/src/test/java/com/github/copilot/ClientOptionsE2ETest.java
+++ b/java/sdk/src/test/java/com/github/copilot/ClientOptionsE2ETest.java
@@ -15,6 +15,8 @@
import org.junit.jupiter.api.Test;
+import com.github.copilot.e2e.SkipInProcess;
+
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.copilot.generated.rpc.SessionLimitsConfig;
@@ -29,6 +31,7 @@ class ClientOptionsE2ETest {
private static final ObjectMapper MAPPER = new ObjectMapper();
@Test
+ @SkipInProcess("Exercises direct CLI argument and working-directory forwarding to a spawned stdio subprocess")
void testShouldForwardAdvancedSessionCreationOptionsToTheCli() throws Exception {
try (var fake = FakeStdioCli.create()) {
var workDir = fake.path("create-work");
@@ -95,6 +98,7 @@ void testShouldForwardAdvancedSessionCreationOptionsToTheCli() throws Exception
}
@Test
+ @SkipInProcess("Exercises direct CLI argument and working-directory forwarding to a spawned stdio subprocess")
void testShouldForwardSingularProviderConfigurationOnSessionCreation() throws Exception {
try (var fake = FakeStdioCli.create()) {
try (var client = fake.createClient()) {
@@ -123,6 +127,7 @@ void testShouldForwardSingularProviderConfigurationOnSessionCreation() throws Ex
}
@Test
+ @SkipInProcess("Exercises direct CLI argument and working-directory forwarding to a spawned stdio subprocess")
void testShouldForwardAdvancedSessionResumeOptionsToTheCli() throws Exception {
try (var fake = FakeStdioCli.create()) {
var workDir = fake.path("resume-work");
diff --git a/java/sdk/src/test/java/com/github/copilot/CopilotClientTest.java b/java/sdk/src/test/java/com/github/copilot/CopilotClientTest.java
index 067571df13..6cfc985fd5 100644
--- a/java/sdk/src/test/java/com/github/copilot/CopilotClientTest.java
+++ b/java/sdk/src/test/java/com/github/copilot/CopilotClientTest.java
@@ -7,6 +7,8 @@
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
+import com.github.copilot.e2e.SkipInProcess;
+
import com.github.copilot.rpc.CopilotClientOptions;
import com.github.copilot.rpc.PermissionHandler;
import com.github.copilot.rpc.PingResponse;
@@ -119,6 +121,7 @@ void testClientConstruction() {
}
@Test
+ @SkipInProcess("Constructs the client with CliPath explicitly, which the in-process transport does not accept")
void testClientConstructionWithOptions() {
var options = new CopilotClientOptions().setCliPath("/path/to/cli").setLogLevel("debug").setAutoStart(false);
@@ -149,6 +152,7 @@ void testCliUrlOnlyConstruction() {
}
@Test
+ @SkipInProcess("Validates CliPath and CliUrl subprocess transport conflicts, which are not applicable in in-process mode")
void testCliUrlMutualExclusionWithCliPath() {
var options = new CopilotClientOptions().setCliUrl("localhost:3000").setCliPath("/path/to/cli");
@@ -156,6 +160,7 @@ void testCliUrlMutualExclusionWithCliPath() {
}
@Test
+ @SkipInProcess("Verifies spawning and talking to a child stdio process instead of the in-process runtime")
void testStartAndConnectUsingStdio() throws Exception {
assertNotNull(cliPath, "Copilot CLI not found in PATH or COPILOT_CLI_PATH");
@@ -173,6 +178,7 @@ void testStartAndConnectUsingStdio() throws Exception {
}
@Test
+ @SkipInProcess("Asserts child-process startup stderr reporting for invalid CLI arguments")
void testShouldReportErrorWithStderrWhenCliFailsToStart() throws Exception {
assertNotNull(cliPath, "Copilot CLI not found in PATH or COPILOT_CLI_PATH");
@@ -190,6 +196,7 @@ void testShouldReportErrorWithStderrWhenCliFailsToStart() throws Exception {
}
@Test
+ @SkipInProcess("Verifies spawning and talking to a child TCP process instead of the in-process runtime")
void testStartAndConnectUsingTcp() throws Exception {
assertNotNull(cliPath, "Copilot CLI not found in PATH or COPILOT_CLI_PATH");
@@ -205,6 +212,7 @@ void testStartAndConnectUsingTcp() throws Exception {
}
@Test
+ @SkipInProcess("Exercises lifecycle of a spawned CLI process selected via CliPath")
void testForceStopWithoutCleanup() throws Exception {
assertNotNull(cliPath, "Copilot CLI not found in PATH or COPILOT_CLI_PATH");
@@ -497,6 +505,7 @@ void testForceStopWithNoConnectionCompletes() throws Exception {
}
@Test
+ @SkipInProcess("Exercises session shutdown after stopping a spawned CLI process selected via CliPath")
void testCloseSessionAfterStoppingClientDoesNotThrow() throws Exception {
assertNotNull(cliPath, "Copilot CLI not found in PATH or COPILOT_CLI_PATH");
diff --git a/java/sdk/src/test/java/com/github/copilot/E2ETestContext.java b/java/sdk/src/test/java/com/github/copilot/E2ETestContext.java
index f524b33dab..f22392f548 100644
--- a/java/sdk/src/test/java/com/github/copilot/E2ETestContext.java
+++ b/java/sdk/src/test/java/com/github/copilot/E2ETestContext.java
@@ -18,7 +18,10 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import com.github.copilot.ffi.InProcessEnvGuard;
import com.github.copilot.rpc.CopilotClientOptions;
+import com.github.copilot.rpc.InProcessRuntimeConnection;
+import com.github.copilot.rpc.RuntimeConnection;
/**
* E2E test context that manages the test environment including the CapiProxy,
@@ -71,6 +74,7 @@ public class E2ETestContext implements AutoCloseable {
private String proxyUrl;
private final CapiProxy proxy;
private final Path repoRoot;
+ private final List inProcessEnvGuards = new ArrayList<>();
private Path currentSnapshotFile;
private E2ETestContext(String cliPath, Path homeDir, Path workDir, String proxyUrl, CapiProxy proxy,
@@ -322,9 +326,8 @@ public Map getEnvironment() {
* @return a new CopilotClient
*/
public CopilotClient createClient() {
- CopilotClientOptions options = new CopilotClientOptions().setCliPath(cliPath).setCwd(workDir.toString())
- .setEnvironment(getEnvironment()).setGitHubToken(DEFAULT_GITHUB_TOKEN);
-
+ CopilotClientOptions options = new CopilotClientOptions().setGitHubToken(DEFAULT_GITHUB_TOKEN);
+ applyContextOptions(options);
return new CopilotClient(options);
}
@@ -338,6 +341,19 @@ public CopilotClient createClient() {
* @return a new CopilotClient
*/
public CopilotClient createClient(CopilotClientOptions options) {
+ applyContextOptions(options);
+ if (options.getGitHubToken() == null) {
+ options.setGitHubToken(DEFAULT_GITHUB_TOKEN);
+ }
+
+ return new CopilotClient(options);
+ }
+
+ private void applyContextOptions(CopilotClientOptions options) {
+ if (isInProcessMode(options)) {
+ inProcessEnvGuards.add(new InProcessEnvGuard(buildInProcessEnvironment(options)));
+ return;
+ }
if (options.getCliPath() == null) {
options.setCliPath(cliPath);
}
@@ -347,11 +363,26 @@ public CopilotClient createClient(CopilotClientOptions options) {
if (options.getEnvironment() == null || options.getEnvironment().isEmpty()) {
options.setEnvironment(getEnvironment());
}
- if (options.getGitHubToken() == null) {
- options.setGitHubToken(DEFAULT_GITHUB_TOKEN);
+ }
+
+ private boolean isInProcessMode(CopilotClientOptions options) {
+ RuntimeConnection connection = options.getConnection();
+ if (connection instanceof InProcessRuntimeConnection) {
+ return true;
}
+ String defaultConnection = System.getenv("COPILOT_SDK_DEFAULT_CONNECTION");
+ return defaultConnection != null && "inprocess".equalsIgnoreCase(defaultConnection.trim());
+ }
- return new CopilotClient(options);
+ private Map buildInProcessEnvironment(CopilotClientOptions options) {
+ Map env = new HashMap<>(getEnvironment());
+ Map optionEnvironment = options.getEnvironment();
+ if (optionEnvironment != null && !optionEnvironment.isEmpty()) {
+ env.putAll(optionEnvironment);
+ options.setEnvironment(null);
+ }
+ env.put("COPILOT_CLI_PATH", cliPath);
+ return env;
}
/**
@@ -428,6 +459,9 @@ public void initializeProxy() throws IOException, InterruptedException {
@Override
public void close() throws Exception {
+ for (int i = inProcessEnvGuards.size() - 1; i >= 0; i--) {
+ inProcessEnvGuards.get(i).close();
+ }
proxy.stop();
// Clean up temp directories (best effort)
diff --git a/java/sdk/src/test/java/com/github/copilot/PerSessionAuthTest.java b/java/sdk/src/test/java/com/github/copilot/PerSessionAuthTest.java
index 9e5cd1b324..974536300c 100644
--- a/java/sdk/src/test/java/com/github/copilot/PerSessionAuthTest.java
+++ b/java/sdk/src/test/java/com/github/copilot/PerSessionAuthTest.java
@@ -13,6 +13,8 @@
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
+import com.github.copilot.e2e.SkipInProcess;
+
import com.github.copilot.generated.rpc.SessionGitHubAuthGetStatusResult;
import com.github.copilot.rpc.CopilotClientOptions;
import com.github.copilot.rpc.PermissionHandler;
@@ -110,6 +112,7 @@ void shouldIsolateAuthBetweenSessions() throws Exception {
}
@Test
+ @SkipInProcess("Builds a client with per-client environment, cwd, and logged-in-user overrides that the shared in-process runtime cannot isolate")
void shouldBeUnauthenticatedWithoutToken() throws Exception {
Map env = new HashMap<>(ctx.getEnvironment());
env.put("COPILOT_DEBUG_GITHUB_API_URL", ctx.getProxyUrl());
diff --git a/java/sdk/src/test/java/com/github/copilot/RpcServerMiscE2ETest.java b/java/sdk/src/test/java/com/github/copilot/RpcServerMiscE2ETest.java
index 1db801d841..21dfb9c08f 100644
--- a/java/sdk/src/test/java/com/github/copilot/RpcServerMiscE2ETest.java
+++ b/java/sdk/src/test/java/com/github/copilot/RpcServerMiscE2ETest.java
@@ -14,6 +14,8 @@
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
+import com.github.copilot.e2e.SkipInProcess;
+
import com.github.copilot.generated.rpc.AccountAllUsers;
import com.github.copilot.generated.rpc.AccountLoginParams;
import com.github.copilot.generated.rpc.AccountLogoutParams;
@@ -70,6 +72,7 @@ void testShouldGetSetAndClearUserSettings() throws Exception {
}
@Test
+ @SkipInProcess("Builds a client with per-client environment and cwd overrides to test account login/logout flows")
void testShouldLoginListGetCurrentAuthAndLogoutAccount() throws Exception {
ctx.configureForTest("rpc_server_misc", "should_login_list_getcurrentauth_and_logout_account");
var token = "java-account-token";
diff --git a/java/sdk/src/test/java/com/github/copilot/SlashCommandsIT.java b/java/sdk/src/test/java/com/github/copilot/SlashCommandsIT.java
index 634c0bad9b..5dec064644 100644
--- a/java/sdk/src/test/java/com/github/copilot/SlashCommandsIT.java
+++ b/java/sdk/src/test/java/com/github/copilot/SlashCommandsIT.java
@@ -22,6 +22,8 @@
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
+import com.github.copilot.e2e.SkipInProcess;
+
import com.github.copilot.generated.rpc.SessionCommandsListResult;
import com.github.copilot.generated.rpc.SessionCommandsInvokeParams;
import com.github.copilot.generated.rpc.SlashCommandAgentPromptResult;
@@ -41,6 +43,7 @@
* Requires the CLI to be installed and the user to be signed in. Uses
* {@link TestUtil#findCliPath()} so the test harness binary is found in CI.
*/
+@SkipInProcess("Requires a live signed-in CLI subprocess and logged-in-user transport behavior rather than the replayed in-process harness")
class SlashCommandsIT {
private static CopilotClient client;
diff --git a/java/sdk/src/test/java/com/github/copilot/e2e/RequireInProcess.java b/java/sdk/src/test/java/com/github/copilot/e2e/RequireInProcess.java
index ef85f261ae..12de4e5b73 100644
--- a/java/sdk/src/test/java/com/github/copilot/e2e/RequireInProcess.java
+++ b/java/sdk/src/test/java/com/github/copilot/e2e/RequireInProcess.java
@@ -44,7 +44,7 @@
/**
* JUnit 5 execution condition backing {@link RequireInProcess}.
*/
- final class Condition implements org.junit.jupiter.api.extension.ExecutionCondition {
+ public static final class Condition implements org.junit.jupiter.api.extension.ExecutionCondition {
private static final String DEFAULT_CONNECTION_ENV_VAR = "COPILOT_SDK_DEFAULT_CONNECTION";
diff --git a/java/sdk/src/test/java/com/github/copilot/e2e/SkipInProcess.java b/java/sdk/src/test/java/com/github/copilot/e2e/SkipInProcess.java
index b1a27ad025..3f626e133f 100644
--- a/java/sdk/src/test/java/com/github/copilot/e2e/SkipInProcess.java
+++ b/java/sdk/src/test/java/com/github/copilot/e2e/SkipInProcess.java
@@ -44,7 +44,7 @@
/**
* JUnit 5 execution condition backing {@link SkipInProcess}.
*/
- final class Condition implements org.junit.jupiter.api.extension.ExecutionCondition {
+ public static final class Condition implements org.junit.jupiter.api.extension.ExecutionCondition {
private static final String DEFAULT_CONNECTION_ENV_VAR = "COPILOT_SDK_DEFAULT_CONNECTION";
From f36371ac769752424a9b84e409559404758247db Mon Sep 17 00:00:00 2001
From: Ed Burns
Date: Wed, 5 Aug 2026 21:44:14 +0000
Subject: [PATCH 3/3] Address Copilot review findings
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
java/sdk/pom.xml | 4 ++--
.../com/github/copilot/CopilotClient.java | 9 ++++++++
.../com/github/copilot/CopilotClientTest.java | 7 ++++++
.../com/github/copilot/E2ETestContext.java | 22 ++++++++++++++-----
.../github/copilot/ExecutorWiringTest.java | 15 ++++++-------
.../com/github/copilot/MetadataApiTest.java | 4 ++++
.../github/copilot/ffi/InProcessEnvGuard.java | 7 +++++-
7 files changed, 51 insertions(+), 17 deletions(-)
diff --git a/java/sdk/pom.xml b/java/sdk/pom.xml
index 7f11023d63..12cb42a3ee 100644
--- a/java/sdk/pom.xml
+++ b/java/sdk/pom.xml
@@ -617,7 +617,7 @@ did not produce the multi-release output. Re-build on JDK 25+ and verify the
maven-surefire-plugin
false
- 0
+ 1
none
inprocess
@@ -628,7 +628,7 @@ did not produce the multi-release output. Re-build on JDK 25+ and verify the
org.apache.maven.plugins
maven-failsafe-plugin
- 0
+ 1
none
${copilot.inprocess.cli.path}
diff --git a/java/sdk/src/main/java/com/github/copilot/CopilotClient.java b/java/sdk/src/main/java/com/github/copilot/CopilotClient.java
index d53be740cf..f420585be7 100644
--- a/java/sdk/src/main/java/com/github/copilot/CopilotClient.java
+++ b/java/sdk/src/main/java/com/github/copilot/CopilotClient.java
@@ -123,6 +123,7 @@ public final class CopilotClient implements AutoCloseable {
private final Integer optionsPort;
private final RuntimeConnection runtimeConnection;
private final String effectiveConnectionToken;
+ private final Runnable closeHook;
private volatile List modelsCache;
private final Object modelsCacheLock = new Object();
@@ -142,7 +143,12 @@ public CopilotClient() {
* if mutually exclusive options are provided
*/
public CopilotClient(CopilotClientOptions options) {
+ this(options, null);
+ }
+
+ CopilotClient(CopilotClientOptions options, Runnable closeHook) {
this.options = options != null ? options : new CopilotClientOptions();
+ this.closeHook = closeHook;
// Resolve the transport: an explicit RuntimeConnection wins; otherwise the
// COPILOT_SDK_DEFAULT_CONNECTION env var, or the individual transport options.
@@ -1663,6 +1669,9 @@ public void close() {
LOG.log(Level.FINE, "Error during close", e);
} finally {
shutdownOwnedExecutor();
+ if (closeHook != null) {
+ closeHook.run();
+ }
}
}
diff --git a/java/sdk/src/test/java/com/github/copilot/CopilotClientTest.java b/java/sdk/src/test/java/com/github/copilot/CopilotClientTest.java
index 6cfc985fd5..7fad694558 100644
--- a/java/sdk/src/test/java/com/github/copilot/CopilotClientTest.java
+++ b/java/sdk/src/test/java/com/github/copilot/CopilotClientTest.java
@@ -131,6 +131,7 @@ void testClientConstructionWithOptions() {
}
@Test
+ @SkipInProcess("Validates external CLI URL transport options")
void testCliUrlAutoCorrectsUseStdio() {
var options = new CopilotClientOptions().setCliUrl("localhost:3000").setUseStdio(true);
@@ -141,6 +142,7 @@ void testCliUrlAutoCorrectsUseStdio() {
}
@Test
+ @SkipInProcess("Validates external CLI URL transport options")
void testCliUrlOnlyConstruction() {
var options = new CopilotClientOptions().setCliUrl("localhost:4321");
@@ -289,6 +291,7 @@ void testTcpConnectionTokenWithUseStdioThrows() {
}
@Test
+ @SkipInProcess("Validates TCP transport options")
void testTcpConnectionTokenAcceptedInTcpMode() {
var options = new CopilotClientOptions().setUseStdio(false).setTcpConnectionToken("my-token");
@@ -409,6 +412,7 @@ void testOnLifecycleMultipleHandlers() throws Exception {
// ===== getState() coverage =====
@Test
+ @SkipInProcess("Validates subprocess CLI path failure handling")
void testGetStateErrorAfterFailedStart() throws Exception {
// Use a non-existent CLI path to trigger a startup failure
var options = new CopilotClientOptions().setCliPath("/nonexistent/path/to/cli").setAutoStart(false);
@@ -429,6 +433,7 @@ void testGetStateErrorAfterFailedStart() throws Exception {
}
@Test
+ @SkipInProcess("Validates subprocess CLI path failure handling")
void testGetStateConnectingDuringStart() throws Exception {
// Use a non-existent CLI path; the future won't complete immediately
var options = new CopilotClientOptions().setCliPath("/nonexistent/path/to/cli").setAutoStart(false);
@@ -469,6 +474,7 @@ void testCloseIsIdempotent() {
}
@Test
+ @SkipInProcess("Validates subprocess CLI path failure handling")
void testCloseAfterFailedStart() throws Exception {
var options = new CopilotClientOptions().setCliPath("/nonexistent/path/to/cli").setAutoStart(false);
var client = new CopilotClient(options);
@@ -528,6 +534,7 @@ void testCloseSessionAfterStoppingClientDoesNotThrow() throws Exception {
// ===== start() idempotency =====
@Test
+ @SkipInProcess("Validates subprocess CLI path failure handling")
void testStartIsIdempotentSingleConnectionAttempt() throws Exception {
var options = new CopilotClientOptions().setCliPath("/nonexistent/path/to/cli").setAutoStart(false);
diff --git a/java/sdk/src/test/java/com/github/copilot/E2ETestContext.java b/java/sdk/src/test/java/com/github/copilot/E2ETestContext.java
index f22392f548..f040607353 100644
--- a/java/sdk/src/test/java/com/github/copilot/E2ETestContext.java
+++ b/java/sdk/src/test/java/com/github/copilot/E2ETestContext.java
@@ -327,8 +327,7 @@ public Map getEnvironment() {
*/
public CopilotClient createClient() {
CopilotClientOptions options = new CopilotClientOptions().setGitHubToken(DEFAULT_GITHUB_TOKEN);
- applyContextOptions(options);
- return new CopilotClient(options);
+ return createClient(options);
}
/**
@@ -341,7 +340,10 @@ public CopilotClient createClient() {
* @return a new CopilotClient
*/
public CopilotClient createClient(CopilotClientOptions options) {
- applyContextOptions(options);
+ CopilotClient client = applyContextOptions(options);
+ if (client != null) {
+ return client;
+ }
if (options.getGitHubToken() == null) {
options.setGitHubToken(DEFAULT_GITHUB_TOKEN);
}
@@ -349,10 +351,17 @@ public CopilotClient createClient(CopilotClientOptions options) {
return new CopilotClient(options);
}
- private void applyContextOptions(CopilotClientOptions options) {
+ private CopilotClient applyContextOptions(CopilotClientOptions options) {
if (isInProcessMode(options)) {
- inProcessEnvGuards.add(new InProcessEnvGuard(buildInProcessEnvironment(options)));
- return;
+ InProcessEnvGuard guard = new InProcessEnvGuard(buildInProcessEnvironment(options));
+ inProcessEnvGuards.add(guard);
+ try {
+ options.setEnvironment(null);
+ return new CopilotClient(options, guard::close);
+ } catch (RuntimeException e) {
+ guard.close();
+ throw e;
+ }
}
if (options.getCliPath() == null) {
options.setCliPath(cliPath);
@@ -363,6 +372,7 @@ private void applyContextOptions(CopilotClientOptions options) {
if (options.getEnvironment() == null || options.getEnvironment().isEmpty()) {
options.setEnvironment(getEnvironment());
}
+ return null;
}
private boolean isInProcessMode(CopilotClientOptions options) {
diff --git a/java/sdk/src/test/java/com/github/copilot/ExecutorWiringTest.java b/java/sdk/src/test/java/com/github/copilot/ExecutorWiringTest.java
index 78764db0fb..a8319475c6 100644
--- a/java/sdk/src/test/java/com/github/copilot/ExecutorWiringTest.java
+++ b/java/sdk/src/test/java/com/github/copilot/ExecutorWiringTest.java
@@ -86,8 +86,7 @@ int getTaskCount() {
}
private CopilotClientOptions createOptionsWithExecutor(TrackingExecutor executor) {
- CopilotClientOptions options = new CopilotClientOptions().setCliPath(ctx.getCliPath())
- .setCwd(ctx.getWorkDir().toString()).setEnvironment(ctx.getEnvironment()).setExecutor(executor)
+ CopilotClientOptions options = new CopilotClientOptions().setExecutor(executor)
.setGitHubToken("fake-token-for-e2e-tests");
return options;
}
@@ -111,7 +110,7 @@ void testClientStartUsesProvidedExecutor() throws Exception {
TrackingExecutor trackingExecutor = new TrackingExecutor(ForkJoinPool.commonPool());
int beforeStart = trackingExecutor.getTaskCount();
- try (CopilotClient client = new CopilotClient(createOptionsWithExecutor(trackingExecutor))) {
+ try (CopilotClient client = ctx.createClient(createOptionsWithExecutor(trackingExecutor))) {
client.start().get(30, TimeUnit.SECONDS);
assertTrue(trackingExecutor.getTaskCount() > beforeStart,
@@ -156,7 +155,7 @@ void testToolCallDispatchUsesProvidedExecutor() throws Exception {
});
// Reset count after client construction to isolate tool-call dispatch
- try (CopilotClient client = new CopilotClient(createOptionsWithExecutor(trackingExecutor))) {
+ try (CopilotClient client = ctx.createClient(createOptionsWithExecutor(trackingExecutor))) {
CopilotSession session = client.createSession(new SessionConfig().setTools(List.of(encryptTool))
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get();
@@ -198,7 +197,7 @@ void testPermissionDispatchUsesProvidedExecutor() throws Exception {
var config = new SessionConfig().setOnPermissionRequest((request, invocation) -> CompletableFuture
.completedFuture(new PermissionRequestResult().setKind(PermissionRequestResultKind.APPROVED)));
- try (CopilotClient client = new CopilotClient(createOptionsWithExecutor(trackingExecutor))) {
+ try (CopilotClient client = ctx.createClient(createOptionsWithExecutor(trackingExecutor))) {
CopilotSession session = client.createSession(config).get();
Path testFile = ctx.getWorkDir().resolve("test.txt");
@@ -247,7 +246,7 @@ void testUserInputDispatchUsesProvidedExecutor() throws Exception {
.completedFuture(new UserInputResponse().setAnswer(answer).setWasFreeform(wasFreeform));
});
- try (CopilotClient client = new CopilotClient(createOptionsWithExecutor(trackingExecutor))) {
+ try (CopilotClient client = ctx.createClient(createOptionsWithExecutor(trackingExecutor))) {
CopilotSession session = client.createSession(config).get();
int beforeSend = trackingExecutor.getTaskCount();
@@ -286,7 +285,7 @@ void testHooksDispatchUsesProvidedExecutor() throws Exception {
.setHooks(new SessionHooks().setOnPreToolUse(
(input, invocation) -> CompletableFuture.completedFuture(PreToolUseHookOutput.allow())));
- try (CopilotClient client = new CopilotClient(createOptionsWithExecutor(trackingExecutor))) {
+ try (CopilotClient client = ctx.createClient(createOptionsWithExecutor(trackingExecutor))) {
CopilotSession session = client.createSession(config).get();
Path testFile = ctx.getWorkDir().resolve("hello.txt");
@@ -342,7 +341,7 @@ void testClientStopUsesProvidedExecutor() throws Exception {
return CompletableFuture.completedFuture(input.toUpperCase());
});
- CopilotClient client = new CopilotClient(createOptionsWithExecutor(trackingExecutor));
+ CopilotClient client = ctx.createClient(createOptionsWithExecutor(trackingExecutor));
client.createSession(new SessionConfig().setTools(List.of(encryptTool))
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get();
diff --git a/java/sdk/src/test/java/com/github/copilot/MetadataApiTest.java b/java/sdk/src/test/java/com/github/copilot/MetadataApiTest.java
index ec3b9ea707..4f06176d93 100644
--- a/java/sdk/src/test/java/com/github/copilot/MetadataApiTest.java
+++ b/java/sdk/src/test/java/com/github/copilot/MetadataApiTest.java
@@ -10,6 +10,7 @@
import com.github.copilot.generated.rpc.ModelBillingTokenPrices;
import com.github.copilot.generated.rpc.ModelBillingTokenPricesLongContext;
import com.github.copilot.rpc.*;
+import com.github.copilot.e2e.SkipInProcess;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -271,6 +272,7 @@ void testGetModelsResponseDeserialization() throws Exception {
// ===== Integration Tests (require CLI) =====
@Test
+ @SkipInProcess("Uses explicit CLI stdio transport")
void testGetStatus() throws Exception {
assertNotNull(cliPath, "Copilot CLI not found in PATH or COPILOT_CLI_PATH");
@@ -287,6 +289,7 @@ void testGetStatus() throws Exception {
}
@Test
+ @SkipInProcess("Uses explicit CLI stdio transport")
void testGetAuthStatus() throws Exception {
assertNotNull(cliPath, "Copilot CLI not found in PATH or COPILOT_CLI_PATH");
@@ -302,6 +305,7 @@ void testGetAuthStatus() throws Exception {
}
@Test
+ @SkipInProcess("Uses explicit CLI stdio transport")
void testListModels() throws Exception {
assertNotNull(cliPath, "Copilot CLI not found in PATH or COPILOT_CLI_PATH");
diff --git a/java/sdk/src/test/java/com/github/copilot/ffi/InProcessEnvGuard.java b/java/sdk/src/test/java/com/github/copilot/ffi/InProcessEnvGuard.java
index 12062ada6a..43df713715 100644
--- a/java/sdk/src/test/java/com/github/copilot/ffi/InProcessEnvGuard.java
+++ b/java/sdk/src/test/java/com/github/copilot/ffi/InProcessEnvGuard.java
@@ -83,6 +83,7 @@ private interface LibcEnv extends Library {
* name -> previous value ({@code null} means the variable was not set before).
*/
private final List> saved = new ArrayList<>();
+ private boolean closed;
/**
* Applies {@code applyEnv} to the native process environment block, saving the
@@ -116,7 +117,11 @@ private void apply(String name, String value) {
* before construction.
*/
@Override
- public void close() {
+ public synchronized void close() {
+ if (closed) {
+ return;
+ }
+ closed = true;
List> reversed = new ArrayList<>(saved);
Collections.reverse(reversed);
for (Map.Entry entry : reversed) {