Skip to content

Commit bb0e83d

Browse files
committed
actually move to showing empty node and comment fixes
1 parent ce9883d commit bb0e83d

File tree

6 files changed

+61
-65
lines changed

6 files changed

+61
-65
lines changed

.github/instructions/testing_feature_area.instructions.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ Project-based testing enables multi-project workspace support where each Python
182182
4. **Test discovery**: For each project, the controller calls `project.discoveryAdapter.discoverTests()` with the project's URI. The adapter sets `PROJECT_ROOT_PATH` environment variable for the Python runner.
183183
5. **Python side**:
184184
- For pytest: `get_test_root_path()` in `vscode_pytest/__init__.py` returns `PROJECT_ROOT_PATH` (if set) or falls back to `cwd`.
185-
- For unittest: `discovery.py` uses `PROJECT_ROOT_PATH` as `top_level_dir` and `cwd_override` to root the test tree at the project directory.
185+
- For unittest: `discovery.py` uses `PROJECT_ROOT_PATH` as `top_level_dir` and `project_root_path` to root the test tree at the project directory.
186186
6. **Test tree**: Each project gets its own root node in the Test Explorer, with test IDs scoped by project ID using the `@@vsc@@` separator (defined in `projectUtils.ts`).
187187

188188
### Nested project handling: pytest vs unittest
@@ -201,9 +201,9 @@ This approach was chosen because:
201201
2. Implementing custom exclusion would add significant complexity with minimal benefit
202202
3. The existing approach is transparent and predictable - each project shows what it finds
203203

204-
### Empty projects and hidden root nodes
204+
### Empty projects and root nodes
205205

206-
**Important:** If a project discovers zero tests, its root node will **not appear** in the Test Explorer. This is by design - the test tree only shows projects that have actual tests.
206+
If a project discovers zero tests, its root node will still appear in the Test Explorer as an empty folder. This ensures consistent behavior and makes it clear which projects were discovered, even if they have no tests yet.
207207

208208
### Logging prefix
209209

@@ -213,17 +213,17 @@ All project-based testing logs use the `[test-by-project]` prefix for easy filte
213213

214214
- Python side:
215215
- `python_files/vscode_pytest/__init__.py``get_test_root_path()` function and `PROJECT_ROOT_PATH` environment variable for pytest.
216-
- `python_files/unittestadapter/discovery.py``discover_tests()` with `cwd_override` parameter and `PROJECT_ROOT_PATH` handling for unittest discovery.
217-
- `python_files/unittestadapter/execution.py``run_tests()` with `cwd_override` parameter and `PROJECT_ROOT_PATH` handling for unittest execution.
216+
- `python_files/unittestadapter/discovery.py``discover_tests()` with `project_root_path` parameter and `PROJECT_ROOT_PATH` handling for unittest discovery.
217+
- `python_files/unittestadapter/execution.py``run_tests()` with `project_root_path` parameter and `PROJECT_ROOT_PATH` handling for unittest execution.
218218
- TypeScript: `testProjectRegistry.ts`, `projectAdapter.ts`, `projectUtils.ts`, and the discovery/execution adapters.
219219

220220
### Tests
221221

222222
- `src/test/testing/testController/common/testProjectRegistry.unit.test.ts` — TestProjectRegistry tests
223223
- `src/test/testing/testController/common/projectUtils.unit.test.ts` — Project utility function tests
224224
- `python_files/tests/pytestadapter/test_discovery.py` — pytest PROJECT_ROOT_PATH tests (see `test_project_root_path_env_var()` and `test_symlink_with_project_root_path()`)
225-
- `python_files/tests/unittestadapter/test_discovery.py` — unittest `cwd_override` / PROJECT_ROOT_PATH discovery tests
226-
- `python_files/tests/unittestadapter/test_execution.py` — unittest `cwd_override` / PROJECT_ROOT_PATH execution tests
225+
- `python_files/tests/unittestadapter/test_discovery.py` — unittest `project_root_path` / PROJECT_ROOT_PATH discovery tests
226+
- `python_files/tests/unittestadapter/test_execution.py` — unittest `project_root_path` / PROJECT_ROOT_PATH execution tests
227227
- `src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts` — unittest discovery adapter PROJECT_ROOT_PATH tests
228228
- `src/test/testing/testController/unittest/testExecutionAdapter.unit.test.ts` — unittest execution adapter PROJECT_ROOT_PATH tests
229229

