Skip to content

Commit baeb4e3

Browse files
Tamer TasCommit Bot
authored andcommitted
[testrunner] enable the progress indicator
Using test generators meant that we had to remove the progress indicator since the total number of tests weren't known before-hand. This CL implements a progress indicator using test number estimations. cctest and unittests progress indicator is accurate, however estimating means the progress will terminate over 100% in big test suites and sometimes under 100%. R=machenbach@chromium.org CC=​sergiyb@chromium.org,yangguo@chromium.org Bug: v8:8769 Change-Id: I40ca5b40f9b1223376d33707f0945900ea98cea3 Reviewed-on: https://chromium-review.googlesource.com/c/1460471 Commit-Queue: Tamer Tas <tmrts@chromium.org> Reviewed-by: Michael Achenbach <machenbach@chromium.org> Cr-Commit-Position: refs/heads/master@{#59538}
1 parent 9d86374 commit baeb4e3

9 files changed

Lines changed: 115 additions & 37 deletions

File tree

test/preparser/testcfg.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ def _get_variants(self, test):
3838

3939

4040
class TestLoader(testsuite.TestLoader):
41-
pass
41+
def _list_test_filenames(self):
42+
for file in os.listdir(self.suite.root):
43+
if file.endswith(".pyt"):
44+
yield file[:-4]
4245

4346

4447
# TODO(tmrts): refactor the python template parsing then use the TestLoader.
@@ -68,11 +71,12 @@ def MkTest(replacement, expectation):
6871
def ListTests(self):
6972
result = []
7073

71-
# Find all .pyt files in this directory.
72-
filenames = [f[:-4] for f in os.listdir(self.root) if f.endswith(".pyt")]
73-
filenames.sort()
74-
for f in filenames:
74+
filenames = self._test_loader._list_test_filenames()
75+
for f in sorted(filenames):
7576
self._ParsePythonTestTemplates(result, f)
77+
78+
# TODO: remove after converting to use a full TestLoader
79+
self._test_loader.test_count_estimation = len(result)
7680
return result
7781

7882
def _create_test(self, path, source, template_flags):

tools/testrunner/base_runner.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -610,25 +610,20 @@ def _load_testsuite_generators(self, args, options):
610610
names = self._args_to_suite_names(args, options.test_root)
611611
test_config = self._create_test_config(options)
612612
variables = self._get_statusfile_variables(options)
613-
slow_chain, fast_chain = [], []
613+
614+
# Head generator with no elements
615+
test_chain = testsuite.TestGenerator(0, [], [])
614616
for name in names:
615617
if options.verbose:
616618
print '>>> Loading test suite: %s' % name
617619
suite = testsuite.TestSuite.Load(
618620
os.path.join(options.test_root, name), test_config)
619621

620622
if self._is_testsuite_supported(suite, options):
621-
slow_tests, fast_tests = suite.load_tests_from_disk(variables)
622-
slow_chain.append(slow_tests)
623-
fast_chain.append(fast_tests)
624-
625-
for tests in slow_chain:
626-
for test in tests:
627-
yield test
623+
tests = suite.load_tests_from_disk(variables)
624+
test_chain.merge(tests)
628625

629-
for tests in fast_chain:
630-
for test in tests:
631-
yield test
626+
return test_chain
632627

633628
def _is_testsuite_supported(self, suite, options):
634629
"""A predicate that can be overridden to filter out unsupported TestSuite
@@ -762,13 +757,20 @@ def _get_shard_info(self, options):
762757

763758
return shard_run, shard_count
764759

765-
def _create_progress_indicators(self, options):
760+
def _create_progress_indicators(self, test_count, options):
766761
procs = [PROGRESS_INDICATORS[options.progress]()]
767762
if options.json_test_results:
768763
procs.append(progress.JsonTestProgressIndicator(
769764
options.json_test_results,
770765
self.build_config.arch,
771766
self.mode_options.execution_mode))
767+
768+
for proc in procs:
769+
try:
770+
proc.set_test_count(test_count)
771+
except AttributeError:
772+
pass
773+
772774
return procs
773775

774776
def _create_result_tracker(self, options):

tools/testrunner/local/fake_testsuite/testcfg.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,18 @@
99

1010

1111
class TestLoader(testsuite.TestLoader):
12-
pass
12+
def _list_test_filenames(self):
13+
return ["fast", "slow"]
14+
15+
def list_tests(self):
16+
self.test_count_estimation = 2
17+
fast = self._create_test("fast", self.suite)
18+
slow = self._create_test("slow", self.suite)
19+
20+
slow._statusfile_outcomes.append(statusfile.SLOW)
21+
yield fast
22+
yield slow
23+
1324

1425
class TestSuite(testsuite.TestSuite):
1526
def _test_loader_class(self):
@@ -18,12 +29,5 @@ def _test_loader_class(self):
1829
def _test_class(self):
1930
return testsuite.TestCase
2031

21-
def ListTests(self):
22-
fast = self._test_loader._create_test("fast", self)
23-
slow = self._test_loader._create_test("slow", self)
24-
slow._statusfile_outcomes.append(statusfile.SLOW)
25-
yield fast
26-
yield slow
27-
2832
def GetSuite(*args, **kwargs):
2933
return TestSuite(*args, **kwargs)

tools/testrunner/local/testsuite.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
import fnmatch
3030
import imp
31+
import itertools
3132
import os
3233
from contextlib import contextmanager
3334

@@ -88,6 +89,7 @@ def __init__(self, suite, test_class, test_config, test_root):
8889
self.test_class = test_class
8990
self.test_config = test_config
9091
self.test_root = test_root
92+
self.test_count_estimation = len(list(self._list_test_filenames()))
9193

9294
def _list_test_filenames(self):
9395
"""Implemented by the subclassed TestLoaders to list filenames.
@@ -199,6 +201,34 @@ def extension(self):
199201
return ".js"
200202

