1
0
mirror of https://github.com/home-assistant/core.git synced 2026-04-02 00:20:30 +01:00

Add Google Drive backup upload progress (#166549)

This commit is contained in:
tronikos
2026-03-26 02:31:07 -07:00
committed by GitHub
parent ea99f88d10
commit 758d5469aa
2 changed files with 63 additions and 3 deletions

View File

@@ -3,6 +3,7 @@
from __future__ import annotations
from collections.abc import AsyncIterator, Callable, Coroutine
from functools import wraps
import logging
from typing import Any
@@ -84,8 +85,22 @@ class GoogleDriveBackupAgent(BackupAgent):
:param open_stream: A function returning an async iterator that yields bytes.
:param backup: Metadata about the backup that should be uploaded.
"""
@wraps(open_stream)
async def wrapped_open_stream() -> AsyncIterator[bytes]:
stream = await open_stream()
async def _progress_stream() -> AsyncIterator[bytes]:
bytes_uploaded = 0
async for chunk in stream:
yield chunk
bytes_uploaded += len(chunk)
on_progress(bytes_uploaded=bytes_uploaded)
return _progress_stream()
try:
await self._client.async_upload_backup(open_stream, backup)
await self._client.async_upload_backup(wrapped_open_stream, backup)
except (GoogleDriveApiError, HomeAssistantError, TimeoutError) as err:
raise BackupAgentError(f"Failed to upload backup: {err}") from err

View File

@@ -1,5 +1,6 @@
"""Test the Google Drive backup platform."""
from collections.abc import AsyncIterator
from io import StringIO
import json
from typing import Any
@@ -16,6 +17,7 @@ from homeassistant.components.backup import (
AgentBackup,
)
from homeassistant.components.google_drive import DOMAIN
from homeassistant.components.google_drive.backup import GoogleDriveBackupAgent
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
@@ -59,6 +61,18 @@ TEST_AGENT_BACKUP_RESULT = {
}
async def consume_stream(
file_metadata: Any,
open_stream: Any,
*args: Any,
**kwargs: Any,
) -> None:
"""Consume the stream from the open_stream callable."""
stream = await open_stream()
async for _ in stream:
pass
@pytest.fixture(autouse=True)
async def setup_integration(
hass: HomeAssistant,
@@ -283,7 +297,7 @@ async def test_agents_upload(
snapshot: SnapshotAssertion,
) -> None:
"""Test agent upload backup."""
mock_api.resumable_upload_file = AsyncMock(return_value=None)
mock_api.resumable_upload_file = AsyncMock(side_effect=consume_stream)
client = await hass_client()
@@ -324,7 +338,7 @@ async def test_agents_upload_create_folder_if_missing(
mock_api.create_file = AsyncMock(
return_value={"id": "new folder id", "name": "Home Assistant"}
)
mock_api.resumable_upload_file = AsyncMock(return_value=None)
mock_api.resumable_upload_file = AsyncMock(side_effect=consume_stream)
client = await hass_client()
@@ -354,6 +368,37 @@ async def test_agents_upload_create_folder_if_missing(
assert [tuple(mock_call) for mock_call in mock_api.mock_calls] == snapshot
async def test_agents_upload_progress(
hass: HomeAssistant,
mock_api: MagicMock,
) -> None:
"""Test agent upload reports progress."""
mock_api.resumable_upload_file = AsyncMock(side_effect=consume_stream)
entries = hass.config_entries.async_entries(DOMAIN)
agent = GoogleDriveBackupAgent(entries[0])
progress_calls = []
def on_progress(*, bytes_uploaded: int, **kwargs: Any) -> None:
progress_calls.append(bytes_uploaded)
async def open_stream() -> AsyncIterator[bytes]:
async def stream() -> AsyncIterator[bytes]:
yield b"chunk1"
yield b"chunk2"
return stream()
await agent.async_upload_backup(
open_stream=open_stream,
backup=TEST_AGENT_BACKUP,
on_progress=on_progress,
)
assert progress_calls == [6, 12]
async def test_agents_upload_fail(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,