python_files/tests/unittestadapter/test_discovery.py

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,7 @@ def test_empty_discovery() -> None:
191191
actual = discover_tests(start_dir, pattern, None)
192192

193193
assert actual["status"] == "success"
194-
# When no tests are found, the tests key should not be present in the payload
195-
assert "tests" not in actual
194+
assert "tests" in actual
196195
assert "error" not in actual
197196

198197

@@ -329,25 +328,25 @@ def test_simple_django_collect():
329328

330329

331330
def test_project_root_path_with_cwd_override() -> None:
332-
"""Test unittest discovery with cwd_override parameter.
331+
"""Test unittest discovery with project_root_path parameter.
333332
334333
This simulates project-based testing where the cwd in the payload should be
335-
the project root (cwd_override) rather than the start_dir.
334+
the project root (project_root_path) rather than the start_dir.
336335
337-
When cwd_override is provided:
338-
- The cwd in the response should match cwd_override
336+
When project_root_path is provided:
337+
- The cwd in the response should match project_root_path
339338
- The test tree root should still be built correctly based on top_level_dir
340339
"""
341340
# Use unittest_skip folder as our "project" directory
342341
project_path = TEST_DATA_PATH / "unittest_skip"
343342
start_dir = os.fsdecode(project_path)
344343
pattern = "unittest_*"
345344

346-
# Call discover_tests with cwd_override to simulate PROJECT_ROOT_PATH
347-
actual = discover_tests(start_dir, pattern, None, cwd_override=start_dir)
345+
# Call discover_tests with project_root_path to simulate PROJECT_ROOT_PATH
346+
actual = discover_tests(start_dir, pattern, None, project_root_path=start_dir)
348347

349348
assert actual["status"] == "success"
350-
# cwd in response should match the cwd_override (project root)
349+
# cwd in response should match the project_root_path (project root)
351350
assert actual["cwd"] == os.fsdecode(project_path), (
352351
f"Expected cwd '{os.fsdecode(project_path)}', got '{actual['cwd']}'"
353352
)
@@ -362,11 +361,11 @@ def test_project_root_path_with_cwd_override() -> None:
362361

363362

364363
def test_project_root_path_with_different_cwd_and_start_dir() -> None:
365-
"""Test unittest discovery where cwd_override differs from start_dir.
364+
"""Test unittest discovery where project_root_path differs from start_dir.
366365
367366
This simulates the scenario where:
368367
- start_dir points to a subfolder where tests are located
369-
- cwd_override (PROJECT_ROOT_PATH) points to the project root
368+
- project_root_path (PROJECT_ROOT_PATH) points to the project root
370369
371370
The cwd in the response should be the project root, while discovery
372371
still runs from the start_dir.
@@ -384,11 +383,11 @@ def test_project_root_path_with_different_cwd_and_start_dir() -> None:
384383
pattern = "test_*.py"
385384
top_level_dir = os.fsdecode(project_path)
386385

387-
# Call discover_tests with cwd_override set to project root
388-
actual = discover_tests(start_dir, pattern, top_level_dir, cwd_override=top_level_dir)
386+
# Call discover_tests with project_root_path set to project root
387+
actual = discover_tests(start_dir, pattern, top_level_dir, project_root_path=top_level_dir)
389388

390389
assert actual["status"] == "success"
391-
# cwd should be the project root (cwd_override), not the start_dir
390+
# cwd should be the project root (project_root_path), not the start_dir
392391
assert actual["cwd"] == os.fsdecode(project_path), (
393392
f"Expected cwd '{os.fsdecode(project_path)}', got '{actual['cwd']}'"
394393
)
@@ -410,29 +409,29 @@ def test_symlink_with_project_root_path() -> None:
410409
411410
This tests the combination of:
412411
1. A symlinked test directory
413-
2. cwd_override (PROJECT_ROOT_PATH) set to the symlink path
412+
2. project_root_path (PROJECT_ROOT_PATH) set to the symlink path
414413
415414
This simulates project-based testing where the project root is a symlink,
416415
ensuring test IDs and paths are correctly resolved through the symlink.
417416
"""
418417
with helpers.create_symlink(TEST_DATA_PATH, "unittest_skip", "symlink_unittest") as (
419-
source,
418+
_source,
420419
destination,
421420
):
422421
assert destination.is_symlink()
423422

