|
2 | 2 |
|
3 | 3 | import sys
|
4 | 4 | from pathlib import Path
|
| 5 | +from textwrap import dedent |
5 | 6 | from typing import Any, Dict, List, Optional, Set, TypeVar, Union
|
6 | 7 |
|
7 | 8 | import pytest
|
8 | 9 |
|
9 | 10 | from tox.config.loader.str_convert import StrConvert
|
10 | 11 | from tox.config.types import Command, EnvList
|
| 12 | +from tox.pytest import MonkeyPatch, SubRequest, ToxProjectCreator |
11 | 13 |
|
12 | 14 | if sys.version_info >= (3, 8): # pragma: no cover (py38+)
|
13 | 15 | from typing import Literal
|
@@ -80,3 +82,127 @@ def test_str_convert_nok(raw: str, of_type: type[Any], msg: str, exc_type: type[
|
80 | 82 | def test_invalid_shell_expression(value: str, expected: list[str]) -> None:
|
81 | 83 | result = StrConvert().to_command(value).args
|
82 | 84 | assert result == expected
|
| 85 | + |
| 86 | + |
| 87 | +SIMPLE_ARGS = [ |
| 88 | + ('foo "bar baz"', ["foo", "bar baz"]), |
| 89 | + ('foo "bar baz"ext', ["foo", "bar bazext"]), |
| 90 | + ('foo="bar baz"', ["foo=bar baz"]), |
| 91 | + ("foo 'bar baz'", ["foo", "bar baz"]), |
| 92 | + ("foo 'bar baz'ext", ["foo", "bar bazext"]), |
| 93 | + ("foo='bar baz'", ["foo=bar baz"]), |
| 94 | + (r"foo=\"bar baz\"", ['foo="bar', 'baz"']), |
| 95 | + (r'foo="bar baz\"', ['foo="bar baz\\"']), |
| 96 | + ("foo='bar baz' quuc", ["foo=bar baz", "quuc"]), |
| 97 | + (r"foo='bar baz\' quuc", ["foo=bar baz\\", "quuc"]), |
| 98 | + (r"foo=\"bar baz\' quuc", ['foo="bar', "baz'", "quuc"]), |
| 99 | + (r"foo=\\\"bar baz\"", ['foo=\\"bar', 'baz"']), |
| 100 | + (r'foo=\\"bar baz\"', [r'foo=\\"bar baz\"']), |
| 101 | +] |
| 102 | +NEWLINE_ARGS = [ |
| 103 | + ('foo\n"bar\nbaz"', ["foo", "bar\nbaz"]), |
| 104 | +] |
| 105 | +INI_CONFIG_NEWLINE_ARGS = [ |
| 106 | + ('foo\\\n "bar\\\n baz"', ["foobarbaz"]), # behavior change from tox 3 |
| 107 | + ('foo\\\n "bar \\\n baz"', ["foobar baz"]), # behavior change from tox 3 |
| 108 | + ('foo \\\n "bar\\\n baz"', ["foo", "barbaz"]), |
| 109 | + ('foo \\\n "bar \\\n baz"', ["foo", "bar baz"]), |
| 110 | + ('foo \\\n \\"bar \\\n baz"', ["foo", '"bar', 'baz"']), |
| 111 | + ("foo \\\n bar \\\n baz", ["foo", "bar", "baz"]), |
| 112 | +] |
| 113 | +WINDOWS_PATH_ARGS = [ |
| 114 | + (r"SPECIAL:\foo\bar --quuz='baz atan'", [r"SPECIAL:\foo\bar", "--quuz=baz atan"]), |
| 115 | + (r"X:\\foo\\bar --quuz='baz atan'", [r"X:\foo\bar", "--quuz=baz atan"]), |
| 116 | + ("/foo/bar --quuz='baz atan'", ["/foo/bar", "--quuz=baz atan"]), |
| 117 | + ('cc --arg "C:\\\\Users\\""', ["cc", "--arg", 'C:\\Users"']), |
| 118 | + ('cc --arg "C:\\\\Users\\"', ["cc", "--arg", '"C:\\\\Users\\"']), |
| 119 | + ('cc --arg "C:\\\\Users"', ["cc", "--arg", "C:\\Users"]), |
| 120 | + ('cc --arg \\"C:\\\\Users"', ["cc", "--arg", '\\"C:\\\\Users"']), |
| 121 | + ('cc --arg "C:\\\\Users\\ "', ["cc", "--arg", "C:\\Users\\ "]), |
| 122 | + # ('cc --arg "C:\\\\Users\\ ', ["cc", "--arg", '"C:\\\\Users\\ ']), |
| 123 | + ('cc --arg "C:\\\\Users\\\\"', ["cc", "--arg", "C:\\Users\\"]), |
| 124 | + ('cc --arg "C:\\\\Users\\\\ "', ["cc", "--arg", "C:\\Users\\ "]), |
| 125 | + # ('cc --arg "C:\\\\Users\\\\ ', ["cc", "--arg", '"C:\\\\Users\\\\ ']), |
| 126 | + ( |
| 127 | + r'cc --arg C:\\Users\\ --arg2 "SPECIAL:\Temp\f o o" --arg3="\\FOO\share\Path name" --arg4 SPECIAL:\Temp\ '[:-1], |
| 128 | + [ |
| 129 | + "cc", |
| 130 | + "--arg", |
| 131 | + "C:\\Users\\", |
| 132 | + "--arg2", |
| 133 | + "SPECIAL:\\Temp\\f o o", |
| 134 | + "--arg3=\\FOO\\share\\Path name", |
| 135 | + "--arg4", |
| 136 | + "SPECIAL:\\Temp\\", |
| 137 | + ], |
| 138 | + ), |
| 139 | +] |
| 140 | +WACKY_SLASH_ARGS = [ |
| 141 | + ("\\\\\\", ["\\\\\\"]), |
| 142 | + (" \\'\\'\\ '", [" \\'\\'\\ '"]), |
| 143 | + ("\\'\\'\\ ", ["'' "]), |
| 144 | + ("\\'\\ \\\\", ["' \\"]), |
| 145 | + ("\\'\\ ", ["' "]), |
| 146 | + ('''"\\'\\"''', ['"\\\'\\"']), |
| 147 | + ("'\\' \\\\", ["\\", "\\"]), |
| 148 | + ('"\\\\" \\\\', ["\\", "\\"]), |
| 149 | +] |
| 150 | + |
| 151 | + |
| 152 | +@pytest.fixture(params=["win32", "linux2"]) |
| 153 | +def sys_platform(request: SubRequest, monkeypatch: MonkeyPatch) -> str: |
| 154 | + monkeypatch.setattr(sys, "platform", request.param) |
| 155 | + return str(request.param) |
| 156 | + |
| 157 | + |
| 158 | +@pytest.mark.parametrize( |
| 159 | + ("value", "expected"), |
| 160 | + [ |
| 161 | + *SIMPLE_ARGS, |
| 162 | + *NEWLINE_ARGS, |
| 163 | + *WINDOWS_PATH_ARGS, |
| 164 | + *WACKY_SLASH_ARGS, |
| 165 | + ], |
| 166 | +) |
| 167 | +def test_shlex_platform_specific(sys_platform: str, value: str, expected: list[str]) -> None: |
| 168 | + if sys_platform != "win32" and value.startswith("SPECIAL:"): |
| 169 | + # on non-windows platform, backslash is always an escape, not path separator |
| 170 | + expected = [exp.replace("\\", "") for exp in expected] |
| 171 | + result = StrConvert().to_command(value).args |
| 172 | + assert result == expected |
| 173 | + |
| 174 | + |
| 175 | +@pytest.mark.parametrize( |
| 176 | + ("value", "expected"), |
| 177 | + [ |
| 178 | + *SIMPLE_ARGS, |
| 179 | + *INI_CONFIG_NEWLINE_ARGS, |
| 180 | + *WINDOWS_PATH_ARGS, |
| 181 | + # *WACKY_SLASH_ARGS, |
| 182 | + ], |
| 183 | +) |
| 184 | +def test_shlex_platform_specific_ini( |
| 185 | + tox_project: ToxProjectCreator, |
| 186 | + sys_platform: str, |
| 187 | + value: str, |
| 188 | + expected: list[str], |
| 189 | +) -> None: |
| 190 | + if sys_platform != "win32" and value.startswith("SPECIAL:"): |
| 191 | + # on non-windows platform, backslash is always an escape, not path separator |
| 192 | + expected = [exp.replace("\\", "") for exp in expected] |
| 193 | + project = tox_project( |
| 194 | + { |
| 195 | + "tox.ini": dedent( |
| 196 | + """ |
| 197 | + [testenv] |
| 198 | + commands = |
| 199 | + %s""", |
| 200 | + ) |
| 201 | + % value, |
| 202 | + }, |
| 203 | + ) |
| 204 | + outcome = project.run("c") |
| 205 | + outcome.assert_success() |
| 206 | + env_config = outcome.env_conf("py") |
| 207 | + result = env_config["commands"] |
| 208 | + assert result == [Command(args=expected)] |
0 commit comments