@@ -52,6 +52,15 @@ getInfoPerOS().forEach(([osName, osType, path]) => {
5252 getDebugEnvironmentVariablesStub = sinon . stub ( helper , 'getDebugEnvironmentVariables' ) ;
5353 getConfigurationStub = sinon . stub ( vscodeapi , 'getConfiguration' ) ;
5454 getConfigurationStub . withArgs ( 'debugpy' , sinon . match . any ) . returns ( createMoqConfiguration ( true ) ) ;
55+ // Mock python configuration for useEnvExtension check
56+ const pythonConfig = TypeMoq . Mock . ofType < WorkspaceConfiguration > ( ) ;
57+ pythonConfig . setup ( ( p ) => p . get < boolean > ( 'useEnvironmentsExtension' , false ) ) . returns ( ( ) => false ) ;
58+ getConfigurationStub . withArgs ( 'python' ) . returns ( pythonConfig . object ) ;
59+ // Mock terminal configuration with default values
60+ const terminalConfig = TypeMoq . Mock . ofType < WorkspaceConfiguration > ( ) ;
61+ terminalConfig . setup ( ( c ) => c . get < string > ( TypeMoq . It . isAnyString ( ) ) ) . returns ( ( ) => undefined ) ;
62+ terminalConfig . setup ( ( c ) => c . get < any > ( TypeMoq . It . isAnyString ( ) ) ) . returns ( ( ) => undefined ) ;
63+ getConfigurationStub . withArgs ( 'terminal' ) . returns ( terminalConfig . object ) ;
5564 } ) ;
5665
5766 teardown ( ( ) => {
@@ -1045,5 +1054,228 @@ getInfoPerOS().forEach(([osName, osType, path]) => {
10451054 await testSetting ( requestType , { subProcess : true } , DebugOptions . SubProcess , true ) ;
10461055 } ) ;
10471056 } ) ;
1057+
1058+ suite ( 'terminalQuoteCharacter tests' , ( ) => {
1059+ let platformStub : sinon . SinonStub ;
1060+
1061+ function setupTerminalProfile (
1062+ profile : string | undefined ,
1063+ path : string = 'bash' ,
1064+ platform : NodeJS . Platform = 'linux' ,
1065+ ) {
1066+ const terminalConfig = TypeMoq . Mock . ofType < WorkspaceConfiguration > ( ) ;
1067+
1068+ // Mock process.platform
1069+ if ( platformStub ) {
1070+ platformStub . restore ( ) ;
1071+ }
1072+ platformStub = sinon . stub ( process , 'platform' ) . value ( platform ) ;
1073+
1074+ if ( platform === 'win32' ) {
1075+ terminalConfig
1076+ . setup ( ( c ) => c . get < string > ( 'integrated.defaultProfile.windows' ) )
1077+ . returns ( ( ) => profile ) ;
1078+ const profiles : any = { } ;
1079+ if ( profile ) {
1080+ profiles [ profile ] = { path } ;
1081+ }
1082+ terminalConfig . setup ( ( c ) => c . get < any > ( 'integrated.profiles.windows' ) ) . returns ( ( ) => profiles ) ;
1083+ } else if ( platform === 'linux' ) {
1084+ terminalConfig
1085+ . setup ( ( c ) => c . get < string > ( 'integrated.defaultProfile.linux' ) )
1086+ . returns ( ( ) => profile ) ;
1087+ const profiles : any = { } ;
1088+ if ( profile ) {
1089+ profiles [ profile ] = { path } ;
1090+ }
1091+ terminalConfig . setup ( ( c ) => c . get < any > ( 'integrated.profiles.linux' ) ) . returns ( ( ) => profiles ) ;
1092+ } else if ( platform === 'darwin' ) {
1093+ terminalConfig . setup ( ( c ) => c . get < string > ( 'integrated.defaultProfile.osx' ) ) . returns ( ( ) => profile ) ;
1094+ const profiles : any = { } ;
1095+ if ( profile ) {
1096+ profiles [ profile ] = { path } ;
1097+ }
1098+ terminalConfig . setup ( ( c ) => c . get < any > ( 'integrated.profiles.osx' ) ) . returns ( ( ) => profiles ) ;
1099+ }
1100+
1101+ getConfigurationStub . withArgs ( 'terminal' ) . returns ( terminalConfig . object ) ;
1102+ }
1103+
1104+ test ( 'Default terminalQuoteCharacter is computed for bash shell' , async ( ) => {
1105+ const pythonPath = `PythonPath_${ new Date ( ) . toString ( ) } ` ;
1106+ const workspaceFolder = createMoqWorkspaceFolder ( __dirname ) ;
1107+ const pythonFile = 'xyz.py' ;
1108+ setupIoc ( pythonPath , workspaceFolder ) ;
1109+ setupActiveEditor ( pythonFile , PYTHON_LANGUAGE ) ;
1110+ setupTerminalProfile ( 'Bash' , '/bin/bash' , 'linux' ) ;
1111+
1112+ const debugConfig = await resolveDebugConfiguration ( workspaceFolder , {
1113+ ...launch ,
1114+ } ) ;
1115+
1116+ expect ( debugConfig ) . to . have . property ( 'terminalQuoteCharacter' , '"' ) ;
1117+ } ) ;
1118+
1119+ test ( 'Default terminalQuoteCharacter is computed for zsh shell' , async ( ) => {
1120+ const pythonPath = `PythonPath_${ new Date ( ) . toString ( ) } ` ;
1121+ const workspaceFolder = createMoqWorkspaceFolder ( __dirname ) ;
1122+ const pythonFile = 'xyz.py' ;
1123+ setupIoc ( pythonPath , workspaceFolder ) ;
1124+ setupActiveEditor ( pythonFile , PYTHON_LANGUAGE ) ;
1125+ setupTerminalProfile ( 'Zsh' , '/bin/zsh' , 'darwin' ) ;
1126+
1127+ const debugConfig = await resolveDebugConfiguration ( workspaceFolder , {
1128+ ...launch ,
1129+ } ) ;
1130+
1131+ expect ( debugConfig ) . to . have . property ( 'terminalQuoteCharacter' , '"' ) ;
1132+ } ) ;
1133+
1134+ test ( 'Default terminalQuoteCharacter is computed for fish shell' , async ( ) => {
1135+ const pythonPath = `PythonPath_${ new Date ( ) . toString ( ) } ` ;
1136+ const workspaceFolder = createMoqWorkspaceFolder ( __dirname ) ;
1137+ const pythonFile = 'xyz.py' ;
1138+ setupIoc ( pythonPath , workspaceFolder ) ;
1139+ setupActiveEditor ( pythonFile , PYTHON_LANGUAGE ) ;
1140+ setupTerminalProfile ( 'Fish' , '/usr/bin/fish' , 'linux' ) ;
1141+
1142+ const debugConfig = await resolveDebugConfiguration ( workspaceFolder , {
1143+ ...launch ,
1144+ } ) ;
1145+
1146+ expect ( debugConfig ) . to . have . property ( 'terminalQuoteCharacter' , '"' ) ;
1147+ } ) ;
1148+
1149+ test ( 'Default terminalQuoteCharacter is computed for PowerShell' , async ( ) => {
1150+ const pythonPath = `PythonPath_${ new Date ( ) . toString ( ) } ` ;
1151+ const workspaceFolder = createMoqWorkspaceFolder ( __dirname ) ;
1152+ const pythonFile = 'xyz.py' ;
1153+ setupIoc ( pythonPath , workspaceFolder ) ;
1154+ setupActiveEditor ( pythonFile , PYTHON_LANGUAGE ) ;
1155+ setupTerminalProfile (
1156+ 'PowerShell' ,
1157+ 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe' ,
1158+ 'win32' ,
1159+ ) ;
1160+
1161+ const debugConfig = await resolveDebugConfiguration ( workspaceFolder , {
1162+ ...launch ,
1163+ } ) ;
1164+
1165+ expect ( debugConfig ) . to . have . property ( 'terminalQuoteCharacter' , "'" ) ;
1166+ } ) ;
1167+
1168+ test ( 'Default terminalQuoteCharacter is computed for cmd.exe' , async ( ) => {
1169+ const pythonPath = `PythonPath_${ new Date ( ) . toString ( ) } ` ;
1170+ const workspaceFolder = createMoqWorkspaceFolder ( __dirname ) ;
1171+ const pythonFile = 'xyz.py' ;
1172+ setupIoc ( pythonPath , workspaceFolder ) ;
1173+ setupActiveEditor ( pythonFile , PYTHON_LANGUAGE ) ;
1174+ setupTerminalProfile ( 'Command Prompt' , 'C:\\Windows\\System32\\cmd.exe' , 'win32' ) ;
1175+
1176+ const debugConfig = await resolveDebugConfiguration ( workspaceFolder , {
1177+ ...launch ,
1178+ } ) ;
1179+
1180+ expect ( debugConfig ) . to . have . property ( 'terminalQuoteCharacter' , '"' ) ;
1181+ } ) ;
1182+
1183+ test ( 'Default terminalQuoteCharacter is double quote when terminal profile is not configured' , async ( ) => {
1184+ const pythonPath = `PythonPath_${ new Date ( ) . toString ( ) } ` ;
1185+ const workspaceFolder = createMoqWorkspaceFolder ( __dirname ) ;
1186+ const pythonFile = 'xyz.py' ;
1187+ setupIoc ( pythonPath , workspaceFolder ) ;
1188+ setupActiveEditor ( pythonFile , PYTHON_LANGUAGE ) ;
1189+ setupTerminalProfile ( undefined , 'bash' , 'linux' ) ;
1190+
1191+ const debugConfig = await resolveDebugConfiguration ( workspaceFolder , {
1192+ ...launch ,
1193+ } ) ;
1194+
1195+ expect ( debugConfig ) . to . have . property ( 'terminalQuoteCharacter' , '"' ) ;
1196+ } ) ;
1197+
1198+ test ( 'User-provided terminalQuoteCharacter is preserved when valid (single character)' , async ( ) => {
1199+ const pythonPath = `PythonPath_${ new Date ( ) . toString ( ) } ` ;
1200+ const workspaceFolder = createMoqWorkspaceFolder ( __dirname ) ;
1201+ const pythonFile = 'xyz.py' ;
1202+ setupIoc ( pythonPath , workspaceFolder ) ;
1203+ setupActiveEditor ( pythonFile , PYTHON_LANGUAGE ) ;
1204+ setupTerminalProfile ( 'Bash' , '/bin/bash' , 'linux' ) ;
1205+
1206+ const debugConfig = await resolveDebugConfiguration ( workspaceFolder , {
1207+ ...launch ,
1208+ terminalQuoteCharacter : "'" ,
1209+ } ) ;
1210+
1211+ expect ( debugConfig ) . to . have . property ( 'terminalQuoteCharacter' , "'" ) ;
1212+ } ) ;
1213+
1214+ test ( 'Invalid terminalQuoteCharacter (empty string) triggers fallback to computed default' , async ( ) => {
1215+ const pythonPath = `PythonPath_${ new Date ( ) . toString ( ) } ` ;
1216+ const workspaceFolder = createMoqWorkspaceFolder ( __dirname ) ;
1217+ const pythonFile = 'xyz.py' ;
1218+ setupIoc ( pythonPath , workspaceFolder ) ;
1219+ setupActiveEditor ( pythonFile , PYTHON_LANGUAGE ) ;
1220+ setupTerminalProfile ( 'PowerShell' , 'powershell.exe' , 'win32' ) ;
1221+
1222+ const debugConfig = await resolveDebugConfiguration ( workspaceFolder , {
1223+ ...launch ,
1224+ terminalQuoteCharacter : '' ,
1225+ } ) ;
1226+
1227+ expect ( debugConfig ) . to . have . property ( 'terminalQuoteCharacter' , "'" ) ;
1228+ } ) ;
1229+
1230+ test ( 'Invalid terminalQuoteCharacter (multi-character) triggers fallback to computed default' , async ( ) => {
1231+ const pythonPath = `PythonPath_${ new Date ( ) . toString ( ) } ` ;
1232+ const workspaceFolder = createMoqWorkspaceFolder ( __dirname ) ;
1233+ const pythonFile = 'xyz.py' ;
1234+ setupIoc ( pythonPath , workspaceFolder ) ;
1235+ setupActiveEditor ( pythonFile , PYTHON_LANGUAGE ) ;
1236+ setupTerminalProfile ( 'Bash' , '/bin/bash' , 'linux' ) ;
1237+
1238+ const debugConfig = await resolveDebugConfiguration ( workspaceFolder , {
1239+ ...launch ,
1240+ terminalQuoteCharacter : '""' ,
1241+ } ) ;
1242+
1243+ expect ( debugConfig ) . to . have . property ( 'terminalQuoteCharacter' , '"' ) ;
1244+ } ) ;
1245+
1246+ test ( 'terminalQuoteCharacter handles pwsh (PowerShell Core)' , async ( ) => {
1247+ const pythonPath = `PythonPath_${ new Date ( ) . toString ( ) } ` ;
1248+ const workspaceFolder = createMoqWorkspaceFolder ( __dirname ) ;
1249+ const pythonFile = 'xyz.py' ;
1250+ setupIoc ( pythonPath , workspaceFolder ) ;
1251+ setupActiveEditor ( pythonFile , PYTHON_LANGUAGE ) ;
1252+ setupTerminalProfile ( 'PowerShell Core' , '/usr/local/bin/pwsh' , 'darwin' ) ;
1253+
1254+ const debugConfig = await resolveDebugConfiguration ( workspaceFolder , {
1255+ ...launch ,
1256+ } ) ;
1257+
1258+ expect ( debugConfig ) . to . have . property ( 'terminalQuoteCharacter' , "'" ) ;
1259+ } ) ;
1260+
1261+ test ( 'terminalQuoteCharacter is case-insensitive for shell detection' , async ( ) => {
1262+ const pythonPath = `PythonPath_${ new Date ( ) . toString ( ) } ` ;
1263+ const workspaceFolder = createMoqWorkspaceFolder ( __dirname ) ;
1264+ const pythonFile = 'xyz.py' ;
1265+ setupIoc ( pythonPath , workspaceFolder ) ;
1266+ setupActiveEditor ( pythonFile , PYTHON_LANGUAGE ) ;
1267+ setupTerminalProfile (
1268+ 'PowerShell' ,
1269+ 'C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\POWERSHELL.EXE' ,
1270+ 'win32' ,
1271+ ) ;
1272+
1273+ const debugConfig = await resolveDebugConfiguration ( workspaceFolder , {
1274+ ...launch ,
1275+ } ) ;
1276+
1277+ expect ( debugConfig ) . to . have . property ( 'terminalQuoteCharacter' , "'" ) ;
1278+ } ) ;
1279+ } ) ;
10481280 } ) ;
10491281} ) ;
0 commit comments