unplugged-system/external/python/absl-py/absl/testing/tests/absltest_sharding_test.py

162 lines
5.4 KiB
Python

# Copyright 2017 The Abseil Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests for test sharding protocol."""
import os
import subprocess
from absl.testing import _bazelize_command
from absl.testing import absltest
from absl.testing.tests import absltest_env
NUM_TEST_METHODS = 8 # Hard-coded, based on absltest_sharding_test_helper.py
class TestShardingTest(absltest.TestCase):
"""Integration tests: Runs a test binary with sharding.
This is done by setting the sharding environment variables.
"""
def setUp(self):
super().setUp()
self._test_name = 'absl/testing/tests/absltest_sharding_test_helper'
self._shard_file = None
def tearDown(self):
super().tearDown()
if self._shard_file is not None and os.path.exists(self._shard_file):
os.unlink(self._shard_file)
def _run_sharded(self,
total_shards,
shard_index,
shard_file=None,
additional_env=None):
"""Runs the py_test binary in a subprocess.
Args:
total_shards: int, the total number of shards.
shard_index: int, the shard index.
shard_file: string, if not 'None', the path to the shard file.
This method asserts it is properly created.
additional_env: Additional environment variables to be set for the py_test
binary.
Returns:
(stdout, exit_code) tuple of (string, int).
"""
env = absltest_env.inherited_env()
if additional_env:
env.update(additional_env)
env.update({
'TEST_TOTAL_SHARDS': str(total_shards),
'TEST_SHARD_INDEX': str(shard_index)
})
if shard_file:
self._shard_file = shard_file
env['TEST_SHARD_STATUS_FILE'] = shard_file
if os.path.exists(shard_file):
os.unlink(shard_file)
proc = subprocess.Popen(
args=[_bazelize_command.get_executable_path(self._test_name)],
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True)
stdout = proc.communicate()[0]
if shard_file:
self.assertTrue(os.path.exists(shard_file))
return (stdout, proc.wait())
def _assert_sharding_correctness(self, total_shards):
"""Assert the primary correctness and performance of sharding.
1. Completeness (all methods are run)
2. Partition (each method run at most once)
3. Balance (for performance)
Args:
total_shards: int, total number of shards.
"""
outerr_by_shard = [] # A list of lists of strings
combined_outerr = [] # A list of strings
exit_code_by_shard = [] # A list of ints
for i in range(total_shards):
(out, exit_code) = self._run_sharded(total_shards, i)
method_list = [x for x in out.split('\n') if x.startswith('class')]
outerr_by_shard.append(method_list)
combined_outerr.extend(method_list)
exit_code_by_shard.append(exit_code)
self.assertLen([x for x in exit_code_by_shard if x != 0], 1,
'Expected exactly one failure')
# Test completeness and partition properties.
self.assertLen(combined_outerr, NUM_TEST_METHODS,
'Partition requirement not met')
self.assertLen(set(combined_outerr), NUM_TEST_METHODS,
'Completeness requirement not met')
# Test balance:
for i in range(len(outerr_by_shard)):
self.assertGreaterEqual(len(outerr_by_shard[i]),
(NUM_TEST_METHODS / total_shards) - 1,
'Shard %d of %d out of balance' %
(i, len(outerr_by_shard)))
def test_shard_file(self):
self._run_sharded(3, 1, os.path.join(
absltest.TEST_TMPDIR.value, 'shard_file'))
def test_zero_shards(self):
out, exit_code = self._run_sharded(0, 0)
self.assertEqual(1, exit_code)
self.assertGreaterEqual(out.find('Bad sharding values. index=0, total=0'),
0, 'Bad output: %s' % (out))
def test_with_four_shards(self):
self._assert_sharding_correctness(4)
def test_with_one_shard(self):
self._assert_sharding_correctness(1)
def test_with_ten_shards(self):
self._assert_sharding_correctness(10)
def test_sharding_with_randomization(self):
# If we're both sharding *and* randomizing, we need to confirm that we
# randomize within the shard; we use two seeds to confirm we're seeing the
# same tests (sharding is consistent) in a different order.
tests_seen = []
for seed in ('7', '17'):
out, exit_code = self._run_sharded(
2, 0, additional_env={'TEST_RANDOMIZE_ORDERING_SEED': seed})
self.assertEqual(0, exit_code)
tests_seen.append([x for x in out.splitlines() if x.startswith('class')])
first_tests, second_tests = tests_seen # pylint: disable=unbalanced-tuple-unpacking
self.assertEqual(set(first_tests), set(second_tests))
self.assertNotEqual(first_tests, second_tests)
if __name__ == '__main__':
absltest.main()