From 75da6373256356b2e1bfa56181a65cb9fc697eac Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 19:45:58 +0000 Subject: [PATCH 1/3] Initial plan From 167473a41171a52b29a15f9fe53628cbf7a61d21 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 20:06:43 +0000 Subject: [PATCH 2/3] Fix case-insensitive comparison of launch configuration strings on Windows Co-authored-by: snehara99 <113148726+snehara99@users.noreply.github.com> --- src/configuration.ts | 12 +++++++-- src/test/fakeSuite/extension.test.ts | 37 ++++++++++++++++++++++++++++ src/util.ts | 12 +++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index 9d5c8de4..efad7513 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -893,7 +893,12 @@ export async function setCurrentLaunchConfiguration( function getLaunchConfiguration(name: string): LaunchConfiguration | undefined { return launchConfigurations.find((k) => { - if (launchConfigurationToString(k) === name) { + if ( + util.areLaunchConfigurationStringsEqual( + launchConfigurationToString(k), + name + ) + ) { return { ...k, keep: true }; } }); @@ -2661,7 +2666,10 @@ export async function selectLaunchConfiguration(): Promise { getCurrentLaunchConfiguration(); if ( !currentLaunchConfiguration || - chosen !== launchConfigurationToString(currentLaunchConfiguration) + !util.areLaunchConfigurationStringsEqual( + chosen, + launchConfigurationToString(currentLaunchConfiguration) + ) ) { let telemetryProperties: telemetry.Properties | null = { state: "launchConfiguration", diff --git a/src/test/fakeSuite/extension.test.ts b/src/test/fakeSuite/extension.test.ts index 5dfcb038..5ac3fe6d 100644 --- a/src/test/fakeSuite/extension.test.ts +++ b/src/test/fakeSuite/extension.test.ts @@ -130,6 +130,43 @@ suite("Unit testing replacing characters in and outside of quotes", () => { }); }); +suite("Launch configuration string comparison", () => { + test("areLaunchConfigurationStringsEqual - same strings", () => { + const str1 = "c:\\Users\\test\\project>out()"; + const str2 = "c:\\Users\\test\\project>out()"; + expect(util.areLaunchConfigurationStringsEqual(str1, str2)).to.be.true; + }); + + test("areLaunchConfigurationStringsEqual - different strings", () => { + const str1 = "c:\\Users\\test\\project>out()"; + const str2 = "c:\\Users\\test\\project>out(arg1)"; + expect(util.areLaunchConfigurationStringsEqual(str1, str2)).to.be.false; + }); + + test("areLaunchConfigurationStringsEqual - same strings with args", () => { + const str1 = "c:\\Users\\test\\project>out(arg1,arg2)"; + const str2 = "c:\\Users\\test\\project>out(arg1,arg2)"; + expect(util.areLaunchConfigurationStringsEqual(str1, str2)).to.be.true; + }); + + // On Windows, paths are case-insensitive + if (process.platform === "win32") { + test("areLaunchConfigurationStringsEqual - different case on Windows", () => { + const str1 = "c:\\Users\\test\\project>out()"; + const str2 = "C:\\Users\\Test\\Project>OUT()"; + expect(util.areLaunchConfigurationStringsEqual(str1, str2)).to.be.true; + }); + + test("areLaunchConfigurationStringsEqual - different case with args on Windows", () => { + const str1 = "c:\\users\\test\\project>out(arg1,arg2)"; + const str2 = "C:\\Users\\TEST\\Project>Out(Arg1,Arg2)"; + // Note: args themselves might have case-sensitive values, but the whole string comparison + // is made case-insensitive on Windows because the path portion is case-insensitive + expect(util.areLaunchConfigurationStringsEqual(str1, str2)).to.be.true; + }); + } +}); + // TODO: refactor initialization and cleanup of each test suite("Fake dryrun parsing", () => { suiteSetup(async function (this: Mocha.Context) { diff --git a/src/util.ts b/src/util.ts index fa362979..5d187b80 100644 --- a/src/util.ts +++ b/src/util.ts @@ -858,6 +858,18 @@ export function hasProperties(obj: any): boolean { return props && props.length > 0; } +// Helper for comparing launch configuration strings. +// On Windows, paths are case-insensitive, so we need to do a case-insensitive comparison. +export function areLaunchConfigurationStringsEqual( + str1: string, + str2: string +): boolean { + if (process.platform === "win32") { + return str1.toLowerCase() === str2.toLowerCase(); + } + return str1 === str2; +} + // Apply any properties from source to destination, logging for overwrite. // To make things simpler for the caller, create a valid dst if given null or undefined. export function mergeProperties(dst: any, src: any): any { From 2a1ddd2eadb280b20ce9d10b58166688b59edcc5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 20:09:25 +0000 Subject: [PATCH 3/3] Address code review: parse launch config to compare paths case-insensitively and args case-sensitively Co-authored-by: snehara99 <113148726+snehara99@users.noreply.github.com> --- src/test/fakeSuite/extension.test.ts | 38 +++++++++++++++++++++++----- src/util.ts | 37 ++++++++++++++++++++++++--- 2 files changed, 66 insertions(+), 9 deletions(-) diff --git a/src/test/fakeSuite/extension.test.ts b/src/test/fakeSuite/extension.test.ts index 5ac3fe6d..c273cb0c 100644 --- a/src/test/fakeSuite/extension.test.ts +++ b/src/test/fakeSuite/extension.test.ts @@ -149,21 +149,47 @@ suite("Launch configuration string comparison", () => { expect(util.areLaunchConfigurationStringsEqual(str1, str2)).to.be.true; }); - // On Windows, paths are case-insensitive + test("areLaunchConfigurationStringsEqual - handles null/undefined", () => { + expect(util.areLaunchConfigurationStringsEqual(null as any, null as any)).to + .be.true; + expect( + util.areLaunchConfigurationStringsEqual(undefined as any, undefined as any) + ).to.be.true; + expect( + util.areLaunchConfigurationStringsEqual( + "c:\\Users\\test\\project>out()", + null as any + ) + ).to.be.false; + expect( + util.areLaunchConfigurationStringsEqual( + null as any, + "c:\\Users\\test\\project>out()" + ) + ).to.be.false; + }); + + // On Windows, paths are case-insensitive but arguments are case-sensitive if (process.platform === "win32") { - test("areLaunchConfigurationStringsEqual - different case on Windows", () => { + test("areLaunchConfigurationStringsEqual - different path case on Windows", () => { const str1 = "c:\\Users\\test\\project>out()"; const str2 = "C:\\Users\\Test\\Project>OUT()"; expect(util.areLaunchConfigurationStringsEqual(str1, str2)).to.be.true; }); - test("areLaunchConfigurationStringsEqual - different case with args on Windows", () => { + test("areLaunchConfigurationStringsEqual - different path case same args on Windows", () => { const str1 = "c:\\users\\test\\project>out(arg1,arg2)"; - const str2 = "C:\\Users\\TEST\\Project>Out(Arg1,Arg2)"; - // Note: args themselves might have case-sensitive values, but the whole string comparison - // is made case-insensitive on Windows because the path portion is case-insensitive + const str2 = "C:\\Users\\TEST\\Project>Out(arg1,arg2)"; + // Paths are compared case-insensitively, args are case-sensitive expect(util.areLaunchConfigurationStringsEqual(str1, str2)).to.be.true; }); + + test("areLaunchConfigurationStringsEqual - different args case on Windows", () => { + const str1 = "c:\\users\\test\\project>out(arg1,arg2)"; + const str2 = "C:\\Users\\TEST\\Project>Out(Arg1,Arg2)"; + // Arguments are case-sensitive even on Windows + expect(util.areLaunchConfigurationStringsEqual(str1, str2)).to.be.false; + }); } }); diff --git a/src/util.ts b/src/util.ts index 5d187b80..7ef44b59 100644 --- a/src/util.ts +++ b/src/util.ts @@ -859,15 +859,46 @@ export function hasProperties(obj: any): boolean { } // Helper for comparing launch configuration strings. -// On Windows, paths are case-insensitive, so we need to do a case-insensitive comparison. +// On Windows, paths are case-insensitive, so we need to do a case-insensitive comparison +// for the path portions (cwd and binaryPath), while keeping the arguments case-sensitive. +// Launch configuration string format: [CWD path]>[binaryPath]([binaryArg1,binaryArg2,...]) export function areLaunchConfigurationStringsEqual( str1: string, str2: string ): boolean { - if (process.platform === "win32") { + // Handle null/undefined inputs + if (str1 === null || str1 === undefined) { + return str2 === null || str2 === undefined; + } + if (str2 === null || str2 === undefined) { + return false; + } + + if (process.platform !== "win32") { + return str1 === str2; + } + + // On Windows, parse the launch configuration strings to compare + // paths case-insensitively while keeping arguments case-sensitive. + // Format: [CWD path]>[binaryPath]([binaryArg1,binaryArg2,...]) + const regexp: RegExp = /^(.*)\>(.*)\((.*)\)$/; + const match1: RegExpExecArray | null = regexp.exec(str1); + const match2: RegExpExecArray | null = regexp.exec(str2); + + // If either string doesn't match the expected format, fall back to case-insensitive comparison + if (!match1 || !match2) { return str1.toLowerCase() === str2.toLowerCase(); } - return str1 === str2; + + // Compare cwd and binaryPath case-insensitively, and args case-sensitively + const cwd1: string = match1[1].toLowerCase(); + const cwd2: string = match2[1].toLowerCase(); + const binPath1: string = match1[2].toLowerCase(); + const binPath2: string = match2[2].toLowerCase(); + const args1: string = match1[3]; + const args2: string = match2[3]; + + return cwd1 === cwd2 && binPath1 === binPath2 && args1 === args2; } // Apply any properties from source to destination, logging for overwrite.