|
| 1 | +"""Tests for Windows compatibility fixes in bin/ask (issue #127).""" |
| 2 | +from __future__ import annotations |
| 3 | + |
| 4 | +from pathlib import Path |
| 5 | + |
| 6 | +REPO_ROOT = Path(__file__).resolve().parents[1] |
| 7 | +ASK_SCRIPT = REPO_ROOT / "bin" / "ask" |
| 8 | + |
| 9 | + |
| 10 | +def _read_ask_source() -> str: |
| 11 | + return ASK_SCRIPT.read_text(encoding="utf-8") |
| 12 | + |
| 13 | + |
| 14 | +def _extract_windows_block(source: str) -> str: |
| 15 | + """Extract the main Windows (os.name == 'nt') block that generates the PowerShell script. |
| 16 | +
|
| 17 | + There are multiple ``os.name == "nt"`` checks in the source. The one we |
| 18 | + care about is the large block inside ``main()`` that writes the .ps1 |
| 19 | + script. We identify it by looking for the block that contains |
| 20 | + 'PowerShell' or 'script_content' so we skip the small helper guard in |
| 21 | + ``_maybe_start_unified_daemon``. |
| 22 | + """ |
| 23 | + lines = source.splitlines() |
| 24 | + blocks: list[list[str]] = [] |
| 25 | + in_block = False |
| 26 | + block_lines: list[str] = [] |
| 27 | + indent_level: int | None = None |
| 28 | + |
| 29 | + for line in lines: |
| 30 | + if ('os.name == "nt"' in line or "os.name == 'nt'" in line) and not in_block: |
| 31 | + in_block = True |
| 32 | + indent_level = len(line) - len(line.lstrip()) |
| 33 | + block_lines = [line] |
| 34 | + continue |
| 35 | + if in_block: |
| 36 | + if line.strip() == "" or len(line) - len(line.lstrip()) > indent_level: |
| 37 | + block_lines.append(line) |
| 38 | + elif line.strip().startswith("else:"): |
| 39 | + blocks.append(block_lines) |
| 40 | + in_block = False |
| 41 | + block_lines = [] |
| 42 | + else: |
| 43 | + block_lines.append(line) |
| 44 | + |
| 45 | + if in_block and block_lines: |
| 46 | + blocks.append(block_lines) |
| 47 | + |
| 48 | + # Return the block that contains the PowerShell script generation |
| 49 | + for block in blocks: |
| 50 | + text = "\n".join(block) |
| 51 | + if "script_content" in text or "PowerShell" in text or ".ps1" in text: |
| 52 | + return text |
| 53 | + |
| 54 | + # Fallback: return all blocks concatenated |
| 55 | + return "\n".join(line for block in blocks for line in block) |
| 56 | + |
| 57 | + |
| 58 | +class TestWindowsPowerShellScript: |
| 59 | + """Verify the Windows PowerShell script template includes required settings.""" |
| 60 | + |
| 61 | + def setup_method(self): |
| 62 | + self.source = _read_ask_source() |
| 63 | + self.win_block = _extract_windows_block(self.source) |
| 64 | + |
| 65 | + def test_ccb_run_dir_in_windows_block(self): |
| 66 | + """CCB_RUN_DIR must be passed to PowerShell script (issue #127).""" |
| 67 | + assert "CCB_RUN_DIR" in self.win_block, ( |
| 68 | + "Windows block must include CCB_RUN_DIR env var" |
| 69 | + ) |
| 70 | + |
| 71 | + def test_utf8_output_encoding(self): |
| 72 | + """$OutputEncoding must be set for proper pipe encoding.""" |
| 73 | + assert "$OutputEncoding" in self.win_block |
| 74 | + |
| 75 | + def test_utf8_input_encoding(self): |
| 76 | + """Console InputEncoding must be set for Chinese chars.""" |
| 77 | + assert "InputEncoding" in self.win_block |
| 78 | + |
| 79 | + def test_pythonioencoding(self): |
| 80 | + """PYTHONIOENCODING must be set for Python subprocess UTF-8.""" |
| 81 | + assert "PYTHONIOENCODING" in self.win_block |
| 82 | + |
| 83 | + def test_console_output_encoding(self): |
| 84 | + """Console OutputEncoding must still be present.""" |
| 85 | + assert "[Console]::OutputEncoding" in self.win_block |
| 86 | + |
| 87 | + def test_email_env_vars_in_windows_block(self): |
| 88 | + """CCB_EMAIL_* env vars must be handled in Windows block.""" |
| 89 | + assert "CCB_EMAIL" in self.win_block or "email_env_lines" in self.win_block |
| 90 | + |
| 91 | + def test_unix_block_still_has_run_dir(self): |
| 92 | + """Unix block must still include CCB_RUN_DIR (no regression).""" |
| 93 | + # Find the else/Unix block |
| 94 | + assert 'export CCB_RUN_DIR' in self.source |
| 95 | + |
| 96 | + |
| 97 | +class TestWindowsUnixParity: |
| 98 | + """Verify Windows and Unix script generation have feature parity.""" |
| 99 | + |
| 100 | + def setup_method(self): |
| 101 | + self.source = _read_ask_source() |
| 102 | + |
| 103 | + def test_both_blocks_set_req_id(self): |
| 104 | + """Both Windows and Unix set CCB_REQ_ID.""" |
| 105 | + assert '$env:CCB_REQ_ID' in self.source # Windows |
| 106 | + assert 'export CCB_REQ_ID' in self.source # Unix |
| 107 | + |
| 108 | + def test_both_blocks_set_caller(self): |
| 109 | + """Both Windows and Unix set CCB_CALLER.""" |
| 110 | + assert '$env:CCB_CALLER' in self.source # Windows |
| 111 | + assert 'export CCB_CALLER' in self.source # Unix |
| 112 | + |
| 113 | + def test_both_blocks_set_work_dir(self): |
| 114 | + """Both Windows and Unix set CCB_WORK_DIR.""" |
| 115 | + assert '$env:CCB_WORK_DIR' in self.source # Windows |
| 116 | + assert 'export CCB_WORK_DIR' in self.source # Unix |
0 commit comments