Skip to content

Do not ignore pytest plugins nodes (like pep8) in tests discovery and results #11824

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from

Conversation

zztalker
Copy link

@zztalker zztalker commented May 15, 2020

POC. Gather plugins nodes (for pytest-pep8 it is (Item, File)) and change nodeid and name of a function to adopt it for current tests processing workflow.

For #8425

  • Pull request represents a single change (i.e. not fixing disparate/unrelated things in a single PR).
  • Title summarizes what is changing.
  • Has a news entry file (remember to thank yourself!).
  • Appropriate comments and documentation strings in the code.
  • Has sufficient logging.
  • Has telemetry for enhancements.
  • Unit tests & system/integration tests are added/updated.
  • Test plan is updated as appropriate.
  • package-lock.json has been regenerated by running npm install (if dependencies have changed).
  • [x ] The wiki is updated with any design decisions/details.

@codecov-io
Copy link

codecov-io commented May 15, 2020

Codecov Report

Merging #11824 into master will decrease coverage by 0.03%.
The diff coverage is 10.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master   #11824      +/-   ##
==========================================
- Coverage   60.45%   60.42%   -0.04%     
==========================================
  Files         631      631              
  Lines       34153    34163      +10     
  Branches     4797     4801       +4     
==========================================
- Hits        20648    20642       -6     
- Misses      12513    12518       +5     
- Partials      992     1003      +11     
Impacted Files Coverage Δ
src/client/testing/common/xUnitParser.ts 61.36% <10.00%> (-5.31%) ⬇️
src/datascience-ui/react-common/arePathsSame.ts 75.00% <0.00%> (-12.50%) ⬇️
src/client/common/utils/platform.ts 64.70% <0.00%> (-11.77%) ⬇️
src/client/logging/logger.ts 75.67% <0.00%> (-8.11%) ⬇️
src/client/linters/pydocstyle.ts 86.66% <0.00%> (-2.23%) ⬇️
src/client/datascience/debugLocationTracker.ts 76.56% <0.00%> (-1.57%) ⬇️
src/client/common/process/proc.ts 14.49% <0.00%> (-0.73%) ⬇️
src/client/datascience/baseJupyterSession.ts 52.94% <0.00%> (-0.32%) ⬇️
...client/datascience/raw-kernel/rawJupyterSession.ts 16.66% <0.00%> (-0.18%) ⬇️
src/client/logging/_console.ts 63.63% <0.00%> (ø)
... and 8 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 90a5ce7...ad264e1. Read the comment docs.

@sonarqubecloud
Copy link

Kudos, SonarCloud Quality Gate passed!

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities (and Security Hotspot 0 Security Hotspots to review)
Code Smell A 0 Code Smells

No Coverage information No Coverage information
0.0% 0.0% Duplication

@zztalker zztalker marked this pull request as ready for review May 17, 2020 18:05
Copy link

@ericsnowcurrently ericsnowcurrently left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR, @zztalker. The overall approach looks okay, but I don't have much context on plugin-generated nodes to be sure.

Comment on lines +548 to +551
# item._nodeid = item.nodeid + "::" + item.location[2]
item.name = item.name.replace(PATH_SEP, ".")
if item.name[-3:] == ".py":
item.name = item.name[:-3]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not modify the item in this function. It should not have side effects. Consider adding a helper function that we can call up in parse_item():

def _fix_plugin_node(item):
    # item._nodeid = item.nodeid + "::" + item.location[2]
    item.name = item.name.replace(PATH_SEP, ".")
    if item.name[-3:] == ".py":
        item.name = item.name[:-3]

You could simplify calling code if you switch on the kind:

def _normalize_node(kind, item):
    if kind == "plugin_node":
        # item._nodeid = item.nodeid + "::" + item.location[2]
        item.name = item.name.replace(PATH_SEP, ".")
        if item.name[-3:] == ".py":
            item.name = item.name[:-3]

@@ -536,6 +544,12 @@ def _get_item_kind(item):
elif isinstance(item, pytest.Function):
# We *could* be more specific, e.g. "method", "subtest".
return "function", False
elif isinstance(item, _pytest.nodes.Item) and isinstance(item, _pytest.nodes.File):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition here is not an obvious one. So it would be helpful to have a comment here with an explanation of how we identified the item as a plugin-generated node, along with information about plugin nodes in general. At the least a link to the relevant pytest docs would be good.

(FWIW, the same applies to the other kinds in this function. However, don't feel like you need to do that in this PR.)

Comment on lines +165 to +169
)

else:

