Skip to content

Commit dcfcdc2

Browse files
authored
support pytest-ruff plugin for testing (#24698)
half fixes #23933
1 parent 2ebfae9 commit dcfcdc2

File tree

8 files changed

+150
-18
lines changed

8 files changed

+150
-18
lines changed

build/test-requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,6 @@ pytest-json
3636

3737
# for pytest-describe related tests
3838
pytest-describe
39+
40+
# for pytest-ruff related tests
41+
pytest-ruff
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
# This file has no test, it's just a random script.
5+
6+
if __name__ == "__main__":
7+
print("Hello World!")
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
5+
# This test passes.
6+
def test_function(): # test_marker--test_function
7+
assert 1 == 1

python_files/tests/pytestadapter/expected_discovery_test_output.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,3 +1577,94 @@
15771577
],
15781578
"id_": TEST_DATA_PATH_STR,
15791579
}
1580+
# This is the expected output for the folder_with_script folder when run with ruff
1581+
# └── .data
1582+
# └── folder_with_script
1583+
# └── script_random.py
1584+
# └── ruff
1585+
# └── test_simple.py
1586+
# └── ruff
1587+
# └── test_function
1588+
ruff_test_expected_output = {
1589+
"name": ".data",
1590+
"path": TEST_DATA_PATH_STR,
1591+
"type_": "folder",
1592+
"children": [
1593+
{
1594+
"name": "folder_with_script",
1595+
"path": os.fspath(TEST_DATA_PATH / "folder_with_script"),
1596+
"type_": "folder",
1597+
"id_": os.fspath(TEST_DATA_PATH / "folder_with_script"),
1598+
"children": [
1599+
{
1600+
"name": "script_random.py",
1601+
"path": os.fspath(TEST_DATA_PATH / "folder_with_script" / "script_random.py"),
1602+
"type_": "file",
1603+
"id_": os.fspath(TEST_DATA_PATH / "folder_with_script" / "script_random.py"),
1604+
"children": [
1605+
{
1606+
"name": "ruff",
1607+
"path": os.fspath(
1608+
TEST_DATA_PATH / "folder_with_script" / "script_random.py"
1609+
),
1610+
"lineno": "",
1611+
"type_": "test",
1612+
"id_": get_absolute_test_id(
1613+
"folder_with_script/script_random.py::ruff",
1614+
TEST_DATA_PATH / "folder_with_script" / "script_random.py",
1615+
),
1616+
"runID": get_absolute_test_id(
1617+
"folder_with_script/script_random.py::ruff",
1618+
TEST_DATA_PATH / "folder_with_script" / "script_random.py",
1619+
),
1620+
}
1621+
],
1622+
},
1623+
{
1624+
"name": "test_simple.py",
1625+
"path": os.fspath(TEST_DATA_PATH / "folder_with_script" / "test_simple.py"),
1626+
"type_": "file",
1627+
"id_": os.fspath(TEST_DATA_PATH / "folder_with_script" / "test_simple.py"),
1628+
"children": [
1629+
{
1630+
"name": "ruff",
1631+
"path": os.fspath(
1632+
TEST_DATA_PATH / "folder_with_script" / "test_simple.py"
1633+
),
1634+
"lineno": "",
1635+
"type_": "test",
1636+
"id_": get_absolute_test_id(
1637+
"folder_with_script/test_simple.py::ruff",
1638+
TEST_DATA_PATH / "folder_with_script" / "test_simple.py",
1639+
),
1640+
"runID": get_absolute_test_id(
1641+
"folder_with_script/test_simple.py::ruff",
1642+
TEST_DATA_PATH / "folder_with_script" / "test_simple.py",
1643+
),
1644+
},
1645+
{
1646+
"name": "test_function",
1647+
"path": os.fspath(
1648+
TEST_DATA_PATH / "folder_with_script" / "test_simple.py"
1649+
),
1650+
"lineno": find_test_line_number(
1651+
"test_function",
1652+
TEST_DATA_PATH / "folder_with_script" / "test_simple.py",
1653+
),
1654+
"type_": "test",
1655+
"id_": get_absolute_test_id(
1656+
"folder_with_script/test_simple.py::test_function",
1657+
TEST_DATA_PATH / "folder_with_script" / "test_simple.py",
1658+
),
1659+
"runID": get_absolute_test_id(
1660+
"folder_with_script/test_simple.py::test_function",
1661+
TEST_DATA_PATH / "folder_with_script" / "test_simple.py",
1662+
),
1663+
},
1664+
],
1665+
},
1666+
],
1667+
}
1668+
],
1669+
"id_": TEST_DATA_PATH_STR,
1670+
}

