169 lines
4.8 KiB
Python
169 lines
4.8 KiB
Python
"""Tests for man page parser."""
|
|
|
|
import pytest
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
from man_card.man_parser import ManPageParser, CommandInfo, Option
|
|
|
|
|
|
SAMPLE_MAN_PAGE = """
|
|
NAME
|
|
ls - list directory contents
|
|
|
|
SYNOPSIS
|
|
ls [OPTION]... [FILE]...
|
|
|
|
DESCRIPTION
|
|
List information about the FILEs (the current directory by default).
|
|
|
|
OPTIONS
|
|
-a, --all
|
|
do not ignore entries starting with .
|
|
|
|
-l
|
|
use a long listing format
|
|
|
|
-h, --human-readable
|
|
with -l and -s, print sizes like 1K 234M 2G etc.
|
|
|
|
EXAMPLES
|
|
ls -la
|
|
ls -lh /var
|
|
"""
|
|
|
|
|
|
class TestCommandInfo:
|
|
"""Tests for CommandInfo dataclass."""
|
|
|
|
def test_default_values(self):
|
|
"""Test CommandInfo has correct default values."""
|
|
info = CommandInfo(name="test")
|
|
assert info.name == "test"
|
|
assert info.synopsis == ""
|
|
assert info.description == ""
|
|
assert info.options == []
|
|
assert info.examples == []
|
|
assert info.section == "1"
|
|
|
|
def test_with_values(self):
|
|
"""Test CommandInfo with all values."""
|
|
info = CommandInfo(
|
|
name="ls",
|
|
synopsis="ls [OPTION]... [FILE]...",
|
|
description="List directory contents",
|
|
options=[Option(flag="-a", description="show all files")],
|
|
examples=["ls -la"],
|
|
section="1"
|
|
)
|
|
assert info.name == "ls"
|
|
assert info.synopsis == "ls [OPTION]... [FILE]..."
|
|
assert len(info.options) == 1
|
|
|
|
|
|
class TestOption:
|
|
"""Tests for Option dataclass."""
|
|
|
|
def test_option_creation(self):
|
|
"""Test Option can be created with all fields."""
|
|
opt = Option(flag="-v", description="verbose output", argument="FILE")
|
|
assert opt.flag == "-v"
|
|
assert opt.description == "verbose output"
|
|
assert opt.argument == "FILE"
|
|
|
|
def test_option_optional_argument(self):
|
|
"""Test Option with optional argument."""
|
|
opt = Option(flag="-h", description="help")
|
|
assert opt.flag == "-h"
|
|
assert opt.argument is None
|
|
|
|
|
|
class TestManPageParser:
|
|
"""Tests for ManPageParser class."""
|
|
|
|
def setup_method(self):
|
|
"""Set up test fixtures."""
|
|
self.parser = ManPageParser()
|
|
|
|
@patch('subprocess.run')
|
|
def test_parse_simple_man_page(self, mock_run):
|
|
"""Test parsing a simple man page."""
|
|
mock_run.return_value = MagicMock(
|
|
returncode=0,
|
|
stdout=SAMPLE_MAN_PAGE
|
|
)
|
|
|
|
result = self.parser.parse("ls")
|
|
|
|
assert result.name == "ls"
|
|
assert "OPTION" in result.synopsis
|
|
|
|
@patch('subprocess.run')
|
|
def test_parse_extracts_options(self, mock_run):
|
|
"""Test that options are correctly extracted."""
|
|
mock_run.return_value = MagicMock(
|
|
returncode=0,
|
|
stdout=SAMPLE_MAN_PAGE
|
|
)
|
|
|
|
result = self.parser.parse("ls")
|
|
|
|
assert len(result.options) >= 2
|
|
|
|
@patch('subprocess.run')
|
|
def test_parse_extracts_examples(self, mock_run):
|
|
"""Test that examples are correctly extracted."""
|
|
mock_run.return_value = MagicMock(
|
|
returncode=0,
|
|
stdout=SAMPLE_MAN_PAGE
|
|
)
|
|
|
|
result = self.parser.parse("ls")
|
|
|
|
assert len(result.examples) >= 1
|
|
|
|
@patch('subprocess.run')
|
|
def test_parse_command_not_found(self, mock_run):
|
|
"""Test handling of missing command."""
|
|
mock_run.return_value = MagicMock(returncode=1, stderr="No manual entry for unknowncmd")
|
|
|
|
with pytest.raises(ValueError) as exc_info:
|
|
self.parser.parse("unknowncmd")
|
|
|
|
assert "command not found" in str(exc_info.value)
|
|
|
|
def test_parse_content_empty(self):
|
|
"""Test parsing empty content."""
|
|
result = self.parser._parse_content("", "test")
|
|
assert result.name == "test"
|
|
assert result.synopsis == ""
|
|
|
|
def test_parse_content_with_font_codes(self):
|
|
"""Test parsing content with font formatting codes."""
|
|
content = """
|
|
NAME
|
|
test - a test command
|
|
|
|
SYNOPSIS
|
|
test \\fB-v\\fR [FILE]
|
|
|
|
OPTIONS
|
|
\\fB-v\\fR
|
|
enable verbose mode
|
|
"""
|
|
result = self.parser._parse_content(content, "test")
|
|
assert result.name == "test"
|
|
assert "-v" in result.synopsis or "v" in result.synopsis
|
|
|
|
def test_section_pattern_recognition(self):
|
|
"""Test section pattern recognition."""
|
|
assert self.parser.SECTION_PATTERN.match("NAME")
|
|
assert self.parser.SECTION_PATTERN.match("SYNOPSIS")
|
|
assert self.parser.SECTION_PATTERN.match("OPTIONS")
|
|
assert not self.parser.SECTION_PATTERN.match("This is not a section")
|
|
|
|
def test_option_pattern_recognition(self):
|
|
"""Test option pattern recognition."""
|
|
match = self.parser.OPTION_PATTERN.match("-a, --all")
|
|
assert match is not None
|
|
assert "-a" in match.group(1)
|