(nodeid, parents, fileid, testfunc, parameterized) = _parse_node_id(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
)
else:
(nodeid, parents, fileid, testfunc, parameterized) = _parse_node_id(
)
else:
(nodeid, parents, fileid, testfunc, parameterized) = _parse_node_id(


if kind == "plugin_node":
(nodeid, parents, fileid, testfunc, parameterized) = _parse_node_id(
item.nodeid + "::" + item.location[2], kind

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
item.nodeid + "::" + item.location[2], kind
item.nodeid + "::" + item.location[2],
kind


if kind == "plugin_node":
(nodeid, parents, fileid, testfunc, parameterized) = _parse_node_id(
item.nodeid + "::" + item.location[2], kind

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It isn't obvious why we're doing this, so a comment would be helpful here.

const fileTest = testcase.$.file && tests.testFiles.find((file) => file.nameToRun === testcase.$.file);
if (fileTest && testcase.error) {
updateResultStatus(fileTest, testcase);
// For pytest plugins like PEP8 it generate node for whole file, and classname == '' in it

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A slight adjustment, plus adding a docs URL to provide more explanation:

Suggested change
// For pytest plugins like PEP8 it generate node for whole file, and classname == '' in it
// For pytest plugins like PEP8 it generates a node for the whole file,
// with classname set to "".
// See: https://docs.pytest.org/...

if (fileTest && testcase.error) {
updateResultStatus(fileTest, testcase);
// For pytest plugins like PEP8 it generate node for whole file, and classname == '' in it
if (testcase.$.classname === '') {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How confident can we be that this condition is accurate enough? Are there plugin nodes that won't match this? Are there unsupported nodes that will match incorrectly? (Is this specific to the PEP8 plugin or to all plugins?) False positives here can result in problems for the extension, particularly due to how the test adapter currently works (relative to stdout).

Comment on lines +122 to +124
if (testcase.$.line === '-1') {
testcase.$.line = '1';
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modification of the data should happen in the adapter script (i.e. pythonFiles/testing_tools/adapter/pytest/_pytest_item.py) rather than in the extension code. Please move this there.

if (fileTest && testcase.error) {
updateResultStatus(fileTest, testcase);
// For pytest plugins like PEP8 it generate node for whole file, and classname == '' in it
if (testcase.$.classname === '') {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep the meaning of the data coming from the adapter script more consistent, such that this special case is unnecessary. That would mean updating the test adapter (in pythonFiles/testing_tools/adapter/pytest/_pytest_item.py) to change testcase.$.classname to the appropriate value in the kind == "plugin_node" case (rather than special-casing here in the extension code).

That would mean this file doesn't need to be changed at all.

@ericsnowcurrently
Copy link

@zztalker, oh, and please make sure to add tests sooner rather than later. They will help to nail down the target behavior.

Copy link

@karrtikr karrtikr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add some more documentation for the changes, as Eric suggested.

@codecov-commenter
Copy link

Codecov Report

Merging #11824 into main will decrease coverage by 0.57%.
The diff coverage is 10.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main   #11824      +/-   ##
==========================================
- Coverage   60.45%   59.88%   -0.58%     
==========================================
  Files         631      682      +51     
  Lines       34153    37942    +3789     
  Branches     4797     5465     +668     
==========================================
+ Hits        20648    22721    +2073     
- Misses      12513    14044    +1531     
- Partials      992     1177     +185     
Impacted Files Coverage Δ
src/client/testing/common/xUnitParser.ts 61.36% <10.00%> (-5.31%) ⬇️
src/client/common/utils/random.ts 21.42% <0.00%> (-64.29%) ⬇️
src/client/datascience/jupyter/kernels/helpers.ts 34.86% <0.00%> (-56.81%) ⬇️
src/datascience-ui/react-common/arePathsSame.ts 37.50% <0.00%> (-50.00%) ⬇️
src/client/datascience/kernel-launcher/helpers.ts 14.28% <0.00%> (-42.86%) ⬇️
src/client/datascience/common.ts 58.66% <0.00%> (-38.56%) ⬇️
src/client/logging/logger.ts 62.74% <0.00%> (-21.04%) ⬇️
src/client/common/utils/platform.ts 56.00% <0.00%> (-20.48%) ⬇️
src/client/interpreter/interpreterVersion.ts 69.56% <0.00%> (-18.44%) ⬇️
src/client/datascience/jupyter/serverSelector.ts 73.40% <0.00%> (-17.78%) ⬇️
... and 376 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 90a5ce7...a5532c5. Read the comment docs.

@brettcannon
Copy link
Member

Closing as out of date.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants