mirror of
https://github.com/Stone-Red-Code/dvc.git
synced 2026-11-02 07:18:25 +01:00
Closes #9058 . Ensures behavior of `_stash_exp` in `exp run` is matched in `exp save` without duplicating logic. The simpler implementation ended up being worse for maintenance when wanting to make `exp run` and `exp save` behavior in sync.
119 lines
3.5 KiB
Python
119 lines
3.5 KiB
Python
import pytest
|
|
from funcy import first
|
|
|
|
from dvc.repo.experiments.exceptions import ExperimentExistsError, InvalidArgumentError
|
|
from dvc.repo.experiments.utils import exp_refs_by_rev
|
|
from dvc.scm import resolve_rev
|
|
|
|
|
|
def setup_stage(tmp_dir, dvc, scm):
|
|
tmp_dir.gen("params.yaml", "foo: 1")
|
|
dvc.run(name="echo-foo", outs=["bar"], cmd="echo ${foo} > bar")
|
|
scm.add(["dvc.yaml", "dvc.lock", ".gitignore", "params.yaml"])
|
|
scm.commit("init")
|
|
|
|
|
|
def test_exp_save_unchanged(tmp_dir, dvc, scm):
|
|
setup_stage(tmp_dir, dvc, scm)
|
|
dvc.experiments.save()
|
|
|
|
|
|
@pytest.mark.parametrize("name", (None, "test"))
|
|
def test_exp_save(tmp_dir, dvc, scm, name):
|
|
setup_stage(tmp_dir, dvc, scm)
|
|
baseline = scm.get_rev()
|
|
|
|
exp = dvc.experiments.save(name=name)
|
|
ref_info = first(exp_refs_by_rev(scm, exp))
|
|
assert ref_info
|
|
assert ref_info.baseline_sha == baseline
|
|
|
|
exp_name = name if name else ref_info.name
|
|
assert dvc.experiments.get_exact_name([exp])[exp] == exp_name
|
|
assert resolve_rev(scm, exp_name) == exp
|
|
|
|
|
|
def test_exp_save_overwrite_experiment(tmp_dir, dvc, scm):
|
|
setup_stage(tmp_dir, dvc, scm)
|
|
name = "dummy"
|
|
dvc.experiments.save(name=name)
|
|
|
|
tmp_dir.gen("params.yaml", "foo: 2")
|
|
with pytest.raises(ExperimentExistsError):
|
|
dvc.experiments.save(name=name)
|
|
|
|
dvc.experiments.save(name=name, force=True)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"name",
|
|
(
|
|
"invalid/name",
|
|
"invalid..name",
|
|
"invalid~name",
|
|
"invalid?name",
|
|
"invalidname.",
|
|
),
|
|
)
|
|
def test_exp_save_invalid_name(tmp_dir, dvc, scm, name):
|
|
setup_stage(tmp_dir, dvc, scm)
|
|
with pytest.raises(InvalidArgumentError):
|
|
dvc.experiments.save(name=name, force=True)
|
|
|
|
|
|
def test_exp_save_after_commit(tmp_dir, dvc, scm):
|
|
setup_stage(tmp_dir, dvc, scm)
|
|
baseline = scm.get_rev()
|
|
dvc.experiments.save(name="exp-1", force=True)
|
|
|
|
tmp_dir.scm_gen({"new_file": "new_file"}, commit="new baseline")
|
|
dvc.experiments.save(name="exp-2", force=True)
|
|
|
|
all_exps = dvc.experiments.ls(all_commits=True)
|
|
assert all_exps[baseline[:7]] == ["exp-1"]
|
|
assert all_exps["master"] == ["exp-2"]
|
|
|
|
|
|
def test_exp_save_with_staged_changes(tmp_dir, dvc, scm):
|
|
setup_stage(tmp_dir, dvc, scm)
|
|
tmp_dir.gen({"deleted": "deleted", "modified": "modified"})
|
|
scm.add_commit(["deleted", "modified"], "init")
|
|
|
|
(tmp_dir / "deleted").unlink()
|
|
tmp_dir.gen({"new_file": "new_file"})
|
|
(tmp_dir / "modified").write_text("foo")
|
|
scm.add(["deleted", "new_file", "modified"])
|
|
|
|
exp_rev = dvc.experiments.save(name="exp")
|
|
scm.checkout(exp_rev, force=True)
|
|
assert not (tmp_dir / "deleted").exists()
|
|
assert (tmp_dir / "new_file").exists()
|
|
assert (tmp_dir / "modified").read_text() == "foo"
|
|
|
|
|
|
def test_exp_save_include_untracked(tmp_dir, dvc, scm):
|
|
setup_stage(tmp_dir, dvc, scm)
|
|
|
|
new_file = tmp_dir / "new_file"
|
|
new_file.write_text("new_file")
|
|
dvc.experiments.save(name="exp", include_untracked=["new_file"])
|
|
|
|
_, _, unstaged = scm.status()
|
|
assert "new_file" in unstaged
|
|
assert new_file.read_text() == "new_file"
|
|
|
|
|
|
def test_exp_save_include_untracked_warning(tmp_dir, dvc, scm, mocker):
|
|
"""Regression test for https://github.com/iterative/dvc/issues/9061"""
|
|
setup_stage(tmp_dir, dvc, scm)
|
|
|
|
new_dir = tmp_dir / "new_dir"
|
|
new_dir.mkdir()
|
|
(new_dir / "foo").write_text("foo")
|
|
(new_dir / "bar").write_text("bar")
|
|
|
|
logger = mocker.patch("dvc.repo.experiments.executor.base.logger")
|
|
|
|
dvc.experiments.save(name="exp", include_untracked=["new_dir"])
|
|
assert not logger.warning.called
|