"""Tests for parsers module.""" import tempfile from pathlib import Path import pytest from doc2man.parsers.python import PythonDocstringParser, parse_python_file from doc2man.parsers.go import GoDocstringParser, parse_go_file from doc2man.parsers.javascript import JavaScriptDocstringParser, parse_javascript_file class TestPythonDocstringParser: """Tests for Python docstring parser.""" def setup_method(self): """Set up test fixtures.""" self.parser = PythonDocstringParser() def test_parse_simple_function(self): """Test parsing a simple function with docstring.""" source = ''' def hello(name): """Say hello to a person. Args: name: The name of the person to greet. Returns: A greeting message. """ return f"Hello, {name}!" ''' result = self.parser.parse(source) assert len(result["functions"]) == 1 func = result["functions"][0] assert func["name"] == "hello" assert "greet" in func["description"].lower() or "hello" in func["description"].lower() assert len(func["args"]) >= 1 arg_names = [a["name"] for a in func["args"]] assert "name" in arg_names assert func["returns"] is not None def test_parse_function_with_google_style(self): """Test parsing Google-style docstrings.""" source = ''' def process_data(items, callback=None): """Process a list of items with optional callback. Args: items: List of items to process. callback: Optional function to call for each item. Returns: dict: A dictionary with processed results. Raises: ValueError: If items is empty. """ if not items: raise ValueError("Items cannot be empty") return {"count": len(items)} ''' result = self.parser.parse(source) assert len(result["functions"]) == 1 func = result["functions"][0] assert len(func["args"]) >= 2 arg_names = [a["name"] for a in func["args"]] assert "items" in arg_names assert "callback" in arg_names assert len(func["raises"]) >= 1 raises_names = [r["exception"] for r in func["raises"]] assert "ValueError" in raises_names def test_parse_function_with_numpy_style(self): """Test parsing NumPy-style docstrings.""" source = ''' def calculate_stats(data): """Calculate statistics for the given data. Parameters ---------- data : array_like Input data array. Returns ------- tuple A tuple containing (mean, std, median). Examples -------- >>> calculate_stats([1, 2, 3, 4, 5]) (3.0, 1.414, 3) """ import numpy as np arr = np.array(data) return arr.mean(), arr.std(), np.median(arr) ''' result = self.parser.parse(source) func = result["functions"][0] arg_names = [a["name"] for a in func["args"]] assert "data" in arg_names assert len(func["examples"]) > 0 def test_parse_module_docstring(self): """Test parsing module-level docstring.""" source = '''"""This is a module docstring. This module provides utility functions for data processing. """ def helper(): """A helper function.""" pass ''' result = self.parser.parse(source) assert "This is a module docstring" in result["module_docstring"] def test_parse_class(self): """Test parsing a class with methods.""" source = ''' class Calculator: """A simple calculator class. Attributes: memory: Current memory value. """ def __init__(self): """Initialize the calculator.""" self.memory = 0 def add(self, a, b): """Add two numbers. Args: a: First number. b: Second number. Returns: Sum of a and b. """ return a + b ''' result = self.parser.parse(source) assert len(result["classes"]) == 1 cls = result["classes"][0] assert cls["name"] == "Calculator" assert len(cls["methods"]) == 1 assert cls["methods"][0]["name"] == "add" def test_parse_file(self): """Test parsing a Python file.""" with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f: f.write(''' def example(): """An example function. Returns: None """ pass ''') f.flush() result = parse_python_file(Path(f.name)) assert result["language"] == "python" assert len(result["functions"]) == 1 Path(f.name).unlink() class TestGoDocstringParser: """Tests for Go docstring parser.""" def setup_method(self): """Set up test fixtures.""" self.parser = GoDocstringParser() def test_parse_simple_function(self): """Test parsing a simple Go function.""" source = '''// Add adds two integers and returns the result. // // Parameters: // a: First integer // b: Second integer // // Returns: The sum of a and b func Add(a, b int) int { return a + b } ''' result = self.parser.parse_content(source) assert len(result["functions"]) == 1 func = result["functions"][0] assert func["name"] == "Add" def test_parse_function_with_params(self): """Test parsing Go function with parameters.""" source = '''// Greet returns a greeting message. // // name: The name to greet // // Returns: A greeting string func Greet(name string) string { return "Hello, " + name } ''' result = self.parser.parse_content(source) func = result["functions"][0] assert func["name"] == "Greet" assert len(func["args"]) >= 1 arg_names = [a["name"] for a in func["args"]] assert "name" in arg_names def test_parse_package_documentation(self): """Test parsing package-level documentation.""" source = '''// Package math provides mathematical utilities. // // This package contains functions for basic // mathematical operations. package math // Add adds two numbers. func Add(a, b int) int { return a + b } ''' result = self.parser.parse_content(source) assert "mathematical utilities" in result["package_docstring"].lower() def test_parse_file(self): """Test parsing a Go file.""" with tempfile.NamedTemporaryFile(mode="w", suffix=".go", delete=False) as f: f.write('''// Hello returns a greeting. // // Returns: A greeting message func Hello() string { return "Hello, World!" } ''') f.flush() result = parse_go_file(Path(f.name)) assert result["language"] == "go" assert len(result["functions"]) == 1 Path(f.name).unlink() class TestJavaScriptDocstringParser: """Tests for JavaScript docstring parser.""" def setup_method(self): """Set up test fixtures.""" self.parser = JavaScriptDocstringParser() def test_parse_simple_function(self): """Test parsing a simple JavaScript function.""" source = ''' /** * Says hello to a person. * * @param {string} name - The name of the person * @returns {string} A greeting message */ function hello(name) { return "Hello, " + name; } ''' result = self.parser.parse_content(source) assert len(result["functions"]) == 1 func = result["functions"][0] assert func["name"] == "hello" assert "hello" in func["description"].lower() or "person" in func["description"].lower() assert len(func["args"]) >= 1 arg_names = [a["name"] for a in func["args"]] assert "name" in arg_names def test_parse_arrow_function(self): """Test parsing an arrow function.""" source = ''' /** * Adds two numbers. * * @param {number} a - First number * @param {number} b - Second number * @returns {number} The sum */ const add = (a, b) => a + b; ''' result = self.parser.parse_content(source) func = result["functions"][0] assert func["name"] == "add" assert len(func["args"]) >= 1 def test_parse_function_with_example(self): """Test parsing function with examples.""" source = ''' /** * Squares a number. * * @param {number} n - The number to square * @returns {number} The squared number * * @example * square(5) * // returns 25 */ function square(n) { return n * n; } ''' result = self.parser.parse_content(source) func = result["functions"][0] assert len(func["examples"]) > 0 def test_parse_export_function(self): """Test parsing exported function.""" source = ''' /** * A public API function. * * @returns {void} */ export function publicApi() { console.log("Hello"); } ''' result = self.parser.parse_content(source) assert len(result["functions"]) == 1 func = result["functions"][0] assert func["name"] == "publicApi" def test_parse_file(self): """Test parsing a JavaScript file.""" with tempfile.NamedTemporaryFile(mode="w", suffix=".js", delete=False) as f: f.write(''' /** * Example function. * * @returns {string} A message */ function example() { return "Hello"; } ''') f.flush() result = parse_javascript_file(Path(f.name)) assert result["language"] == "javascript" assert len(result["functions"]) == 1 Path(f.name).unlink() def test_parse_typescript(self): """Test parsing a TypeScript file.""" with tempfile.NamedTemporaryFile(mode="w", suffix=".ts", delete=False) as f: f.write(''' /** * Adds two numbers. * * @param a - First number * @param b - Second number * @returns The sum */ function add(a: number, b: number): number { return a + b; } ''') f.flush() result = parse_javascript_file(Path(f.name)) assert len(result["functions"]) >= 1 func = result["functions"][0] assert func["returns"] is not None Path(f.name).unlink()