se2p / pynguin

The PYthoN General UnIt Test geNerator is a test-generation tool for Python
https://www.pynguin.eu
MIT License
1.22k stars 74 forks source link

Assertion error when analyzing a sklearn module #71

Closed BergLucas closed 1 month ago

BergLucas commented 1 month ago

Describe the bug Hello, I tried to generate tests for the sklearn.cluster._kmeans module of sklearn, and Pynguin crashed during the dependencies analysis step. After a small investigation, I found that the source of the problem came from the scipy.optimize._minpack_py module, and I was finally able to create a minimal reproducible example.

To Reproduce Steps to reproduce the behaviour:

  1. Use Pynguin version '0.36.0'
  2. Use the following (minimal) code as a subject for test generation:
    
    exceptions = [IndexError]

def foo(x: int) -> int: if x < 0: raise ValueError(x)

if x == 0:
    raise exceptions[0]()

return x
3. Use the following command line arguments to Pynguin:

--module-name --project-path --output-path

4. Give the error (stack trace, etc) you are encountering:

╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮ │ /home/lucas/.conda/envs/pynguin-for-ML-libraries/bin/pynguin:8 in │ │ │ │ 5 from pynguin.cli import main │ │ 6 if name == 'main': │ │ 7 │ sys.argv[0] = re.sub(r'(-script.pyw|.exe)?$', '', sys.argv[0]) │ │ ❱ 8 │ sys.exit(main()) │ │ 9 │ │ │ │ /home/lucas/Documents/GitHub/pynguin-for-ML-libraries/pynguin/src/pynguin/cli.py:193 in main │ │ │ │ 190 │ set_configuration(parsed.config) │ │ 191 │ if console is not None: │ │ 192 │ │ with console.status("Running Pynguin..."): │ │ ❱ 193 │ │ │ return run_pynguin().value │ │ 194 │ else: │ │ 195 │ │ return run_pynguin().value │ │ 196 │ │ │ │ /home/lucas/Documents/GitHub/pynguin-for-ML-libraries/pynguin/src/pynguin/generator.py:108 in │ │ run_pynguin │ │ │ │ 105 │ """ │ │ 106 │ try: │ │ 107 │ │ _LOGGER.info("Start Pynguin Test Generation…") │ │ ❱ 108 │ │ return _run() │ │ 109 │ finally: │ │ 110 │ │ _LOGGER.info("Stop Pynguin Test Generation…") │ │ 111 │ │ │ │ /home/lucas/Documents/GitHub/pynguin-for-ML-libraries/pynguin/src/pynguin/generator.py:506 in │ │ _run │ │ │ │ 503 │ │ 504 │ │ 505 def _run() -> ReturnCode: │ │ ❱ 506 │ if (setup_result := _setup_and_check()) is None: │ │ 507 │ │ return ReturnCode.SETUP_FAILED │ │ 508 │ executor, test_cluster, constant_provider = setup_result │ │ 509 │ # traces slices for test cases after execution │ │ │ │ /home/lucas/Documents/GitHub/pynguin-for-ML-libraries/pynguin/src/pynguin/generator.py:257 in │ │ _setup_and_check │ │ │ │ 254 │ │ │ 255 │ # Analyzing the SUT should not cause any coverage. │ │ 256 │ tracer.disable() │ │ ❱ 257 │ if (test_cluster := _setup_test_cluster()) is None: │ │ 258 │ │ return None │ │ 259 │ tracer.enable() │ │ 260 │ │ │ │ /home/lucas/Documents/GitHub/pynguin-for-ML-libraries/pynguin/src/pynguin/generator.py:114 in │ │ _setup_test_cluster │ │ │ │ 111 │ │ 112 │ │ 113 def _setup_test_cluster() -> ModuleTestCluster | None: │ │ ❱ 114 │ test_cluster = generate_test_cluster( │ │ 115 │ │ config.configuration.module_name, │ │ 116 │ │ config.configuration.type_inference.type_inference_strategy, │ │ 117 │ ) │ │ │ │ /home/lucas/Documents/GitHub/pynguin-for-ML-libraries/pynguin/src/pynguin/analyses/module.py:147 │ │ 8 in generate_test_cluster │ │ │ │ 1475 │ Returns: │ │ 1476 │ │ A new test cluster for the given module │ │ 1477 │ """ │ │ ❱ 1478 │ return analyse_module(parse_module(module_name), type_inference_strategy) │ │ 1479 │ │ │ │ /home/lucas/Documents/GitHub/pynguin-for-ML-libraries/pynguin/src/pynguin/analyses/module.py:145 │ │ 7 in analyse_module │ │ │ │ 1454 │ │ A test cluster for the module │ │ 1455 │ """ │ │ 1456 │ test_cluster = ModuleTestCluster(linenos=parsed_module.linenos) │ │ ❱ 1457 │ resolve_dependencies( │ │ 1458 │ │ root_module=parsed_module, │ │ 1459 │ │ type_inference_strategy=type_inference_strategy, │ │ 1460 │ │ test_cluster=test_cluster, │ │ │ │ /home/lucas/Documents/GitHub/pynguin-for-ML-libraries/pynguin/src/pynguin/analyses/module.py:132 │ │ 7 in __resolve_dependencies │ │ │ │ 1324 │ │ ) │ │ 1325 │ │ │ │ 1326 │ │ # Analyze all functions found in the current module │ │ ❱ 1327 │ │ analyse_included_functions( │ │ 1328 │ │ │ module=current_module, │ │ 1329 │ │ │ root_module_name=root_module.module_name, │ │ 1330 │ │ │ type_inference_strategy=type_inference_strategy, │ │ │ │ /home/lucas/Documents/GitHub/pynguin-for-ML-libraries/pynguin/src/pynguin/analyses/module.py:143 │ │ 3 in analyse_included_functions │ │ │ │ 1430 │ │ if current in seen_functions: │ │ 1431 │ │ │ continue │ │ 1432 │ │ seen_functions.add(current) │ │ ❱ 1433 │ │ analyse_function( │ │ 1434 │ │ │ func_name=current.qualname, │ │ 1435 │ │ │ func=current, │ │ 1436 │ │ │ type_inference_strategy=type_inference_strategy, │ │ │ │ /home/lucas/Documents/GitHub/pynguin-for-ML-libraries/pynguin/src/pynguin/analyses/module.py:110 │ │ 1 in analyse_function │ │ │ │ 1098 │ │ type_inference_strategy=type_inference_strategy, │ │ 1099 │ ) │ │ 1100 │ func_ast = get_function_node_from_ast(module_tree, func_name) │ │ ❱ 1101 │ description = get_function_description(func_ast) │ │ 1102 │ raised_exceptions = description.raises if description is not None else set() │ │ 1103 │ cyclomatic_complexity = get_mccabe_complexity(func_ast) │ │ 1104 │ generic_function = GenericFunction( │ │ │ │ /home/lucas/Documents/GitHub/pynguin-for-ML-libraries/pynguin/src/pynguin/analyses/syntaxtree.py │ │ :547 in get_function_description │ │ │ │ 544 │ │ │ 545 │ function_analysis = FunctionAnalysisVisitor() │ │ 546 │ try: │ │ ❱ 547 │ │ function_analysis.visit(astroid_to_ast(func)) │ │ 548 │ except SyntaxError: │ │ 549 │ │ LOGGER.debug("Analysis of %s failed", func.name) │ │ 550 │ │ return None │ │ │ │ /home/lucas/.conda/envs/pynguin-for-ML-libraries/lib/python3.10/ast.py:418 in visit │ │ │ │ 415 │ │ """Visit a node.""" │ │ 416 │ │ method = 'visit' + node.class.name__ │ │ 417 │ │ visitor = getattr(self, method, self.generic_visit) │ │ ❱ 418 │ │ return visitor(node) │ │ 419 │ │ │ 420 │ def generic_visit(self, node): │ │ 421 │ │ """Called if no explicit visitor function exists for a node.""" │ │ │ │ /home/lucas/Documents/GitHub/pynguin-for-ML-libraries/pynguin/src/pynguin/analyses/syntaxtree.py │ │ :78 in visit_FunctionDef │ │ │ │ 75 │ def visit_FunctionDef(self, node: ast.FunctionDef) -> ast.AST: # noqa: N802 │ │ 76 │ │ if not self.in_function: │ │ 77 │ │ │ self.in_function = True │ │ ❱ 78 │ │ │ return getattr(super(), "visit_FunctionDef", super().generic_visit)(node) │ │ 79 │ │ return ast.Pass() │ │ 80 │ │ │ 81 │ def visit_Lambda(self, node: ast.Lambda) -> ast.AST: # noqa: N802 │ │ │ │ /home/lucas/.conda/envs/pynguin-for-ML-libraries/lib/python3.10/ast.py:426 in genericvisit │ │ │ │ 423 │ │ │ if isinstance(value, list): │ │ 424 │ │ │ │ for item in value: │ │ 425 │ │ │ │ │ if isinstance(item, AST): │ │ ❱ 426 │ │ │ │ │ │ self.visit(item) │ │ 427 │ │ │ elif isinstance(value, AST): │ │ 428 │ │ │ │ self.visit(value) │ │ 429 │ │ │ │ /home/lucas/.conda/envs/pynguin-for-ML-libraries/lib/python3.10/ast.py:418 in visit │ │ │ │ 415 │ │ """Visit a node.""" │ │ 416 │ │ method = 'visit' + node.class.name │ │ 417 │ │ visitor = getattr(self, method, self.generic_visit) │ │ ❱ 418 │ │ return visitor(node) │ │ 419 │ │ │ 420 │ def generic_visit(self, node): │ │ 421 │ │ """Called if no explicit visitor function exists for a node.""" │ │ │ │ /home/lucas/.conda/envs/pynguin-for-ML-libraries/lib/python3.10/ast.py:426 in genericvisit │ │ │ │ 423 │ │ │ if isinstance(value, list): │ │ 424 │ │ │ │ for item in value: │ │ 425 │ │ │ │ │ if isinstance(item, AST): │ │ ❱ 426 │ │ │ │ │ │ self.visit(item) │ │ 427 │ │ │ elif isinstance(value, AST): │ │ 428 │ │ │ │ self.visit(value) │ │ 429 │ │ │ │ /home/lucas/.conda/envs/pynguin-for-ML-libraries/lib/python3.10/ast.py:418 in visit │ │ │ │ 415 │ │ """Visit a node.""" │ │ 416 │ │ method = 'visit' + node.class.name__ │ │ 417 │ │ visitor = getattr(self, method, self.generic_visit) │ │ ❱ 418 │ │ return visitor(node) │ │ 419 │ │ │ 420 │ def generic_visit(self, node): │ │ 421 │ │ """Called if no explicit visitor function exists for a node.""" │ │ │ │ /home/lucas/Documents/GitHub/pynguin-for-ML-libraries/pynguin/src/pynguin/analyses/syntaxtree.py │ │ :322 in visit_Raise │ │ │ │ 319 │ def visit_Raise(self, node: ast.Raise) -> ast.AST: # noqa: N802 │ │ 320 │ │ bubbles = self.context.add_exception(node) │ │ 321 │ │ if bubbles: │ │ ❱ 322 │ │ │ assert len(self.contexts) > 1 │ │ 323 │ │ │ if len(self.contexts) < 2: │ │ 324 │ │ │ │ return self.generic_visit(node) │ │ 325 │ │ │ parent_context = self.contexts[-2] │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ AssertionError



**Expected behavior**
Pynguin should not crash and should generate tests.

**Software Version (please complete the following information):**
 - OS: Fedora 39
 - Python version: 3.10
 - Pynguin Version: 0.36.0
stephanlukasczyk commented 1 month ago

Fixed with Pynguin 0.37.0, thanks for the fix!