201203

204+
class TestGenerator(object):
205+
def __init__(self, test_count_estimate, slow_tests, fast_tests):
206+
self.test_count_estimate = test_count_estimate
207+
self.slow_tests = slow_tests
208+
self.fast_tests = fast_tests
209+
self._rebuild_iterator()
210+
211+
def _rebuild_iterator(self):
212+
self._iterator = itertools.chain(self.slow_tests, self.fast_tests)
213+
214+
def __iter__(self):
215+
return self
216+
217+
def __next__(self):
218+
return self.next()
219+
220+
def next(self):
221+
return next(self._iterator)
222+
223+
def merge(self, test_generator):
224+
self.test_count_estimate += test_generator.test_count_estimate
225+
self.slow_tests = itertools.chain(
226+
self.slow_tests, test_generator.slow_tests)
227+
self.fast_tests = itertools.chain(
228+
self.fast_tests, test_generator.fast_tests)
229+
self._rebuild_iterator()
230+
231+
202232
@contextmanager
203233
def _load_testsuite_module(name, root):
204234
f = None
@@ -236,14 +266,22 @@ def _test_loader_class(self):
236266
def ListTests(self):
237267
return self._test_loader.list_tests()
238268

269+
def __initialize_test_count_estimation(self):
270+
# Retrieves a single test to initialize the test generator.
271+
next(iter(self.ListTests()))
272+
273+
def __calculate_test_count(self):
274+
self.__initialize_test_count_estimation()
275+
return self._test_loader.test_count_estimation
276+
239277
def load_tests_from_disk(self, statusfile_variables):
240278
self.statusfile = statusfile.StatusFile(
241279
self.status_file(), statusfile_variables)
242280

281+
test_count = self.__calculate_test_count()
243282
slow_tests = (test for test in self.ListTests() if test.is_slow)
244283
fast_tests = (test for test in self.ListTests() if not test.is_slow)
245-
246-
return slow_tests, fast_tests
284+
return TestGenerator(test_count, slow_tests, fast_tests)
247285

248286
def get_variants_gen(self, variants):
249287
return self._variants_gen_class()(variants)

tools/testrunner/local/testsuite_unittest.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# Use of this source code is governed by a BSD-style license that can be
44
# found in the LICENSE file.
55

6+
import itertools
67
import os
78
import sys
89
import tempfile
@@ -13,7 +14,7 @@
1314
os.path.abspath(__file__))))
1415
sys.path.append(TOOLS_PATH)
1516

16-
from testrunner.local.testsuite import TestSuite
17+
from testrunner.local.testsuite import TestSuite, TestGenerator
1718
from testrunner.objects.testcase import TestCase
1819
from testrunner.test_config import TestConfig
1920

@@ -47,21 +48,38 @@ def testLoadingTestSuites(self):
4748
self.assertIsNone(self.suite.statusfile)
4849

