--- phase: 02-driver-management plan: "01" type: tdd wave: 1 depends_on: [] files_modified: - imptune/services/__init__.py - imptune/services/inf_parser.py - tests/test_inf_parser.py - tests/fixtures/sample.inf - tests/fixtures/sample_utf16.inf - tests/fixtures/sample_multi_model.inf autonomous: true requirements: [DRV-02, DRV-05] must_haves: truths: - "parse_inf extracts DriverDesc values from a simple INF with literal names" - "parse_inf resolves %TOKEN% references via the [Strings] section" - "parse_inf handles UTF-16 LE BOM, UTF-8 BOM, and ANSI (cp1252) encoded INF files" - "parse_inf deduplicates driver names from multi-model INFs (NTamd64 + undecorated)" - "parse_inf returns a list of unused files not referenced in the INF text" - "parse_inf detects architecture from section decorations (x64, x86, arm64)" - "parse_inf detects presence of .cat file in ZIP member list" artifacts: - path: "imptune/services/inf_parser.py" provides: "ParsedInf dataclass and parse_inf() + _detect_encoding() functions" exports: ["ParsedInf", "parse_inf", "_detect_encoding"] - path: "tests/test_inf_parser.py" provides: "Unit tests covering all DRV-02 and DRV-05 behaviors" min_lines: 80 - path: "tests/fixtures/sample.inf" provides: "Minimal valid INF with %TOKEN% values and [Strings] section" - path: "tests/fixtures/sample_utf16.inf" provides: "UTF-16 LE encoded INF for encoding detection test" - path: "tests/fixtures/sample_multi_model.inf" provides: "INF with NTamd64 and undecorated Models sections" key_links: - from: "imptune/services/inf_parser.py" to: "configparser.RawConfigParser" via: "stdlib import" pattern: "RawConfigParser.*strict=False" - from: "imptune/services/inf_parser.py" to: "[Strings] section" via: "_resolve_tokens regex expansion" pattern: "re\\.sub.*%([^%]+)%" --- Create the INF parser service that extracts driver names (DriverDesc) from Windows INF files, with encoding auto-detection, %TOKEN% resolution, multi-model support, and unused-file detection. Purpose: This is the core novel logic of Phase 2. The INF parser is a pure function with defined I/O — ideal for TDD. All other Phase 2 work (upload endpoint, UI) consumes this parser's output. Output: `imptune/services/inf_parser.py` with `ParsedInf` dataclass and `parse_inf()` function, plus comprehensive unit tests and INF fixture files. @C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md @C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/02-driver-management/02-RESEARCH.md From imptune/config.py: ```python DATA_DIR = os.environ.get("DATA_DIR", "/data") DRIVERS_DIR = str(Path(DATA_DIR) / "drivers") ``` From imptune/storage/driver_store.py: ```python class DriverStore: def save(self, data: bytes) -> str: ... # returns SHA256 hex digest def get_path(self, sha256: str) -> Path: ... def exists(self, sha256: str) -> bool: ... ``` From imptune/db/models.py: ```python class Driver(BaseModel): sha256 = CharField(unique=True, index=True) original_filename = CharField() size_bytes = IntegerField() uploaded_at = DateTimeField(default=datetime.utcnow) driver_desc = CharField(null=True) # Store json.dumps(list) for multi-model inf_filename = CharField(null=True) architecture = CharField(null=True) # 'x64', 'x86', 'arm64', or None has_cat_file = BooleanField(default=False) ``` Task 1: INF parser with TDD (RED then GREEN) imptune/services/__init__.py, imptune/services/inf_parser.py, tests/test_inf_parser.py, tests/fixtures/sample.inf, tests/fixtures/sample_utf16.inf, tests/fixtures/sample_multi_model.inf - test_detect_encoding_utf16le: _detect_encoding(b'\xff\xfe...') returns 'utf-16' - test_detect_encoding_utf8bom: _detect_encoding(b'\xef\xbb\xbf...') returns 'utf-8-sig' - test_detect_encoding_ansi: _detect_encoding(b'[Version]...') returns 'cp1252' - test_simple_driver_desc: parse_inf with literal DriverDesc in [Models] section returns those names in driver_names - test_token_resolution: parse_inf with %HP_DRIVER% in [Models] and HP_DRIVER="HP LaserJet" in [Strings] resolves to "HP LaserJet" - test_utf16_encoding: Reading a UTF-16 LE BOM fixture, decoding with _detect_encoding, and passing to parse_inf produces correct driver_names - test_multi_model_inf: INF with both [Mfg.NTamd64] and [Mfg] sections returns deduplicated driver_names; architecture='x64' when NTamd64 is present alone - test_architecture_detection: NTamd64 -> 'x64', NTarm64 -> 'arm64', undecorated only -> 'x86', mixed -> None - test_cat_file_detection: zip_names containing 'driver.cat' -> has_cat_file=True; without -> False - test_unused_files: ZIP members ['driver.inf', 'driver.dll', 'readme.txt'] where INF text mentions 'driver.inf' and 'driver.dll' but not 'readme.txt' -> unused_files=['readme.txt'] - test_empty_models_section: INF with [Manufacturer] but empty Models section returns empty driver_names list **Phase: RED** 1. Create `tests/fixtures/` directory if it does not exist. 2. Create `tests/fixtures/sample.inf` — minimal valid INF with %TOKEN% values: ```ini [Version] Signature="$Windows NT$" Class=Printer Provider=%MFG% [Manufacturer] %MFG%=Models,NTamd64 [Models.NTamd64] %DRIVER_NAME%=Install,{GUID} [Strings] MFG="Test Manufacturer" DRIVER_NAME="Test LaserJet Pro" ``` 3. Create `tests/fixtures/sample_utf16.inf` — same content as sample.inf but encoded as UTF-16 LE with BOM. Write using Python: `content.encode('utf-16-le')` prepended with `b'\xff\xfe'`. Actually, create this fixture programmatically within the test (or as a conftest fixture) since writing binary fixtures from plan text is fragile. 4. Create `tests/fixtures/sample_multi_model.inf` — INF with both decorated and undecorated sections: ```ini [Version] Signature="$Windows NT$" Class=Printer [Manufacturer] %MFG%=Models,Models.NTamd64 [Models] %DRIVER_A%=InstallA,{GUID1} [Models.NTamd64] %DRIVER_A%=InstallA,{GUID1} %DRIVER_B%=InstallB,{GUID2} [Strings] MFG="Multi Corp" DRIVER_A="Multi Printer 1000" DRIVER_B="Multi Printer 2000" ``` 5. Create `imptune/services/__init__.py` — empty package marker. 6. Create `tests/test_inf_parser.py` with all 11 test functions listed in behavior. Tests import from `imptune.services.inf_parser` and call `parse_inf()` / `_detect_encoding()`. Each test asserts specific expected outputs. For the UTF-16 test, generate the fixture bytes inline: `sample_text.encode('utf-16')`. 7. Run `pytest tests/test_inf_parser.py -x` — all tests MUST FAIL (ImportError or assertion errors). Commit: `test(02-01): add failing tests for INF parser` **Phase: GREEN** 8. Create `imptune/services/inf_parser.py` implementing: - `ParsedInf` dataclass with fields: `driver_names: list[str]`, `inf_filename: str`, `architecture: str | None`, `has_cat_file: bool`, `unused_files: list[str]` - `_detect_encoding(raw: bytes) -> str` — BOM sniffing (UTF-16 BOM -> 'utf-16', UTF-8 BOM -> 'utf-8-sig', else -> 'cp1252') - `_resolve_tokens(value: str, strings: dict[str, str]) -> str` — regex `%TOKEN%` expansion from strings dict - `parse_inf(inf_text: str, inf_filename: str, zip_names: list[str]) -> ParsedInf` — uses `configparser.RawConfigParser(strict=False, comment_prefixes=(';', '#'), delimiters=('=',))`, reads [Manufacturer] to find Models section names, iterates all matching sections (decorated: .NTamd64, .NTarm64, .NTx86; undecorated), extracts left-hand keys as device-descriptions, resolves tokens, deduplicates with set(), detects architecture from section suffix, detects .cat in zip_names, computes unused files by checking if each zip member's basename appears in inf_text (case-insensitive) Follow the exact code patterns from 02-RESEARCH.md "Pattern 2: INF DriverDesc Extraction". Key points: - Use `RawConfigParser` (NOT `ConfigParser` — avoids %(interpolation)s interference) - `strict=False` to handle duplicate keys in real INFs - Strings dict keys must be lowercased (configparser lowercases keys by default) - Strip surrounding double-quotes from [Strings] values - Architecture: if exactly one arch hint in set -> return it; multiple -> None - `sorted(driver_names)` for deterministic dropdown order 9. Run `pytest tests/test_inf_parser.py -x` — all tests MUST PASS. Commit: `feat(02-01): implement INF parser with encoding detection and token resolution` pytest tests/test_inf_parser.py -v All 11 tests pass. ParsedInf dataclass and parse_inf() function correctly extract driver names from simple, tokenized, UTF-16, and multi-model INF files. Unused files detected. Architecture and .cat presence detected. ```bash pytest tests/test_inf_parser.py -v pytest tests/ -x -q # no regressions in existing tests ``` - parse_inf() extracts DriverDesc from all 3 fixture types (simple, UTF-16, multi-model) - %TOKEN% references resolved to human-readable names - Unused files correctly identified - All 11+ unit tests green, zero regressions in existing suite After completion, create `.planning/phases/02-driver-management/02-01-SUMMARY.md`