aboutsummaryrefslogtreecommitdiff
path: root/run_unittests.py
diff options
context:
space:
mode:
authorAleksey Gurtovoy <agurtovoy@acm.org>2019-08-09 16:06:47 -0500
committerJussi Pakkanen <jpakkane@gmail.com>2019-09-05 23:42:47 +0300
commit75daed27bc4e363696157617c7461414fc4e707b (patch)
tree2863934de82e0a7cc6a3dcd9ee23b4c4e378c550 /run_unittests.py
parentcaec875fe1922b40037e1fd9229433ede64f9f25 (diff)
downloadmeson-75daed27bc4e363696157617c7461414fc4e707b.zip
meson-75daed27bc4e363696157617c7461414fc4e707b.tar.gz
meson-75daed27bc4e363696157617c7461414fc4e707b.tar.bz2
mesonlib.split_args/quote_arg/join_args
Diffstat (limited to 'run_unittests.py')
-rwxr-xr-xrun_unittests.py116
1 files changed, 106 insertions, 10 deletions
diff --git a/run_unittests.py b/run_unittests.py
index dd5b434..f9ba017 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -14,7 +14,6 @@
# limitations under the License.
import stat
-import shlex
import subprocess
import re
import json
@@ -51,7 +50,7 @@ from mesonbuild.ast import AstInterpreter
from mesonbuild.mesonlib import (
BuildDirLock, LibType, MachineChoice, PerMachine, Version,
is_windows, is_osx, is_cygwin, is_dragonflybsd, is_openbsd, is_haiku,
- windows_proof_rmtree, python_command, version_compare,
+ windows_proof_rmtree, python_command, version_compare, split_args, quote_arg
)
from mesonbuild.environment import detect_ninja
from mesonbuild.mesonlib import MesonException, EnvironmentException
@@ -1022,6 +1021,103 @@ class InternalTests(unittest.TestCase):
self.assertTrue(vctools_ver.startswith(toolset_ver),
msg='{!r} does not start with {!r}'.format(vctools_ver, toolset_ver))
+ def test_split_args(self):
+ split_args = mesonbuild.mesonlib.split_args
+ join_args = mesonbuild.mesonlib.join_args
+ if is_windows():
+ test_data = [
+ # examples from https://docs.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments
+ (r'"a b c" d e', ['a b c', 'd', 'e'], True),
+ (r'"ab\"c" "\\" d', ['ab"c', '\\', 'd'], False),
+ (r'a\\\b d"e f"g h', [r'a\\\b', 'de fg', 'h'], False),
+ (r'a\\\"b c d', [r'a\"b', 'c', 'd'], False),
+ (r'a\\\\"b c" d e', [r'a\\b c', 'd', 'e'], False),
+ # other basics
+ (r'""', [''], True),
+ (r'a b c d "" e', ['a', 'b', 'c', 'd', '', 'e'], True),
+ (r"'a b c' d e", ["'a", 'b', "c'", 'd', 'e'], True),
+ (r"'a&b&c' d e", ["'a&b&c'", 'd', 'e'], True),
+ (r"a & b & c d e", ['a', '&', 'b', '&', 'c', 'd', 'e'], True),
+ (r"'a & b & c d e'", ["'a", '&', 'b', '&', 'c', 'd', "e'"], True),
+ ('a b\nc\rd \n\re', ['a', 'b', 'c', 'd', 'e'], False),
+ # more illustrative tests
+ (r'cl test.cpp /O1 /Fe:test.exe', ['cl', 'test.cpp', '/O1', '/Fe:test.exe'], True),
+ (r'cl "test.cpp /O1 /Fe:test.exe"', ['cl', 'test.cpp /O1 /Fe:test.exe'], True),
+ (r'cl /DNAME=\"Bob\" test.cpp', ['cl', '/DNAME="Bob"', 'test.cpp'], False),
+ (r'cl "/DNAME=\"Bob\"" test.cpp', ['cl', '/DNAME="Bob"', 'test.cpp'], True),
+ (r'cl /DNAME=\"Bob, Alice\" test.cpp', ['cl', '/DNAME="Bob,', 'Alice"', 'test.cpp'], False),
+ (r'cl "/DNAME=\"Bob, Alice\"" test.cpp', ['cl', '/DNAME="Bob, Alice"', 'test.cpp'], True),
+ (r'cl C:\path\with\backslashes.cpp', ['cl', r'C:\path\with\backslashes.cpp'], True),
+ (r'cl C:\\path\\with\\double\\backslashes.cpp', ['cl', r'C:\\path\\with\\double\\backslashes.cpp'], True),
+ (r'cl "C:\\path\\with\\double\\backslashes.cpp"', ['cl', r'C:\\path\\with\\double\\backslashes.cpp'], False),
+ (r'cl C:\path with spaces\test.cpp', ['cl', r'C:\path', 'with', r'spaces\test.cpp'], False),
+ (r'cl "C:\path with spaces\test.cpp"', ['cl', r'C:\path with spaces\test.cpp'], True),
+ (r'cl /DPATH="C:\path\with\backslashes test.cpp', ['cl', r'/DPATH=C:\path\with\backslashes test.cpp'], False),
+ (r'cl /DPATH=\"C:\\ends\\with\\backslashes\\\" test.cpp', ['cl', r'/DPATH="C:\\ends\\with\\backslashes\"', 'test.cpp'], False),
+ (r'cl /DPATH="C:\\ends\\with\\backslashes\\" test.cpp', ['cl', '/DPATH=C:\\\\ends\\\\with\\\\backslashes\\', 'test.cpp'], False),
+ (r'cl "/DNAME=\"C:\\ends\\with\\backslashes\\\"" test.cpp', ['cl', r'/DNAME="C:\\ends\\with\\backslashes\"', 'test.cpp'], True),
+ (r'cl "/DNAME=\"C:\\ends\\with\\backslashes\\\\"" test.cpp', ['cl', r'/DNAME="C:\\ends\\with\\backslashes\\ test.cpp'], False),
+ (r'cl "/DNAME=\"C:\\ends\\with\\backslashes\\\\\"" test.cpp', ['cl', r'/DNAME="C:\\ends\\with\\backslashes\\"', 'test.cpp'], True),
+ ]
+ else:
+ test_data = [
+ (r"'a b c' d e", ['a b c', 'd', 'e'], True),
+ (r"a/b/c d e", ['a/b/c', 'd', 'e'], True),
+ (r"a\b\c d e", [r'abc', 'd', 'e'], False),
+ (r"a\\b\\c d e", [r'a\b\c', 'd', 'e'], False),
+ (r'"a b c" d e', ['a b c', 'd', 'e'], False),
+ (r'"a\\b\\c\\" d e', ['a\\b\\c\\', 'd', 'e'], False),
+ (r"'a\b\c\' d e", ['a\\b\\c\\', 'd', 'e'], True),
+ (r"'a&b&c' d e", ['a&b&c', 'd', 'e'], True),
+ (r"a & b & c d e", ['a', '&', 'b', '&', 'c', 'd', 'e'], False),
+ (r"'a & b & c d e'", ['a & b & c d e'], True),
+ (r"abd'e f'g h", [r'abde fg', 'h'], False),
+ ('a b\nc\rd \n\re', ['a', 'b', 'c', 'd', 'e'], False),
+
+ ('g++ -DNAME="Bob" test.cpp', ['g++', '-DNAME=Bob', 'test.cpp'], False),
+ ("g++ '-DNAME=\"Bob\"' test.cpp", ['g++', '-DNAME="Bob"', 'test.cpp'], True),
+ ('g++ -DNAME="Bob, Alice" test.cpp', ['g++', '-DNAME=Bob, Alice', 'test.cpp'], False),
+ ("g++ '-DNAME=\"Bob, Alice\"' test.cpp", ['g++', '-DNAME="Bob, Alice"', 'test.cpp'], True),
+ ]
+
+ for (cmd, expected, roundtrip) in test_data:
+ self.assertEqual(split_args(cmd), expected)
+ if roundtrip:
+ self.assertEqual(join_args(expected), cmd)
+
+ def test_quote_arg(self):
+ split_args = mesonbuild.mesonlib.split_args
+ quote_arg = mesonbuild.mesonlib.quote_arg
+ if is_windows():
+ test_data = [
+ ('', '""'),
+ ('arg1', 'arg1'),
+ ('/option1', '/option1'),
+ ('/Ovalue', '/Ovalue'),
+ ('/OBob&Alice', '/OBob&Alice'),
+ ('/Ovalue with spaces', r'"/Ovalue with spaces"'),
+ (r'/O"value with spaces"', r'"/O\"value with spaces\""'),
+ (r'/OC:\path with spaces\test.exe', r'"/OC:\path with spaces\test.exe"'),
+ ('/LIBPATH:C:\\path with spaces\\ends\\with\\backslashes\\', r'"/LIBPATH:C:\path with spaces\ends\with\backslashes\\"'),
+ ('/LIBPATH:"C:\\path with spaces\\ends\\with\\backslashes\\\\"', r'"/LIBPATH:\"C:\path with spaces\ends\with\backslashes\\\\\""'),
+ (r'/DMSG="Alice said: \"Let\'s go\""', r'"/DMSG=\"Alice said: \\\"Let\'s go\\\"\""'),
+ ]
+ else:
+ test_data = [
+ ('arg1', 'arg1'),
+ ('--option1', '--option1'),
+ ('-O=value', '-O=value'),
+ ('-O=Bob&Alice', "'-O=Bob&Alice'"),
+ ('-O=value with spaces', "'-O=value with spaces'"),
+ ('-O="value with spaces"', '\'-O=\"value with spaces\"\''),
+ ('-O=/path with spaces/test', '\'-O=/path with spaces/test\''),
+ ('-DMSG="Alice said: \\"Let\'s go\\""', "'-DMSG=\"Alice said: \\\"Let'\"'\"'s go\\\"\"'"),
+ ]
+
+ for (arg, expected) in test_data:
+ self.assertEqual(quote_arg(arg), expected)
+ self.assertEqual(split_args(expected)[0], arg)
+
@unittest.skipIf(is_tarball(), 'Skipping because this is a tarball release')
class DataTests(unittest.TestCase):
@@ -2033,7 +2129,7 @@ class AllPlatformTests(BasePlatformTests):
if not execmd or not fxecmd:
raise Exception('Could not find someexe and somfxe commands')
# Check include order for 'someexe'
- incs = [a for a in shlex.split(execmd) if a.startswith("-I")]
+ incs = [a for a in split_args(execmd) if a.startswith("-I")]
self.assertEqual(len(incs), 9)
# target private dir
someexe_id = Target.construct_id_from_path("sub4", "someexe", "@exe")
@@ -2055,7 +2151,7 @@ class AllPlatformTests(BasePlatformTests):
# custom target include dir
self.assertPathEqual(incs[8], '-Ictsub')
# Check include order for 'somefxe'
- incs = [a for a in shlex.split(fxecmd) if a.startswith('-I')]
+ incs = [a for a in split_args(fxecmd) if a.startswith('-I')]
self.assertEqual(len(incs), 9)
# target private dir
self.assertPathEqual(incs[0], '-Isomefxe@exe')
@@ -2123,7 +2219,7 @@ class AllPlatformTests(BasePlatformTests):
else:
raise AssertionError('Unknown compiler {!r}'.format(evalue))
# Check that we actually used the evalue correctly as the compiler
- self.assertEqual(ecc.get_exelist(), shlex.split(evalue))
+ self.assertEqual(ecc.get_exelist(), split_args(evalue))
# Do auto-detection of compiler based on platform, PATH, etc.
cc = getattr(env, 'detect_{}_compiler'.format(lang))(MachineChoice.HOST)
self.assertTrue(cc.version)
@@ -2174,14 +2270,14 @@ class AllPlatformTests(BasePlatformTests):
wrappercc = python_command + [wrapper] + cc.get_exelist() + ['-DSOME_ARG']
wrappercc_s = ''
for w in wrappercc:
- wrappercc_s += shlex.quote(w) + ' '
+ wrappercc_s += quote_arg(w) + ' '
os.environ[evar] = wrappercc_s
wcc = getattr(env, 'detect_{}_compiler'.format(lang))(MachineChoice.HOST)
# Check static linker too
wrapperlinker = python_command + [wrapper] + linker.get_exelist() + linker.get_always_args()
wrapperlinker_s = ''
for w in wrapperlinker:
- wrapperlinker_s += shlex.quote(w) + ' '
+ wrapperlinker_s += quote_arg(w) + ' '
os.environ['AR'] = wrapperlinker_s
wlinker = env.detect_static_linker(wcc)
# Pop it so we don't use it for the next detection
@@ -2207,7 +2303,7 @@ class AllPlatformTests(BasePlatformTests):
commands = {'c-asm': {}, 'cpp-asm': {}, 'cpp-c-asm': {}, 'c-cpp-asm': {}}
for cmd in self.get_compdb():
# Get compiler
- split = shlex.split(cmd['command'])
+ split = split_args(cmd['command'])
if split[0] == 'ccache':
compiler = split[1]
else:
@@ -2272,7 +2368,7 @@ class AllPlatformTests(BasePlatformTests):
define = 'MESON_TEST_DEFINE_VALUE'
# NOTE: this list can't have \n, ' or "
# \n is never substituted by the GNU pre-processor via a -D define
- # ' and " confuse shlex.split() even when they are escaped
+ # ' and " confuse split_args() even when they are escaped
# % and # confuse the MSVC preprocessor
# !, ^, *, and < confuse lcc preprocessor
value = 'spaces and fun@$&()-=_+{}[]:;>?,./~`'
@@ -3154,7 +3250,7 @@ recommended as it is not supported on some platforms''')
self.assertEqual(obj.user_options['subp:subp_opt'].value, 'foo')
self.wipe()
- # c_args value should be parsed with shlex
+ # c_args value should be parsed with split_args
self.init(testdir, extra_args=['-Dc_args=-Dfoo -Dbar "-Dthird=one two"'])
obj = mesonbuild.coredata.load(self.builddir)
self.assertEqual(obj.compiler_options.host['c_args'].value, ['-Dfoo', '-Dbar', '-Dthird=one two'])