22# Licensed under the MIT License. See LICENSE in the project root
33# for license information.
44
5+ import os
56import pickle
67import pytest
78import subprocess
89import sys
910
11+ # This is used for mocking environment variables
12+ # See https://docs.python.org/3/library/unittest.mock-examples.html for more info
13+ from unittest import mock
14+
1015from debugpy .common import log
1116from tests .patterns import some
1217
@@ -21,7 +26,7 @@ def cli_parser():
2126 from debugpy .server import cli
2227
2328 try :
24- cli .parse_argv ()
29+ cli .parse_args ()
2530 except Exception as exc :
2631 os .write (1 , pickle .dumps (exc ))
2732 sys .exit (1 )
@@ -43,14 +48,19 @@ def cli_parser():
4348 "wait_for_client" ,
4449 ]
4550 }
51+
52+ # Serialize the command line args and the options to stdout
4653 os .write (1 , pickle .dumps ([sys .argv [1 :], options ]))
4754
4855 def parse (args ):
4956 log .debug ("Parsing argv: {0!r}" , args )
5057 try :
58+ # Run the CLI parser in a subprocess, and capture its output.
5159 output = subprocess .check_output (
5260 [sys .executable , "-u" , cli_parser .strpath ] + args
5361 )
62+
63+ # Deserialize the output and return the parsed argv and options.
5464 argv , options = pickle .loads (output )
5565 except subprocess .CalledProcessError as exc :
5666 raise pickle .loads (exc .output )
@@ -62,6 +72,7 @@ def parse(args):
6272 return parse
6373
6474
75+ # Test a combination of command line switches
6576@pytest .mark .parametrize ("target_kind" , ["file" , "module" , "code" ])
6677@pytest .mark .parametrize ("mode" , ["listen" , "connect" ])
6778@pytest .mark .parametrize ("address" , ["8888" , "localhost:8888" ])
@@ -71,7 +82,7 @@ def test_targets(cli, target_kind, mode, address, wait_for_client, script_args):
7182 expected_options = {
7283 "mode" : mode ,
7384 "target_kind" : target_kind ,
74- "wait_for_client" : bool ( wait_for_client ),
85+ "wait_for_client" : False
7586 }
7687
7788 args = ["--" + mode , address ]
@@ -84,6 +95,7 @@ def test_targets(cli, target_kind, mode, address, wait_for_client, script_args):
8495
8596 if wait_for_client :
8697 args += ["--wait-for-client" ]
98+ expected_options ["wait_for_client" ] = True
8799
88100 if target_kind == "file" :
89101 target = "spam.py"
@@ -119,32 +131,98 @@ def test_targets(cli, target_kind, mode, address, wait_for_client, script_args):
119131 assert argv == script_args
120132 assert options == some .dict .containing (expected_options )
121133
122-
123- @pytest .mark .parametrize ("value" , ["" , True , False ])
134+ @pytest .mark .parametrize ("value" , [True , False ])
124135def test_configure_subProcess (cli , value ):
125- args = ["--listen" , "8888" ]
126-
127- if value == "" :
128- value = True
129- else :
130- args += ["--configure-subProcess" , str (value )]
131-
132- args += ["spam.py" ]
136+ args = ["--listen" , "8888" , "--configure-subProcess" , str (value ), "spam.py" ]
133137 _ , options = cli (args )
134138
135139 assert options ["config" ]["subProcess" ] == value
136140
141+ @pytest .mark .parametrize ("value" , [True , False ])
142+ def test_configure_subProcess_from_environment (cli , value ):
143+ args = ["--listen" , "8888" , "spam.py" ]
144+ with mock .patch .dict (os .environ , {"DEBUGPY_EXTRA_ARGV" : "--configure-subProcess " + str (value )}):
145+ _ , options = cli (args )
146+
147+ assert options ["config" ]["subProcess" ] == value
137148
138149def test_unsupported_switch (cli ):
139- with pytest .raises (Exception ) :
150+ with pytest .raises (ValueError ) as ex :
140151 cli (["--listen" , "8888" , "--xyz" , "123" , "spam.py" ])
152+
153+ assert "unrecognized switch --xyz" in str (ex .value )
141154
155+ def test_unsupported_switch_from_environment (cli ):
156+ with pytest .raises (ValueError ) as ex :
157+ with mock .patch .dict (os .environ , {"DEBUGPY_EXTRA_ARGV" : "--xyz 123" }):
158+ cli (["--listen" , "8888" , "spam.py" ])
159+
160+ assert "unrecognized switch --xyz" in str (ex .value )
142161
143162def test_unsupported_configure (cli ):
144- with pytest .raises (Exception ) :
163+ with pytest .raises (ValueError ) as ex :
145164 cli (["--connect" , "127.0.0.1:8888" , "--configure-xyz" , "123" , "spam.py" ])
165+
166+ assert "unknown property 'xyz'" in str (ex .value )
167+
168+ def test_unsupported_configure_from_environment (cli ):
169+ with pytest .raises (ValueError ) as ex :
170+ with mock .patch .dict (os .environ , {"DEBUGPY_EXTRA_ARGV" : "--configure-xyz 123" }):
171+ cli (["--connect" , "127.0.0.1:8888" , "spam.py" ])
146172
173+ assert "unknown property 'xyz'" in str (ex .value )
147174
148175def test_address_required (cli ):
149- with pytest .raises (Exception ) :
176+ with pytest .raises (ValueError ) as ex :
150177 cli (["-m" , "spam" ])
178+
179+ assert "either --listen or --connect is required" in str (ex .value )
180+
181+ def test_missing_target (cli ):
182+ with pytest .raises (ValueError ) as ex :
183+ cli (["--listen" , "8888" ])
184+
185+ assert "missing target" in str (ex .value )
186+
187+ def test_duplicate_switch (cli ):
188+ with pytest .raises (ValueError ) as ex :
189+ cli (["--listen" , "8888" , "--listen" , "9999" , "spam.py" ])
190+
191+ assert "duplicate switch on command line: --listen" in str (ex .value )
192+
193+ def test_duplicate_switch_from_environment (cli ):
194+ with pytest .raises (ValueError ) as ex :
195+ with mock .patch .dict (os .environ , {"DEBUGPY_EXTRA_ARGV" : "--listen 8888 --listen 9999" }):
196+ cli (["spam.py" ])
197+
198+ assert "duplicate switch from environment: --listen" in str (ex .value )
199+
200+ # Test that switches can be read from the environment
201+ def test_read_switches_from_environment (cli ):
202+ args = ["spam.py" ]
203+
204+ with mock .patch .dict (os .environ , {"DEBUGPY_EXTRA_ARGV" : "--connect 5678" }):
205+ _ , options = cli (args )
206+
207+ assert options ["mode" ] == "connect"
208+ assert options ["address" ] == ("127.0.0.1" , 5678 )
209+ assert options ["target" ] == "spam.py"
210+
211+ # Test that command line switches override environment variables
212+ def test_override_environment_switch (cli ):
213+ args = ["--connect" , "8888" , "spam.py" ]
214+
215+ with mock .patch .dict (os .environ , {"DEBUGPY_EXTRA_ARGV" : "--connect 5678" }):
216+ _ , options = cli (args )
217+
218+ assert options ["mode" ] == "connect"
219+ assert options ["address" ] == ("127.0.0.1" , 8888 )
220+ assert options ["target" ] == "spam.py"
221+
222+ # Test that script args (passed to target) are preserved
223+ def test_script_args (cli ):
224+ args = ["--listen" , "8888" , "spam.py" , "arg1" , "arg2" ]
225+ argv , options = cli (args )
226+
227+ assert argv == ["arg1" , "arg2" ]
228+ assert options ["target" ] == "spam.py"
0 commit comments