- Fixed undefined 'tool' variable in display_history function - Changed '[tool]' markup tag usage to proper Rich syntax - All tests now pass (38/38 unit tests) - Type checking passes with mypy --strict
396 lines
10 KiB
Python
396 lines
10 KiB
Python
"""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()
|