Files
shell-speak/tests/test_parsers.py
Auto User 95459fb4c8 fix: resolve CI test failure in output.py
- 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
2026-01-31 06:22:27 +00:00

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()