Do not generate empty Rust tests.

cargo2android.py currently builds whatever cargo does, which includes
empty tests.  We can filter those out by parsing the output of "cargo
test" and looking for things with no tests.

Fixes: 159039990
Test: Run on a crate with empty integration tests, one with empty unit
tests, and one with no empty tests.

Change-Id: If8b1126d82ce6c2851b90de738ea24b129b81feb
This commit is contained in:
Joel Galenson
2021-10-14 13:09:32 -07:00
parent f78de4f942
commit 7cfc6fe5de

View File

@@ -125,6 +125,12 @@ CC_AR_VV_PAT = re.compile(r'^\[([^ ]*)[^\]]*\] running:? "(cc|ar)" (.*)$')
# Rustc output of file location path pattern for a warning message.
WARNING_FILE_PAT = re.compile('^ *--> ([^:]*):[0-9]+')
# cargo test --list output of the start of running a binary.
CARGO_TEST_LIST_START_PAT = re.compile('^\s*Running (.*) \(.*\)$')
# cargo test --list output of the end of running a binary.
CARGO_TEST_LIST_END_PAT = re.compile('^(\d+) tests, (\d+) benchmarks$')
# Rust package name with suffix -d1.d2.d3(+.*)?.
VERSION_SUFFIX_PAT = re.compile(r'^(.*)-[0-9]+\.[0-9]+\.[0-9]+(?:\+.*)?$')
@@ -313,8 +319,8 @@ class Crate(object):
# which can be changed if self is a merged test module.
self.decide_module_type()
if should_merge_test:
if (self.main_src in self.runner.args.test_blocklist and
not other.main_src in self.runner.args.test_blocklist):
if (self.runner.should_ignore_test(self.main_src)
and not self.runner.should_ignore_test(other.main_src)):
self.main_src = other.main_src
self.srcs.append(other.main_src)
# use a short unique name as the merged module name.
@@ -667,7 +673,7 @@ class Crate(object):
return
# Dump one test module per source file, and separate host and device tests.
# crate_type == 'test'
self.srcs = [src for src in self.srcs if not src in self.runner.args.test_blocklist]
self.srcs = [src for src in self.srcs if not self.runner.should_ignore_test(src)]
if ((self.host_supported and self.device_supported and len(self.srcs) > 0) or
len(self.srcs) > 1):
self.srcs = sorted(set(self.srcs))
@@ -1145,6 +1151,8 @@ class Runner(object):
self.cargo = ['clean', 'build ' + default_target]
if args.tests:
self.cargo.append('build --tests ' + default_target)
self.empty_tests = set()
self.empty_unittests = False
def setup_cargo_path(self):
"""Find cargo in the --cargo_bin or prebuilt rust bin directory."""
@@ -1329,7 +1337,8 @@ class Runner(object):
os.remove(cargo_out)
if not self.args.use_cargo_lock and had_cargo_lock: # save it
os.rename(cargo_lock, cargo_lock_saved)
cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> ' + cargo_out + ' 2>&1'
cmd_tail_target = ' --target-dir ' + TARGET_TMP
cmd_tail_redir = ' >> ' + cargo_out + ' 2>&1'
# set up search PATH for cargo to find the correct rustc
saved_path = os.environ['PATH']
os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
@@ -1356,20 +1365,13 @@ class Runner(object):
features += ' --features ' + self.args.features
cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
cmd = self.cargo_path + cmd_v_flag
cmd += c + features + cmd_tail
cmd += c + features + cmd_tail_target + cmd_tail_redir
if self.args.rustflags and c != 'clean':
cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
if self.dry_run:
print('Dry-run skip:', cmd)
else:
if self.args.verbose:
print('Running:', cmd)
with open(cargo_out, 'a') as out_file:
out_file.write('### Running: ' + cmd + '\n')
ret = os.system(cmd)
if ret != 0:
print('*** There was an error while running cargo. ' +
'See the cargo.out file for details.')
self.run_cmd(cmd, cargo_out)
if self.args.tests:
cmd = self.cargo_path + ' test' + features + cmd_tail_target + ' -- --list' + cmd_tail_redir
self.run_cmd(cmd, cargo_out)
if added_workspace: # restore original Cargo.toml
with open(cargo_toml, 'w') as out_file:
out_file.writelines(cargo_toml_lines)
@@ -1383,6 +1385,19 @@ class Runner(object):
os.rename(cargo_lock_saved, cargo_lock)
return self
def run_cmd(self, cmd, cargo_out):
if self.dry_run:
print('Dry-run skip:', cmd)
else:
if self.args.verbose:
print('Running:', cmd)
with open(cargo_out, 'a') as out_file:
out_file.write('### Running: ' + cmd + '\n')
ret = os.system(cmd)
if ret != 0:
print('*** There was an error while running cargo. ' +
'See the cargo.out file for details.')
def dump_pkg_obj2cc(self):
"""Dump debug info of the pkg_obj2cc map."""
if not self.args.debug:
@@ -1522,9 +1537,36 @@ class Runner(object):
self.append_to_bp('ERROR -vv line: ' + line)
return ''
def add_empty_test(self, name):
if name == 'unittests':
self.empty_unittests = True
else:
self.empty_tests.add(name)
def should_ignore_test(self, src):
# cargo test outputs the source file for integration tests but "unittests"
# for unit tests. To figure out to which crate this corresponds, we check
# if the current source file is the main source of a non-test crate, e.g.,
# a library or a binary.
return (src in self.args.test_blocklist or src in self.empty_tests
or (self.empty_unittests
and src in [c.main_src for c in self.crates if c.crate_types != ['test']]))
def parse(self, inf, outf_name):
"""Parse rustc and warning messages in inf, return a list of Crates."""
"""Parse rustc, test, and warning messages in inf, return a list of Crates."""
n = 0 # line number
# We read the file in two passes, where the first simply checks for empty tests.
# Otherwise we would add and merge tests before seeing they're empty.
cur_test_name = None
for line in inf:
if CARGO_TEST_LIST_START_PAT.match(line):
cur_test_name = CARGO_TEST_LIST_START_PAT.match(line).group(1)
elif cur_test_name and CARGO_TEST_LIST_END_PAT.match(line):
match = CARGO_TEST_LIST_END_PAT.match(line)
if int(match.group(1)) + int(match.group(2)) == 0:
self.add_empty_test(cur_test_name)
cur_test_name = None
inf.seek(0)
prev_warning = False # true if the previous line was warning: ...
rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
for line in inf: