Initial upload: man-card CLI tool with PDF/PNG generation, templates, and tests
Some checks failed
CI / test (push) Has been cancelled
Some checks failed
CI / test (push) Has been cancelled
This commit is contained in:
168
tests/test_man_parser.py
Normal file
168
tests/test_man_parser.py
Normal file
@@ -0,0 +1,168 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user