Skip to content
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

Function/class never tested #90

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion pynblint/nb_linting.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,34 @@ def non_executed_notebook(notebook: Notebook) -> bool:
return notebook.non_executed


# ========== #
def function_or_class_never_tested(notebook: Notebook) -> bool:
"""Check whether the notebook have function or class not tested

Args:
notebook (Notebook): the notebook to be analyzed.

Returns:
bool: ``True`` if the notebook contains class or function
but not the coverage file; ``False`` otherwise.
"""

func_class_set = set()
for node in ast.walk(notebook.ast):
if isinstance(node, ast.FunctionDef):
for name in node.name:
func_class_set.add(name.split(".")[0])
elif isinstance(node, ast.ClassDef):
func_class_set.add(node.name.split(".")[0])
if len(func_class_set) > 0:
# qui dovrei inserire il contrtrollo sul boolean
# 'has_test_file' contenuto nel repository
return True
else:
return False

# ========== #


# CELL LEVEL #
# ========== #

Expand Down Expand Up @@ -386,6 +413,13 @@ def long_multiline_python_comment(notebook: Notebook) -> List[Cell]:
"that all cells are executed.",
linting_function=non_executed_notebook,
),
LintDefinition(
slug="function-or-class-not-tested",
description="the notebook contains untested functions or classes.",
recommendation="Before committing, be sure to always test your "
"functions and classes for greater code coverage.",
linting_function=function_or_class_never_tested,
),
]

cell_level_lints: List[LintDefinition] = [
Expand Down
9 changes: 8 additions & 1 deletion pynblint/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ def __init__(self, path: Path):

# Extracted content
self.notebooks: List[Notebook] = [] # List of Notebook objects
self.has_test_file: bool = self._has_test_file()

def _has_test_file(self) -> bool:
final_path = os.path.join(self.path, "coverage.py")
if os.path.exists(final_path):
return True
else:
return False

def retrieve_notebooks(self):

Expand Down Expand Up @@ -114,7 +122,6 @@ class GitHubRepository(Repository):
"""

def __init__(self, github_url: str):

self.url = github_url

# Clone the repo in a temp directory
Expand Down
8 changes: 8 additions & 0 deletions tests/unit/test_nb_linting_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,11 @@ def test_long_filename(test_input, expected, notebooks):
)
def test_invalid_python_syntax(test_input, expected, notebooks):
assert nb_linting.invalid_python_syntax(notebooks[test_input]) == expected


@pytest.mark.parametrize(
"test_input, expected",
[("FullNotebook2.ipynb", True), ("NotebookBackupCopy.ipynb", False)],
)
def test_function_or_class_never_tested(test_input, expected, notebooks):
assert nb_linting.function_or_class_never_tested(notebooks[test_input]) == expected