1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
|
# Copyright 2018 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from collections import defaultdict
from . import base
class CombinerProc(base.TestProc):
@staticmethod
def create(options):
if not options.combine_tests:
return None
return CombinerProc(options.fuzzer_rng(), options.combine_min, options.combine_max,
options.tests_count)
def __init__(self, rng, min_group_size, max_group_size, count):
"""
Args:
rng: random number generator
min_group_size: minimum number of tests to combine
max_group_size: maximum number of tests to combine
count: how many tests to generate. 0 means infinite running
"""
super(CombinerProc, self).__init__()
self._requirement = base.DROP_RESULT
self._rng = rng
self._min_size = min_group_size
self._max_size = max_group_size
self._count = count
# Index of the last generated test
self._current_num = 0
# {suite name: instance of TestGroups}
self._groups = defaultdict(TestGroups)
# {suite name: instance of TestCombiner}
self._combiners = {}
def next_test(self, test):
group_key = self._get_group_key(test)
if not group_key:
# Test not suitable for combining
return False
self._groups[test.suite.name].add_test(group_key, test)
return True
def _get_group_key(self, test):
combiner = self._get_combiner(test.suite)
if not combiner:
print ('>>> Warning: There is no combiner for %s testsuite' %
test.suite.name)
return None
return combiner.get_group_key(test)
def result_for(self, test, result):
self._send_next_test()
def generate_initial_tests(self, num=1):
for _ in range(0, num):
self._send_next_test()
def _send_next_test(self):
if self.is_stopped:
return False
if self._count and self._current_num >= self._count:
return False
combined_test = self._create_new_test()
if not combined_test:
# Not enough tests
return False
return self._send_test(combined_test)
def _create_new_test(self):
suite, combiner = self._select_suite()
groups = self._groups[suite]
max_size = self._rng.randint(self._min_size, self._max_size)
sample = groups.sample(self._rng, max_size)
if not sample:
return None
self._current_num += 1
return combiner.combine('%s-%d' % (suite, self._current_num), sample)
def _select_suite(self):
"""Returns pair (suite name, combiner)."""
selected = self._rng.randint(0, len(self._groups) - 1)
for n, suite in enumerate(self._groups):
if n == selected:
return suite, self._combiners[suite]
def _get_combiner(self, suite):
combiner = self._combiners.get(suite.name)
if not combiner:
combiner = suite.get_test_combiner()
self._combiners[suite.name] = combiner
return combiner
class TestGroups(object):
def __init__(self):
self._groups = defaultdict(list)
self._keys = []
def add_test(self, key, test):
self._groups[key].append(test)
self._keys.append(key)
def sample(self, rng, max_size):
# Not enough tests
if not self._groups:
return None
group_key = rng.choice(self._keys)
tests = self._groups[group_key]
return [rng.choice(tests) for _ in range(0, max_size)]
|