Commit initial
This commit is contained in:
@@ -0,0 +1,318 @@
|
||||
"""
|
||||
Unit tests for imptune.services.inf_parser.
|
||||
|
||||
Covers DRV-02 (INF parsing, token resolution, encoding detection,
|
||||
multi-model) and DRV-05 (unused file detection).
|
||||
"""
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from imptune.services.inf_parser import ParsedInf, _detect_encoding, parse_inf
|
||||
|
||||
FIXTURES = Path(__file__).parent / "fixtures"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# _detect_encoding tests
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_detect_encoding_utf16le():
|
||||
"""UTF-16 LE BOM (\xff\xfe) is detected as 'utf-16'."""
|
||||
raw = b"\xff\xfe" + "[Version]\r\n".encode("utf-16-le")
|
||||
assert _detect_encoding(raw) == "utf-16"
|
||||
|
||||
|
||||
def test_detect_encoding_utf16be():
|
||||
"""UTF-16 BE BOM (\xfe\xff) is also detected as 'utf-16'."""
|
||||
raw = b"\xfe\xff" + "[Version]\r\n".encode("utf-16-be")
|
||||
assert _detect_encoding(raw) == "utf-16"
|
||||
|
||||
|
||||
def test_detect_encoding_utf8bom():
|
||||
"""UTF-8 BOM (\xef\xbb\xbf) is detected as 'utf-8-sig'."""
|
||||
raw = b"\xef\xbb\xbf" + b"[Version]\r\n"
|
||||
assert _detect_encoding(raw) == "utf-8-sig"
|
||||
|
||||
|
||||
def test_detect_encoding_ansi():
|
||||
"""Byte sequence with no BOM falls back to cp1252 (ANSI)."""
|
||||
raw = b"[Version]\r\nSignature=\"$Windows NT$\"\r\n"
|
||||
assert _detect_encoding(raw) == "cp1252"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# parse_inf – simple INF with literal driver description
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
SIMPLE_INF = """
|
||||
[Version]
|
||||
Signature="$Windows NT$"
|
||||
Class=Printer
|
||||
|
||||
[Manufacturer]
|
||||
Acme=AcmeModels,NTamd64
|
||||
|
||||
[AcmeModels.NTamd64]
|
||||
Acme SuperPrint 9000=Install,{AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA}
|
||||
|
||||
[Strings]
|
||||
"""
|
||||
|
||||
|
||||
def test_simple_driver_desc():
|
||||
"""parse_inf extracts literal DriverDesc from a Models section."""
|
||||
result = parse_inf(SIMPLE_INF, "acme.inf", ["acme.inf", "acme.dll"])
|
||||
assert "Acme SuperPrint 9000" in result.driver_names
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# parse_inf – %TOKEN% resolution from [Strings]
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
TOKEN_INF = """
|
||||
[Version]
|
||||
Signature="$Windows NT$"
|
||||
|
||||
[Manufacturer]
|
||||
%MFG%=Models,NTamd64
|
||||
|
||||
[Models.NTamd64]
|
||||
%HP_DRIVER%=Install,{BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB}
|
||||
|
||||
[Strings]
|
||||
MFG="HP"
|
||||
HP_DRIVER="HP LaserJet"
|
||||
"""
|
||||
|
||||
|
||||
def test_token_resolution():
|
||||
"""parse_inf resolves %TOKEN% references via the [Strings] section."""
|
||||
result = parse_inf(TOKEN_INF, "hp.inf", ["hp.inf"])
|
||||
assert "HP LaserJet" in result.driver_names
|
||||
# Raw token should NOT appear in the output
|
||||
assert "%HP_DRIVER%" not in result.driver_names
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# parse_inf – UTF-16 LE fixture
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_utf16_encoding():
|
||||
"""Reading a UTF-16 LE BOM fixture and passing decoded text to parse_inf produces correct driver_names."""
|
||||
raw = (FIXTURES / "sample_utf16.inf").read_bytes()
|
||||
encoding = _detect_encoding(raw)
|
||||
assert encoding == "utf-16"
|
||||
text = raw.decode(encoding, errors="replace")
|
||||
result = parse_inf(text, "sample_utf16.inf", ["sample_utf16.inf"])
|
||||
assert "Test LaserJet Pro" in result.driver_names
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# parse_inf – multi-model INF (NTamd64 + undecorated)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_multi_model_inf():
|
||||
"""INF with both NTamd64 and undecorated sections returns deduplicated driver_names."""
|
||||
text = (FIXTURES / "sample_multi_model.inf").read_text(encoding="cp1252")
|
||||
result = parse_inf(text, "multi.inf", ["multi.inf"])
|
||||
# Both driver names must appear exactly once
|
||||
assert "Multi Printer 1000" in result.driver_names
|
||||
assert "Multi Printer 2000" in result.driver_names
|
||||
assert result.driver_names.count("Multi Printer 1000") == 1
|
||||
assert result.driver_names.count("Multi Printer 2000") == 1
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# parse_inf – architecture detection
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
AMD64_INF = """
|
||||
[Version]
|
||||
Signature="$Windows NT$"
|
||||
|
||||
[Manufacturer]
|
||||
Mfg=Mfg.Models,NTamd64
|
||||
|
||||
[Mfg.Models.NTamd64]
|
||||
Some Driver=Install,{CCCCCCCC-CCCC-CCCC-CCCC-CCCCCCCCCCCC}
|
||||
|
||||
[Strings]
|
||||
"""
|
||||
|
||||
ARM64_INF = """
|
||||
[Version]
|
||||
Signature="$Windows NT$"
|
||||
|
||||
[Manufacturer]
|
||||
Mfg=Mfg.Models,NTarm64
|
||||
|
||||
[Mfg.Models.NTarm64]
|
||||
Some Driver=Install,{CCCCCCCC-CCCC-CCCC-CCCC-CCCCCCCCCCCC}
|
||||
|
||||
[Strings]
|
||||
"""
|
||||
|
||||
UNDECORATED_INF = """
|
||||
[Version]
|
||||
Signature="$Windows NT$"
|
||||
|
||||
[Manufacturer]
|
||||
Mfg=Mfg.Models
|
||||
|
||||
[Mfg.Models]
|
||||
Some Driver=Install,{CCCCCCCC-CCCC-CCCC-CCCC-CCCCCCCCCCCC}
|
||||
|
||||
[Strings]
|
||||
"""
|
||||
|
||||
MIXED_INF = """
|
||||
[Version]
|
||||
Signature="$Windows NT$"
|
||||
|
||||
[Manufacturer]
|
||||
Mfg=Mfg.Models,Mfg.Models.NTamd64
|
||||
|
||||
[Mfg.Models]
|
||||
Some Driver=Install,{CCCCCCCC-CCCC-CCCC-CCCC-CCCCCCCCCCCC}
|
||||
|
||||
[Mfg.Models.NTamd64]
|
||||
Some Driver=Install,{CCCCCCCC-CCCC-CCCC-CCCC-CCCCCCCCCCCC}
|
||||
|
||||
[Strings]
|
||||
"""
|
||||
|
||||
|
||||
def test_architecture_detection_amd64():
|
||||
"""NTamd64 decoration -> architecture='x64'."""
|
||||
result = parse_inf(AMD64_INF, "amd64.inf", [])
|
||||
assert result.architecture == "x64"
|
||||
|
||||
|
||||
def test_architecture_detection_arm64():
|
||||
"""NTarm64 decoration -> architecture='arm64'."""
|
||||
result = parse_inf(ARM64_INF, "arm64.inf", [])
|
||||
assert result.architecture == "arm64"
|
||||
|
||||
|
||||
def test_architecture_detection_undecorated():
|
||||
"""Undecorated section only -> architecture='x86'."""
|
||||
result = parse_inf(UNDECORATED_INF, "x86.inf", [])
|
||||
assert result.architecture == "x86"
|
||||
|
||||
|
||||
def test_architecture_detection_mixed():
|
||||
"""Mixed architectures -> architecture=None (ambiguous)."""
|
||||
result = parse_inf(MIXED_INF, "mixed.inf", [])
|
||||
assert result.architecture is None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# parse_inf – .cat file detection
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_cat_file_detection_present():
|
||||
"""zip_names containing a .cat file -> has_cat_file=True."""
|
||||
result = parse_inf(SIMPLE_INF, "acme.inf", ["acme.inf", "driver.cat", "acme.dll"])
|
||||
assert result.has_cat_file is True
|
||||
|
||||
|
||||
def test_cat_file_detection_absent():
|
||||
"""zip_names without a .cat file -> has_cat_file=False."""
|
||||
result = parse_inf(SIMPLE_INF, "acme.inf", ["acme.inf", "acme.dll"])
|
||||
assert result.has_cat_file is False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# parse_inf – unused files detection
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_unused_files():
|
||||
"""ZIP members not referenced in INF text appear in unused_files."""
|
||||
inf_text = """
|
||||
[Version]
|
||||
Signature="$Windows NT$"
|
||||
|
||||
[Manufacturer]
|
||||
Mfg=Mfg.Models
|
||||
|
||||
[Mfg.Models]
|
||||
Some Driver=Install,{DDDDDDDD-DDDD-DDDD-DDDD-DDDDDDDDDDDD}
|
||||
|
||||
[SourceDisksFiles]
|
||||
driver.inf=1
|
||||
driver.dll=1
|
||||
|
||||
[Strings]
|
||||
"""
|
||||
zip_names = ["driver.inf", "driver.dll", "readme.txt"]
|
||||
result = parse_inf(inf_text, "driver.inf", zip_names)
|
||||
# readme.txt is not mentioned anywhere in inf_text
|
||||
assert "readme.txt" in result.unused_files
|
||||
# driver.inf and driver.dll ARE referenced in inf_text
|
||||
assert "driver.inf" not in result.unused_files
|
||||
assert "driver.dll" not in result.unused_files
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# parse_inf – empty models section
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
EMPTY_MODELS_INF = """
|
||||
[Version]
|
||||
Signature="$Windows NT$"
|
||||
|
||||
[Manufacturer]
|
||||
Mfg=MfgModels
|
||||
|
||||
[MfgModels]
|
||||
|
||||
[Strings]
|
||||
"""
|
||||
|
||||
|
||||
def test_empty_models_section():
|
||||
"""INF with empty Models section returns empty driver_names list."""
|
||||
result = parse_inf(EMPTY_MODELS_INF, "empty.inf", [])
|
||||
assert result.driver_names == []
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# parse_inf – tolerates bare-line sections (real-world Ricoh oemsetup.inf)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
BARE_LINE_INF = """\
|
||||
[Version]
|
||||
Signature="$Windows NT$"
|
||||
|
||||
[Manufacturer]
|
||||
%MFG%=Models,NTamd64
|
||||
|
||||
[Models.NTamd64]
|
||||
%RICOH_DRIVER%=Install,{CCCCCCCC-CCCC-CCCC-CCCC-CCCCCCCCCCCC}
|
||||
|
||||
[SourceDisksFiles]
|
||||
ricu18ui.dll,ricu18ui.dl_
|
||||
ricu18ui.irj
|
||||
ricu18ui.rdj
|
||||
ricu18gl.dll,ricu18gl.dl_
|
||||
RD01Kd64.dll,RD01Kd64.dl_,,0x00000020
|
||||
|
||||
[Strings]
|
||||
MFG="Ricoh"
|
||||
RICOH_DRIVER="Ricoh PCL6 Universal"
|
||||
"""
|
||||
|
||||
|
||||
def test_bare_line_sections_do_not_raise():
|
||||
"""Real INFs (e.g. Ricoh oemsetup.inf) include [SourceDisksFiles] entries
|
||||
with bare filename lines and no '=' — parse_inf must not raise and must
|
||||
still extract DriverDesc from the Models section."""
|
||||
result = parse_inf(BARE_LINE_INF, "oemsetup.inf", ["oemsetup.inf"])
|
||||
assert "Ricoh PCL6 Universal" in result.driver_names
|
||||
# Synthetic __bare_N keys must not leak into driver_names
|
||||
assert not any(n.startswith("__bare_") for n in result.driver_names)
|
||||
Reference in New Issue
Block a user