424423
# Run discovery with:
425424
# - start_dir pointing to the symlink destination
426-
# - cwd_override set to the symlink destination (simulating PROJECT_ROOT_PATH)
425+
# - project_root_path set to the symlink destination (simulating PROJECT_ROOT_PATH)
427426
start_dir = os.fsdecode(destination)
428427
pattern = "unittest_*"
429428

430-
actual = discover_tests(start_dir, pattern, None, cwd_override=start_dir)
429+
actual = discover_tests(start_dir, pattern, None, project_root_path=start_dir)
431430

432431
assert actual["status"] == "success", (
433432
f"Status is not 'success', error is: {actual.get('error')}"
434433
)
435-
# cwd should be the symlink path (cwd_override)
434+
# cwd should be the symlink path (project_root_path)
436435
assert actual["cwd"] == os.fsdecode(destination), (
437436
f"CWD does not match symlink path: expected {os.fsdecode(destination)}, got {actual['cwd']}"
438437
)

python_files/tests/unittestadapter/test_execution.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -344,13 +344,13 @@ def test_basic_run_django():
344344

345345

346346
def test_project_root_path_with_cwd_override(mock_send_run_data) -> None: # noqa: ARG001
347-
"""Test unittest execution with cwd_override parameter.
347+
"""Test unittest execution with project_root_path parameter.
348348
349349
This simulates project-based testing where the cwd in the payload should be
350-
the project root (cwd_override) rather than the start_dir.
350+
the project root (project_root_path) rather than the start_dir.
351351
352-
When cwd_override is provided:
353-
- The cwd in the response should match cwd_override
352+
When project_root_path is provided:
353+
- The cwd in the response should match project_root_path
354354
- Test execution should still work correctly with start_dir
355355
"""
356356
# Use unittest_folder as our "project" directory
@@ -363,19 +363,19 @@ def test_project_root_path_with_cwd_override(mock_send_run_data) -> None: # noq
363363

364364
os.environ["TEST_RUN_PIPE"] = "fake"
365365

366-
# Call run_tests with cwd_override to simulate PROJECT_ROOT_PATH
366+
# Call run_tests with project_root_path to simulate PROJECT_ROOT_PATH
367367
actual = run_tests(
368368
start_dir,
369369
test_ids,
370370
pattern,
371371
None,
372372
1,
373373
None,
374-
cwd_override=start_dir,
374+
project_root_path=start_dir,
375375
)
376376

377377
assert actual["status"] == "success"
378-
# cwd in response should match the cwd_override (project root)
378+
# cwd in response should match the project_root_path (project root)
379379
assert actual["cwd"] == os.fsdecode(project_path), (
380380
f"Expected cwd '{os.fsdecode(project_path)}', got '{actual['cwd']}'"
381381
)
@@ -384,12 +384,12 @@ def test_project_root_path_with_cwd_override(mock_send_run_data) -> None: # noq
384384
assert actual["result"][test_ids[0]]["outcome"] == "success"
385385

386386

387-
def test_project_root_path_with_different_cwd_and_start_dir() -> None:
388-
"""Test unittest execution where cwd_override differs from start_dir.
387+
def test_project_root_path_with_different_cwd_and_start_dir(mock_send_run_data) -> None: # noqa: ARG001
388+
"""Test unittest execution where project_root_path differs from start_dir.
389389
390390
This simulates the scenario where:
391391
- start_dir points to a subfolder where tests are located
392-
- cwd_override (PROJECT_ROOT_PATH) points to the project root
392+
- project_root_path (PROJECT_ROOT_PATH) points to the project root
393393
394394
The cwd in the response should be the project root, while execution
395395
still runs from the start_dir.
@@ -404,19 +404,19 @@ def test_project_root_path_with_different_cwd_and_start_dir() -> None:
404404

405405
os.environ["TEST_RUN_PIPE"] = "fake"
406406

407-
# Call run_tests with cwd_override set to project root
407+
# Call run_tests with project_root_path set to project root
408408
actual = run_tests(
409409
start_dir,
410410
test_ids,
411411
pattern,
412412
None,
413413
1,
414414
None,
415-
cwd_override=os.fsdecode(project_path),
415+
project_root_path=os.fsdecode(project_path),
416416
)
417417

418418
assert actual["status"] == "success"
419-
# cwd should be the project root (cwd_override)
419+
# cwd should be the project root (project_root_path)
420420
assert actual["cwd"] == os.fsdecode(project_path), (
421421
f"Expected cwd '{os.fsdecode(project_path)}', got '{actual['cwd']}'"
422422
)
@@ -429,11 +429,11 @@ def test_project_root_path_with_different_cwd_and_start_dir() -> None:
429429
reason="Symlinks require elevated privileges on Windows",
430430
)
431431
def test_symlink_with_project_root_path(mock_send_run_data) -> None: # noqa: ARG001
432-
"""Test unittest execution with both symlink and cwd_override set.
432+
"""Test unittest execution with both symlink and project_root_path set.
433433
434434
This tests the combination of:
435435
1. A symlinked test directory
436-
2. cwd_override (PROJECT_ROOT_PATH) set to the symlink path
436+
2. project_root_path (PROJECT_ROOT_PATH) set to the symlink path
437437
438438
This simulates project-based testing where the project root is a symlink,
439439
ensuring execution payloads correctly use the symlink path.
@@ -446,7 +446,7 @@ def test_symlink_with_project_root_path(mock_send_run_data) -> None: # noqa: AR
446446

447447
# Run execution with:
448448
# - start_dir pointing to the symlink destination
449-
# - cwd_override set to the symlink destination (simulating PROJECT_ROOT_PATH)
449+
# - project_root_path set to the symlink destination (simulating PROJECT_ROOT_PATH)
450450
start_dir = os.fsdecode(destination)
451451
pattern = "test_add*"
452452
test_ids = [
@@ -462,13 +462,13 @@ def test_symlink_with_project_root_path(mock_send_run_data) -> None: # noqa: AR
462462
None,
463463
1,
464464
None,
465-
cwd_override=start_dir,
465+
project_root_path=start_dir,
466466
)
467467

468468
assert actual["status"] == "success", (
469469
f"Status is not 'success', error is: {actual.get('error')}"
470470
)
471-
# cwd should be the symlink path (cwd_override)
471+
# cwd should be the symlink path (project_root_path)
472472
assert actual["cwd"] == os.fsdecode(destination), (
473473
f"CWD does not match symlink path: expected {os.fsdecode(destination)}, got {actual['cwd']}"
474474
)

python_files/unittestadapter/discovery.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ def discover_tests(
2727
start_dir: str,
2828
pattern: str,
2929
top_level_dir: Optional[str],
30-
cwd_override: Optional[str] = None,
30+
project_root_path: Optional[str] = None,
3131
) -> DiscoveryPayloadDict:
3232
"""Returns a dictionary containing details of the discovered tests.
3333
3434
The returned dict has the following keys:
3535
36-
- cwd: Absolute path to the test start directory (or cwd_override if provided);
36+
- cwd: Absolute path to the test start directory (or project_root_path if provided);
3737
- status: Test discovery status, can be "success" or "error";
3838
- tests: Discoverered tests if any, not present otherwise. Note that the status can be "error" but the payload can still contain tests;
3939
- error: Discovery error if any, not present otherwise.
@@ -62,10 +62,10 @@ def discover_tests(
6262
start_dir: Directory where test discovery starts
6363
pattern: Pattern to match test files (e.g., "test*.py")
6464
top_level_dir: Top-level directory for the test tree hierarchy
65-
cwd_override: Optional override for the cwd in the response payload
66-
(used for project-based testing to set project root)
65+
project_root_path: Optional project root path for the cwd in the response payload
66+
(used for project-based testing to root test tree at project)
6767
"""
68-
cwd = os.path.abspath(cwd_override if cwd_override else start_dir) # noqa: PTH100
68+
cwd = os.path.abspath(project_root_path or start_dir) # noqa: PTH100
6969
if "/" in start_dir: # is a subdir
7070
parent_dir = os.path.dirname(start_dir) # noqa: PTH120
7171
sys.path.insert(0, parent_dir)
@@ -91,10 +91,9 @@ def discover_tests(
9191
except Exception:
9292
error.append(traceback.format_exc())
9393

94-
# Only include tests in the payload if tests were discovered.
95-
# If no tests found (tests is None), omit the tests key per the docstring contract.
96-
if tests is not None:
97-
payload["tests"] = tests
94+
# Still include the tests in the payload even if there are errors so that the TS
95+
# side can determine if it is from run or discovery.
96+
payload["tests"] = tests if tests is not None else None
9897

9998
if len(error):
10099
payload["status"] = "error"
@@ -152,7 +151,9 @@ def discover_tests(
152151
)
153152

154153
# Perform regular unittest test discovery.
155-
# Pass project_root_path as cwd_override so the payload's cwd matches the project root.
156-
payload = discover_tests(start_dir, pattern, top_level_dir, cwd_override=project_root_path)
154+
# Pass project_root_path so the payload's cwd matches the project root.
155+
payload = discover_tests(
156+
start_dir, pattern, top_level_dir, project_root_path=project_root_path
157+
)
157158
# Post this discovery payload.
158159
send_post_request(payload, test_run_pipe)

python_files/unittestadapter/execution.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ def run_tests(
194194
verbosity: int,
195195
failfast: Optional[bool], # noqa: FBT001
196196
locals_: Optional[bool] = None, # noqa: FBT001
197-
cwd_override: Optional[str] = None,
197+
project_root_path: Optional[str] = None,
198198
) -> ExecutionPayloadDict:
199199
"""Run unittests and return the execution payload.
200200
@@ -206,10 +206,10 @@ def run_tests(
206206
verbosity: Verbosity level for test output
207207
failfast: Stop on first failure
208208
locals_: Show local variables in tracebacks
209-
cwd_override: Optional override for the cwd in the response payload
210-
(used for project-based testing to set project root)
209+
project_root_path: Optional project root path for the cwd in the response payload
210+
(used for project-based testing to root test tree at project)
211211
"""
212-
cwd = os.path.abspath(cwd_override or start_dir) # noqa: PTH100
212+
cwd = os.path.abspath(project_root_path or start_dir) # noqa: PTH100
213213
if "/" in start_dir: # is a subdir
214214
parent_dir = os.path.dirname(start_dir) # noqa: PTH120
215215
sys.path.insert(0, parent_dir)
@@ -378,7 +378,7 @@ def send_run_data(raw_data, test_run_pipe):
378378
)
379379

380380
# Perform regular unittest execution.
381-
# Pass project_root_path as cwd_override so the payload's cwd matches the project root.
381+
# Pass project_root_path so the payload's cwd matches the project root.
382382
payload = run_tests(
383383
start_dir,
384384
test_ids,
@@ -387,7 +387,7 @@ def send_run_data(raw_data, test_run_pipe):
387387
verbosity,
388388
failfast,
389389
locals_,
390-
cwd_override=project_root_path,
390+
project_root_path=project_root_path,
391391
)
392392

393393
if is_coverage_run:

python_files/unittestadapter/pvsc_utils.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -281,10 +281,6 @@ def build_test_tree(
281281
} # concatenate class name and function test name
282282
current_node["children"].append(test_node)
283283

284-
# If no tests were discovered, return None instead of an empty root node
285-
if not root["children"]:
286-
return None, error
287-
288284
return root, error
289285

290286

0 commit comments

Comments
 (0)