Files
dvc/tests/unit/repo/experiments/queue/test_celery.py
T
pre-commit-ci[bot]andSaugat Pachhai ca3704997b [pre-commit.ci] pre-commit autoupdate (#9632)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/asottile/pyupgrade: v3.3.1 → v3.9.0](https://github.com/asottile/pyupgrade/compare/v3.3.1...v3.9.0)
- https://github.com/charliermarsh/ruff-pre-commit → https://github.com/astral-sh/ruff-pre-commit
- [github.com/astral-sh/ruff-pre-commit: v0.0.261 → v0.0.277](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.261...v0.0.277)
- [github.com/codespell-project/codespell: v2.2.4 → v2.2.5](https://github.com/codespell-project/codespell/compare/v2.2.4...v2.2.5)

* fix ruff complaints

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Saugat Pachhai (सौगात) <[email protected]>
2023-07-14 20:11:15 +05:45

265 lines
7.5 KiB
Python

import time
import pytest
from celery import shared_task
from celery.result import AsyncResult
from dvc.exceptions import DvcException
from dvc.repo.experiments.exceptions import UnresolvedExpNamesError
from dvc.repo.experiments.queue.base import QueueDoneResult
from dvc.repo.experiments.queue.exceptions import CannotKillTasksError
def test_shutdown_no_tasks(test_queue, mocker):
shutdown_spy = mocker.spy(test_queue.celery.control, "shutdown")
test_queue.shutdown()
shutdown_spy.assert_called_once()
@shared_task
def _foo(arg=None):
return "foo"
def test_shutdown(test_queue, mocker):
shutdown_spy = mocker.patch("celery.app.control.Control.shutdown")
test_queue.shutdown()
shutdown_spy.assert_called_once()
def test_shutdown_with_kill(test_queue, mocker):
mock_entry_foo = mocker.Mock(stash_rev="af12de")
mock_entry_foo.name = "foo"
mock_entry_bar = mocker.Mock(stash_rev="bar")
mock_entry_bar.name = None
shutdown_spy = mocker.patch("celery.app.control.Control.shutdown")
mocker.patch.object(
test_queue,
"iter_active",
return_value=[mock_entry_foo, mock_entry_bar],
)
kill_spy = mocker.patch.object(test_queue, "_kill_entries")
test_queue.shutdown(kill=True)
shutdown_spy.assert_called_once()
kill_spy.assert_called_once_with(
{mock_entry_foo: "foo", mock_entry_bar: "bar"}, True
)
def test_post_run_after_kill(test_queue):
from celery import chain
sig_bar = test_queue.proc.run_signature(
["python3", "-c", "import time; time.sleep(10)"], name="bar"
)
sig_bar.freeze()
sig_foo = _foo.s()
result_foo = sig_foo.freeze()
run_chain = chain(sig_bar, sig_foo)
run_chain.delay()
timeout = time.time() + 10
while True:
try:
test_queue.proc.kill("bar")
assert result_foo.status == "PENDING"
break
except ProcessLookupError:
time.sleep(0.1)
if time.time() > timeout:
raise TimeoutError
assert result_foo.get(timeout=10) == "foo"
@pytest.mark.parametrize("force", [True, False])
def test_celery_queue_kill(test_queue, mocker, force):
mock_entry_foo = mocker.Mock(stash_rev="foo")
mock_entry_bar = mocker.Mock(stash_rev="bar")
mock_entry_foobar = mocker.Mock(stash_rev="foobar")
mocker.patch.object(
test_queue,
"iter_active",
return_value={mock_entry_foo, mock_entry_bar, mock_entry_foobar},
)
mocker.patch.object(
test_queue,
"match_queue_entry_by_name",
return_value={
"bar": mock_entry_bar,
"foo": mock_entry_foo,
"foobar": mock_entry_foobar,
},
)
mocker.patch.object(
test_queue,
"_get_running_task_ids",
return_value={"foo", "foobar"},
)
mocker.patch.object(
test_queue,
"_iter_processed",
return_value=[
(mocker.Mock(headers={"id": "foo"}), mock_entry_foo),
(mocker.Mock(headers={"id": "bar"}), mock_entry_bar),
(mocker.Mock(headers={"id": "foobar"}), mock_entry_foobar),
],
)
mocker.patch.object(
AsyncResult,
"ready",
return_value=False,
)
mark_mocker = mocker.patch.object(
test_queue.celery.backend,
"mark_as_failure",
)
def kill_function(rev):
if rev == "foo":
return True
raise ProcessLookupError
kill_mock = mocker.patch.object(
test_queue.proc,
"kill" if force else "interrupt",
side_effect=mocker.MagicMock(side_effect=kill_function),
)
with pytest.raises(CannotKillTasksError, match="Task 'foobar' is initializing,"):
test_queue.kill(["bar", "foo", "foobar"], force=force)
assert kill_mock.call_args_list == [
mocker.call(mock_entry_bar.stash_rev),
mocker.call(mock_entry_foo.stash_rev),
mocker.call(mock_entry_foobar.stash_rev),
]
mark_mocker.assert_called_once_with("bar", None)
@pytest.mark.parametrize("force", [True, False])
def test_celery_queue_kill_invalid(test_queue, mocker, force):
mock_entry_foo = mocker.Mock(stash_rev="foo")
mock_entry_bar = mocker.Mock(stash_rev="bar")
mocker.patch.object(
test_queue,
"match_queue_entry_by_name",
return_value={
"bar": mock_entry_bar,
"foo": mock_entry_foo,
"foobar": None,
},
)
kill_mock = mocker.patch.object(test_queue, "_kill_entries")
with pytest.raises(UnresolvedExpNamesError):
test_queue.kill(["bar", "foo", "foobar"], force=force)
kill_mock.assert_called_once_with(
{mock_entry_foo: "foo", mock_entry_bar: "bar"}, force
)
@pytest.mark.parametrize("status", ["FAILURE", "SUCCESS"])
def test_queue_iter_done_task(test_queue, mocker, status):
mock_entry = mocker.Mock(stash_rev=_foo.name)
result = mocker.Mock(status=status)
mocker.patch.object(
test_queue,
"_iter_done_tasks",
return_value=[(result, mock_entry)],
)
if status == "FAILURE":
assert list(test_queue.iter_failed()) == [QueueDoneResult(mock_entry, None)]
elif status == "SUCCESS":
with pytest.raises(DvcException, match="Invalid experiment"):
assert list(test_queue.iter_success())
def test_queue_status(test_queue, scm, mocker):
from datetime import datetime
active_entry = mocker.Mock(stash_rev="active")
active_entry.name = "foo"
queued_entry = mocker.Mock(stash_rev="queued")
queued_entry.name = None
failed_entry = mocker.Mock(stash_rev="failed")
failed_entry.name = "bar"
success_entry = mocker.Mock(stash_rev="success")
success_entry.name = None
success_result = mocker.Mock(ref_info=mocker.Mock())
success_result.ref_info.name = "foobar"
def resolve_commit(rev):
if rev == "active":
commit_time = datetime(2022, 8, 7).timestamp()
elif rev == "queued":
commit_time = datetime(2022, 8, 6).timestamp()
elif rev == "failed":
commit_time = datetime(2022, 8, 5).timestamp()
elif rev == "success":
commit_time = datetime(2022, 8, 4).timestamp()
return mocker.Mock(commit_time=commit_time)
mocker.patch.object(
scm,
"resolve_commit",
side_effect=mocker.MagicMock(side_effect=resolve_commit),
)
mocker.patch.object(
test_queue,
"iter_active",
return_value=[active_entry],
)
mocker.patch.object(
test_queue,
"iter_queued",
return_value=[queued_entry],
)
mocker.patch.object(
test_queue,
"iter_failed",
return_value=[(failed_entry, None)],
)
mocker.patch.object(
test_queue,
"iter_success",
return_value=[(success_entry, success_result)],
)
assert test_queue.status() == [
{
"name": "foo",
"rev": "active",
"status": "Running",
"timestamp": datetime(2022, 8, 7, 0, 0, 0),
},
{
"name": None,
"rev": "queued",
"status": "Queued",
"timestamp": datetime(2022, 8, 6, 0, 0, 0),
},
{
"name": "bar",
"rev": "failed",
"status": "Failed",
"timestamp": datetime(2022, 8, 5, 0, 0, 0),
},
{
"name": "foobar",
"rev": "success",
"status": "Success",
"timestamp": datetime(2022, 8, 4, 0, 0, 0),
},
]