4950
def testLoadingTestsFromDisk(self):
50-
slow_tests, fast_tests = self.suite.load_tests_from_disk(
51+
tests = self.suite.load_tests_from_disk(
5152
statusfile_variables={})
5253
def is_generator(iterator):
5354
return iterator == iter(iterator)
5455

55-
self.assertTrue(is_generator(slow_tests))
56-
self.assertTrue(is_generator(fast_tests))
56+
self.assertTrue(is_generator(tests))
57+
self.assertEquals(tests.test_count_estimate, 2)
5758

58-
slow_tests, fast_tests = list(slow_tests), list(fast_tests)
59+
slow_tests, fast_tests = list(tests.slow_tests), list(tests.fast_tests)
5960
# Verify that the components of the TestSuite are loaded.
6061
self.assertTrue(len(slow_tests) == len(fast_tests) == 1)
6162
self.assertTrue(all(test.is_slow for test in slow_tests))
6263
self.assertFalse(any(test.is_slow for test in fast_tests))
6364
self.assertIsNotNone(self.suite.statusfile)
6465

66+
def testMergingTestGenerators(self):
67+
tests = self.suite.load_tests_from_disk(
68+
statusfile_variables={})
69+
more_tests = self.suite.load_tests_from_disk(
70+
statusfile_variables={})
71+
72+
# Merge the test generators
73+
tests.merge(more_tests)
74+
self.assertEquals(tests.test_count_estimate, 4)
75+
76+
# Check the tests are sorted by speed
77+
test_speeds = []
78+
for test in tests:
79+
test_speeds.append(test.is_slow)
80+
81+
self.assertEquals(test_speeds, [True, True, False, False])
82+
6583

6684
if __name__ == '__main__':
6785
unittest.main()

tools/testrunner/num_fuzzer.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,8 @@ def _do_execute(self, tests, args, options):
134134
results = self._create_result_tracker(options)
135135
execproc = ExecutionProc(options.j)
136136
sigproc = self._create_signal_proc()
137-
indicators = self._create_progress_indicators(options)
137+
indicators = self._create_progress_indicators(
138+
tests.test_count_estimate, options)
138139
procs = [
139140
loader,
140141
NameFilterProc(args) if args else None,

tools/testrunner/standard_runner.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,8 @@ def _do_execute(self, tests, args, options):
283283
print '>>> Running with test processors'
284284
loader = LoadProc(tests)
285285
results = self._create_result_tracker(options)
286-
indicators = self._create_progress_indicators(options)
286+
indicators = self._create_progress_indicators(
287+
tests.test_count_estimate, options)
287288

288289
outproc_factory = None
289290
if self.build_config.predictable:

tools/testrunner/testproc/progress.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ def __init__(self, templates):
162162
self._passed = 0
163163
self._failed = 0
164164

165+
def set_test_count(self, test_count):
166+
self._total = test_count
167+
165168
def _on_result_for(self, test, result):
166169
# TODO(majeski): Support for dummy/grouped results
167170
if result.has_unexpected_output:
@@ -195,8 +198,13 @@ def finished(self):
195198
def _print_progress(self, name):
196199
self._clear_line(self._last_status_length)
197200
elapsed = time.time() - self._start_time
201+
if self._total:
202+
progress = (self._passed + self._failed) * 100 // self._total
203+
else:
204+
progress = 0
198205
status = self._templates['status_line'] % {
199206
'passed': self._passed,
207+
'progress': progress,
200208
'failed': self._failed,
201209
'test': name,
202210
'mins': int(elapsed) / 60,
@@ -221,6 +229,7 @@ class ColorProgressIndicator(CompactProgressIndicator):
221229
def __init__(self):
222230
templates = {
223231
'status_line': ("[%(mins)02i:%(secs)02i|"
232+
"\033[34m%%%(progress) 4d\033[0m|"
224233
"\033[32m+%(passed) 4d\033[0m|"
225234
"\033[31m-%(failed) 4d\033[0m]: %(test)s"),
226235
'stdout': "\033[1m%s\033[0m",
@@ -235,7 +244,7 @@ def _clear_line(self, last_length):
235244
class MonochromeProgressIndicator(CompactProgressIndicator):
236245
def __init__(self):
237246
templates = {
238-
'status_line': ("[%(mins)02i:%(secs)02i|"
247+
'status_line': ("[%(mins)02i:%(secs)02i|%%%(progress) 4d|"
239248
"+%(passed) 4d|-%(failed) 4d]: %(test)s"),
240249
'stdout': '%s',
241250
'stderr': '%s',

tools/unittests/run_tests_test.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -622,10 +622,11 @@ def _testCompactProgress(self, name):
622622
infra_staging=False,
623623
)
624624
if name == 'color':
625-
expected = ('\033[32m+ 1\033[0m|'
625+
expected = ('\033[34m% 28\033[0m|'
626+
'\033[32m+ 1\033[0m|'
626627
'\033[31m- 1\033[0m]: Done')
627628
else:
628-
expected = '+ 1|- 1]: Done'
629+
expected = '% 28|+ 1|- 1]: Done'
629630
self.assertIn(expected, result.stdout)
630631
self.assertIn('sweet/cherries', result.stdout)
631632
self.assertIn('sweet/bananas', result.stdout)

0 commit comments

Comments
 (0)