xref: /third_party/vixl/tools/threaded_tests.py (revision b8021494)
1# Copyright 2015, VIXL authors
2# All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are met:
6#
7#   * Redistributions of source code must retain the above copyright notice,
8#     this list of conditions and the following disclaimer.
9#   * Redistributions in binary form must reproduce the above copyright notice,
10#     this list of conditions and the following disclaimer in the documentation
11#     and/or other materials provided with the distribution.
12#   * Neither the name of ARM Limited nor the names of its contributors may be
13#     used to endorse or promote products derived from this software without
14#     specific prior written permission.
15#
16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27import collections
28import multiprocessing
29import re
30import time
31
32import printer
33import thread_pool
34
35REGEXP_MISSING_FEATURES = "Missing features: { ([^,}]+(, [^,}]+)*) }"
36
37class Test(object):
38  # Shared state for multiprocessing. Ideally the context should be passed with
39  # arguments, but constraints from the multiprocessing module prevent us from
40  # doing so: the shared variables (multiprocessing.Value) must be either global
41  # or static, or no work is started.
42  n_tests_passed = multiprocessing.Value('i', 0)
43  n_tests_failed = multiprocessing.Value('i', 0)
44  n_tests_skipped = multiprocessing.Value('i', 0)
45
46  def __init__(self, name, shared, **kwargs):
47    self.name = name
48    self.shared = shared
49    self.args = kwargs
50
51class TestQueue(object):
52  def __init__(self, prefix = ''):
53    self.progress_prefix = prefix
54    self.queue = []
55    self.tests_skipped = None
56    self.n_known_failures = 0
57    self.known_failures = collections.Counter()
58
59  def AddKnownFailures(self, reason, n_tests):
60    self.n_known_failures += n_tests
61    self.known_failures[reason] += n_tests
62
63  def AddTest(self, name, **kwargs):
64    self.queue.append(Test(name, self, **kwargs))
65
66  # Run the specified tests.
67  def Run(self, jobs, verbose, run_function):
68    with multiprocessing.Manager() as manager:
69      def InitGlobals():
70        # Initialisation.
71        self.start_time = time.time()
72        self.n_tests = len(self.queue)
73        if self.n_tests == 0:
74          printer.Print('No tests to run.')
75          return False
76        Test.n_tests_passed.value = 0
77        Test.n_tests_failed.value = 0
78        Test.n_tests_skipped.value = 0
79        self.tests_skipped = manager.dict()
80        return True
81
82      thread_pool.Multithread(run_function, self.queue, jobs, InitGlobals)
83
84      printer.UpdateProgress(self.start_time,
85                             Test.n_tests_passed.value,
86                             Test.n_tests_failed.value,
87                             self.n_tests,
88                             Test.n_tests_skipped.value,
89                             self.n_known_failures,
90                             '== Done ==',
91                             prevent_next_overwrite = True,
92                             prefix = self.progress_prefix)
93      n_tests_features = 0
94      features = set()
95      for reason, n_tests in self.tests_skipped.items():
96        m = re.match(REGEXP_MISSING_FEATURES, reason)
97        if m:
98          if verbose:
99            printer.Print("%d tests skipped because the following features are "
100                          "not available '%s'" % (n_tests, m.group(1)))
101          else:
102            n_tests_features += n_tests
103            features.update(m.group(1).split(', '))
104        else:
105          printer.Print("%d tests skipped because '%s'" % (n_tests, reason))
106
107      n_tests_other = 0
108      if n_tests_features > 0 :
109        printer.Print("%d tests skipped because the CPU does not support "
110                      "the following features: '%s'" %
111                      (n_tests_features, ", ".join(features)))
112
113      for reason, n_tests in self.known_failures.items():
114          printer.Print("%d tests skipped because '%s'" % (n_tests, reason))
115
116      # Empty the queue now that the tests have been run.
117      self.queue = []
118      # `0` indicates success
119      return Test.n_tests_failed.value
120
121