# coding: utf-8 # # Copyright 2018 The Oppia Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS-IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Unit tests for scripts/docstrings_checker.""" from __future__ import absolute_import # pylint: disable=import-only-modules from __future__ import unicode_literals # pylint: disable=import-only-modules import ast import contextlib import unittest from . import docstrings_checker # isort:skip import astroid # isort:skip from pylint.checkers import utils # isort:skip class ASTDocstringsCheckerTest(unittest.TestCase): """Class for testing the docstrings_checker script.""" def test_build_regex_from_args_one_arg(self): docstring_checker = docstrings_checker.ASTDocStringChecker() args = ['arg_name0'] expected_result = r'(Args:)[\S\s]*(arg_name0:)' result = docstring_checker.build_regex_from_args(args) self.assertEqual(result, expected_result) def test_build_regex_from_args_multiple_args(self): docstring_checker = docstrings_checker.ASTDocStringChecker() args = ['arg_name0', 'arg_name1'] expected_result = r'(Args:)[\S\s]*(arg_name0:)[\S\s]*(arg_name1:)' result = docstring_checker.build_regex_from_args(args) self.assertEqual(result, expected_result) def test_build_regex_from_args_empty_list_returns_none(self): docstring_checker = docstrings_checker.ASTDocStringChecker() args = [] expected_result = None result = docstring_checker.build_regex_from_args(args) self.assertEqual(result, expected_result) def test_compare_arg_order_one_matching_arg_returns_empty_list(self): docstring_checker = docstrings_checker.ASTDocStringChecker() func_args = ['arg_name'] docstring_args = """Description Args: arg_name: description """ expected_result = [] result = docstring_checker.compare_arg_order(func_args, docstring_args) self.assertEqual(result, expected_result) def test_compare_arg_order_no_colon_returns_error(self): docstring_checker = docstrings_checker.ASTDocStringChecker() func_args = ['arg_name'] docstring_args = """Description Args: arg_name """ expected_result = ['Arg not followed by colon: arg_name'] result = docstring_checker.compare_arg_order(func_args, docstring_args) self.assertEqual(result, expected_result) def test_compare_arg_order_two_matching_ordered_args_success(self): docstring_checker = docstrings_checker.ASTDocStringChecker() func_args = ['arg_name1', 'arg_name2'] docstring_args = """Description Args: arg_name1: description, arg_name2: description """ expected_result = [] result = docstring_checker.compare_arg_order(func_args, docstring_args) self.assertEqual(result, expected_result) def test_compare_arg_order_empty_docstring_exits_without_error(self): docstring_checker = docstrings_checker.ASTDocStringChecker() func_args = ['arg_name1'] docstring_args = '' expected_result = [] result = docstring_checker.compare_arg_order(func_args, docstring_args) self.assertEqual(result, expected_result) def test_compare_arg_order_no_arg_header_exits_without_error(self): docstring_checker = docstrings_checker.ASTDocStringChecker() func_args = ['arg_name1'] docstring_args = 'I only have a description.' expected_result = [] result = docstring_checker.compare_arg_order(func_args, docstring_args) self.assertEqual(result, expected_result) def test_compare_arg_order_missing_arg_returns_arg(self): docstring_checker = docstrings_checker.ASTDocStringChecker() func_args = ['arg_name1', 'arg_name2'] docstring_args = """Description Args: arg_name1: description """ expected_result = ['Arg missing from docstring: arg_name2'] result = docstring_checker.compare_arg_order(func_args, docstring_args) self.assertEqual(result, expected_result) def test_compare_arg_order_missing_first_arg_returns_one_error(self): docstring_checker = docstrings_checker.ASTDocStringChecker() func_args = ['arg_name1', 'arg_name2'] docstring_args = """Description Args: arg_name2: description """ expected_result = ['Arg missing from docstring: arg_name1'] result = docstring_checker.compare_arg_order(func_args, docstring_args) self.assertEqual(result, expected_result) def test_compare_arg_order_misordered_args_returns_one_error(self): docstring_checker = docstrings_checker.ASTDocStringChecker() func_args = ['arg_name1', 'arg_name2'] docstring_args = """Description Args: arg_name2: description arg_name1: description """ expected_result = ['Arg ordering error in docstring.'] result = docstring_checker.compare_arg_order(func_args, docstring_args) self.assertEqual(result, expected_result) def test_compare_arg_order_mention_arg_without_colon_has_no_effect(self): docstring_checker = docstrings_checker.ASTDocStringChecker() func_args = ['arg_name1', 'arg_name2'] docstring_args = """Description Args: arg_name1: description involving arg_name2, arg_name2: description involving arg_name1 """ expected_result = [] result = docstring_checker.compare_arg_order(func_args, docstring_args) self.assertEqual(result, expected_result) def test_compare_arg_order_arg_substring_not_confused(self): docstring_checker = docstrings_checker.ASTDocStringChecker() func_args = ['this_has_a_substring', 'intermediate_arg', 'substring'] docstring_args = """Description Args: this_has_a_substring: description, intermediate_arg: description, substring: description """ expected_result = [] result = docstring_checker.compare_arg_order(func_args, docstring_args) self.assertEqual(result, expected_result) def test_compare_arg_order_multi_line_descriptions_success(self): docstring_checker = docstrings_checker.ASTDocStringChecker() func_args = ['arg_name1', 'arg_name2'] docstring_args = """Description Args: arg_name1: description that goes on for a long time. arg_name2: description """ expected_result = [] result = docstring_checker.compare_arg_order(func_args, docstring_args) self.assertEqual(result, expected_result) def test_space_indentation(self): sample_string = ' This is a sample string.' self.assertEqual(docstrings_checker.space_indentation(sample_string), 5) def test_check_docstrings_arg_order(self): docstring_checker = docstrings_checker.ASTDocStringChecker() ast_file = ast.walk(ast.parse( """ def func(test_var_one, test_var_two): #@ \"\"\"Function to test docstring parameters. Args: test_var_one: int. First test variable. test_var_two: str. Second test variable. Returns: int. The test result. \"\"\" result = test_var_one + test_var_two return result""")) func_defs = [n for n in ast_file if isinstance(n, ast.FunctionDef)] self.assertEqual(len(func_defs), 1) func_result = docstring_checker.check_docstrings_arg_order(func_defs[0]) self.assertEqual(func_result, []) def test_possible_exc_types_with_inference_error(self): @contextlib.contextmanager def swap(obj, attr, newvalue): """Swap an object's attribute value within the context of a 'with' statement. The object can be anything that supports getattr and setattr, such as class instances, modules, etc. """ original = getattr(obj, attr) setattr(obj, attr, newvalue) try: yield finally: setattr(obj, attr, original) raise_node = astroid.extract_node(""" def func(): raise Exception('An exception.') #@ """) node_ignores_exception_swap = swap( utils, 'node_ignores_exception', lambda _, __: (_ for _ in ()).throw(astroid.InferenceError())) with node_ignores_exception_swap: exceptions = docstrings_checker.possible_exc_types(raise_node) self.assertEqual(exceptions, set([])) def test_possible_exc_types_with_exception_message(self): raise_node = astroid.extract_node(""" def func(): \"\"\"Function to test raising exceptions.\"\"\" raise Exception('An exception.') #@ """) exceptions = docstrings_checker.possible_exc_types(raise_node) self.assertEqual(exceptions, set(['Exception'])) def test_possible_exc_types_with_no_exception(self): raise_node = astroid.extract_node(""" def func(): \"\"\"Function to test raising exceptions.\"\"\" raise #@ """) exceptions = docstrings_checker.possible_exc_types(raise_node) self.assertEqual(exceptions, set([])) def test_possible_exc_types_with_exception_inside_function(self): raise_node = astroid.extract_node(""" def func(): try: raise Exception('An exception.') except Exception: raise #@ """) exceptions = docstrings_checker.possible_exc_types(raise_node) self.assertEqual(exceptions, set(['Exception']))