Generate a .bat file for running tests in a rust_unit_test_group
When targetting Windows, generate a .bat file instead of a bash script.
Bug: 1271215
Change-Id: I682614081af8a30de10ae95f496373a665c078c8
Cq-Include-Trybots: luci.chromium.try:android-rust-arm32-rel,android-rust-arm64-dbg,android-rust-arm64-rel,linux-rust-x64-dbg,linux-rust-x64-rel
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4436654
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Reviewed-by: Ćukasz Anforowicz <lukasza@chromium.org>
Commit-Queue: danakj <danakj@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1132123}
diff --git a/build/rust/rust_unit_tests_group.gni b/build/rust/rust_unit_tests_group.gni
index a210d9e..c2cdfe4 100644
--- a/build/rust/rust_unit_tests_group.gni
+++ b/build/rust/rust_unit_tests_group.gni
@@ -20,11 +20,12 @@
#
# Example usage:
#
-# # This will generate a script at out/Default/bin/run_foo_tests that wraps
-# # out/Default/foo_crate1_unittests,
-# # out/Default/foo_mixed_source_set2_rs_unittests,
-# # and out/Default/foo_mixed_source_set3_rs_unittests executables containing
-# # native Rust unit tests.
+# # This will generate a script at out/Default/bin/run_foo_tests (or
+# # run_foo_tests.bat on Windows) that wraps the executables containing
+# # native Rust unit tests:
+# # * out/Default/foo_crate1_unittests
+# # * out/Default/foo_mixed_source_set2_rs_unittests
+# # * out/Default/foo_mixed_source_set3_rs_unittests
# rust_unit_tests_group("foo_tests") {
# deps = [
# "foo_crate1",
@@ -32,7 +33,6 @@
# "foo_mixed_source_set3",
# ]
# }
-# # TODO(https://crbug.com/1271215): Mention .bat generation once implemented.
template("rust_unit_tests_group") {
assert(defined(invoker.deps), "deps must be listed")
@@ -40,7 +40,11 @@
# As noted in the top-level comment of //testing/buildbot/gn_isolate_map.pyl
# the script *must* be in output_dir/bin/run_$target (or
# output_dir\bin\run_$target.bat on Windows).
- _script_filepath = "$root_out_dir/bin/run_${target_name}"
+ bat = ""
+ if (is_win) {
+ bat = ".bat"
+ }
+ _script_filepath = "$root_out_dir/bin/run_${target_name}${bat}"
# Gathering metadata provided by the rust_unit_test gni template from all of
# our dependencies.
@@ -56,15 +60,11 @@
# Generating a script that can run all of the wrapped Rust unit test
# executables.
- #
- # TODO(https://crbug.com/1271215): Also generate: bin/run_${target_name}.bat
- # when *targeting* Windows: if (is_win) { ... }. (The "targeting" part means
- # that we can't just detect whether the build is *hosted* on Windows.)
action(target_name) {
forward_variables_from(invoker, "*", [])
testonly = true
- script = "//testing/scripts/rust/generate_bash_script.py"
+ script = "//testing/scripts/rust/generate_script.py"
inputs = [ _metadata_filepath ]
outputs = [ _script_filepath ]
@@ -86,5 +86,8 @@
"--script-path",
rebase_path(_script_filepath, root_build_dir),
]
+ if (is_win) {
+ args += [ "--make-bat" ]
+ }
}
}
diff --git a/testing/scripts/rust/exe_util.py b/testing/scripts/rust/exe_util.py
index ed6fe18..32e284d54c 100644
--- a/testing/scripts/rust/exe_util.py
+++ b/testing/scripts/rust/exe_util.py
@@ -5,7 +5,7 @@
"""
import os
-import pty
+import subprocess
import re
# Regex for matching 7-bit and 8-bit C1 ANSI sequences.
@@ -37,15 +37,7 @@
Returns:
The full executable output as an UTF-8 string.
"""
- output_bytes = bytearray()
-
- def read(fd):
- data = os.read(fd, 1024)
- output_bytes.extend(data)
- return data
-
- pty.spawn(args, read)
-
+ output_bytes = subprocess.check_output(args)
# Strip ANSI / terminal escapes.
output_bytes = _ANSI_ESCAPE_8BIT_REGEX.sub(b'', output_bytes)
diff --git a/testing/scripts/rust/generate_bash_script_unittests.py b/testing/scripts/rust/generate_bash_script_unittests.py
deleted file mode 100755
index cd0c6f4..0000000
--- a/testing/scripts/rust/generate_bash_script_unittests.py
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env vpython3
-
-# Copyright 2021 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import os
-from pyfakefs import fake_filesystem_unittest
-import tempfile
-import unittest
-
-from generate_bash_script import _parse_args
-from generate_bash_script import _generate_script
-
-
-class Tests(fake_filesystem_unittest.TestCase):
- def test_parse_args(self):
- raw_args = [
- '--script-path=./bin/run_foobar', '--exe-dir=.',
- '--rust-test-executables=metadata.json'
- ]
- parsed_args = _parse_args(raw_args)
- self.assertEqual('./bin/run_foobar', parsed_args.script_path)
- self.assertEqual('.', parsed_args.exe_dir)
- self.assertEqual('metadata.json', parsed_args.rust_test_executables)
-
- def test_generate_script(self):
- lib_dir = os.path.dirname(__file__)
- out_dir = os.path.join(lib_dir, '../../../out/rust')
- args = type('', (), {})()
- args.script_path = os.path.join(out_dir, 'bin/run_foo_bar')
- args.exe_dir = out_dir
-
- # pylint: disable=unexpected-keyword-arg
- with tempfile.NamedTemporaryFile(delete=False,
- mode='w',
- encoding='utf-8') as f:
- filepath = f.name
- f.write("foo\n")
- f.write("bar\n")
- try:
- args.rust_test_executables = filepath
- actual = _generate_script(args,
- should_validate_if_exes_exist=False)
- finally:
- os.remove(filepath)
-
- expected = '''
-#!/bin/bash
-SCRIPT_DIR=`dirname $0`
-EXE_DIR="$SCRIPT_DIR/.."
-LIB_DIR="$SCRIPT_DIR/../../../testing/scripts/rust"
-env vpython3 "$LIB_DIR/rust_main_program.py" \\
- "--rust-test-executable=$EXE_DIR/bar" \\
- "--rust-test-executable=$EXE_DIR/foo" \\
- "$@"
-'''.strip()
-
- self.assertEqual(expected, actual)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/testing/scripts/rust/generate_bash_script.py b/testing/scripts/rust/generate_script.py
similarity index 75%
rename from testing/scripts/rust/generate_bash_script.py
rename to testing/scripts/rust/generate_script.py
index e80ef3b..eb57652 100755
--- a/testing/scripts/rust/generate_bash_script.py
+++ b/testing/scripts/rust/generate_script.py
@@ -19,26 +19,25 @@
'library for running Rust unit tests with support for ' \
'Chromium test filters, sharding, and test output.'
parser = argparse.ArgumentParser(description=description)
-
parser.add_argument('--script-path',
dest='script_path',
help='Where to write the bash script.',
metavar='FILEPATH',
required=True)
-
parser.add_argument('--exe-dir',
dest='exe_dir',
help='Directory where the wrapped executables are',
metavar='PATH',
required=True)
-
parser.add_argument('--rust-test-executables',
dest='rust_test_executables',
help='File listing one or more executables to wrap. ' \
'(basenames - no .exe extension or directory)',
metavar='FILEPATH',
required=True)
-
+ parser.add_argument('--make-bat',
+ action='store_true',
+ help='Generate a .bat file instead of a bash script')
return parser.parse_args(args=args)
@@ -72,29 +71,36 @@
def _generate_script(args, should_validate_if_exes_exist=True):
- res = '#!/bin/bash\n'
+ THIS_DIR = os.path.abspath(os.path.dirname(__file__))
+ GEN_SCRIPT_DIR = os.path.dirname(args.script_path)
- script_dir = os.path.dirname(args.script_path)
- res += 'SCRIPT_DIR=`dirname $0`\n'
-
- exe_dir = os.path.relpath(args.exe_dir, start=script_dir)
+ # Path from the .bat or bash script to the test exes.
+ exe_dir = os.path.relpath(args.exe_dir, start=GEN_SCRIPT_DIR)
exe_dir = os.path.normpath(exe_dir)
- res += 'EXE_DIR="$SCRIPT_DIR/{}"\n'.format(exe_dir)
- generator_script_dir = os.path.dirname(__file__)
- lib_dir = os.path.relpath(generator_script_dir, script_dir)
- lib_dir = os.path.normpath(lib_dir)
- res += 'LIB_DIR="$SCRIPT_DIR/{}"\n'.format(lib_dir)
+ # Path from the .bat or bash script to the python main.
+ main_dir = os.path.relpath(THIS_DIR, start=GEN_SCRIPT_DIR)
+ main_dir = os.path.normpath(main_dir)
exes = _find_test_executables(args)
if should_validate_if_exes_exist:
_validate_if_test_executables_exist(exes)
- res += 'env vpython3 "$LIB_DIR/rust_main_program.py" \\\n'
- for exe in exes:
- res += ' "--rust-test-executable=$EXE_DIR/{}" \\\n'.format(exe)
- res += ' "$@"'
-
+ if args.make_bat:
+ res = '@echo off\n'
+ res += f'vpython3 "%~dp0\\{main_dir}\\rust_main_program.py" ^\n'
+ for exe in exes:
+ res += f' "--rust-test-executable=%~dp0\\{exe_dir}\\{exe}" ^\n'
+ res += ' %*'
+ else:
+ res = '#!/bin/bash\n'
+ res += (f'env vpython3 '
+ f'"$(dirname $0)/{main_dir}/rust_main_program.py" \\\n')
+ for exe in exes:
+ res += (
+ f' '
+ f'"--rust-test-executable=$(dirname $0)/{exe_dir}/{exe}" \\\n')
+ res += ' "$@"'
return res
diff --git a/testing/scripts/rust/generate_script_unittests.py b/testing/scripts/rust/generate_script_unittests.py
new file mode 100755
index 0000000..ba91e804
--- /dev/null
+++ b/testing/scripts/rust/generate_script_unittests.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env vpython3
+
+# Copyright 2021 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+from pyfakefs import fake_filesystem_unittest
+import tempfile
+import unittest
+
+from generate_script import _parse_args
+from generate_script import _generate_script
+
+
+class Tests(fake_filesystem_unittest.TestCase):
+ def test_parse_args(self):
+ raw_args = [
+ '--script-path=./bin/run_foobar', '--exe-dir=.',
+ '--rust-test-executables=metadata.json'
+ ]
+ parsed_args = _parse_args(raw_args)
+ self.assertEqual('./bin/run_foobar', parsed_args.script_path)
+ self.assertEqual('.', parsed_args.exe_dir)
+ self.assertEqual('metadata.json', parsed_args.rust_test_executables)
+
+ def test_generate_script(self):
+ lib_dir = os.path.dirname(__file__)
+ out_dir = os.path.join(lib_dir, '../../../out/rust')
+ args = type('', (), {})()
+ args.make_bat = False
+ args.script_path = os.path.join(out_dir, 'bin/run_foo_bar')
+ args.exe_dir = out_dir
+
+ # pylint: disable=unexpected-keyword-arg
+ with tempfile.NamedTemporaryFile(delete=False,
+ mode='w',
+ encoding='utf-8') as f:
+ filepath = f.name
+ f.write("foo\n")
+ f.write("bar\n")
+ try:
+ args.rust_test_executables = filepath
+ actual = _generate_script(args,
+ should_validate_if_exes_exist=False)
+ finally:
+ os.remove(filepath)
+
+ expected = '''
+#!/bin/bash
+env vpython3 "$(dirname $0)/../../../testing/scripts/rust/rust_main_program.py" \\
+ "--rust-test-executable=$(dirname $0)/../bar" \\
+ "--rust-test-executable=$(dirname $0)/../foo" \\
+ "$@"
+'''.strip()
+
+ self.assertEqual(expected, actual)
+
+ def test_generate_bat(self):
+ lib_dir = os.path.dirname(__file__)
+ out_dir = os.path.join(lib_dir, '../../../out/rust')
+ args = type('', (), {})()
+ args.make_bat = True
+ args.script_path = os.path.join(out_dir, 'bin/run_foo_bar')
+ args.exe_dir = out_dir
+
+ # pylint: disable=unexpected-keyword-arg
+ with tempfile.NamedTemporaryFile(delete=False,
+ mode='w',
+ encoding='utf-8') as f:
+ filepath = f.name
+ f.write("foo\n")
+ f.write("bar\n")
+ try:
+ args.rust_test_executables = filepath
+ actual = _generate_script(args,
+ should_validate_if_exes_exist=False)
+ finally:
+ os.remove(filepath)
+
+ expected = '''
+@echo off
+vpython3 "%~dp0\\../../../testing/scripts/rust\\rust_main_program.py" ^
+ "--rust-test-executable=%~dp0\\..\\bar" ^
+ "--rust-test-executable=%~dp0\\..\\foo" ^
+ %*
+'''.strip()
+
+ self.assertEqual(expected, actual)
+
+
+if __name__ == '__main__':
+ unittest.main()