mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 17:57:11 +00:00
Fix split tests
This commit is contained in:
parent
7b0e4871da
commit
b42f1395fa
9
.github/workflows/ci.yaml
vendored
9
.github/workflows/ci.yaml
vendored
@ -876,15 +876,6 @@ jobs:
|
|||||||
- mypy
|
- mypy
|
||||||
name: Split tests for full run
|
name: Split tests for full run
|
||||||
steps:
|
steps:
|
||||||
- name: Install additional OS dependencies
|
|
||||||
run: |
|
|
||||||
sudo rm /etc/apt/sources.list.d/microsoft-prod.list
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get -y install \
|
|
||||||
bluez \
|
|
||||||
ffmpeg \
|
|
||||||
libturbojpeg \
|
|
||||||
libgammu-dev
|
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
|
@ -46,10 +46,18 @@ class BucketHolder:
|
|||||||
"""Split tests into buckets."""
|
"""Split tests into buckets."""
|
||||||
avg_execution_time = test_folder.approx_execution_time / self._bucket_count
|
avg_execution_time = test_folder.approx_execution_time / self._bucket_count
|
||||||
avg_not_measured_files = test_folder.not_measured_files / self._bucket_count
|
avg_not_measured_files = test_folder.not_measured_files / self._bucket_count
|
||||||
digits = len(str(test_folder.approx_execution_time))
|
digits = len(str(round(test_folder.approx_execution_time, 0)))
|
||||||
sorted_tests = sorted(
|
sorted_tests = sorted(
|
||||||
test_folder.get_all_flatten(),
|
test_folder.get_all_flatten(),
|
||||||
key=lambda x: (x.not_measured_files, -x.approx_execution_time),
|
key=lambda x: (
|
||||||
|
-x.approx_execution_time,
|
||||||
|
-x.count_children() if isinstance(x, TestFolder) else 0,
|
||||||
|
x.not_measured_files,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
bucket_sort_keys = (
|
||||||
|
lambda x: (x.not_measured_files, x.approx_execution_time),
|
||||||
|
lambda x: (x.approx_execution_time, x.not_measured_files),
|
||||||
)
|
)
|
||||||
for tests in sorted_tests:
|
for tests in sorted_tests:
|
||||||
if tests.added_to_bucket:
|
if tests.added_to_bucket:
|
||||||
@ -57,19 +65,13 @@ class BucketHolder:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
print(
|
print(
|
||||||
f"{tests.approx_execution_time:>{digits}} approx execution time for {tests.path}"
|
f"~{round(tests.approx_execution_time, 2):>{digits}}s execution time for {tests.path}"
|
||||||
)
|
)
|
||||||
is_file = isinstance(tests, TestFile)
|
is_file = isinstance(tests, TestFile)
|
||||||
for smallest_bucket in (
|
|
||||||
min(
|
for sort_key in bucket_sort_keys:
|
||||||
self._buckets,
|
smallest_bucket = min(self._buckets, key=sort_key)
|
||||||
key=lambda x: (x.not_measured_files, x.approx_execution_time),
|
|
||||||
),
|
|
||||||
min(
|
|
||||||
self._buckets,
|
|
||||||
key=lambda x: (x.approx_execution_time, x.not_measured_files),
|
|
||||||
),
|
|
||||||
):
|
|
||||||
if (
|
if (
|
||||||
(
|
(
|
||||||
smallest_bucket.approx_execution_time
|
smallest_bucket.approx_execution_time
|
||||||
@ -103,7 +105,8 @@ class BucketHolder:
|
|||||||
with Path("pytest_buckets.txt").open("w") as file:
|
with Path("pytest_buckets.txt").open("w") as file:
|
||||||
for idx, bucket in enumerate(self._buckets):
|
for idx, bucket in enumerate(self._buckets):
|
||||||
print(
|
print(
|
||||||
f"Bucket {idx + 1} execution time is ~{bucket.approx_execution_time}s with {bucket.not_measured_files} not measured files"
|
f"Bucket {idx + 1} execution time should be ~{bucket.approx_execution_time}s"
|
||||||
|
f" with {bucket.not_measured_files} not measured files"
|
||||||
)
|
)
|
||||||
file.write(bucket.get_paths_line())
|
file.write(bucket.get_paths_line())
|
||||||
|
|
||||||
@ -157,6 +160,14 @@ class TestFolder:
|
|||||||
"""Return if added to bucket."""
|
"""Return if added to bucket."""
|
||||||
return all(test.added_to_bucket for test in self.children.values())
|
return all(test.added_to_bucket for test in self.children.values())
|
||||||
|
|
||||||
|
def count_children(self) -> int:
|
||||||
|
"""Return the number of children."""
|
||||||
|
return len(self.children) + sum(
|
||||||
|
child.count_children()
|
||||||
|
for child in self.children.values()
|
||||||
|
if isinstance(child, TestFolder)
|
||||||
|
)
|
||||||
|
|
||||||
def add_to_bucket(self) -> None:
|
def add_to_bucket(self) -> None:
|
||||||
"""Add test file to bucket."""
|
"""Add test file to bucket."""
|
||||||
if self.added_to_bucket:
|
if self.added_to_bucket:
|
||||||
@ -168,11 +179,13 @@ class TestFolder:
|
|||||||
"""Return representation."""
|
"""Return representation."""
|
||||||
return f"TestFolder(approx_execution_time={self.approx_execution_time}, children={len(self.children)})"
|
return f"TestFolder(approx_execution_time={self.approx_execution_time}, children={len(self.children)})"
|
||||||
|
|
||||||
def add_test_file(self, path: Path, execution_time: float) -> None:
|
def add_test_file(
|
||||||
|
self, path: Path, execution_time: float, skip_file_if_present: bool
|
||||||
|
) -> None:
|
||||||
"""Add test file to folder."""
|
"""Add test file to folder."""
|
||||||
self._add_test_file(TestFile(path, self, execution_time))
|
self._add_test_file(TestFile(path, self, execution_time), skip_file_if_present)
|
||||||
|
|
||||||
def _add_test_file(self, file: TestFile) -> None:
|
def _add_test_file(self, file: TestFile, skip_file_if_present: bool) -> None:
|
||||||
"""Add test file to folder."""
|
"""Add test file to folder."""
|
||||||
path = file.path
|
path = file.path
|
||||||
file.parent = self
|
file.parent = self
|
||||||
@ -180,7 +193,11 @@ class TestFolder:
|
|||||||
if not relative_path.parts:
|
if not relative_path.parts:
|
||||||
raise ValueError("Path is not a child of this folder")
|
raise ValueError("Path is not a child of this folder")
|
||||||
|
|
||||||
if len(relative_path.parts) == 1 and path not in self.children:
|
if len(relative_path.parts) == 1:
|
||||||
|
if path in self.children:
|
||||||
|
if skip_file_if_present:
|
||||||
|
return
|
||||||
|
raise ValueError(f"File already exists: {path}")
|
||||||
self.children[path] = file
|
self.children[path] = file
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -189,7 +206,7 @@ class TestFolder:
|
|||||||
self.children[child_path] = child = TestFolder(child_path)
|
self.children[child_path] = child = TestFolder(child_path)
|
||||||
elif not isinstance(child, TestFolder):
|
elif not isinstance(child, TestFolder):
|
||||||
raise ValueError("Child is not a folder")
|
raise ValueError("Child is not a folder")
|
||||||
child._add_test_file(file)
|
child._add_test_file(file, skip_file_if_present)
|
||||||
|
|
||||||
def get_all_flatten(self) -> list[TestFolder | TestFile]:
|
def get_all_flatten(self) -> list[TestFolder | TestFile]:
|
||||||
"""Return self and all children as flatten list."""
|
"""Return self and all children as flatten list."""
|
||||||
@ -207,7 +224,7 @@ def process_execution_time_file(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Process the execution time file."""
|
"""Process the execution time file."""
|
||||||
for file, execution_time in load_json_object(execution_time_file).items():
|
for file, execution_time in load_json_object(execution_time_file).items():
|
||||||
test_folder.add_test_file(Path(file), cast(float, execution_time))
|
test_folder.add_test_file(Path(file), cast(float, execution_time), False)
|
||||||
|
|
||||||
|
|
||||||
def add_missing_test_files(folder: Path, test_folder: TestFolder) -> None:
|
def add_missing_test_files(folder: Path, test_folder: TestFolder) -> None:
|
||||||
@ -216,7 +233,7 @@ def add_missing_test_files(folder: Path, test_folder: TestFolder) -> None:
|
|||||||
if path.is_dir():
|
if path.is_dir():
|
||||||
add_missing_test_files(path, test_folder)
|
add_missing_test_files(path, test_folder)
|
||||||
elif path.name.startswith("test_") and path.suffix == ".py":
|
elif path.name.startswith("test_") and path.suffix == ".py":
|
||||||
test_folder.add_test_file(path, 0.0)
|
test_folder.add_test_file(path, 0.0, True)
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user