python_files/tests/pytestadapter/test_discovery.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,3 +329,30 @@ def test_config_sub_folder():
329329
if actual_item.get("tests") is not None:
330330
tests: Any = actual_item.get("tests")
331331
assert tests.get("name") == "config_sub_folder"
332+
333+
334+
def test_ruff_plugin():
335+
"""Here the session node will be a subfolder of the workspace root and the test are in another subfolder.
336+
337+
This tests checks to see if test node path are under the session node and if so the
338+
session node is correctly updated to the common path.
339+
"""
340+
file_path = helpers.TEST_DATA_PATH / "folder_with_script"
341+
actual = helpers.runner(
342+
[os.fspath(file_path), "--collect-only", "--ruff"],
343+
)
344+
345+
assert actual
346+
actual_list: List[Dict[str, Any]] = actual
347+
if actual_list is not None:
348+
actual_item = actual_list.pop(0)
349+
assert all(item in actual_item for item in ("status", "cwd", "error"))
350+
assert (
351+
actual_item.get("status") == "success"
352+
), f"Status is not 'success', error is: {actual_item.get('error')}"
353+
assert actual_item.get("cwd") == os.fspath(helpers.TEST_DATA_PATH)
354+
assert is_same_tree(
355+
actual_item.get("tests"),
356+
expected_discovery_test_output.ruff_test_expected_output,
357+
["id_", "lineno", "name", "runID"],
358+
), f"Tests tree does not match expected value. \n Expected: {json.dumps(expected_discovery_test_output.ruff_test_expected_output, indent=4)}. \n Actual: {json.dumps(actual_item.get('tests'), indent=4)}"

python_files/vscode_pytest/__init__.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,7 @@
1010
import pathlib
1111
import sys
1212
import traceback
13-
from typing import (
14-
TYPE_CHECKING,
15-
Any,
16-
Dict,
17-
Generator,
18-
Literal,
19-
TypedDict,
20-
)
13+
from typing import TYPE_CHECKING, Any, Dict, Generator, Literal, TypedDict
2114

2215
import pytest
2316

src/client/testing/testController/common/resultResolver.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,10 @@ export class PythonResultResolver implements ITestResultResolver {
189189
// search through freshly built array of testItem to find the failed test and update UI.
190190
testCases.forEach((indiItem) => {
191191
if (indiItem.id === grabVSid) {
192-
if (indiItem.uri && indiItem.range) {
193-
message.location = new Location(indiItem.uri, indiItem.range);
192+
if (indiItem.uri) {
193+
if (indiItem.range) {
194+
message.location = new Location(indiItem.uri, indiItem.range);
195+
}
194196
runInstance.errored(indiItem, message);
195197
}
196198
}
@@ -210,8 +212,10 @@ export class PythonResultResolver implements ITestResultResolver {
210212
// search through freshly built array of testItem to find the failed test and update UI.
211213
testCases.forEach((indiItem) => {
212214
if (indiItem.id === grabVSid) {
213-
if (indiItem.uri && indiItem.range) {
214-
message.location = new Location(indiItem.uri, indiItem.range);
215+
if (indiItem.uri) {
216+
if (indiItem.range) {
217+
message.location = new Location(indiItem.uri, indiItem.range);
218+
}
215219
runInstance.failed(indiItem, message);
216220
}
217221
}
@@ -222,7 +226,7 @@ export class PythonResultResolver implements ITestResultResolver {
222226
if (grabTestItem !== undefined) {
223227
testCases.forEach((indiItem) => {
224228
if (indiItem.id === grabVSid) {
225-
if (indiItem.uri && indiItem.range) {
229+
if (indiItem.uri) {
226230
runInstance.passed(grabTestItem);
227231
}
228232
}
@@ -234,7 +238,7 @@ export class PythonResultResolver implements ITestResultResolver {
234238
if (grabTestItem !== undefined) {
235239
testCases.forEach((indiItem) => {
236240
if (indiItem.id === grabVSid) {
237-
if (indiItem.uri && indiItem.range) {
241+
if (indiItem.uri) {
238242
runInstance.skipped(grabTestItem);
239243
}
240244
}

src/client/testing/testController/common/utils.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,10 +195,10 @@ export function populateTestTree(
195195
const testItem = testController.createTestItem(child.id_, child.name, Uri.file(child.path));
196196
testItem.tags = [RunTestTag, DebugTestTag];
197197

198-
const range = new Range(
199-
new Position(Number(child.lineno) - 1, 0),
200-
new Position(Number(child.lineno), 0),
201-
);
198+
let range: Range | undefined;
199+
if (child.lineno) {
200+
range = new Range(new Position(Number(child.lineno) - 1, 0), new Position(Number(child.lineno), 0));
201+
}
202202
testItem.canResolveChildren = false;
203203
testItem.range = range;
204204
testItem.tags = [RunTestTag, DebugTestTag];

0 commit comments

Comments
 (0)