def _get_shard_conftest_content(self): shard_spec = self.get_options().test_shard if shard_spec is None: return '' try: sharder = Sharder(shard_spec) if sharder.nshards < 2: return '' return dedent(""" ### GENERATED BY PANTS ### def pytest_report_header(config): return 'shard: {shard} of {nshards} (0-based shard numbering)' def pytest_collection_modifyitems(session, config, items): total_count = len(items) removed = 0 def is_conftest(itm): return itm.fspath and itm.fspath.basename == 'conftest.py' for i, item in enumerate(list(x for x in items if not is_conftest(x))): if i % {nshards} != {shard}: del items[i - removed] removed += 1 reporter = config.pluginmanager.getplugin('terminalreporter') reporter.write_line('Only executing {{}} of {{}} total tests in shard {shard} of ' '{nshards}'.format(total_count - removed, total_count), bold=True, invert=True, yellow=True) """.format(shard=sharder.shard, nshards=sharder.nshards)) except Sharder.InvalidShardSpec as e: raise self.InvalidShardSpecification(e)
def _get_sharding_args(self): shard_spec = self.get_options().test_shard if shard_spec is None: return [] try: sharder = Sharder(shard_spec) return ["--pants-shard", f"{sharder.shard}", "--pants-num-shards", f"{sharder.nshards}"] except Sharder.InvalidShardSpec as e: raise self.InvalidShardSpecification(e)
def _get_sharding_args(self): shard_spec = self.get_options().test_shard if shard_spec is None: return [] try: sharder = Sharder(shard_spec) return [ '--pants-shard', '{}'.format(sharder.shard), '--pants-num-shards', '{}'.format(sharder.nshards) ] except Sharder.InvalidShardSpec as e: raise self.InvalidShardSpecification(e)
def test_compute_shard_distribution(self): # Check that shard distribution isn't obviously broken. nshards = 7 mean_samples_per_shard = 10000 nsamples = nshards * mean_samples_per_shard distribution = [0] * nshards for n in range(0, nsamples): shard = Sharder.compute_shard(str(n), nshards) distribution[shard] += 1 variance = sum([(x - mean_samples_per_shard) ** 2 for x in distribution]) / nshards stddev = math.sqrt(variance) # We arbitrarily assert that a stddev of less than 1% of the mean is good enough # for sanity-checking purposes. self.assertLess(stddev, 100)
def _maybe_shard(self): shard_spec = self.get_options().test_shard if shard_spec is None: yield [] return try: sharder = Sharder(shard_spec) if sharder.nshards < 2: yield [] return # Note that it's important to put the tmpdir under the workdir, because pytest # uses all arguments that look like paths to compute its rootdir, and we want # it to pick the buildroot. with temporary_dir(root_dir=self.workdir) as tmp: path = os.path.join(tmp, 'conftest.py') with open(path, 'w') as fp: fp.write( dedent(""" def pytest_report_header(config): return 'shard: {shard} of {nshards} (0-based shard numbering)' def pytest_collection_modifyitems(session, config, items): total_count = len(items) removed = 0 for i, item in enumerate(list(items)): if i % {nshards} != {shard}: del items[i - removed] removed += 1 reporter = config.pluginmanager.getplugin('terminalreporter') reporter.write_line('Only executing {{}} of {{}} total tests in shard {shard} of ' '{nshards}'.format(total_count - removed, total_count), bold=True, invert=True, yellow=True) """.format(shard=sharder.shard, nshards=sharder.nshards))) yield [path] except Sharder.InvalidShardSpec as e: raise self.InvalidShardSpecification(e)
def check_bad_spec(spec): self.assertRaises(Sharder.InvalidShardSpec, lambda: Sharder(spec))
def check(spec, expected_shard, expected_nshards): sharder = Sharder(spec) self.assertEqual(expected_shard, sharder.shard) self.assertEqual(expected_nshards, sharder.nshards)
def test_compute_shard(self): # Spot-check a couple of values, to make sure compute_shard doesn't do something # completely degenerate. self.assertEqual(31, Sharder.compute_shard('', 42)) self.assertEqual(35, Sharder.compute_shard('foo', 42)) self.assertEqual(5, Sharder.compute_shard('bar', 42))