mirror of
https://github.com/Stone-Red-Code/dvc.git
synced 2026-11-02 07:18:25 +01:00
test: move func tests to tests/func
Signed-off-by: Ruslan Kuprieiev <[email protected]>
This commit is contained in:
committed by
Ruslan Kuprieiev
parent
6c730dd5cb
commit
6f11f1f51f
@@ -0,0 +1,490 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
|
||||
import dvc
|
||||
import time
|
||||
import shutil
|
||||
import filecmp
|
||||
import posixpath
|
||||
|
||||
from dvc.system import System
|
||||
from mock import patch
|
||||
|
||||
from dvc.main import main
|
||||
from dvc.utils import file_md5, load_stage_file
|
||||
from dvc.stage import Stage
|
||||
from dvc.exceptions import DvcException, RecursiveAddingWhileUsingFilename
|
||||
from dvc.output.base import OutputAlreadyTrackedError
|
||||
from dvc.repo import Repo as DvcRepo
|
||||
|
||||
from tests.basic_env import TestDvc
|
||||
from tests.utils import spy, get_gitignore_content
|
||||
|
||||
|
||||
class TestAdd(TestDvc):
|
||||
def test(self):
|
||||
md5 = file_md5(self.FOO)[0]
|
||||
|
||||
stages = self.dvc.add(self.FOO)
|
||||
self.assertEqual(len(stages), 1)
|
||||
stage = stages[0]
|
||||
self.assertTrue(stage is not None)
|
||||
|
||||
self.assertIsInstance(stage, Stage)
|
||||
self.assertTrue(os.path.isfile(stage.path))
|
||||
self.assertEqual(len(stage.outs), 1)
|
||||
self.assertEqual(len(stage.deps), 0)
|
||||
self.assertEqual(stage.cmd, None)
|
||||
self.assertEqual(stage.outs[0].info["md5"], md5)
|
||||
self.assertEqual(stage.md5, "ee343f2482f53efffc109be83cc976ac")
|
||||
|
||||
def test_unicode(self):
|
||||
fname = "\xe1"
|
||||
|
||||
with open(fname, "w") as fobj:
|
||||
fobj.write("something")
|
||||
|
||||
stage = self.dvc.add(fname)[0]
|
||||
|
||||
self.assertTrue(os.path.isfile(stage.path))
|
||||
|
||||
|
||||
class TestAddUnupportedFile(TestDvc):
|
||||
def test(self):
|
||||
with self.assertRaises(DvcException):
|
||||
self.dvc.add("unsupported://unsupported")
|
||||
|
||||
|
||||
class TestAddDirectory(TestDvc):
|
||||
def test(self):
|
||||
dname = "directory"
|
||||
os.mkdir(dname)
|
||||
self.create(os.path.join(dname, "file"), "file")
|
||||
stages = self.dvc.add(dname)
|
||||
self.assertEqual(len(stages), 1)
|
||||
stage = stages[0]
|
||||
self.assertTrue(stage is not None)
|
||||
self.assertEqual(len(stage.deps), 0)
|
||||
self.assertEqual(len(stage.outs), 1)
|
||||
|
||||
md5 = stage.outs[0].info["md5"]
|
||||
|
||||
dir_info = self.dvc.cache.local.load_dir_cache(md5)
|
||||
for info in dir_info:
|
||||
self.assertTrue("\\" not in info["relpath"])
|
||||
|
||||
|
||||
class TestAddDirectoryRecursive(TestDvc):
|
||||
def test(self):
|
||||
stages = self.dvc.add(self.DATA_DIR, recursive=True)
|
||||
self.assertEqual(len(stages), 2)
|
||||
|
||||
|
||||
class TestAddCmdDirectoryRecursive(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["add", "--recursive", self.DATA_DIR])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
|
||||
class TestAddDirectoryWithForwardSlash(TestDvc):
|
||||
def test(self):
|
||||
dname = "directory/"
|
||||
os.mkdir(dname)
|
||||
self.create(os.path.join(dname, "file"), "file")
|
||||
stages = self.dvc.add(dname)
|
||||
self.assertEqual(len(stages), 1)
|
||||
stage = stages[0]
|
||||
self.assertTrue(stage is not None)
|
||||
self.assertEqual(os.path.abspath("directory.dvc"), stage.path)
|
||||
|
||||
|
||||
class TestAddTrackedFile(TestDvc):
|
||||
def test(self):
|
||||
fname = "tracked_file"
|
||||
self.create(fname, "tracked file contents")
|
||||
self.dvc.scm.add([fname])
|
||||
self.dvc.scm.commit("add {}".format(fname))
|
||||
|
||||
with self.assertRaises(OutputAlreadyTrackedError):
|
||||
self.dvc.add(fname)
|
||||
|
||||
|
||||
class TestAddDirWithExistingCache(TestDvc):
|
||||
def test(self):
|
||||
dname = "a"
|
||||
fname = os.path.join(dname, "b")
|
||||
os.mkdir(dname)
|
||||
shutil.copyfile(self.FOO, fname)
|
||||
|
||||
stages = self.dvc.add(self.FOO)
|
||||
self.assertEqual(len(stages), 1)
|
||||
self.assertTrue(stages[0] is not None)
|
||||
stages = self.dvc.add(dname)
|
||||
self.assertEqual(len(stages), 1)
|
||||
self.assertTrue(stages[0] is not None)
|
||||
|
||||
|
||||
class TestAddModifiedDir(TestDvc):
|
||||
def test(self):
|
||||
stages = self.dvc.add(self.DATA_DIR)
|
||||
self.assertEqual(len(stages), 1)
|
||||
self.assertTrue(stages[0] is not None)
|
||||
os.unlink(self.DATA)
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
stages = self.dvc.add(self.DATA_DIR)
|
||||
self.assertEqual(len(stages), 1)
|
||||
self.assertTrue(stages[0] is not None)
|
||||
|
||||
|
||||
class TestAddFileInDir(TestDvc):
|
||||
def test(self):
|
||||
stages = self.dvc.add(self.DATA_SUB)
|
||||
self.assertEqual(len(stages), 1)
|
||||
stage = stages[0]
|
||||
self.assertNotEqual(stage, None)
|
||||
self.assertEqual(len(stage.deps), 0)
|
||||
self.assertEqual(len(stage.outs), 1)
|
||||
self.assertEqual(stage.relpath, self.DATA_SUB + ".dvc")
|
||||
|
||||
|
||||
class TestAddExternalLocalFile(TestDvc):
|
||||
def test(self):
|
||||
dname = TestDvc.mkdtemp()
|
||||
fname = os.path.join(dname, "foo")
|
||||
shutil.copyfile(self.FOO, fname)
|
||||
|
||||
stages = self.dvc.add(fname)
|
||||
self.assertEqual(len(stages), 1)
|
||||
stage = stages[0]
|
||||
self.assertNotEqual(stage, None)
|
||||
self.assertEqual(len(stage.deps), 0)
|
||||
self.assertEqual(len(stage.outs), 1)
|
||||
self.assertEqual(stage.relpath, "foo.dvc")
|
||||
self.assertEqual(len(os.listdir(dname)), 1)
|
||||
self.assertTrue(os.path.isfile(fname))
|
||||
self.assertTrue(filecmp.cmp(fname, "foo", shallow=False))
|
||||
|
||||
|
||||
class TestAddLocalRemoteFile(TestDvc):
|
||||
def test(self):
|
||||
"""
|
||||
Making sure that 'remote' syntax is handled properly for local outs.
|
||||
"""
|
||||
cwd = os.getcwd()
|
||||
remote = "myremote"
|
||||
|
||||
ret = main(["remote", "add", remote, cwd])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.dvc = DvcRepo()
|
||||
|
||||
foo = "remote://{}/{}".format(remote, self.FOO)
|
||||
ret = main(["add", foo])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
d = load_stage_file("foo.dvc")
|
||||
self.assertEqual(d["outs"][0]["path"], foo)
|
||||
|
||||
bar = os.path.join(cwd, self.BAR)
|
||||
ret = main(["add", bar])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
d = load_stage_file("bar.dvc")
|
||||
self.assertEqual(d["outs"][0]["path"], bar)
|
||||
|
||||
|
||||
class TestCmdAdd(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["add", self.FOO])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["add", "non-existing-file"])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
|
||||
class TestDoubleAddUnchanged(TestDvc):
|
||||
def test_file(self):
|
||||
ret = main(["add", self.FOO])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["add", self.FOO])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_dir(self):
|
||||
ret = main(["add", self.DATA_DIR])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["add", self.DATA_DIR])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
|
||||
class TestShouldUpdateStateEntryForFileAfterAdd(TestDvc):
|
||||
def test(self):
|
||||
file_md5_counter = spy(dvc.state.file_md5)
|
||||
with patch.object(dvc.state, "file_md5", file_md5_counter):
|
||||
|
||||
ret = main(["config", "cache.type", "copy"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["add", self.FOO])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertEqual(file_md5_counter.mock.call_count, 1)
|
||||
|
||||
ret = main(["status"])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertEqual(file_md5_counter.mock.call_count, 1)
|
||||
|
||||
ret = main(["run", "-d", self.FOO, "cat {}".format(self.FOO)])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertEqual(file_md5_counter.mock.call_count, 1)
|
||||
|
||||
|
||||
class TestShouldUpdateStateEntryForDirectoryAfterAdd(TestDvc):
|
||||
def test(self):
|
||||
file_md5_counter = spy(dvc.state.file_md5)
|
||||
with patch.object(dvc.state, "file_md5", file_md5_counter):
|
||||
|
||||
ret = main(["config", "cache.type", "copy"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["add", self.DATA_DIR])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertEqual(file_md5_counter.mock.call_count, 3)
|
||||
|
||||
ret = main(["status"])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertEqual(file_md5_counter.mock.call_count, 3)
|
||||
|
||||
ret = main(
|
||||
["run", "-d", self.DATA_DIR, "ls {}".format(self.DATA_DIR)]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertEqual(file_md5_counter.mock.call_count, 3)
|
||||
|
||||
|
||||
class TestAddCommit(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["add", self.FOO, "--no-commit"])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertTrue(os.path.isfile(self.FOO))
|
||||
self.assertEqual(len(os.listdir(self.dvc.cache.local.cache_dir)), 0)
|
||||
|
||||
ret = main(["commit", self.FOO + ".dvc"])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertTrue(os.path.isfile(self.FOO))
|
||||
self.assertEqual(len(os.listdir(self.dvc.cache.local.cache_dir)), 1)
|
||||
|
||||
|
||||
class TestShouldNotCheckCacheForDirIfCacheMetadataDidNotChange(TestDvc):
|
||||
def test(self):
|
||||
remote_local_loader_spy = spy(
|
||||
dvc.remote.local.RemoteLOCAL.load_dir_cache
|
||||
)
|
||||
with patch.object(
|
||||
dvc.remote.local.RemoteLOCAL,
|
||||
"load_dir_cache",
|
||||
remote_local_loader_spy,
|
||||
):
|
||||
|
||||
ret = main(["config", "cache.type", "copy"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["add", self.DATA_DIR])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertEqual(1, remote_local_loader_spy.mock.call_count)
|
||||
|
||||
ret = main(["status", "{}.dvc".format(self.DATA_DIR)])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertEqual(1, remote_local_loader_spy.mock.call_count)
|
||||
|
||||
|
||||
class TestShouldCollectDirCacheOnlyOnce(TestDvc):
|
||||
NEW_LARGE_DIR_SIZE = 1
|
||||
|
||||
@patch("dvc.remote.local.LARGE_DIR_SIZE", NEW_LARGE_DIR_SIZE)
|
||||
def test(self):
|
||||
from dvc.remote.local import RemoteLOCAL
|
||||
|
||||
collect_dir_counter = spy(RemoteLOCAL.collect_dir_cache)
|
||||
with patch.object(
|
||||
RemoteLOCAL, "collect_dir_cache", collect_dir_counter
|
||||
):
|
||||
|
||||
LARGE_DIR_FILES_NUM = self.NEW_LARGE_DIR_SIZE + 1
|
||||
data_dir = "dir"
|
||||
|
||||
os.makedirs(data_dir)
|
||||
|
||||
for i in range(LARGE_DIR_FILES_NUM):
|
||||
with open(os.path.join(data_dir, str(i)), "w+") as f:
|
||||
f.write(str(i))
|
||||
|
||||
ret = main(["add", data_dir])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
ret = main(["status"])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
ret = main(["status"])
|
||||
self.assertEqual(0, ret)
|
||||
self.assertEqual(1, collect_dir_counter.mock.call_count)
|
||||
|
||||
|
||||
class SymlinkAddTestBase(TestDvc):
|
||||
def _get_data_dir(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def _prepare_external_data(self):
|
||||
data_dir = self._get_data_dir()
|
||||
|
||||
self.data_file_name = "data_file"
|
||||
external_data_path = os.path.join(data_dir, self.data_file_name)
|
||||
with open(external_data_path, "w+") as f:
|
||||
f.write("data")
|
||||
|
||||
self.link_name = "data_link"
|
||||
System.symlink(data_dir, self.link_name)
|
||||
|
||||
def _test(self):
|
||||
self._prepare_external_data()
|
||||
|
||||
ret = main(["add", os.path.join(self.link_name, self.data_file_name)])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
stage_file = self.data_file_name + Stage.STAGE_FILE_SUFFIX
|
||||
self.assertTrue(os.path.exists(stage_file))
|
||||
|
||||
d = load_stage_file(stage_file)
|
||||
relative_data_path = posixpath.join(
|
||||
self.link_name, self.data_file_name
|
||||
)
|
||||
self.assertEqual(relative_data_path, d["outs"][0]["path"])
|
||||
|
||||
|
||||
class TestShouldAddDataFromExternalSymlink(SymlinkAddTestBase):
|
||||
def _get_data_dir(self):
|
||||
return self.mkdtemp()
|
||||
|
||||
def test(self):
|
||||
self._test()
|
||||
|
||||
|
||||
class TestShouldAddDataFromInternalSymlink(SymlinkAddTestBase):
|
||||
def _get_data_dir(self):
|
||||
return self.DATA_DIR
|
||||
|
||||
def test(self):
|
||||
self._test()
|
||||
|
||||
|
||||
class TestShouldPlaceStageInDataDirIfRepositoryBelowSymlink(TestDvc):
|
||||
def test(self):
|
||||
def is_symlink_true_below_dvc_root(path):
|
||||
if path == os.path.dirname(self.dvc.root_dir):
|
||||
return True
|
||||
return False
|
||||
|
||||
with patch.object(
|
||||
System, "is_symlink", side_effect=is_symlink_true_below_dvc_root
|
||||
):
|
||||
|
||||
ret = main(["add", self.DATA])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
stage_file_path_on_data_below_symlink = (
|
||||
os.path.basename(self.DATA) + Stage.STAGE_FILE_SUFFIX
|
||||
)
|
||||
self.assertFalse(
|
||||
os.path.exists(stage_file_path_on_data_below_symlink)
|
||||
)
|
||||
|
||||
stage_file_path = self.DATA + Stage.STAGE_FILE_SUFFIX
|
||||
self.assertTrue(os.path.exists(stage_file_path))
|
||||
|
||||
|
||||
class TestShouldThrowProperExceptionOnCorruptedStageFile(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["add", self.FOO])
|
||||
assert 0 == ret
|
||||
|
||||
foo_stage = os.path.relpath(self.FOO + Stage.STAGE_FILE_SUFFIX)
|
||||
|
||||
# corrupt stage file
|
||||
with open(foo_stage, "a+") as file:
|
||||
file.write("this will break yaml file structure")
|
||||
|
||||
self._caplog.clear()
|
||||
|
||||
ret = main(["add", self.BAR])
|
||||
assert 1 == ret
|
||||
|
||||
expected_error = (
|
||||
"unable to read stage file: {} "
|
||||
"YAML file structure is corrupted".format(foo_stage)
|
||||
)
|
||||
|
||||
assert expected_error in self._caplog.text
|
||||
|
||||
|
||||
class TestAddFilename(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["add", self.FOO, self.BAR, "-f", "error.dvc"])
|
||||
self.assertNotEqual(0, ret)
|
||||
|
||||
ret = main(["add", "-R", self.DATA_DIR, "-f", "error.dvc"])
|
||||
self.assertNotEqual(0, ret)
|
||||
|
||||
with self.assertRaises(RecursiveAddingWhileUsingFilename):
|
||||
self.dvc.add(self.DATA_DIR, recursive=True, fname="error.dvc")
|
||||
|
||||
ret = main(["add", self.DATA_DIR, "-f", "data_directory.dvc"])
|
||||
self.assertEqual(0, ret)
|
||||
self.assertTrue(os.path.exists("data_directory.dvc"))
|
||||
|
||||
ret = main(["add", self.FOO, "-f", "bar.dvc"])
|
||||
self.assertEqual(0, ret)
|
||||
self.assertTrue(os.path.exists("bar.dvc"))
|
||||
self.assertFalse(os.path.exists("foo.dvc"))
|
||||
|
||||
os.remove("bar.dvc")
|
||||
|
||||
ret = main(["add", self.FOO, "--file", "bar.dvc"])
|
||||
self.assertEqual(0, ret)
|
||||
self.assertTrue(os.path.exists("bar.dvc"))
|
||||
self.assertFalse(os.path.exists("foo.dvc"))
|
||||
|
||||
|
||||
class TestShouldCleanUpAfterFailedAdd(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["add", self.FOO])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
foo_stage_file = self.FOO + Stage.STAGE_FILE_SUFFIX
|
||||
# corrupt stage file
|
||||
with open(foo_stage_file, "a+") as file:
|
||||
file.write("this will break yaml file structure")
|
||||
|
||||
ret = main(["add", self.BAR])
|
||||
self.assertEqual(1, ret)
|
||||
|
||||
bar_stage_file = self.BAR + Stage.STAGE_FILE_SUFFIX
|
||||
self.assertFalse(os.path.exists(bar_stage_file))
|
||||
|
||||
gitignore_content = get_gitignore_content()
|
||||
self.assertNotIn("/" + self.BAR, gitignore_content)
|
||||
|
||||
|
||||
class TestShouldNotTrackGitInternalFiles(TestDvc):
|
||||
def test(self):
|
||||
stage_creator_spy = spy(dvc.repo.add._create_stages)
|
||||
|
||||
with patch.object(dvc.repo.add, "_create_stages", stage_creator_spy):
|
||||
ret = main(["add", "-R", self.dvc.root_dir])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
created_stages_filenames = stage_creator_spy.mock.call_args[0][0]
|
||||
for fname in created_stages_filenames:
|
||||
self.assertNotIn(".git", fname)
|
||||
@@ -0,0 +1,68 @@
|
||||
import os
|
||||
import mock
|
||||
import requests
|
||||
|
||||
from dvc.main import main
|
||||
from dvc.analytics import Analytics
|
||||
from tests.basic_env import TestDvc, TestGit, TestDir
|
||||
|
||||
|
||||
def _clean_getenv(key, default=None):
|
||||
"""
|
||||
Remove env vars that affect dvc behavior in tests
|
||||
"""
|
||||
if key in ["DVC_TEST", "CI"]:
|
||||
return None
|
||||
return os.environ.get(key, default)
|
||||
|
||||
|
||||
class TestAnalytics(TestDir):
|
||||
def test(self):
|
||||
a = Analytics()
|
||||
a.collect()
|
||||
self.assertTrue(isinstance(a.info, dict))
|
||||
self.assertNotEqual(a.info, {})
|
||||
self.assertTrue(a.PARAM_USER_ID in a.info.keys())
|
||||
self.assertTrue(a.PARAM_SYSTEM_INFO in a.info.keys())
|
||||
self.assertNotEqual(a.info[a.PARAM_SYSTEM_INFO], {})
|
||||
|
||||
@mock.patch.object(os, "getenv", new=_clean_getenv)
|
||||
@mock.patch(
|
||||
"dvc.analytics.Analytics._is_enabled_config", return_value=True
|
||||
)
|
||||
@mock.patch("requests.post")
|
||||
def test_send(self, mockpost, _):
|
||||
ret = main(["daemon", "analytics", Analytics().dump(), "-v"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertTrue(mockpost.called)
|
||||
|
||||
@mock.patch.object(os, "getenv", new=_clean_getenv)
|
||||
@mock.patch(
|
||||
"dvc.analytics.Analytics._is_enabled_config", return_value=True
|
||||
)
|
||||
@mock.patch.object(
|
||||
requests, "post", side_effect=requests.exceptions.RequestException()
|
||||
)
|
||||
def test_send_failed(self, mockpost, _):
|
||||
ret = main(["daemon", "analytics", Analytics().dump(), "-v"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertTrue(mockpost.called)
|
||||
|
||||
|
||||
class TestAnalyticsGit(TestAnalytics, TestGit):
|
||||
pass
|
||||
|
||||
|
||||
class TestAnalyticsDvc(TestAnalytics, TestDvc):
|
||||
@mock.patch("requests.post")
|
||||
def test_send_disabled(self, mockpost):
|
||||
ret = main(["config", "core.analytics", "false"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
with mock.patch.object(os, "getenv", new=_clean_getenv):
|
||||
ret = main(["daemon", "analytics", Analytics().dump(), "-v"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertFalse(mockpost.called)
|
||||
@@ -0,0 +1,175 @@
|
||||
import os
|
||||
import shutil
|
||||
import configobj
|
||||
|
||||
from dvc.cache import Cache
|
||||
from dvc.main import main
|
||||
|
||||
from tests.basic_env import TestDvc, TestDir
|
||||
|
||||
|
||||
class TestCache(TestDvc):
|
||||
def setUp(self):
|
||||
super(TestCache, self).setUp()
|
||||
self.cache1_md5 = "123"
|
||||
self.cache2_md5 = "234"
|
||||
self.cache1 = os.path.join(
|
||||
self.dvc.cache.local.cache_dir,
|
||||
self.cache1_md5[0:2],
|
||||
self.cache1_md5[2:],
|
||||
)
|
||||
self.cache2 = os.path.join(
|
||||
self.dvc.cache.local.cache_dir,
|
||||
self.cache2_md5[0:2],
|
||||
self.cache2_md5[2:],
|
||||
)
|
||||
self.create(self.cache1, "1")
|
||||
self.create(self.cache2, "2")
|
||||
|
||||
def test_all(self):
|
||||
md5_list = list(Cache(self.dvc).local.all())
|
||||
self.assertEqual(len(md5_list), 2)
|
||||
self.assertIn(self.cache1_md5, md5_list)
|
||||
self.assertIn(self.cache2_md5, md5_list)
|
||||
|
||||
def test_get(self):
|
||||
cache = Cache(self.dvc).local.get(self.cache1_md5)
|
||||
self.assertEqual(cache, self.cache1)
|
||||
|
||||
|
||||
class TestCacheLoadBadDirCache(TestDvc):
|
||||
def _do_test(self, ret):
|
||||
self.assertTrue(isinstance(ret, list))
|
||||
self.assertEqual(len(ret), 0)
|
||||
|
||||
def test(self):
|
||||
fname = self.dvc.cache.local.get("123.dir")
|
||||
self.create(fname, "<clearly>not,json")
|
||||
self._do_test(self.dvc.cache.local.load_dir_cache(fname))
|
||||
|
||||
fname = self.dvc.cache.local.get("234.dir")
|
||||
self.create(fname, '{"a": "b"}')
|
||||
self._do_test(self.dvc.cache.local.load_dir_cache(fname))
|
||||
|
||||
|
||||
class TestExternalCacheDir(TestDvc):
|
||||
def test(self):
|
||||
cache_dir = TestDvc.mkdtemp()
|
||||
|
||||
ret = main(["config", "cache.dir", cache_dir])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
shutil.rmtree(".dvc/cache")
|
||||
|
||||
ret = main(["add", self.FOO])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["add", self.DATA_DIR])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertFalse(os.path.exists(".dvc/cache"))
|
||||
self.assertNotEqual(len(os.listdir(cache_dir)), 0)
|
||||
|
||||
def test_remote_references(self):
|
||||
assert main(["remote", "add", "storage", "ssh://localhost"]) == 0
|
||||
assert main(["remote", "add", "cache", "remote://storage/tmp"]) == 0
|
||||
assert main(["config", "cache.ssh", "cache"]) == 0
|
||||
|
||||
self.dvc.__init__()
|
||||
|
||||
assert self.dvc.cache.ssh.url == "ssh://localhost/tmp"
|
||||
|
||||
|
||||
class TestSharedCacheDir(TestDir):
|
||||
def test(self):
|
||||
cache_dir = os.path.abspath(os.path.join(os.curdir, "cache"))
|
||||
for d in ["dir1", "dir2"]:
|
||||
os.mkdir(d)
|
||||
os.chdir(d)
|
||||
|
||||
ret = main(["init", "--no-scm"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["config", "cache.dir", cache_dir])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
shutil.rmtree(os.path.join(".dvc", "cache"))
|
||||
|
||||
with open("common", "w+") as fd:
|
||||
fd.write("common")
|
||||
|
||||
with open("unique", "w+") as fd:
|
||||
fd.write(d)
|
||||
|
||||
ret = main(["add", "common", "unique"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
os.chdir("..")
|
||||
|
||||
self.assertFalse(os.path.exists(os.path.join("dir1", ".dvc", "cache")))
|
||||
self.assertFalse(os.path.exists(os.path.join("dir2", ".dvc", "cache")))
|
||||
|
||||
subdirs = list(
|
||||
filter(
|
||||
lambda x: os.path.isdir(os.path.join(cache_dir, x)),
|
||||
os.listdir(cache_dir),
|
||||
)
|
||||
)
|
||||
self.assertEqual(len(subdirs), 3)
|
||||
self.assertEqual(
|
||||
len(os.listdir(os.path.join(cache_dir, subdirs[0]))), 1
|
||||
)
|
||||
self.assertEqual(
|
||||
len(os.listdir(os.path.join(cache_dir, subdirs[1]))), 1
|
||||
)
|
||||
self.assertEqual(
|
||||
len(os.listdir(os.path.join(cache_dir, subdirs[2]))), 1
|
||||
)
|
||||
|
||||
|
||||
class TestCacheLinkType(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["config", "cache.type", "reflink,copy"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["add", self.FOO])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
|
||||
class TestCmdCacheDir(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["cache", "dir"])
|
||||
self.assertEqual(ret, 254)
|
||||
|
||||
def test_abs_path(self):
|
||||
dname = os.path.join(os.path.dirname(self._root_dir), "dir")
|
||||
ret = main(["cache", "dir", dname])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
config = configobj.ConfigObj(self.dvc.config.config_file)
|
||||
self.assertEqual(config["cache"]["dir"], dname)
|
||||
|
||||
def test_relative_path(self):
|
||||
tmpdir = self.mkdtemp()
|
||||
dname = os.path.relpath(tmpdir)
|
||||
ret = main(["cache", "dir", dname])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
# NOTE: we are in the repo's root and config is in .dvc/, so
|
||||
# dir path written to config should be just one level above.
|
||||
rel = os.path.join("..", dname)
|
||||
config = configobj.ConfigObj(self.dvc.config.config_file)
|
||||
self.assertEqual(config["cache"]["dir"], rel)
|
||||
|
||||
ret = main(["add", self.FOO])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
subdirs = os.listdir(tmpdir)
|
||||
self.assertEqual(len(subdirs), 1)
|
||||
files = os.listdir(os.path.join(tmpdir, subdirs[0]))
|
||||
self.assertEqual(len(files), 1)
|
||||
|
||||
|
||||
class TestShouldCacheBeReflinkOrCopyByDefault(TestDvc):
|
||||
def test(self):
|
||||
self.assertEqual(self.dvc.cache.local.cache_types, ["reflink", "copy"])
|
||||
@@ -0,0 +1,575 @@
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
|
||||
import yaml
|
||||
import shutil
|
||||
import filecmp
|
||||
import collections
|
||||
import logging
|
||||
|
||||
from dvc.main import main
|
||||
from dvc import progress
|
||||
from dvc.repo import Repo as DvcRepo
|
||||
from dvc.system import System
|
||||
from dvc.utils import walk_files, load_stage_file
|
||||
from tests.basic_env import TestDvc
|
||||
from tests.func.test_repro import TestRepro
|
||||
from dvc.stage import Stage, StageFileBadNameError, StageFileDoesNotExistError
|
||||
from dvc.remote.local import RemoteLOCAL
|
||||
from dvc.exceptions import (
|
||||
DvcException,
|
||||
ConfirmRemoveError,
|
||||
TargetNotDirectoryError,
|
||||
CheckoutErrorSuggestGit,
|
||||
)
|
||||
|
||||
from mock import patch
|
||||
|
||||
|
||||
logger = logging.getLogger("dvc")
|
||||
|
||||
|
||||
class TestCheckout(TestRepro):
|
||||
def setUp(self):
|
||||
super(TestCheckout, self).setUp()
|
||||
|
||||
stages = self.dvc.add(self.DATA_DIR)
|
||||
self.assertEqual(len(stages), 1)
|
||||
self.data_dir_stage = stages[0]
|
||||
self.assertTrue(self.data_dir_stage is not None)
|
||||
|
||||
self.orig = "orig"
|
||||
shutil.copy(self.FOO, self.orig)
|
||||
os.unlink(self.FOO)
|
||||
|
||||
self.orig_dir = "orig_dir"
|
||||
shutil.copytree(self.DATA_DIR, self.orig_dir)
|
||||
shutil.rmtree(self.DATA_DIR)
|
||||
|
||||
def test(self):
|
||||
self.dvc.checkout(force=True)
|
||||
self._test_checkout()
|
||||
|
||||
def _test_checkout(self):
|
||||
self.assertTrue(os.path.isfile(self.FOO))
|
||||
self.assertTrue(filecmp.cmp(self.FOO, self.orig, shallow=False))
|
||||
|
||||
|
||||
class TestCheckoutSingleStage(TestCheckout):
|
||||
def test(self):
|
||||
ret = main(["checkout", "--force", self.foo_stage.path])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["checkout", "--force", self.data_dir_stage.path])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self._test_checkout()
|
||||
|
||||
|
||||
class TestCheckoutCorruptedCacheFile(TestRepro):
|
||||
def test(self):
|
||||
cache = self.foo_stage.outs[0].cache
|
||||
|
||||
with open(cache, "a") as fd:
|
||||
fd.write("1")
|
||||
|
||||
self.dvc.checkout(force=True)
|
||||
|
||||
self.assertFalse(os.path.isfile(self.FOO))
|
||||
self.assertFalse(os.path.isfile(cache))
|
||||
|
||||
|
||||
class TestCheckoutCorruptedCacheDir(TestDvc):
|
||||
def test(self):
|
||||
# NOTE: using 'copy' so that cache and link don't have same inode
|
||||
ret = main(["config", "cache.type", "copy"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.dvc = DvcRepo(".")
|
||||
stages = self.dvc.add(self.DATA_DIR)
|
||||
self.assertEqual(len(stages), 1)
|
||||
self.assertEqual(len(stages[0].outs), 1)
|
||||
out = stages[0].outs[0]
|
||||
|
||||
# NOTE: modifying cache file for one of the files inside the directory
|
||||
# to check if dvc will detect that the cache is corrupted.
|
||||
entry = self.dvc.cache.local.load_dir_cache(out.checksum)[0]
|
||||
checksum = entry[self.dvc.cache.local.PARAM_CHECKSUM]
|
||||
cache = self.dvc.cache.local.get(checksum)
|
||||
|
||||
with open(cache, "w+") as fobj:
|
||||
fobj.write("1")
|
||||
|
||||
self.dvc.checkout(force=True)
|
||||
|
||||
self.assertFalse(os.path.exists(cache))
|
||||
|
||||
|
||||
class TestCmdCheckout(TestCheckout):
|
||||
def test(self):
|
||||
ret = main(["checkout", "--force"])
|
||||
self.assertEqual(ret, 0)
|
||||
self._test_checkout()
|
||||
|
||||
|
||||
class CheckoutBase(TestDvc):
|
||||
GIT_IGNORE = ".gitignore"
|
||||
|
||||
def commit_data_file(self, fname, content="random text"):
|
||||
with open(fname, "w") as fd:
|
||||
fd.write(content)
|
||||
stages = self.dvc.add(fname)
|
||||
self.assertEqual(len(stages), 1)
|
||||
self.assertTrue(stages[0] is not None)
|
||||
self.dvc.scm.add([fname + ".dvc", ".gitignore"])
|
||||
self.dvc.scm.commit("adding " + fname)
|
||||
|
||||
def read_ignored(self):
|
||||
return list(
|
||||
map(lambda s: s.strip("\n"), open(self.GIT_IGNORE).readlines())
|
||||
)
|
||||
|
||||
def outs_info(self, stage):
|
||||
FileInfo = collections.namedtuple("FileInfo", "path inode")
|
||||
|
||||
paths = [
|
||||
path
|
||||
for output in stage["outs"]
|
||||
for path in walk_files(output["path"])
|
||||
]
|
||||
|
||||
return [
|
||||
FileInfo(path=path, inode=System.inode(path)) for path in paths
|
||||
]
|
||||
|
||||
|
||||
class TestRemoveFilesWhenCheckout(CheckoutBase):
|
||||
def test(self):
|
||||
fname = "file_in_a_branch"
|
||||
branch_master = "master"
|
||||
branch_1 = "b1"
|
||||
|
||||
self.dvc.scm.add(self.dvc.scm.untracked_files())
|
||||
self.dvc.scm.commit("add all files")
|
||||
|
||||
# add the file into a separate branch
|
||||
self.dvc.scm.checkout(branch_1, True)
|
||||
ret = main(["checkout", "--force"])
|
||||
self.assertEqual(ret, 0)
|
||||
self.commit_data_file(fname)
|
||||
|
||||
# Checkout back in master
|
||||
self.dvc.scm.checkout(branch_master)
|
||||
self.assertTrue(os.path.exists(fname))
|
||||
|
||||
# Make sure `dvc checkout` removes the file
|
||||
# self.dvc.checkout()
|
||||
ret = main(["checkout", "--force"])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertFalse(os.path.exists(fname))
|
||||
|
||||
|
||||
class TestCheckoutCleanWorkingDir(CheckoutBase):
|
||||
@patch("dvc.prompt.confirm", return_value=True)
|
||||
def test(self, mock_prompt):
|
||||
mock_prompt.return_value = True
|
||||
|
||||
stages = self.dvc.add(self.DATA_DIR)
|
||||
stage = stages[0]
|
||||
|
||||
working_dir_change = os.path.join(self.DATA_DIR, "not_cached.txt")
|
||||
with open(working_dir_change, "w") as f:
|
||||
f.write("not_cached")
|
||||
|
||||
ret = main(["checkout", stage.relpath])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertFalse(os.path.exists(working_dir_change))
|
||||
|
||||
@patch("dvc.prompt.confirm", return_value=False)
|
||||
def test_force(self, mock_prompt):
|
||||
mock_prompt.return_value = False
|
||||
|
||||
stages = self.dvc.add(self.DATA_DIR)
|
||||
self.assertEqual(len(stages), 1)
|
||||
stage = stages[0]
|
||||
|
||||
working_dir_change = os.path.join(self.DATA_DIR, "not_cached.txt")
|
||||
with open(working_dir_change, "w") as f:
|
||||
f.write("not_cached")
|
||||
|
||||
ret = main(["checkout", stage.relpath])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
mock_prompt.assert_called()
|
||||
self.assertNotEqual(ret, 0)
|
||||
self.assertRaises(DvcException)
|
||||
|
||||
|
||||
class TestCheckoutSelectiveRemove(CheckoutBase):
|
||||
def test(self):
|
||||
# Use copy to test for changes in the inodes
|
||||
ret = main(["config", "cache.type", "copy"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["add", self.DATA_DIR])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
stage_path = self.DATA_DIR + Stage.STAGE_FILE_SUFFIX
|
||||
stage = load_stage_file(stage_path)
|
||||
staged_files = self.outs_info(stage)
|
||||
|
||||
# move instead of remove, to lock inode assigned to stage_files[0].path
|
||||
# if we were to use remove, we might end up with same inode assigned to
|
||||
# newly checked out file
|
||||
shutil.move(staged_files[0].path, "random_name")
|
||||
|
||||
ret = main(["checkout", "--force", stage_path])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
checkedout_files = self.outs_info(stage)
|
||||
|
||||
self.assertEqual(len(staged_files), len(checkedout_files))
|
||||
self.assertEqual(staged_files[0].path, checkedout_files[0].path)
|
||||
self.assertNotEqual(staged_files[0].inode, checkedout_files[0].inode)
|
||||
self.assertEqual(staged_files[1].inode, checkedout_files[1].inode)
|
||||
|
||||
|
||||
class TestGitIgnoreBasic(CheckoutBase):
|
||||
def test(self):
|
||||
fname1 = "file_1"
|
||||
fname2 = "file_2"
|
||||
fname3 = "file_3"
|
||||
|
||||
self.dvc.scm.add(self.dvc.scm.untracked_files())
|
||||
self.dvc.scm.commit("add all files")
|
||||
|
||||
self.assertFalse(os.path.exists(self.GIT_IGNORE))
|
||||
|
||||
self.commit_data_file(fname1)
|
||||
self.commit_data_file(fname2)
|
||||
self.dvc.run(
|
||||
cmd="python {} {} {}".format(self.CODE, self.FOO, fname3),
|
||||
deps=[self.CODE, self.FOO],
|
||||
outs_no_cache=[fname3],
|
||||
)
|
||||
|
||||
self.assertTrue(os.path.exists(self.GIT_IGNORE))
|
||||
|
||||
ignored = self.read_ignored()
|
||||
|
||||
self.assertEqual(len(ignored), 2)
|
||||
|
||||
self.assertIn("/" + fname1, ignored)
|
||||
self.assertIn("/" + fname2, ignored)
|
||||
|
||||
|
||||
class TestGitIgnoreWhenCheckout(CheckoutBase):
|
||||
def test(self):
|
||||
fname_master = "file_in_a_master"
|
||||
branch_master = "master"
|
||||
fname_branch = "file_in_a_branch"
|
||||
branch_1 = "b1"
|
||||
|
||||
self.dvc.scm.add(self.dvc.scm.untracked_files())
|
||||
self.dvc.scm.commit("add all files")
|
||||
self.commit_data_file(fname_master)
|
||||
|
||||
self.dvc.scm.checkout(branch_1, True)
|
||||
ret = main(["checkout", "--force"])
|
||||
self.assertEqual(ret, 0)
|
||||
self.commit_data_file(fname_branch)
|
||||
|
||||
self.dvc.scm.checkout(branch_master)
|
||||
ret = main(["checkout", "--force"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ignored = self.read_ignored()
|
||||
|
||||
self.assertEqual(len(ignored), 1)
|
||||
self.assertIn("/" + fname_master, ignored)
|
||||
|
||||
self.dvc.scm.checkout(branch_1)
|
||||
ret = main(["checkout", "--force"])
|
||||
self.assertEqual(ret, 0)
|
||||
ignored = self.read_ignored()
|
||||
self.assertIn("/" + fname_branch, ignored)
|
||||
|
||||
|
||||
class TestCheckoutMissingMd5InStageFile(TestRepro):
|
||||
def test(self):
|
||||
with open(self.file1_stage, "r") as fd:
|
||||
d = yaml.load(fd)
|
||||
|
||||
del d[Stage.PARAM_OUTS][0][RemoteLOCAL.PARAM_CHECKSUM]
|
||||
del d[Stage.PARAM_DEPS][0][RemoteLOCAL.PARAM_CHECKSUM]
|
||||
|
||||
with open(self.file1_stage, "w") as fd:
|
||||
yaml.dump(d, fd)
|
||||
|
||||
self.dvc.checkout(force=True)
|
||||
|
||||
|
||||
class TestCheckoutEmptyDir(TestDvc):
|
||||
def test(self):
|
||||
dname = "empty_dir"
|
||||
os.mkdir(dname)
|
||||
|
||||
stages = self.dvc.add(dname)
|
||||
self.assertEqual(len(stages), 1)
|
||||
stage = stages[0]
|
||||
self.assertTrue(stage is not None)
|
||||
self.assertEqual(len(stage.outs), 1)
|
||||
|
||||
stage.outs[0].remove()
|
||||
self.assertFalse(os.path.exists(dname))
|
||||
|
||||
self.dvc.checkout(force=True)
|
||||
|
||||
self.assertTrue(os.path.isdir(dname))
|
||||
self.assertEqual(len(os.listdir(dname)), 0)
|
||||
|
||||
|
||||
class TestCheckoutNotCachedFile(TestDvc):
|
||||
def test(self):
|
||||
cmd = "python {} {} {}".format(self.CODE, self.FOO, "out")
|
||||
|
||||
self.dvc.add(self.FOO)
|
||||
stage = self.dvc.run(
|
||||
cmd=cmd, deps=[self.FOO, self.CODE], outs_no_cache=["out"]
|
||||
)
|
||||
self.assertTrue(stage is not None)
|
||||
|
||||
self.dvc.checkout(force=True)
|
||||
|
||||
|
||||
class TestCheckoutWithDeps(TestRepro):
|
||||
def test(self):
|
||||
os.unlink(self.FOO)
|
||||
os.unlink(self.file1)
|
||||
|
||||
self.assertFalse(os.path.exists(self.FOO))
|
||||
self.assertFalse(os.path.exists(self.file1))
|
||||
|
||||
ret = main(["checkout", "--force", self.file1_stage, "--with-deps"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertTrue(os.path.exists(self.FOO))
|
||||
self.assertTrue(os.path.exists(self.file1))
|
||||
|
||||
|
||||
class TestCheckoutDirectory(TestRepro):
|
||||
def test(self):
|
||||
stage = self.dvc.add(self.DATA_DIR)[0]
|
||||
|
||||
shutil.rmtree(self.DATA_DIR)
|
||||
self.assertFalse(os.path.exists(self.DATA_DIR))
|
||||
|
||||
ret = main(["checkout", stage.path])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertTrue(os.path.exists(self.DATA_DIR))
|
||||
|
||||
|
||||
class TestCheckoutHook(TestDvc):
|
||||
@patch("sys.stdout.isatty", return_value=True)
|
||||
@patch("dvc.prompt.input", side_effect=EOFError)
|
||||
def test(self, mock_input, mock_isatty):
|
||||
""" Test that dvc checkout handles EOFError gracefully, which is what
|
||||
it will experience when running in a git hook.
|
||||
"""
|
||||
stages = self.dvc.add(self.DATA_DIR)
|
||||
self.assertEqual(len(stages), 1)
|
||||
stage = stages[0]
|
||||
self.assertNotEqual(stage, None)
|
||||
|
||||
self.create(os.path.join(self.DATA_DIR, "test"), "test")
|
||||
|
||||
with self.assertRaises(ConfirmRemoveError):
|
||||
self.dvc.checkout()
|
||||
|
||||
|
||||
class TestCheckoutSuggestGit(TestRepro):
|
||||
def test(self):
|
||||
|
||||
try:
|
||||
self.dvc.checkout(target="gitbranch")
|
||||
except DvcException as exc:
|
||||
self.assertIsInstance(exc, CheckoutErrorSuggestGit)
|
||||
self.assertIsInstance(exc.cause, StageFileDoesNotExistError)
|
||||
self.assertIsNone(exc.cause.cause)
|
||||
|
||||
try:
|
||||
self.dvc.checkout(target=self.FOO)
|
||||
except DvcException as exc:
|
||||
self.assertIsInstance(exc, CheckoutErrorSuggestGit)
|
||||
self.assertIsInstance(exc.cause, StageFileBadNameError)
|
||||
self.assertIsNone(exc.cause.cause)
|
||||
|
||||
|
||||
class TestCheckoutShouldHaveSelfClearingProgressBar(TestDvc):
|
||||
def setUp(self):
|
||||
super(TestCheckoutShouldHaveSelfClearingProgressBar, self).setUp()
|
||||
self._prepare_repo()
|
||||
|
||||
def test(self):
|
||||
with self._caplog.at_level(logging.INFO, logger="dvc"), patch.object(
|
||||
sys, "stdout"
|
||||
) as stdout_mock:
|
||||
self.stdout_mock = logger.handlers[0].stream = stdout_mock
|
||||
|
||||
ret = main(["checkout"])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
stdout_calls = self.stdout_mock.method_calls
|
||||
write_calls = self.filter_out_non_write_calls(stdout_calls)
|
||||
write_calls = self.filter_out_empty_write_calls(write_calls)
|
||||
self.write_args = [w_c[1][0] for w_c in write_calls]
|
||||
|
||||
pattern = re.compile(".*\\[.{30}\\].*%.*")
|
||||
progress_bars = [arg for arg in self.write_args if pattern.match(arg)]
|
||||
|
||||
update_bars = progress_bars[:-1]
|
||||
finish_bar = progress_bars[-1]
|
||||
|
||||
self.assertEqual(4, len(update_bars))
|
||||
self.assertRegexpMatches(progress_bars[0], ".*\\[#{7} {23}\\] 25%.*")
|
||||
self.assertRegexpMatches(progress_bars[1], ".*\\[#{15} {15}\\] 50%.*")
|
||||
self.assertRegexpMatches(progress_bars[2], ".*\\[#{22} {8}\\] 75%.*")
|
||||
self.assertRegexpMatches(progress_bars[3], ".*\\[#{30}\\] 100%.*")
|
||||
|
||||
self.assertCaretReturnFollowsEach(update_bars)
|
||||
self.assertNewLineFollows(finish_bar)
|
||||
|
||||
self.assertAnyEndsWith(update_bars, self.FOO)
|
||||
self.assertAnyEndsWith(update_bars, self.BAR)
|
||||
self.assertAnyEndsWith(update_bars, self.DATA)
|
||||
self.assertAnyEndsWith(update_bars, self.DATA_SUB)
|
||||
|
||||
self.assertTrue(finish_bar.endswith("Checkout finished!"))
|
||||
|
||||
def filter_out_empty_write_calls(self, calls):
|
||||
def is_not_empty_write(call):
|
||||
assert call[0] == "write"
|
||||
return call[1][0] != ""
|
||||
|
||||
return list(filter(is_not_empty_write, calls))
|
||||
|
||||
def filter_out_non_write_calls(self, calls):
|
||||
def is_write_call(call):
|
||||
return call[0] == "write"
|
||||
|
||||
return list(filter(is_write_call, calls))
|
||||
|
||||
def _prepare_repo(self):
|
||||
storage = self.mkdtemp()
|
||||
|
||||
ret = main(["remote", "add", "-d", "myremote", storage])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
ret = main(["add", self.DATA_DIR])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
ret = main(["add", self.FOO])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
ret = main(["add", self.BAR])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
ret = main(["push"])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
shutil.rmtree(self.DATA_DIR)
|
||||
os.unlink(self.FOO)
|
||||
os.unlink(self.BAR)
|
||||
|
||||
def assertCaretReturnFollowsEach(self, update_bars):
|
||||
for update_bar in update_bars:
|
||||
|
||||
self.assertIn(update_bar, self.write_args)
|
||||
|
||||
for index, arg in enumerate(self.write_args):
|
||||
if arg == update_bar:
|
||||
self.assertEqual(
|
||||
progress.CLEARLINE_PATTERN, self.write_args[index + 1]
|
||||
)
|
||||
|
||||
def assertNewLineFollows(self, finish_bar):
|
||||
self.assertIn(finish_bar, self.write_args)
|
||||
|
||||
for index, arg in enumerate(self.write_args):
|
||||
if arg == finish_bar:
|
||||
self.assertEqual("\n", self.write_args[index + 1])
|
||||
|
||||
def assertAnyEndsWith(self, update_bars, name):
|
||||
self.assertTrue(any(ub for ub in update_bars if ub.endswith(name)))
|
||||
|
||||
|
||||
class TestCheckoutTargetRecursiveShouldNotRemoveOtherUsedFiles(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["add", self.DATA_DIR, self.FOO, self.BAR])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
ret = main(["checkout", "-R", self.DATA_DIR])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
self.assertTrue(os.path.exists(self.FOO))
|
||||
self.assertTrue(os.path.exists(self.BAR))
|
||||
|
||||
|
||||
class TestCheckoutRecursiveNotDirectory(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["add", self.FOO])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
with self.assertRaises(TargetNotDirectoryError):
|
||||
self.dvc.checkout(target=self.FOO, recursive=True)
|
||||
|
||||
|
||||
class TestCheckoutMovedCacheDirWithSymlinks(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["config", "cache.type", "symlink"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["add", self.FOO])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["add", self.DATA_DIR])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
if os.name == "nt":
|
||||
from jaraco.windows.filesystem import readlink
|
||||
else:
|
||||
readlink = os.readlink
|
||||
|
||||
self.assertTrue(System.is_symlink(self.FOO))
|
||||
old_foo_link = readlink(self.FOO)
|
||||
|
||||
self.assertTrue(System.is_symlink(self.DATA))
|
||||
old_data_link = readlink(self.DATA)
|
||||
|
||||
old_cache_dir = self.dvc.cache.local.cache_dir
|
||||
new_cache_dir = old_cache_dir + "_new"
|
||||
os.rename(old_cache_dir, new_cache_dir)
|
||||
|
||||
ret = main(["cache", "dir", new_cache_dir])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["checkout", "-f"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertTrue(System.is_symlink(self.FOO))
|
||||
new_foo_link = readlink(self.FOO)
|
||||
|
||||
self.assertTrue(System.is_symlink(self.DATA))
|
||||
new_data_link = readlink(self.DATA)
|
||||
|
||||
self.assertEqual(
|
||||
os.path.relpath(old_foo_link, old_cache_dir),
|
||||
os.path.relpath(new_foo_link, new_cache_dir),
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
os.path.relpath(old_data_link, old_cache_dir),
|
||||
os.path.relpath(new_data_link, new_cache_dir),
|
||||
)
|
||||
@@ -0,0 +1,178 @@
|
||||
import os
|
||||
|
||||
from dvc.cli import parse_args
|
||||
from dvc.command.init import CmdInit
|
||||
from dvc.command.run import CmdRun
|
||||
from dvc.command.data_sync import CmdDataPull, CmdDataPush
|
||||
from dvc.command.status import CmdDataStatus
|
||||
from dvc.command.repro import CmdRepro
|
||||
from dvc.command.add import CmdAdd
|
||||
from dvc.command.remove import CmdRemove
|
||||
from dvc.command.gc import CmdGC
|
||||
from dvc.command.config import CmdConfig
|
||||
from dvc.command.checkout import CmdCheckout
|
||||
from dvc.exceptions import DvcException
|
||||
from dvc.command.base import CmdBase
|
||||
|
||||
from tests.basic_env import TestDvc
|
||||
|
||||
|
||||
class TestArgParse(TestDvc):
|
||||
def test(self):
|
||||
args = parse_args(["init"])
|
||||
self.assertIsInstance(args.func(args), CmdInit)
|
||||
|
||||
|
||||
class TestRun(TestDvc):
|
||||
def test(self):
|
||||
dep1 = "dep1"
|
||||
dep2 = "dep2"
|
||||
|
||||
out1 = "out1"
|
||||
out2 = "out2"
|
||||
|
||||
out_no_cache1 = "out_no_cache1"
|
||||
out_no_cache2 = "out_no_cache2"
|
||||
|
||||
fname = "dvc.dvc"
|
||||
cwd = os.curdir
|
||||
cmd = "cmd"
|
||||
arg1 = "arg1"
|
||||
arg2 = "arg2"
|
||||
|
||||
args = parse_args(
|
||||
[
|
||||
"run",
|
||||
"-d",
|
||||
dep1,
|
||||
"--deps",
|
||||
dep2,
|
||||
"-o",
|
||||
out1,
|
||||
"--outs",
|
||||
out2,
|
||||
"-O",
|
||||
out_no_cache1,
|
||||
"--outs-no-cache",
|
||||
out_no_cache2,
|
||||
"-f",
|
||||
fname,
|
||||
"--file",
|
||||
fname,
|
||||
"-c",
|
||||
cwd,
|
||||
cmd,
|
||||
arg1,
|
||||
arg2,
|
||||
]
|
||||
)
|
||||
|
||||
self.assertIsInstance(args.func(args), CmdRun)
|
||||
self.assertEqual(args.deps, [dep1, dep2])
|
||||
self.assertEqual(args.outs, [out1, out2])
|
||||
self.assertEqual(args.outs_no_cache, [out_no_cache1, out_no_cache2])
|
||||
self.assertEqual(args.file, fname)
|
||||
self.assertEqual(args.cwd, cwd)
|
||||
self.assertEqual(args.command, [cmd, arg1, arg2])
|
||||
|
||||
|
||||
class TestPull(TestDvc):
|
||||
def test(self):
|
||||
args = parse_args(["pull"])
|
||||
self.assertIsInstance(args.func(args), CmdDataPull)
|
||||
|
||||
|
||||
class TestPush(TestDvc):
|
||||
def test(self):
|
||||
args = parse_args(["push"])
|
||||
self.assertIsInstance(args.func(args), CmdDataPush)
|
||||
|
||||
|
||||
class TestStatus(TestDvc):
|
||||
def test(self):
|
||||
args = parse_args(["status"])
|
||||
self.assertIsInstance(args.func(args), CmdDataStatus)
|
||||
|
||||
|
||||
class TestRepro(TestDvc):
|
||||
def test(self):
|
||||
target1 = "1"
|
||||
target2 = "2"
|
||||
|
||||
args = parse_args(
|
||||
["repro", target1, target2, "-f", "--force", "-s", "--single-item"]
|
||||
)
|
||||
|
||||
self.assertIsInstance(args.func(args), CmdRepro)
|
||||
self.assertEqual(args.targets, [target1, target2])
|
||||
self.assertEqual(args.force, True)
|
||||
self.assertEqual(args.single_item, True)
|
||||
|
||||
|
||||
class TestRemove(TestDvc):
|
||||
def test(self):
|
||||
target1 = "1"
|
||||
target2 = "2"
|
||||
|
||||
args = parse_args(["remove", target1, target2])
|
||||
|
||||
self.assertIsInstance(args.func(args), CmdRemove)
|
||||
self.assertEqual(args.targets, [target1, target2])
|
||||
|
||||
|
||||
class TestAdd(TestDvc):
|
||||
def test(self):
|
||||
target1 = "1"
|
||||
target2 = "2"
|
||||
|
||||
args = parse_args(["add", target1, target2])
|
||||
|
||||
self.assertIsInstance(args.func(args), CmdAdd)
|
||||
self.assertEqual(args.targets, [target1, target2])
|
||||
|
||||
|
||||
class TestGC(TestDvc):
|
||||
def test(self):
|
||||
args = parse_args(["gc"])
|
||||
self.assertIsInstance(args.func(args), CmdGC)
|
||||
|
||||
|
||||
class TestGCMultipleDvcRepos(TestDvc):
|
||||
def test(self):
|
||||
args = parse_args(["gc", "-p", "/tmp/asdf", "/tmp/xyz"])
|
||||
|
||||
self.assertIsInstance(args.func(args), CmdGC)
|
||||
|
||||
self.assertEqual(args.repos, ["/tmp/asdf", "/tmp/xyz"])
|
||||
|
||||
|
||||
class TestConfig(TestDvc):
|
||||
def test(self):
|
||||
name = "param"
|
||||
value = "1"
|
||||
|
||||
args = parse_args(["config", "-u", "--unset", name, value])
|
||||
|
||||
self.assertIsInstance(args.func(args), CmdConfig)
|
||||
self.assertEqual(args.unset, True)
|
||||
self.assertEqual(args.name, name)
|
||||
self.assertEqual(args.value, value)
|
||||
|
||||
|
||||
class TestCheckout(TestDvc):
|
||||
def test(self):
|
||||
args = parse_args(["checkout"])
|
||||
self.assertIsInstance(args.func(args), CmdCheckout)
|
||||
|
||||
|
||||
class TestFindRoot(TestDvc):
|
||||
def test(self):
|
||||
os.chdir("..")
|
||||
|
||||
class A(object):
|
||||
quiet = False
|
||||
verbose = True
|
||||
|
||||
args = A()
|
||||
with self.assertRaises(DvcException):
|
||||
CmdBase(args)
|
||||
@@ -0,0 +1,89 @@
|
||||
import yaml
|
||||
from dvc.utils import load_stage_file
|
||||
|
||||
from tests.basic_env import TestDvc
|
||||
from dvc.stage import StageCommitError
|
||||
|
||||
|
||||
class TestCommitRecursive(TestDvc):
|
||||
def test(self):
|
||||
stages = self.dvc.add(self.DATA_DIR, recursive=True, no_commit=True)
|
||||
self.assertEqual(len(stages), 2)
|
||||
|
||||
self.assertNotEqual(self.dvc.status(), {})
|
||||
|
||||
self.dvc.commit(self.DATA_DIR, recursive=True)
|
||||
|
||||
self.assertEqual(self.dvc.status(), {})
|
||||
|
||||
|
||||
class TestCommitForce(TestDvc):
|
||||
def test(self):
|
||||
stages = self.dvc.add(self.FOO, no_commit=True)
|
||||
self.assertEqual(len(stages), 1)
|
||||
stage = stages[0]
|
||||
|
||||
with self.dvc.state:
|
||||
self.assertTrue(stage.outs[0].changed_cache())
|
||||
|
||||
with open(self.FOO, "a") as fobj:
|
||||
fobj.write(self.FOO_CONTENTS)
|
||||
|
||||
with self.dvc.state:
|
||||
self.assertTrue(stage.outs[0].changed_cache())
|
||||
|
||||
with self.assertRaises(StageCommitError):
|
||||
self.dvc.commit(stage.path)
|
||||
|
||||
with self.dvc.state:
|
||||
self.assertTrue(stage.outs[0].changed_cache())
|
||||
|
||||
self.dvc.commit(stage.path, force=True)
|
||||
|
||||
self.assertEqual(self.dvc.status(stage.path), {})
|
||||
|
||||
|
||||
class TestCommitWithDeps(TestDvc):
|
||||
def test(self):
|
||||
stages = self.dvc.add(self.FOO, no_commit=True)
|
||||
self.assertEqual(len(stages), 1)
|
||||
foo_stage = stages[0]
|
||||
self.assertTrue(foo_stage is not None)
|
||||
self.assertEqual(len(foo_stage.outs), 1)
|
||||
|
||||
fname = "file"
|
||||
stage = self.dvc.run(
|
||||
cmd="python {} {} {}".format(self.CODE, self.FOO, fname),
|
||||
outs=[fname],
|
||||
deps=[self.FOO, self.CODE],
|
||||
no_commit=True,
|
||||
)
|
||||
self.assertTrue(stage is not None)
|
||||
self.assertEqual(len(stage.outs), 1)
|
||||
|
||||
with self.dvc.state:
|
||||
self.assertTrue(foo_stage.outs[0].changed_cache())
|
||||
self.assertTrue(stage.outs[0].changed_cache())
|
||||
|
||||
self.dvc.commit(stage.path, with_deps=True)
|
||||
with self.dvc.state:
|
||||
self.assertFalse(foo_stage.outs[0].changed_cache())
|
||||
self.assertFalse(stage.outs[0].changed_cache())
|
||||
|
||||
|
||||
class TestCommitChangedMd5(TestDvc):
|
||||
def test(self):
|
||||
stages = self.dvc.add(self.FOO, no_commit=True)
|
||||
self.assertEqual(len(stages), 1)
|
||||
stage = stages[0]
|
||||
|
||||
st = load_stage_file(stage.path)
|
||||
|
||||
st["md5"] = "1111111111"
|
||||
with open(stage.path, "w") as fobj:
|
||||
yaml.safe_dump(st, fobj, default_flow_style=False)
|
||||
|
||||
with self.assertRaises(StageCommitError):
|
||||
self.dvc.commit(stage.path)
|
||||
|
||||
self.dvc.commit(stage.path, force=True)
|
||||
@@ -0,0 +1,104 @@
|
||||
import configobj
|
||||
|
||||
from dvc.main import main
|
||||
from dvc.command.config import CmdConfig
|
||||
from tests.basic_env import TestDvc
|
||||
|
||||
|
||||
class TestConfigCLI(TestDvc):
|
||||
def _contains(self, section, field, value, local=False):
|
||||
if local:
|
||||
fname = self.dvc.config.config_local_file
|
||||
else:
|
||||
fname = self.dvc.config.config_file
|
||||
|
||||
config = configobj.ConfigObj(fname)
|
||||
if section not in config.keys():
|
||||
return False
|
||||
|
||||
if field not in config[section].keys():
|
||||
return False
|
||||
|
||||
if config[section][field] != value:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def test_root(self):
|
||||
ret = main(["root"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
# NOTE: check that `dvc root` is not blocked with dvc lock
|
||||
with self.dvc.lock:
|
||||
ret = main(["root"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def _do_test(self, local=False):
|
||||
section = "setsection"
|
||||
field = "setfield"
|
||||
section_field = "{}.{}".format(section, field)
|
||||
value = "setvalue"
|
||||
newvalue = "setnewvalue"
|
||||
|
||||
base = ["config"]
|
||||
if local:
|
||||
base.append("--local")
|
||||
|
||||
ret = main(base + [section_field, value])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertTrue(self._contains(section, field, value, local))
|
||||
|
||||
ret = main(base + [section_field])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(base + [section_field, newvalue])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertTrue(self._contains(section, field, newvalue, local))
|
||||
self.assertFalse(self._contains(section, field, value, local))
|
||||
|
||||
ret = main(base + [section_field, "--unset"])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertFalse(self._contains(section, field, value, local))
|
||||
|
||||
def test(self):
|
||||
self._do_test(False)
|
||||
|
||||
def test_local(self):
|
||||
self._do_test(True)
|
||||
|
||||
def test_non_existing(self):
|
||||
# FIXME check stdout/err
|
||||
|
||||
ret = main(["config", "non_existing_section.field"])
|
||||
self.assertEqual(ret, 1)
|
||||
|
||||
ret = main(["config", "global.non_existing_field"])
|
||||
self.assertEqual(ret, 1)
|
||||
|
||||
ret = main(["config", "non_existing_section.field", "-u"])
|
||||
self.assertEqual(ret, 1)
|
||||
|
||||
ret = main(["config", "global.non_existing_field", "-u"])
|
||||
self.assertEqual(ret, 1)
|
||||
|
||||
ret = main(["config", "core.remote", "myremote"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["config", "core.non_existing_field", "-u"])
|
||||
self.assertEqual(ret, 1)
|
||||
|
||||
def test_failed_write(self):
|
||||
class A(object):
|
||||
system = False
|
||||
glob = False
|
||||
local = False
|
||||
name = "core.remote"
|
||||
value = "myremote"
|
||||
unset = False
|
||||
|
||||
args = A()
|
||||
cmd = CmdConfig(args)
|
||||
|
||||
cmd.configobj.write = None
|
||||
ret = cmd.run()
|
||||
self.assertNotEqual(ret, 0)
|
||||
@@ -0,0 +1,797 @@
|
||||
from subprocess import CalledProcessError
|
||||
from subprocess import check_output, Popen
|
||||
from unittest import SkipTest
|
||||
import os
|
||||
import uuid
|
||||
import shutil
|
||||
import getpass
|
||||
import platform
|
||||
import yaml
|
||||
import copy
|
||||
import logging
|
||||
|
||||
from dvc.state import State
|
||||
from mock import patch
|
||||
|
||||
from dvc.utils.compat import str
|
||||
from dvc.main import main
|
||||
from dvc.config import Config
|
||||
from dvc.data_cloud import (
|
||||
DataCloud,
|
||||
RemoteS3,
|
||||
RemoteGS,
|
||||
RemoteAzure,
|
||||
RemoteLOCAL,
|
||||
RemoteSSH,
|
||||
RemoteHDFS,
|
||||
RemoteHTTP,
|
||||
)
|
||||
from dvc.remote.base import STATUS_OK, STATUS_NEW, STATUS_DELETED
|
||||
from dvc.utils import file_md5, load_stage_file
|
||||
|
||||
from tests.basic_env import TestDvc
|
||||
from tests.utils import spy
|
||||
|
||||
|
||||
TEST_REMOTE = "upstream"
|
||||
TEST_SECTION = 'remote "{}"'.format(TEST_REMOTE)
|
||||
TEST_CONFIG = {
|
||||
Config.SECTION_CORE: {Config.SECTION_CORE_REMOTE: TEST_REMOTE},
|
||||
TEST_SECTION: {Config.SECTION_REMOTE_URL: ""},
|
||||
}
|
||||
|
||||
TEST_AWS_REPO_BUCKET = "dvc-test"
|
||||
TEST_GCP_REPO_BUCKET = "dvc-test"
|
||||
|
||||
|
||||
def _should_test_aws():
|
||||
dvc_test_aws = os.getenv("DVC_TEST_AWS")
|
||||
if dvc_test_aws == "true":
|
||||
return True
|
||||
elif dvc_test_aws == "false":
|
||||
return False
|
||||
|
||||
if os.getenv("AWS_ACCESS_KEY_ID") and os.getenv("AWS_SECRET_ACCESS_KEY"):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _should_test_gcp():
|
||||
if os.getenv("DVC_TEST_GCP") == "true":
|
||||
return True
|
||||
|
||||
if not os.path.exists(TestDvc.GCP_CREDS_FILE):
|
||||
return False
|
||||
|
||||
creds = os.getenv("GOOGLE_APPLICATION_CREDENTIALS")
|
||||
if creds and os.getenv("GCP_CREDS"):
|
||||
if os.path.exists(creds):
|
||||
os.unlink(creds)
|
||||
shutil.copyfile(TestDvc.GCP_CREDS_FILE, creds)
|
||||
try:
|
||||
check_output(
|
||||
[
|
||||
"gcloud",
|
||||
"auth",
|
||||
"activate-service-account",
|
||||
"--key-file",
|
||||
creds,
|
||||
]
|
||||
)
|
||||
except (CalledProcessError, OSError):
|
||||
return False
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _should_test_azure():
|
||||
if os.getenv("DVC_TEST_AZURE") == "true":
|
||||
return True
|
||||
elif os.getenv("DVC_TEST_AZURE") == "false":
|
||||
return False
|
||||
|
||||
return os.getenv("AZURE_STORAGE_CONTAINER_NAME") and os.getenv(
|
||||
"AZURE_STORAGE_CONNECTION_STRING"
|
||||
)
|
||||
|
||||
|
||||
def _should_test_ssh():
|
||||
if os.getenv("DVC_TEST_SSH") == "true":
|
||||
return True
|
||||
|
||||
# FIXME: enable on windows
|
||||
if os.name == "nt":
|
||||
return False
|
||||
|
||||
try:
|
||||
check_output(["ssh", "-o", "BatchMode=yes", "127.0.0.1", "ls"])
|
||||
except (CalledProcessError, IOError):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def _should_test_hdfs():
|
||||
if platform.system() != "Linux":
|
||||
return False
|
||||
|
||||
try:
|
||||
check_output(
|
||||
["hadoop", "version"], shell=True, executable=os.getenv("SHELL")
|
||||
)
|
||||
except (CalledProcessError, IOError):
|
||||
return False
|
||||
|
||||
p = Popen(
|
||||
"hadoop fs -ls hdfs://127.0.0.1/",
|
||||
shell=True,
|
||||
executable=os.getenv("SHELL"),
|
||||
)
|
||||
p.communicate()
|
||||
if p.returncode != 0:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def get_local_storagepath():
|
||||
return TestDvc.mkdtemp()
|
||||
|
||||
|
||||
def get_local_url():
|
||||
return get_local_storagepath()
|
||||
|
||||
|
||||
def get_ssh_url():
|
||||
return "ssh://{}@127.0.0.1:{}".format(
|
||||
getpass.getuser(), get_local_storagepath()
|
||||
)
|
||||
|
||||
|
||||
def get_hdfs_url():
|
||||
return "hdfs://{}@127.0.0.1{}".format(
|
||||
getpass.getuser(), get_local_storagepath()
|
||||
)
|
||||
|
||||
|
||||
def get_aws_storagepath():
|
||||
return TEST_AWS_REPO_BUCKET + "/" + str(uuid.uuid4())
|
||||
|
||||
|
||||
def get_aws_url():
|
||||
return "s3://" + get_aws_storagepath()
|
||||
|
||||
|
||||
def get_gcp_storagepath():
|
||||
return TEST_GCP_REPO_BUCKET + "/" + str(uuid.uuid4())
|
||||
|
||||
|
||||
def get_gcp_url():
|
||||
return "gs://" + get_gcp_storagepath()
|
||||
|
||||
|
||||
def get_azure_url_compat():
|
||||
container_name = os.getenv("AZURE_STORAGE_CONTAINER_NAME")
|
||||
connection_string = os.getenv("AZURE_STORAGE_CONNECTION_STRING")
|
||||
assert container_name is not None
|
||||
return "azure://ContainerName={};{}".format(
|
||||
container_name, connection_string
|
||||
)
|
||||
|
||||
|
||||
def get_azure_url():
|
||||
container_name = os.getenv("AZURE_STORAGE_CONTAINER_NAME")
|
||||
assert container_name is not None
|
||||
return "azure://{}/{}".format(container_name, str(uuid.uuid4()))
|
||||
|
||||
|
||||
class TestDataCloud(TestDvc):
|
||||
def _test_cloud(self, config, cl):
|
||||
cloud = DataCloud(self.dvc, config=config)
|
||||
self.assertIsInstance(cloud._cloud, cl)
|
||||
|
||||
def test(self):
|
||||
config = copy.deepcopy(TEST_CONFIG)
|
||||
|
||||
clist = [
|
||||
("s3://mybucket/", RemoteS3),
|
||||
("gs://mybucket/", RemoteGS),
|
||||
("ssh://user@localhost:/", RemoteSSH),
|
||||
("http://localhost:8000/", RemoteHTTP),
|
||||
("azure://ContainerName=mybucket;conn_string;", RemoteAzure),
|
||||
(TestDvc.mkdtemp(), RemoteLOCAL),
|
||||
]
|
||||
|
||||
for scheme, cl in clist:
|
||||
remote_url = scheme + str(uuid.uuid4())
|
||||
config[TEST_SECTION][Config.SECTION_REMOTE_URL] = remote_url
|
||||
self._test_cloud(config, cl)
|
||||
|
||||
|
||||
class TestDataCloudBase(TestDvc):
|
||||
def _get_cloud_class(self):
|
||||
return None
|
||||
|
||||
def _should_test(self):
|
||||
return False
|
||||
|
||||
def _get_url(self):
|
||||
return ""
|
||||
|
||||
def _ensure_should_run(self):
|
||||
if not self._should_test():
|
||||
raise SkipTest(
|
||||
"Test {} is disabled".format(self.__class__.__name__)
|
||||
)
|
||||
|
||||
def _setup_cloud(self):
|
||||
self._ensure_should_run()
|
||||
|
||||
repo = self._get_url()
|
||||
|
||||
config = copy.deepcopy(TEST_CONFIG)
|
||||
config[TEST_SECTION][Config.SECTION_REMOTE_URL] = repo
|
||||
self.cloud = DataCloud(self.dvc, config)
|
||||
|
||||
self.assertIsInstance(self.cloud._cloud, self._get_cloud_class())
|
||||
|
||||
def _test_cloud(self):
|
||||
self._setup_cloud()
|
||||
|
||||
stages = self.dvc.add(self.FOO)
|
||||
self.assertEqual(len(stages), 1)
|
||||
stage = stages[0]
|
||||
self.assertTrue(stage is not None)
|
||||
cache = stage.outs[0].cache
|
||||
info = stage.outs[0].dumpd()
|
||||
md5 = info["md5"]
|
||||
|
||||
stages = self.dvc.add(self.DATA_DIR)
|
||||
self.assertEqual(len(stages), 1)
|
||||
stage_dir = stages[0]
|
||||
self.assertTrue(stage_dir is not None)
|
||||
|
||||
cache_dir = stage_dir.outs[0].cache
|
||||
info_dir = stage_dir.outs[0].dumpd()
|
||||
md5_dir = info_dir["md5"]
|
||||
|
||||
with self.cloud.repo.state:
|
||||
# Check status
|
||||
status = self.cloud.status([info], show_checksums=True)
|
||||
expected = {md5: {"name": md5, "status": STATUS_NEW}}
|
||||
self.assertEqual(status, expected)
|
||||
|
||||
status_dir = self.cloud.status([info_dir], show_checksums=True)
|
||||
expected = {md5_dir: {"name": md5_dir, "status": STATUS_NEW}}
|
||||
self.assertEqual(status_dir, expected)
|
||||
|
||||
# Push and check status
|
||||
self.cloud.push([info])
|
||||
self.assertTrue(os.path.exists(cache))
|
||||
self.assertTrue(os.path.isfile(cache))
|
||||
|
||||
self.cloud.push([info_dir])
|
||||
self.assertTrue(os.path.isfile(cache_dir))
|
||||
|
||||
status = self.cloud.status([info], show_checksums=True)
|
||||
expected = {md5: {"name": md5, "status": STATUS_OK}}
|
||||
self.assertEqual(status, expected)
|
||||
|
||||
status_dir = self.cloud.status([info_dir], show_checksums=True)
|
||||
expected = {md5_dir: {"name": md5_dir, "status": STATUS_OK}}
|
||||
self.assertEqual(status_dir, expected)
|
||||
|
||||
# Remove and check status
|
||||
shutil.rmtree(self.dvc.cache.local.cache_dir)
|
||||
|
||||
status = self.cloud.status([info], show_checksums=True)
|
||||
expected = {md5: {"name": md5, "status": STATUS_DELETED}}
|
||||
self.assertEqual(status, expected)
|
||||
|
||||
status_dir = self.cloud.status([info_dir], show_checksums=True)
|
||||
expected = {md5_dir: {"name": md5_dir, "status": STATUS_DELETED}}
|
||||
self.assertEqual(status_dir, expected)
|
||||
|
||||
# Pull and check status
|
||||
self.cloud.pull([info])
|
||||
self.assertTrue(os.path.exists(cache))
|
||||
self.assertTrue(os.path.isfile(cache))
|
||||
with open(cache, "r") as fd:
|
||||
self.assertEqual(fd.read(), self.FOO_CONTENTS)
|
||||
|
||||
self.cloud.pull([info_dir])
|
||||
self.assertTrue(os.path.isfile(cache_dir))
|
||||
|
||||
status = self.cloud.status([info], show_checksums=True)
|
||||
expected = {md5: {"name": md5, "status": STATUS_OK}}
|
||||
self.assertEqual(status, expected)
|
||||
|
||||
status_dir = self.cloud.status([info_dir], show_checksums=True)
|
||||
expected = {md5_dir: {"name": md5_dir, "status": STATUS_OK}}
|
||||
self.assertTrue(status_dir, expected)
|
||||
|
||||
def test(self):
|
||||
self._ensure_should_run()
|
||||
self._test_cloud()
|
||||
|
||||
|
||||
class TestRemoteS3(TestDataCloudBase):
|
||||
def _should_test(self):
|
||||
return _should_test_aws()
|
||||
|
||||
def _get_url(self):
|
||||
return get_aws_url()
|
||||
|
||||
def _get_cloud_class(self):
|
||||
return RemoteS3
|
||||
|
||||
|
||||
class TestRemoteGS(TestDataCloudBase):
|
||||
def _should_test(self):
|
||||
return _should_test_gcp()
|
||||
|
||||
def _setup_cloud(self):
|
||||
self._ensure_should_run()
|
||||
|
||||
repo = self._get_url()
|
||||
|
||||
config = copy.deepcopy(TEST_CONFIG)
|
||||
config[TEST_SECTION][Config.SECTION_REMOTE_URL] = repo
|
||||
config[TEST_SECTION][
|
||||
Config.SECTION_GCP_CREDENTIALPATH
|
||||
] = TestDvc.GCP_CREDS_FILE
|
||||
self.cloud = DataCloud(self.dvc, config)
|
||||
|
||||
self.assertIsInstance(self.cloud._cloud, self._get_cloud_class())
|
||||
|
||||
def _get_url(self):
|
||||
return get_gcp_url()
|
||||
|
||||
def _get_cloud_class(self):
|
||||
return RemoteGS
|
||||
|
||||
|
||||
class TestRemoteAzureCompat(TestDataCloudBase):
|
||||
def _should_test(self):
|
||||
return _should_test_azure()
|
||||
|
||||
def _get_url(self):
|
||||
return get_azure_url_compat()
|
||||
|
||||
def _get_cloud_class(self):
|
||||
return RemoteAzure
|
||||
|
||||
|
||||
class TestRemoteAzure(TestRemoteAzureCompat):
|
||||
def _get_url(self):
|
||||
return get_azure_url()
|
||||
|
||||
|
||||
class TestRemoteLOCAL(TestDataCloudBase):
|
||||
def _should_test(self):
|
||||
return True
|
||||
|
||||
def _get_url(self):
|
||||
self.dname = get_local_url()
|
||||
return self.dname
|
||||
|
||||
def _get_cloud_class(self):
|
||||
return RemoteLOCAL
|
||||
|
||||
def test(self):
|
||||
super(TestRemoteLOCAL, self).test()
|
||||
self.assertTrue(os.path.isdir(self.dname))
|
||||
|
||||
|
||||
class TestRemoteSSH(TestDataCloudBase):
|
||||
def _should_test(self):
|
||||
return _should_test_ssh()
|
||||
|
||||
def _get_url(self):
|
||||
return get_ssh_url()
|
||||
|
||||
def _get_cloud_class(self):
|
||||
return RemoteSSH
|
||||
|
||||
|
||||
class TestRemoteHDFS(TestDataCloudBase):
|
||||
def _should_test(self):
|
||||
return _should_test_hdfs()
|
||||
|
||||
def _get_url(self):
|
||||
return get_hdfs_url()
|
||||
|
||||
def _get_cloud_class(self):
|
||||
return RemoteHDFS
|
||||
|
||||
|
||||
class TestDataCloudCLIBase(TestDvc):
|
||||
def main(self, args):
|
||||
ret = main(args)
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def _test_cloud(self, remote=None):
|
||||
args = ["-v", "-j", "2"]
|
||||
if remote:
|
||||
args += ["-r", remote]
|
||||
else:
|
||||
args += []
|
||||
|
||||
stages = self.dvc.add(self.FOO)
|
||||
self.assertEqual(len(stages), 1)
|
||||
stage = stages[0]
|
||||
self.assertTrue(stage is not None)
|
||||
cache = stage.outs[0].cache
|
||||
|
||||
stages = self.dvc.add(self.DATA_DIR)
|
||||
self.assertEqual(len(stages), 1)
|
||||
stage_dir = stages[0]
|
||||
self.assertTrue(stage_dir is not None)
|
||||
cache_dir = stage_dir.outs[0].cache
|
||||
|
||||
# FIXME check status output
|
||||
self.main(["status", "-c", "--show-checksums"] + args)
|
||||
|
||||
self.main(["push"] + args)
|
||||
self.assertTrue(os.path.exists(cache))
|
||||
self.assertTrue(os.path.isfile(cache))
|
||||
self.assertTrue(os.path.isfile(cache_dir))
|
||||
|
||||
self.main(["status", "-c", "--show-checksums"] + args)
|
||||
|
||||
shutil.rmtree(self.dvc.cache.local.cache_dir)
|
||||
|
||||
self.main(["status", "-c", "--show-checksums"] + args)
|
||||
|
||||
self.main(["fetch"] + args)
|
||||
self.assertTrue(os.path.exists(cache))
|
||||
self.assertTrue(os.path.isfile(cache))
|
||||
self.assertTrue(os.path.isfile(cache_dir))
|
||||
|
||||
self.main(["pull"] + args)
|
||||
self.assertTrue(os.path.exists(cache))
|
||||
self.assertTrue(os.path.isfile(cache))
|
||||
self.assertTrue(os.path.isfile(cache_dir))
|
||||
self.assertTrue(os.path.isfile(self.FOO))
|
||||
self.assertTrue(os.path.isdir(self.DATA_DIR))
|
||||
|
||||
with open(cache, "r") as fd:
|
||||
self.assertEqual(fd.read(), self.FOO_CONTENTS)
|
||||
self.assertTrue(os.path.isfile(cache_dir))
|
||||
|
||||
self.main(["status", "-c", "--show-checksums"] + args)
|
||||
|
||||
# NOTE: check if remote gc works correctly on directories
|
||||
self.main(["gc", "-c", "-f"] + args)
|
||||
shutil.move(
|
||||
self.dvc.cache.local.cache_dir,
|
||||
self.dvc.cache.local.cache_dir + ".back",
|
||||
)
|
||||
|
||||
self.main(["fetch"] + args)
|
||||
|
||||
self.main(["pull", "-f"] + args)
|
||||
self.assertTrue(os.path.exists(cache))
|
||||
self.assertTrue(os.path.isfile(cache))
|
||||
self.assertTrue(os.path.isfile(cache_dir))
|
||||
self.assertTrue(os.path.isfile(self.FOO))
|
||||
self.assertTrue(os.path.isdir(self.DATA_DIR))
|
||||
|
||||
def _should_test(self):
|
||||
return True
|
||||
|
||||
def _test(self):
|
||||
pass
|
||||
|
||||
def test(self):
|
||||
if self._should_test():
|
||||
self._test()
|
||||
|
||||
|
||||
class TestCompatRemoteLOCALCLI(TestDataCloudCLIBase):
|
||||
def _test(self):
|
||||
storagepath = get_local_storagepath()
|
||||
self.main(["config", "core.cloud", "local"])
|
||||
self.main(["config", "local.storagepath", storagepath])
|
||||
|
||||
self._test_cloud()
|
||||
|
||||
|
||||
class TestRemoteLOCALCLI(TestDataCloudCLIBase):
|
||||
def _test(self):
|
||||
url = get_local_url()
|
||||
|
||||
self.main(["remote", "add", TEST_REMOTE, url])
|
||||
|
||||
self._test_cloud(TEST_REMOTE)
|
||||
|
||||
|
||||
class TestRemoteSSHCLI(TestDataCloudCLIBase):
|
||||
def _should_test(self):
|
||||
return _should_test_ssh()
|
||||
|
||||
def _test(self):
|
||||
url = get_ssh_url()
|
||||
|
||||
self.main(["remote", "add", TEST_REMOTE, url])
|
||||
|
||||
self._test_cloud(TEST_REMOTE)
|
||||
|
||||
|
||||
class TestRemoteHDFSCLI(TestDataCloudCLIBase):
|
||||
def _should_test(self):
|
||||
return _should_test_hdfs()
|
||||
|
||||
def _test(self):
|
||||
url = get_hdfs_url()
|
||||
|
||||
self.main(["remote", "add", TEST_REMOTE, url])
|
||||
|
||||
self._test_cloud(TEST_REMOTE)
|
||||
|
||||
|
||||
class TestCompatRemoteS3CLI(TestDataCloudCLIBase):
|
||||
def _should_test(self):
|
||||
return _should_test_aws()
|
||||
|
||||
def _test(self):
|
||||
storagepath = get_aws_storagepath()
|
||||
self.main(["config", "core.cloud", "aws"])
|
||||
self.main(["config", "aws.storagepath", storagepath])
|
||||
|
||||
self._test_cloud()
|
||||
|
||||
|
||||
class TestRemoteS3CLI(TestDataCloudCLIBase):
|
||||
def _should_test(self):
|
||||
return _should_test_aws()
|
||||
|
||||
def _test(self):
|
||||
url = get_aws_url()
|
||||
|
||||
self.main(["remote", "add", TEST_REMOTE, url])
|
||||
|
||||
self._test_cloud(TEST_REMOTE)
|
||||
|
||||
|
||||
class TestCompatRemoteGSCLI(TestDataCloudCLIBase):
|
||||
def _should_test(self):
|
||||
return _should_test_gcp()
|
||||
|
||||
def _test(self):
|
||||
storagepath = get_gcp_storagepath()
|
||||
self.main(["config", "core.cloud", "gcp"])
|
||||
self.main(["config", "gcp.storagepath", storagepath])
|
||||
|
||||
self._test_cloud()
|
||||
|
||||
|
||||
class TestRemoteGSCLI(TestDataCloudCLIBase):
|
||||
def _should_test(self):
|
||||
return _should_test_gcp()
|
||||
|
||||
def _test(self):
|
||||
url = get_gcp_url()
|
||||
|
||||
self.main(["remote", "add", TEST_REMOTE, url])
|
||||
self.main(
|
||||
[
|
||||
"remote",
|
||||
"modify",
|
||||
TEST_REMOTE,
|
||||
"credentialpath",
|
||||
TestDvc.GCP_CREDS_FILE,
|
||||
]
|
||||
)
|
||||
|
||||
self._test_cloud(TEST_REMOTE)
|
||||
|
||||
|
||||
class TestRemoteAzureCLI(TestDataCloudCLIBase):
|
||||
def _should_test(self):
|
||||
return _should_test_azure()
|
||||
|
||||
def _test(self):
|
||||
url = get_azure_url()
|
||||
|
||||
self.main(["remote", "add", TEST_REMOTE, url])
|
||||
|
||||
self._test_cloud(TEST_REMOTE)
|
||||
|
||||
|
||||
class TestDataCloudErrorCLI(TestDvc):
|
||||
def main_fail(self, args):
|
||||
ret = main(args)
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
def test_error(self):
|
||||
f = "non-existing-file"
|
||||
self.main_fail(["status", "-c", f])
|
||||
self.main_fail(["push", f])
|
||||
self.main_fail(["pull", f])
|
||||
self.main_fail(["fetch", f])
|
||||
|
||||
|
||||
class TestWarnOnOutdatedStage(TestDvc):
|
||||
def main(self, args):
|
||||
ret = main(args)
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def _test(self):
|
||||
url = get_local_url()
|
||||
self.main(["remote", "add", "-d", TEST_REMOTE, url])
|
||||
|
||||
stage = self.dvc.run(outs=["bar"], cmd="echo bar > bar")
|
||||
self.main(["push"])
|
||||
|
||||
stage_file_path = stage.relpath
|
||||
content = load_stage_file(stage_file_path)
|
||||
del content["outs"][0]["md5"]
|
||||
with open(stage_file_path, "w") as stage_file:
|
||||
yaml.dump(content, stage_file)
|
||||
|
||||
with self._caplog.at_level(logging.WARNING, logger="dvc"):
|
||||
self._caplog.clear()
|
||||
self.main(["status", "-c"])
|
||||
expected_warning = (
|
||||
"Output 'bar'(Stage: 'bar.dvc') is missing version info."
|
||||
" Cache for it will not be collected."
|
||||
" Use dvc repro to get your pipeline up to date."
|
||||
)
|
||||
|
||||
assert expected_warning in self._caplog.text
|
||||
|
||||
def test(self):
|
||||
self._test()
|
||||
|
||||
|
||||
class TestRecursiveSyncOperations(TestDataCloudBase):
|
||||
def main(self, args):
|
||||
ret = main(args)
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def _get_url(self):
|
||||
self.dname = get_local_url()
|
||||
return self.dname
|
||||
|
||||
def _should_test(self):
|
||||
return True
|
||||
|
||||
def _get_cloud_class(self):
|
||||
return RemoteLOCAL
|
||||
|
||||
def _prepare_repo(self):
|
||||
self.main(
|
||||
["remote", "add", "-d", TEST_REMOTE, self.cloud._cloud.cache_dir]
|
||||
)
|
||||
|
||||
self.dvc.add(self.DATA)
|
||||
self.dvc.add(self.DATA_SUB)
|
||||
|
||||
def _remove_local_data_files(self):
|
||||
os.remove(self.DATA)
|
||||
os.remove(self.DATA_SUB)
|
||||
|
||||
def _test_recursive_pull(self):
|
||||
self._remove_local_data_files()
|
||||
self._clear_local_cache()
|
||||
|
||||
self.assertFalse(os.path.exists(self.DATA))
|
||||
self.assertFalse(os.path.exists(self.DATA_SUB))
|
||||
|
||||
self.main(["pull", "-R", self.DATA_DIR])
|
||||
|
||||
self.assertTrue(os.path.exists(self.DATA))
|
||||
self.assertTrue(os.path.exists(self.DATA_SUB))
|
||||
|
||||
def _clear_local_cache(self):
|
||||
shutil.rmtree(self.dvc.cache.local.cache_dir)
|
||||
|
||||
def _test_recursive_fetch(self, data_md5, data_sub_md5):
|
||||
self._clear_local_cache()
|
||||
|
||||
local_cache_data_path = self.dvc.cache.local.get(data_md5)
|
||||
local_cache_data_sub_path = self.dvc.cache.local.get(data_sub_md5)
|
||||
|
||||
self.assertFalse(os.path.exists(local_cache_data_path))
|
||||
self.assertFalse(os.path.exists(local_cache_data_sub_path))
|
||||
|
||||
self.main(["fetch", "-R", self.DATA_DIR])
|
||||
|
||||
self.assertTrue(os.path.exists(local_cache_data_path))
|
||||
self.assertTrue(os.path.exists(local_cache_data_sub_path))
|
||||
|
||||
def _test_recursive_push(self, data_md5, data_sub_md5):
|
||||
cloud_data_path = self.cloud._cloud.get(data_md5)
|
||||
cloud_data_sub_path = self.cloud._cloud.get(data_sub_md5)
|
||||
|
||||
self.assertFalse(os.path.exists(cloud_data_path))
|
||||
self.assertFalse(os.path.exists(cloud_data_sub_path))
|
||||
|
||||
self.main(["push", "-R", self.DATA_DIR])
|
||||
|
||||
self.assertTrue(os.path.exists(cloud_data_path))
|
||||
self.assertTrue(os.path.exists(cloud_data_sub_path))
|
||||
|
||||
def test(self):
|
||||
self._setup_cloud()
|
||||
self._prepare_repo()
|
||||
|
||||
data_md5 = file_md5(self.DATA)[0]
|
||||
data_sub_md5 = file_md5(self.DATA_SUB)[0]
|
||||
|
||||
self._test_recursive_push(data_md5, data_sub_md5)
|
||||
|
||||
self._test_recursive_fetch(data_md5, data_sub_md5)
|
||||
|
||||
self._test_recursive_pull()
|
||||
|
||||
|
||||
class TestCheckSumRecalculation(TestDvc):
|
||||
def test(self):
|
||||
test_collect = spy(State._collect)
|
||||
with patch.object(State, "_collect", test_collect):
|
||||
url = get_local_url()
|
||||
ret = main(["remote", "add", "-d", TEST_REMOTE, url])
|
||||
self.assertEqual(ret, 0)
|
||||
ret = main(["config", "cache.type", "hardlink"])
|
||||
self.assertEqual(ret, 0)
|
||||
ret = main(["add", self.FOO])
|
||||
self.assertEqual(ret, 0)
|
||||
ret = main(["push"])
|
||||
self.assertEqual(ret, 0)
|
||||
ret = main(["run", "-d", self.FOO, "echo foo"])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertEqual(test_collect.mock.call_count, 1)
|
||||
|
||||
|
||||
class TestShouldWarnOnNoChecksumInLocalAndRemoteCache(TestDvc):
|
||||
def setUp(self):
|
||||
super(TestShouldWarnOnNoChecksumInLocalAndRemoteCache, self).setUp()
|
||||
|
||||
cache_dir = self.mkdtemp()
|
||||
ret = main(["add", self.FOO])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
ret = main(["add", self.BAR])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
# purge cache
|
||||
shutil.rmtree(self.dvc.cache.local.cache_dir)
|
||||
|
||||
ret = main(["remote", "add", "remote_name", "-d", cache_dir])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
checksum_foo = file_md5(self.FOO)[0]
|
||||
checksum_bar = file_md5(self.BAR)[0]
|
||||
self.message_header = (
|
||||
"Some of the cache files do not exist neither locally "
|
||||
"nor on remote. Missing cache files: \n"
|
||||
)
|
||||
self.message_bar_part = "\nname: {}, md5: {}\n".format(
|
||||
self.BAR, checksum_bar
|
||||
)
|
||||
self.message_foo_part = "\nname: {}, md5: {}\n".format(
|
||||
self.FOO, checksum_foo
|
||||
)
|
||||
|
||||
def test(self):
|
||||
self._caplog.clear()
|
||||
main(["push"])
|
||||
assert self.message_header in self._caplog.text
|
||||
assert self.message_foo_part in self._caplog.text
|
||||
assert self.message_bar_part in self._caplog.text
|
||||
|
||||
self._caplog.clear()
|
||||
main(["pull"])
|
||||
assert self.message_header in self._caplog.text
|
||||
assert self.message_foo_part in self._caplog.text
|
||||
assert self.message_bar_part in self._caplog.text
|
||||
|
||||
self._caplog.clear()
|
||||
main(["status", "-c"])
|
||||
assert self.message_header in self._caplog.text
|
||||
assert self.message_foo_part in self._caplog.text
|
||||
assert self.message_bar_part in self._caplog.text
|
||||
@@ -0,0 +1,15 @@
|
||||
from dvc.stage import Stage
|
||||
from dvc.dependency import _get, DEP_MAP
|
||||
|
||||
from tests.func.test_output import TestOutScheme
|
||||
|
||||
|
||||
class TestDepScheme(TestOutScheme):
|
||||
MAP = DEP_MAP
|
||||
|
||||
TESTS = TestOutScheme.TESTS.copy()
|
||||
TESTS["http://example.com/path/to/file"] = "http"
|
||||
TESTS["https://example.com/path/to/file"] = "https"
|
||||
|
||||
def _get(self, path):
|
||||
return _get(Stage(self.dvc), path, None)
|
||||
@@ -0,0 +1,21 @@
|
||||
import os
|
||||
|
||||
from dvc.main import main
|
||||
|
||||
from tests.func.test_repro import TestRepro
|
||||
|
||||
|
||||
class TestDestroyNoConfirmation(TestRepro):
|
||||
def test(self):
|
||||
ret = main(["destroy"])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
|
||||
class TestDestroyForce(TestRepro):
|
||||
def test(self):
|
||||
ret = main(["destroy", "-f"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertFalse(os.path.exists(self.dvc.dvc_dir))
|
||||
self.assertFalse(os.path.exists(self.file1_stage))
|
||||
self.assertFalse(os.path.exists(self.file1))
|
||||
@@ -0,0 +1,189 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
from mock import patch, Mock
|
||||
|
||||
from dvc.main import main
|
||||
|
||||
|
||||
from dvc.scm.base import FileNotInCommitError
|
||||
import dvc.repo.diff as diff
|
||||
from dvc.command.diff import CmdDiff
|
||||
|
||||
|
||||
from tests.basic_env import TestDvc
|
||||
|
||||
|
||||
def _get_checksum(repo, file_name):
|
||||
outs = [out for s in repo.stages() for out in s.outs]
|
||||
for out in outs:
|
||||
if out.url == file_name:
|
||||
return out.checksum
|
||||
|
||||
|
||||
class TestDiff(TestDvc):
|
||||
def setUp(self):
|
||||
super(TestDiff, self).setUp()
|
||||
|
||||
self.new_file = "new_test_file"
|
||||
self.create(self.new_file, self.new_file)
|
||||
self.dvc.add(self.new_file)
|
||||
self.a_ref = self.git.head.commit
|
||||
self.new_checksum = _get_checksum(self.dvc, self.new_file)
|
||||
self.git.index.add([self.new_file + ".dvc"])
|
||||
self.git.index.commit("adds new_file")
|
||||
self.test_dct = {
|
||||
diff.DIFF_A_REF: str(self.a_ref),
|
||||
diff.DIFF_B_REF: str(self.git.head.commit),
|
||||
diff.DIFF_LIST: [
|
||||
{
|
||||
diff.DIFF_TARGET: self.new_file,
|
||||
diff.DIFF_NEW_FILE: self.new_file,
|
||||
diff.DIFF_NEW_CHECKSUM: self.new_checksum,
|
||||
diff.DIFF_SIZE: 13,
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
def test(self):
|
||||
out = self.dvc.scm.get_diff_trees(self.a_ref)
|
||||
self.assertFalse(out[diff.DIFF_EQUAL])
|
||||
self.assertEqual(str(self.a_ref), out[diff.DIFF_A_REF])
|
||||
self.assertEqual(str(self.git.head.commit), out[diff.DIFF_B_REF])
|
||||
|
||||
|
||||
class TestDiffRepo(TestDiff):
|
||||
def test(self):
|
||||
result = self.dvc.diff(self.a_ref, target=self.new_file)
|
||||
self.assertEqual(self.test_dct, result)
|
||||
|
||||
|
||||
class TestDiffCmdLine(TestDiff):
|
||||
def test(self):
|
||||
with patch("dvc.cli.diff.CmdDiff._show", autospec=True):
|
||||
with patch("dvc.repo.Repo", config="testing") as MockRepo:
|
||||
MockRepo.return_value.diff.return_value = "testing"
|
||||
ret = main(["diff", "-t", self.new_file, str(self.a_ref)])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
|
||||
class TestDiffCmdMessage(TestDiff):
|
||||
maxDiff = None
|
||||
|
||||
def test(self):
|
||||
with patch("dvc.repo.Repo", config="testing"):
|
||||
m = Mock()
|
||||
cmd_diff = CmdDiff(m)
|
||||
msg = cmd_diff._show(self.test_dct)
|
||||
test_msg = (
|
||||
"dvc diff from {0} to {1}\n\n"
|
||||
"diff for '{2}'\n"
|
||||
"+{2} with md5 {3}\n\n"
|
||||
"added file with size 13 Bytes"
|
||||
)
|
||||
test_msg = test_msg.format(
|
||||
self.test_dct[diff.DIFF_A_REF],
|
||||
self.test_dct[diff.DIFF_B_REF],
|
||||
self.test_dct[diff.DIFF_LIST][0][diff.DIFF_TARGET],
|
||||
self.test_dct[diff.DIFF_LIST][0][diff.DIFF_NEW_CHECKSUM],
|
||||
)
|
||||
self.assertEqual(test_msg, msg)
|
||||
|
||||
|
||||
class TestDiffDir(TestDvc):
|
||||
def setUp(self):
|
||||
super(TestDiffDir, self).setUp()
|
||||
|
||||
self.dvc.add(self.DATA_DIR)
|
||||
self.git.index.add([self.DATA_DIR + ".dvc"])
|
||||
self.git.index.commit("adds data_dir")
|
||||
self.a_ref = str(self.dvc.scm.git.head.commit)
|
||||
self.old_checksum = _get_checksum(self.dvc, self.DATA_DIR)
|
||||
self.new_file = os.path.join(self.DATA_SUB_DIR, diff.DIFF_NEW_FILE)
|
||||
self.create(self.new_file, self.new_file)
|
||||
self.dvc.add(self.DATA_DIR)
|
||||
self.git.index.add([self.DATA_DIR + ".dvc"])
|
||||
self.git.index.commit(message="adds data_dir with new_file")
|
||||
self.new_checksum = _get_checksum(self.dvc, self.DATA_DIR)
|
||||
|
||||
def test(self):
|
||||
out = self.dvc.scm.get_diff_trees(self.a_ref)
|
||||
self.assertFalse(out[diff.DIFF_EQUAL])
|
||||
self.assertEqual(str(self.a_ref), out[diff.DIFF_A_REF])
|
||||
self.assertEqual(str(self.git.head.commit), out[diff.DIFF_B_REF])
|
||||
|
||||
|
||||
class TestDiffDirRepo(TestDiffDir):
|
||||
maxDiff = None
|
||||
|
||||
def test(self):
|
||||
result = self.dvc.diff(self.a_ref, target=self.DATA_DIR)
|
||||
test_dct = {
|
||||
diff.DIFF_A_REF: str(self.a_ref),
|
||||
diff.DIFF_B_REF: str(self.git.head.commit),
|
||||
diff.DIFF_LIST: [
|
||||
{
|
||||
diff.DIFF_CHANGE: 0,
|
||||
diff.DIFF_DEL: 0,
|
||||
diff.DIFF_IDENT: 2,
|
||||
diff.DIFF_MOVE: 0,
|
||||
diff.DIFF_NEW: 1,
|
||||
diff.DIFF_IS_DIR: True,
|
||||
diff.DIFF_TARGET: self.DATA_DIR,
|
||||
diff.DIFF_NEW_FILE: self.DATA_DIR,
|
||||
diff.DIFF_NEW_CHECKSUM: self.new_checksum,
|
||||
diff.DIFF_OLD_FILE: self.DATA_DIR,
|
||||
diff.DIFF_OLD_CHECKSUM: self.old_checksum,
|
||||
diff.DIFF_SIZE: 30,
|
||||
}
|
||||
],
|
||||
}
|
||||
self.assertEqual(test_dct, result)
|
||||
|
||||
|
||||
class TestDiffDirRepoDeletedFile(TestDiffDir):
|
||||
maxDiff = None
|
||||
|
||||
def setUp(self):
|
||||
super(TestDiffDirRepoDeletedFile, self).setUp()
|
||||
|
||||
self.b_ref = self.a_ref
|
||||
self.new_checksum = self.old_checksum
|
||||
self.a_ref = str(self.dvc.scm.git.head.commit)
|
||||
self.old_checksum = _get_checksum(self.dvc, self.DATA_DIR)
|
||||
|
||||
def test(self):
|
||||
result = self.dvc.diff(
|
||||
self.a_ref, b_ref=self.b_ref, target=self.DATA_DIR
|
||||
)
|
||||
test_dct = {
|
||||
diff.DIFF_A_REF: str(self.a_ref),
|
||||
diff.DIFF_B_REF: str(self.b_ref),
|
||||
diff.DIFF_LIST: [
|
||||
{
|
||||
diff.DIFF_CHANGE: 0,
|
||||
diff.DIFF_DEL: 1,
|
||||
diff.DIFF_IDENT: 2,
|
||||
diff.DIFF_MOVE: 0,
|
||||
diff.DIFF_NEW: 0,
|
||||
diff.DIFF_IS_DIR: True,
|
||||
diff.DIFF_TARGET: self.DATA_DIR,
|
||||
diff.DIFF_NEW_FILE: self.DATA_DIR,
|
||||
diff.DIFF_NEW_CHECKSUM: self.new_checksum,
|
||||
diff.DIFF_OLD_FILE: self.DATA_DIR,
|
||||
diff.DIFF_OLD_CHECKSUM: self.old_checksum,
|
||||
diff.DIFF_SIZE: -30,
|
||||
}
|
||||
],
|
||||
}
|
||||
self.assertEqual(test_dct, result)
|
||||
|
||||
|
||||
class TestDiffFileNotFound(TestDiffDir):
|
||||
def setUp(self):
|
||||
super(TestDiffFileNotFound, self).setUp()
|
||||
self.unknown_file = "unknown_file_" + str(id(self))
|
||||
|
||||
def test(self):
|
||||
with self.assertRaises(FileNotInCommitError):
|
||||
self.dvc.diff(self.a_ref, target=self.unknown_file)
|
||||
@@ -0,0 +1,175 @@
|
||||
import os
|
||||
import configobj
|
||||
|
||||
from git import Repo
|
||||
|
||||
from dvc.main import main
|
||||
from dvc.repo import Repo as DvcRepo
|
||||
|
||||
from tests.basic_env import TestDvc
|
||||
from tests.basic_env import TestDir
|
||||
|
||||
|
||||
class TestGC(TestDvc):
|
||||
def setUp(self):
|
||||
super(TestGC, self).setUp()
|
||||
|
||||
self.dvc.add(self.FOO)
|
||||
self.dvc.add(self.DATA_DIR)
|
||||
self.good_cache = [
|
||||
self.dvc.cache.local.get(md5) for md5 in self.dvc.cache.local.all()
|
||||
]
|
||||
|
||||
self.bad_cache = []
|
||||
for i in ["123", "234", "345"]:
|
||||
path = os.path.join(self.dvc.cache.local.cache_dir, i[0:2], i[2:])
|
||||
self.create(path, i)
|
||||
self.bad_cache.append(path)
|
||||
|
||||
def test_api(self):
|
||||
self.dvc.gc()
|
||||
self._test_gc()
|
||||
|
||||
def test_cli(self):
|
||||
ret = main(["gc", "-f"])
|
||||
self.assertEqual(ret, 0)
|
||||
self._test_gc()
|
||||
|
||||
def _test_gc(self):
|
||||
self.assertTrue(os.path.isdir(self.dvc.cache.local.cache_dir))
|
||||
for c in self.bad_cache:
|
||||
self.assertFalse(os.path.exists(c))
|
||||
|
||||
for c in self.good_cache:
|
||||
self.assertTrue(os.path.exists(c))
|
||||
|
||||
|
||||
class TestGCBranchesTags(TestDvc):
|
||||
def _check_cache(self, num):
|
||||
total = 0
|
||||
for root, dirs, files in os.walk(os.path.join(".dvc", "cache")):
|
||||
total += len(files)
|
||||
self.assertEqual(total, num)
|
||||
|
||||
def test(self):
|
||||
fname = "file"
|
||||
|
||||
with open(fname, "w+") as fobj:
|
||||
fobj.write("v1.0")
|
||||
|
||||
stages = self.dvc.add(fname)
|
||||
self.assertEqual(len(stages), 1)
|
||||
self.dvc.scm.add([".gitignore", stages[0].relpath])
|
||||
self.dvc.scm.commit("v1.0")
|
||||
self.dvc.scm.tag("v1.0")
|
||||
|
||||
self.dvc.scm.checkout("test", create_new=True)
|
||||
self.dvc.remove(stages[0].relpath, outs_only=True)
|
||||
with open(fname, "w+") as fobj:
|
||||
fobj.write("test")
|
||||
stages = self.dvc.add(fname)
|
||||
self.assertEqual(len(stages), 1)
|
||||
self.dvc.scm.add([".gitignore", stages[0].relpath])
|
||||
self.dvc.scm.commit("test")
|
||||
|
||||
self.dvc.scm.checkout("master")
|
||||
self.dvc.remove(stages[0].relpath, outs_only=True)
|
||||
with open(fname, "w+") as fobj:
|
||||
fobj.write("trash")
|
||||
stages = self.dvc.add(fname)
|
||||
self.assertEqual(len(stages), 1)
|
||||
self.dvc.scm.add([".gitignore", stages[0].relpath])
|
||||
self.dvc.scm.commit("trash")
|
||||
|
||||
self.dvc.remove(stages[0].relpath, outs_only=True)
|
||||
with open(fname, "w+") as fobj:
|
||||
fobj.write("master")
|
||||
stages = self.dvc.add(fname)
|
||||
self.assertEqual(len(stages), 1)
|
||||
self.dvc.scm.add([".gitignore", stages[0].relpath])
|
||||
self.dvc.scm.commit("master")
|
||||
|
||||
self._check_cache(4)
|
||||
|
||||
self.dvc.gc(all_tags=True, all_branches=True)
|
||||
|
||||
self._check_cache(3)
|
||||
|
||||
self.dvc.gc(all_tags=False, all_branches=True)
|
||||
|
||||
self._check_cache(2)
|
||||
|
||||
self.dvc.gc(all_tags=True, all_branches=False)
|
||||
|
||||
self._check_cache(1)
|
||||
|
||||
|
||||
class TestGCMultipleDvcRepos(TestDvc):
|
||||
def _check_cache(self, num):
|
||||
total = 0
|
||||
for root, dirs, files in os.walk(os.path.join(".dvc", "cache")):
|
||||
total += len(files)
|
||||
self.assertEqual(total, num)
|
||||
|
||||
def setUp(self):
|
||||
super(TestGCMultipleDvcRepos, self).setUp()
|
||||
self.additional_path = TestDir.mkdtemp()
|
||||
self.additional_git = Repo.init(self.additional_path)
|
||||
self.additional_dvc = DvcRepo.init(self.additional_path)
|
||||
|
||||
cache_path = os.path.join(self._root_dir, ".dvc", "cache")
|
||||
config_path = os.path.join(
|
||||
self.additional_path, ".dvc", "config.local"
|
||||
)
|
||||
cfg = configobj.ConfigObj()
|
||||
cfg.filename = config_path
|
||||
cfg["cache"] = {"dir": cache_path}
|
||||
cfg.write()
|
||||
|
||||
self.additional_dvc = DvcRepo(self.additional_path)
|
||||
|
||||
def test(self):
|
||||
|
||||
# ADD FILE ONLY IN MAIN PROJECT
|
||||
fname = "only_in_first"
|
||||
with open(fname, "w+") as fobj:
|
||||
fobj.write("only in main repo")
|
||||
|
||||
stages = self.dvc.add(fname)
|
||||
self.assertEqual(len(stages), 1)
|
||||
|
||||
# ADD FILE IN MAIN PROJECT THAT IS ALSO IN SECOND PROJECT
|
||||
fname = "in_both"
|
||||
with open(fname, "w+") as fobj:
|
||||
fobj.write("in both repos")
|
||||
|
||||
stages = self.dvc.add(fname)
|
||||
self.assertEqual(len(stages), 1)
|
||||
|
||||
cwd = os.getcwd()
|
||||
os.chdir(self.additional_path)
|
||||
# ADD FILE ONLY IN SECOND PROJECT
|
||||
fname = os.path.join(self.additional_path, "only_in_second")
|
||||
with open(fname, "w+") as fobj:
|
||||
fobj.write("only in additional repo")
|
||||
|
||||
stages = self.additional_dvc.add(fname)
|
||||
self.assertEqual(len(stages), 1)
|
||||
|
||||
# ADD FILE IN SECOND PROJECT THAT IS ALSO IN MAIN PROJECT
|
||||
fname = os.path.join(self.additional_path, "in_both")
|
||||
with open(fname, "w+") as fobj:
|
||||
fobj.write("in both repos")
|
||||
|
||||
stages = self.additional_dvc.add(fname)
|
||||
self.assertEqual(len(stages), 1)
|
||||
|
||||
os.chdir(cwd)
|
||||
|
||||
self._check_cache(3)
|
||||
|
||||
self.dvc.gc(repos=[self.additional_path])
|
||||
self._check_cache(3)
|
||||
|
||||
self.dvc.gc()
|
||||
self._check_cache(2)
|
||||
@@ -0,0 +1,68 @@
|
||||
import os
|
||||
|
||||
from dvc.ignore import DvcIgnoreFromFile, DvcIgnore, DvcIgnoreFileHandler
|
||||
from dvc.utils.compat import cast_bytes
|
||||
from tests.basic_env import TestDvc
|
||||
|
||||
|
||||
class TestDvcIgnore(TestDvc):
|
||||
def setUp(self):
|
||||
super(TestDvcIgnore, self).setUp()
|
||||
self.ignore_file_handler = DvcIgnoreFileHandler(self.dvc.tree)
|
||||
|
||||
def _get_all_paths(self):
|
||||
|
||||
paths = []
|
||||
ignore_file_handler = DvcIgnoreFileHandler(self.dvc.tree)
|
||||
for root, dirs, files in self.dvc.tree.walk(
|
||||
self.dvc.root_dir, ignore_file_handler=ignore_file_handler
|
||||
):
|
||||
for dname in dirs:
|
||||
paths.append(os.path.join(root, dname))
|
||||
|
||||
for fname in files:
|
||||
paths.append(os.path.join(root, fname))
|
||||
|
||||
return paths
|
||||
|
||||
def test_ignore_comments(self):
|
||||
ignore_file = os.path.join(self.dvc.root_dir, DvcIgnore.DVCIGNORE_FILE)
|
||||
with open(ignore_file, "w") as fobj:
|
||||
fobj.write(os.path.basename(self.DATA))
|
||||
fobj.write(" #this is comment")
|
||||
|
||||
ignore = DvcIgnoreFromFile(ignore_file, self.ignore_file_handler)
|
||||
|
||||
self.assertEqual(1, len(ignore.patterns))
|
||||
|
||||
def test_ignore_in_child_dir(self):
|
||||
ignore_file = os.path.join(self.dvc.root_dir, DvcIgnore.DVCIGNORE_FILE)
|
||||
with open(ignore_file, "w") as fobj:
|
||||
fobj.write("data_dir/data")
|
||||
|
||||
forbidden_path = os.path.join(self.dvc.root_dir, self.DATA)
|
||||
all_paths = self._get_all_paths()
|
||||
|
||||
self.assertNotIn(forbidden_path, all_paths)
|
||||
|
||||
def test_ignore_in_child_dir_unicode(self):
|
||||
ignore_file = os.path.join(self.dvc.root_dir, DvcIgnore.DVCIGNORE_FILE)
|
||||
with open(ignore_file, "wb") as fobj:
|
||||
fobj.write(cast_bytes(self.UNICODE, "utf-8"))
|
||||
|
||||
forbidden_path = os.path.join(self.dvc.root_dir, self.UNICODE)
|
||||
all_paths = self._get_all_paths()
|
||||
|
||||
self.assertNotIn(forbidden_path, all_paths)
|
||||
|
||||
def test_ignore_in_parent_dir(self):
|
||||
ignore_file = os.path.join(self.dvc.root_dir, DvcIgnore.DVCIGNORE_FILE)
|
||||
with open(ignore_file, "w") as fobj:
|
||||
fobj.write("data_dir/data")
|
||||
|
||||
os.chdir(self.DATA_DIR)
|
||||
|
||||
forbidden_path = os.path.join(self.dvc.root_dir, self.DATA)
|
||||
all_paths = self._get_all_paths()
|
||||
|
||||
self.assertNotIn(forbidden_path, all_paths)
|
||||
@@ -0,0 +1,158 @@
|
||||
import dvc
|
||||
|
||||
from dvc.utils.compat import str
|
||||
|
||||
import os
|
||||
import logging
|
||||
from uuid import uuid4
|
||||
|
||||
from dvc.utils.compat import urljoin
|
||||
from dvc.exceptions import DvcException
|
||||
from dvc.main import main
|
||||
from mock import patch, mock_open, call
|
||||
from tests.basic_env import TestDvc
|
||||
from tests.utils import spy
|
||||
from tests.utils.httpd import StaticFileServer
|
||||
|
||||
|
||||
class TestCmdImport(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["import", self.FOO, "import"])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertTrue(os.path.exists("import.dvc"))
|
||||
|
||||
ret = main(["import", "non-existing-file", "import"])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
def test_unsupported(self):
|
||||
ret = main(["import", "unsupported://path", "import_unsupported"])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
|
||||
class TestDefaultOutput(TestDvc):
|
||||
def test(self):
|
||||
tmpdir = self.mkdtemp()
|
||||
filename = str(uuid4())
|
||||
tmpfile = os.path.join(tmpdir, filename)
|
||||
|
||||
with open(tmpfile, "w") as fd:
|
||||
fd.write("content")
|
||||
|
||||
ret = main(["import", tmpfile])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertTrue(os.path.exists(filename))
|
||||
self.assertEqual(open(filename).read(), "content")
|
||||
|
||||
|
||||
class TestFailedImportMessage(TestDvc):
|
||||
@patch("dvc.command.imp.urlparse")
|
||||
def test(self, imp_urlparse_patch):
|
||||
page_address = "http://somesite.com/file_name"
|
||||
|
||||
def dvc_exception(*args, **kwargs):
|
||||
raise DvcException("message")
|
||||
|
||||
imp_urlparse_patch.side_effect = dvc_exception
|
||||
|
||||
self._caplog.clear()
|
||||
|
||||
with self._caplog.at_level(logging.ERROR, logger="dvc"):
|
||||
main(["import", page_address])
|
||||
|
||||
expected_error = (
|
||||
"failed to import http://somesite.com/file_name."
|
||||
" You could also try downloading it manually and"
|
||||
" adding it with `dvc add` command."
|
||||
)
|
||||
|
||||
assert expected_error in self._caplog.text
|
||||
|
||||
|
||||
class TestInterruptedDownload(TestDvc):
|
||||
@property
|
||||
def remote(self):
|
||||
return "http://localhost:8000/"
|
||||
|
||||
def _prepare_interrupted_download(self):
|
||||
import_url = urljoin(self.remote, self.FOO)
|
||||
import_output = "imported_file"
|
||||
tmp_file_name = import_output + ".part"
|
||||
tmp_file_path = os.path.realpath(
|
||||
os.path.join(self._root_dir, tmp_file_name)
|
||||
)
|
||||
self._import_with_interrupt(import_output, import_url)
|
||||
self.assertTrue(os.path.exists(tmp_file_name))
|
||||
self.assertFalse(os.path.exists(import_output))
|
||||
return import_output, import_url, tmp_file_path
|
||||
|
||||
def _import_with_interrupt(self, import_output, import_url):
|
||||
def interrupting_generator():
|
||||
yield self.FOO[0].encode("utf8")
|
||||
raise KeyboardInterrupt
|
||||
|
||||
with patch(
|
||||
"requests.models.Response.iter_content",
|
||||
return_value=interrupting_generator(),
|
||||
):
|
||||
with patch(
|
||||
"dvc.remote.http.RemoteHTTP._content_length", return_value=3
|
||||
):
|
||||
result = main(["import", import_url, import_output])
|
||||
self.assertEqual(result, 252)
|
||||
|
||||
|
||||
class TestShouldResumeDownload(TestInterruptedDownload):
|
||||
@patch("dvc.remote.http.RemoteHTTP.CHUNK_SIZE", 1)
|
||||
def test(self):
|
||||
with StaticFileServer():
|
||||
import_output, import_url, tmp_file_path = (
|
||||
self._prepare_interrupted_download()
|
||||
)
|
||||
|
||||
m = mock_open()
|
||||
with patch("dvc.remote.http.open", m):
|
||||
result = main(
|
||||
["import", "--resume", import_url, import_output]
|
||||
)
|
||||
self.assertEqual(result, 0)
|
||||
m.assert_called_once_with(tmp_file_path, "ab")
|
||||
m_handle = m()
|
||||
expected_calls = [call(b"o"), call(b"o")]
|
||||
m_handle.write.assert_has_calls(expected_calls, any_order=False)
|
||||
|
||||
|
||||
class TestShouldNotResumeDownload(TestInterruptedDownload):
|
||||
@patch("dvc.remote.http.RemoteHTTP.CHUNK_SIZE", 1)
|
||||
def test(self):
|
||||
with StaticFileServer():
|
||||
import_output, import_url, tmp_file_path = (
|
||||
self._prepare_interrupted_download()
|
||||
)
|
||||
|
||||
m = mock_open()
|
||||
with patch("dvc.remote.http.open", m):
|
||||
result = main(["import", import_url, import_output])
|
||||
self.assertEqual(result, 0)
|
||||
m.assert_called_once_with(tmp_file_path, "wb")
|
||||
m_handle = m()
|
||||
expected_calls = [call(b"f"), call(b"o"), call(b"o")]
|
||||
m_handle.write.assert_has_calls(expected_calls, any_order=False)
|
||||
|
||||
|
||||
class TestShouldRemoveOutsBeforeImport(TestDvc):
|
||||
def setUp(self):
|
||||
super(TestShouldRemoveOutsBeforeImport, self).setUp()
|
||||
tmp_dir = self.mkdtemp()
|
||||
self.external_source = os.path.join(tmp_dir, "file")
|
||||
with open(self.external_source, "w") as fobj:
|
||||
fobj.write("content")
|
||||
|
||||
def test(self):
|
||||
remove_outs_call_counter = spy(dvc.stage.Stage.remove_outs)
|
||||
with patch.object(
|
||||
dvc.stage.Stage, "remove_outs", remove_outs_call_counter
|
||||
):
|
||||
ret = main(["import", self.external_source])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
self.assertEqual(1, remove_outs_call_counter.mock.call_count)
|
||||
@@ -0,0 +1,66 @@
|
||||
import os
|
||||
|
||||
from dvc.main import main
|
||||
from dvc.repo import Repo as DvcRepo
|
||||
from dvc.exceptions import InitError
|
||||
|
||||
from tests.basic_env import TestGit, TestDir
|
||||
|
||||
|
||||
class TestInit(TestGit):
|
||||
def _test_init(self):
|
||||
self.assertTrue(os.path.exists(DvcRepo.DVC_DIR))
|
||||
self.assertTrue(os.path.isdir(DvcRepo.DVC_DIR))
|
||||
|
||||
def test_api(self):
|
||||
DvcRepo.init()
|
||||
|
||||
self._test_init()
|
||||
|
||||
def test_cli(self):
|
||||
ret = main(["init"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self._test_init()
|
||||
|
||||
|
||||
class TestDoubleInit(TestInit):
|
||||
def test(self):
|
||||
ret = main(["init"])
|
||||
self.assertEqual(ret, 0)
|
||||
self._test_init()
|
||||
|
||||
ret = main(["init"])
|
||||
self.assertNotEqual(ret, 0)
|
||||
self._test_init()
|
||||
|
||||
ret = main(["init", "--force"])
|
||||
self.assertEqual(ret, 0)
|
||||
self._test_init()
|
||||
|
||||
|
||||
class TestInitNoSCMFail(TestDir):
|
||||
def test_api(self):
|
||||
with self.assertRaises(InitError):
|
||||
DvcRepo.init()
|
||||
|
||||
def test_cli(self):
|
||||
ret = main(["init"])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
|
||||
class TestInitNoSCM(TestDir):
|
||||
def _test_init(self):
|
||||
self.assertTrue(os.path.exists(DvcRepo.DVC_DIR))
|
||||
self.assertTrue(os.path.isdir(DvcRepo.DVC_DIR))
|
||||
|
||||
def test_api(self):
|
||||
DvcRepo.init(no_scm=True)
|
||||
|
||||
self._test_init()
|
||||
|
||||
def test_cli(self):
|
||||
ret = main(["init", "--no-scm"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self._test_init()
|
||||
@@ -0,0 +1,33 @@
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from dvc.main import main
|
||||
|
||||
from tests.basic_env import TestDvc
|
||||
|
||||
|
||||
@unittest.skipIf(
|
||||
sys.platform == "win32", "Git hooks aren't supported on Windows"
|
||||
)
|
||||
class TestInstall(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["install"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["install"])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
def hook(name):
|
||||
return os.path.join(".git", "hooks", name)
|
||||
|
||||
self.assertTrue(os.path.isfile(hook("post-checkout")))
|
||||
self.assertTrue(os.path.isfile(hook("pre-commit")))
|
||||
|
||||
self.dvc.add(self.FOO)
|
||||
self.dvc.scm.add([".gitignore", self.FOO + ".dvc"])
|
||||
self.dvc.scm.commit("add")
|
||||
os.unlink(self.FOO)
|
||||
|
||||
self.dvc.scm.checkout("branch", create_new=True)
|
||||
self.assertTrue(os.path.isfile(self.FOO))
|
||||
@@ -0,0 +1,21 @@
|
||||
from dvc.lock import LockError
|
||||
from dvc.main import main
|
||||
from dvc.lock import Lock
|
||||
|
||||
from tests.basic_env import TestDvc
|
||||
|
||||
|
||||
class TestLock(TestDvc):
|
||||
def test_with(self):
|
||||
lock = Lock(self.dvc.dvc_dir)
|
||||
with lock:
|
||||
with self.assertRaises(LockError):
|
||||
lock2 = Lock(self.dvc.dvc_dir)
|
||||
with lock2:
|
||||
self.assertTrue(False)
|
||||
|
||||
def test_cli(self):
|
||||
lock = Lock(self.dvc.dvc_dir)
|
||||
with lock:
|
||||
ret = main(["add", self.FOO])
|
||||
self.assertEqual(ret, 1)
|
||||
@@ -0,0 +1,697 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
|
||||
from dvc.repo import Repo as DvcRepo
|
||||
from dvc.main import main
|
||||
from dvc.exceptions import DvcException, BadMetricError, NoMetricsError
|
||||
from tests.basic_env import TestDvc
|
||||
|
||||
|
||||
class TestMetrics(TestDvc):
|
||||
def setUp(self):
|
||||
super(TestMetrics, self).setUp()
|
||||
self.dvc.scm.commit("init")
|
||||
|
||||
for branch in ["foo", "bar", "baz"]:
|
||||
self.dvc.scm.checkout(branch, create_new=True)
|
||||
|
||||
with open("metric", "w+") as fd:
|
||||
fd.write(branch)
|
||||
|
||||
with open("metric_json", "w+") as fd:
|
||||
json.dump({"branch": branch}, fd)
|
||||
|
||||
with open("metric_csv", "w+") as fd:
|
||||
fd.write(branch)
|
||||
|
||||
with open("metric_hcsv", "w+") as fd:
|
||||
fd.write("branch\n")
|
||||
fd.write(branch)
|
||||
|
||||
with open("metric_tsv", "w+") as fd:
|
||||
fd.write(branch)
|
||||
|
||||
with open("metric_htsv", "w+") as fd:
|
||||
fd.write("branch\n")
|
||||
fd.write(branch)
|
||||
|
||||
files = [
|
||||
"metric",
|
||||
"metric_json",
|
||||
"metric_tsv",
|
||||
"metric_htsv",
|
||||
"metric_csv",
|
||||
"metric_hcsv",
|
||||
]
|
||||
|
||||
self.dvc.run(metrics_no_cache=files, overwrite=True)
|
||||
|
||||
self.dvc.scm.add(files + ["metric.dvc"])
|
||||
|
||||
self.dvc.scm.commit("metric")
|
||||
|
||||
self.dvc.scm.checkout("master")
|
||||
|
||||
def test_show(self):
|
||||
ret = self.dvc.metrics.show("metric", all_branches=True)
|
||||
self.assertEqual(len(ret), 3)
|
||||
self.assertEqual(ret["foo"]["metric"], "foo")
|
||||
self.assertEqual(ret["bar"]["metric"], "bar")
|
||||
self.assertEqual(ret["baz"]["metric"], "baz")
|
||||
|
||||
ret = self.dvc.metrics.show(
|
||||
"metric_json", typ="json", xpath="branch", all_branches=True
|
||||
)
|
||||
self.assertEqual(len(ret), 3)
|
||||
self.assertSequenceEqual(ret["foo"]["metric_json"], ["foo"])
|
||||
self.assertSequenceEqual(ret["bar"]["metric_json"], ["bar"])
|
||||
self.assertSequenceEqual(ret["baz"]["metric_json"], ["baz"])
|
||||
|
||||
ret = self.dvc.metrics.show(
|
||||
"metric_tsv", typ="tsv", xpath="0,0", all_branches=True
|
||||
)
|
||||
self.assertEqual(len(ret), 3)
|
||||
self.assertSequenceEqual(ret["foo"]["metric_tsv"], ["foo"])
|
||||
self.assertSequenceEqual(ret["bar"]["metric_tsv"], ["bar"])
|
||||
self.assertSequenceEqual(ret["baz"]["metric_tsv"], ["baz"])
|
||||
|
||||
ret = self.dvc.metrics.show(
|
||||
"metric_htsv", typ="htsv", xpath="0,branch", all_branches=True
|
||||
)
|
||||
self.assertEqual(len(ret), 3)
|
||||
self.assertSequenceEqual(ret["foo"]["metric_htsv"], ["foo"])
|
||||
self.assertSequenceEqual(ret["bar"]["metric_htsv"], ["bar"])
|
||||
self.assertSequenceEqual(ret["baz"]["metric_htsv"], ["baz"])
|
||||
|
||||
ret = self.dvc.metrics.show(
|
||||
"metric_csv", typ="csv", xpath="0,0", all_branches=True
|
||||
)
|
||||
self.assertEqual(len(ret), 3)
|
||||
self.assertSequenceEqual(ret["foo"]["metric_csv"], ["foo"])
|
||||
self.assertSequenceEqual(ret["bar"]["metric_csv"], ["bar"])
|
||||
self.assertSequenceEqual(ret["baz"]["metric_csv"], ["baz"])
|
||||
|
||||
ret = self.dvc.metrics.show(
|
||||
"metric_hcsv", typ="hcsv", xpath="0,branch", all_branches=True
|
||||
)
|
||||
self.assertEqual(len(ret), 3)
|
||||
self.assertSequenceEqual(ret["foo"]["metric_hcsv"], ["foo"])
|
||||
self.assertSequenceEqual(ret["bar"]["metric_hcsv"], ["bar"])
|
||||
self.assertSequenceEqual(ret["baz"]["metric_hcsv"], ["baz"])
|
||||
|
||||
def test_unknown_type_ignored(self):
|
||||
ret = self.dvc.metrics.show(
|
||||
"metric_hcsv", typ="unknown", xpath="0,branch", all_branches=True
|
||||
)
|
||||
self.assertEqual(len(ret), 3)
|
||||
for b in ["foo", "bar", "baz"]:
|
||||
self.assertEqual(ret[b]["metric_hcsv"].split(), ["branch", b])
|
||||
|
||||
def test_type_case_normalized(self):
|
||||
ret = self.dvc.metrics.show(
|
||||
"metric_hcsv", typ=" hCSV ", xpath="0,branch", all_branches=True
|
||||
)
|
||||
self.assertEqual(len(ret), 3)
|
||||
for b in ["foo", "bar", "baz"]:
|
||||
self.assertSequenceEqual(ret[b]["metric_hcsv"], [b])
|
||||
|
||||
def test_xpath_is_empty(self):
|
||||
ret = self.dvc.metrics.show(
|
||||
"metric_json", typ="json", xpath="", all_branches=True
|
||||
)
|
||||
self.assertEqual(len(ret), 3)
|
||||
for b in ["foo", "bar", "baz"]:
|
||||
self.assertEqual(ret[b]["metric_json"], json.dumps({"branch": b}))
|
||||
|
||||
def test_xpath_is_none(self):
|
||||
ret = self.dvc.metrics.show(
|
||||
"metric_json", typ="json", xpath=None, all_branches=True
|
||||
)
|
||||
self.assertEqual(len(ret), 3)
|
||||
for b in ["foo", "bar", "baz"]:
|
||||
self.assertEqual(ret[b]["metric_json"], json.dumps({"branch": b}))
|
||||
|
||||
def test_xpath_all_columns(self):
|
||||
ret = self.dvc.metrics.show(
|
||||
"metric_hcsv", typ="hcsv ", xpath="0,", all_branches=True
|
||||
)
|
||||
self.assertEqual(len(ret), 3)
|
||||
for b in ["foo", "bar", "baz"]:
|
||||
self.assertSequenceEqual(ret[b]["metric_hcsv"], [b])
|
||||
|
||||
def test_xpath_all_rows(self):
|
||||
ret = self.dvc.metrics.show(
|
||||
"metric_csv", typ="csv", xpath=",0", all_branches=True
|
||||
)
|
||||
self.assertEqual(len(ret), 3)
|
||||
for b in ["foo", "bar", "baz"]:
|
||||
self.assertSequenceEqual(ret[b]["metric_csv"], [b])
|
||||
|
||||
def test_xpath_all(self):
|
||||
ret = self.dvc.metrics.show(
|
||||
"metric_csv", typ="csv", xpath=",", all_branches=True
|
||||
)
|
||||
self.assertEqual(len(ret), 3)
|
||||
for b in ["foo", "bar", "baz"]:
|
||||
self.assertSequenceEqual(ret[b]["metric_csv"], [[b]])
|
||||
|
||||
def test_xpath_all_with_header(self):
|
||||
ret = self.dvc.metrics.show(
|
||||
"metric_hcsv", typ="hcsv", xpath=",", all_branches=True
|
||||
)
|
||||
self.assertEqual(len(ret), 3)
|
||||
for b in ["foo", "bar", "baz"]:
|
||||
self.assertSequenceEqual(ret[b]["metric_hcsv"], [[b]])
|
||||
|
||||
def test_formatted_output(self):
|
||||
with open("metrics.csv", "w") as fobj:
|
||||
# Labels are in Spanish to test unicode characters
|
||||
fobj.write(
|
||||
"valor_mse,desviación_mse,data_set\n"
|
||||
"0.421601,0.173461,entrenamiento\n"
|
||||
"0.67528,0.289545,pruebas\n"
|
||||
"0.671502,0.297848,validación\n"
|
||||
)
|
||||
|
||||
with open("metrics.tsv", "w") as fobj:
|
||||
# Contains quoted newlines to test output correctness
|
||||
fobj.write(
|
||||
"value_mse\tdeviation_mse\tdata_set\n"
|
||||
"0.421601\t0.173461\ttrain\n"
|
||||
'0.67528\t0.289545\t"test\\ning"\n'
|
||||
"0.671502\t0.297848\tvalidation\n"
|
||||
)
|
||||
|
||||
with open("metrics.json", "w") as fobj:
|
||||
fobj.write(
|
||||
"{\n"
|
||||
' "data_set": [\n'
|
||||
' "train",\n'
|
||||
' "testing",\n'
|
||||
' "validation"\n'
|
||||
" ],\n"
|
||||
' "deviation_mse": [\n'
|
||||
' "0.173461",\n'
|
||||
' "0.289545",\n'
|
||||
' "0.297848"\n'
|
||||
" ],\n"
|
||||
' "value_mse": [\n'
|
||||
' "0.421601",\n'
|
||||
' "0.67528",\n'
|
||||
' "0.671502"\n'
|
||||
" ]\n"
|
||||
"}"
|
||||
)
|
||||
|
||||
with open("metrics.txt", "w") as fobj:
|
||||
fobj.write("ROC_AUC: 0.64\nKS: 78.9999999996\nF_SCORE: 77\n")
|
||||
|
||||
self.dvc.run(
|
||||
fname="testing_metrics_output.dvc",
|
||||
metrics_no_cache=[
|
||||
"metrics.csv",
|
||||
"metrics.tsv",
|
||||
"metrics.json",
|
||||
"metrics.txt",
|
||||
],
|
||||
)
|
||||
|
||||
self.dvc.metrics.modify("metrics.csv", typ="csv")
|
||||
self.dvc.metrics.modify("metrics.tsv", typ="tsv")
|
||||
self.dvc.metrics.modify("metrics.json", typ="json")
|
||||
|
||||
self._caplog.clear()
|
||||
|
||||
with self._caplog.at_level(logging.INFO, logger="dvc"):
|
||||
ret = main(["metrics", "show"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
expected_csv = (
|
||||
u"\tmetrics.csv:\n"
|
||||
u"\t\tvalor_mse desviación_mse data_set \n"
|
||||
u"\t\t0.421601 0.173461 entrenamiento \n"
|
||||
u"\t\t0.67528 0.289545 pruebas \n"
|
||||
u"\t\t0.671502 0.297848 validación"
|
||||
)
|
||||
|
||||
expected_tsv = (
|
||||
"\tmetrics.tsv:\n"
|
||||
"\t\tvalue_mse deviation_mse data_set \n"
|
||||
"\t\t0.421601 0.173461 train \n"
|
||||
"\t\t0.67528 0.289545 test\\ning \n"
|
||||
"\t\t0.671502 0.297848 validation"
|
||||
)
|
||||
|
||||
expected_txt = (
|
||||
"\tmetrics.txt:\n"
|
||||
"\t\tROC_AUC: 0.64\n"
|
||||
"\t\tKS: 78.9999999996\n"
|
||||
"\t\tF_SCORE: 77"
|
||||
)
|
||||
|
||||
expected_json = (
|
||||
"\tmetrics.json:\n"
|
||||
"\t\t{\n"
|
||||
'\t\t "data_set": [\n'
|
||||
'\t\t "train",\n'
|
||||
'\t\t "testing",\n'
|
||||
'\t\t "validation"\n'
|
||||
"\t\t ],\n"
|
||||
'\t\t "deviation_mse": [\n'
|
||||
'\t\t "0.173461",\n'
|
||||
'\t\t "0.289545",\n'
|
||||
'\t\t "0.297848"\n'
|
||||
"\t\t ],\n"
|
||||
'\t\t "value_mse": [\n'
|
||||
'\t\t "0.421601",\n'
|
||||
'\t\t "0.67528",\n'
|
||||
'\t\t "0.671502"\n'
|
||||
"\t\t ]\n"
|
||||
"\t\t}"
|
||||
)
|
||||
|
||||
stdout = "\n".join(record.message for record in self._caplog.records)
|
||||
|
||||
assert expected_tsv in stdout
|
||||
assert expected_csv in stdout
|
||||
assert expected_txt in stdout
|
||||
assert expected_json in stdout
|
||||
|
||||
|
||||
class TestMetricsRecursive(TestDvc):
|
||||
def setUp(self):
|
||||
super(TestMetricsRecursive, self).setUp()
|
||||
self.dvc.scm.commit("init")
|
||||
|
||||
self.dvc.scm.checkout("nested", create_new=True)
|
||||
|
||||
os.mkdir("nested")
|
||||
os.mkdir(os.path.join("nested", "subnested"))
|
||||
|
||||
ret = main(
|
||||
[
|
||||
"run",
|
||||
"-M",
|
||||
os.path.join("nested", "metric_nested"),
|
||||
"echo",
|
||||
"nested",
|
||||
">>",
|
||||
os.path.join("nested", "metric_nested"),
|
||||
]
|
||||
)
|
||||
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(
|
||||
[
|
||||
"run",
|
||||
"-M",
|
||||
os.path.join("nested", "subnested", "metric_subnested"),
|
||||
"echo",
|
||||
"subnested",
|
||||
">>",
|
||||
os.path.join("nested", "subnested", "metric_subnested"),
|
||||
]
|
||||
)
|
||||
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.dvc.scm.add(
|
||||
["nested", "metric_nested.dvc", "metric_subnested.dvc"]
|
||||
)
|
||||
self.dvc.scm.commit("nested metrics")
|
||||
|
||||
self.dvc.scm.checkout("master")
|
||||
|
||||
def test(self):
|
||||
with self.assertRaises(BadMetricError):
|
||||
self.dvc.metrics.show("nested", all_branches=True, recursive=False)
|
||||
|
||||
ret = self.dvc.metrics.show(
|
||||
"nested", all_branches=True, recursive=True
|
||||
)
|
||||
self.assertEqual(len(ret), 1)
|
||||
self.assertEqual(
|
||||
ret["nested"][
|
||||
os.path.join("nested", "subnested", "metric_subnested")
|
||||
],
|
||||
"subnested",
|
||||
)
|
||||
self.assertEqual(
|
||||
ret["nested"][os.path.join("nested", "metric_nested")], "nested"
|
||||
)
|
||||
|
||||
|
||||
class TestMetricsReproCLI(TestDvc):
|
||||
def test(self):
|
||||
stage = self.dvc.run(
|
||||
metrics_no_cache=["metrics"],
|
||||
cmd="python {} {} {}".format(self.CODE, self.FOO, "metrics"),
|
||||
)
|
||||
|
||||
ret = main(["repro", "-m", stage.path])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["metrics", "remove", "metrics"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["repro", "-f", "-m", stage.path])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
ret = main(["metrics", "add", "metrics"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["metrics", "modify", "-t", "CSV", "-x", "0,0", "metrics"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["repro", "-f", "-m", stage.path])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_dir(self):
|
||||
os.mkdir("metrics_dir")
|
||||
|
||||
with self.assertRaises(DvcException):
|
||||
self.dvc.run(metrics_no_cache=["metrics_dir"])
|
||||
|
||||
def test_binary(self):
|
||||
with open("metrics_bin", "w+") as fd:
|
||||
fd.write("\0\0\0\0\0\0\0\0")
|
||||
|
||||
with self.assertRaises(DvcException):
|
||||
self.dvc.run(metrics_no_cache=["metrics_bin"])
|
||||
|
||||
|
||||
class TestMetricsCLI(TestMetrics):
|
||||
def test(self):
|
||||
# FIXME check output
|
||||
ret = main(["metrics", "show", "-a", "metric", "-v"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(
|
||||
[
|
||||
"metrics",
|
||||
"show",
|
||||
"-a",
|
||||
"metric_json",
|
||||
"-t",
|
||||
"json",
|
||||
"-x",
|
||||
"branch",
|
||||
]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
ret = main(
|
||||
["metrics", "show", "-a", "metric_tsv", "-t", "tsv", "-x", "0,0"]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
ret = main(
|
||||
[
|
||||
"metrics",
|
||||
"show",
|
||||
"-a",
|
||||
"metric_htsv",
|
||||
"-t",
|
||||
"htsv",
|
||||
"-x",
|
||||
"0,branch",
|
||||
]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(
|
||||
["metrics", "show", "-a", "metric_csv", "-t", "csv", "-x", "0,0"]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(
|
||||
[
|
||||
"metrics",
|
||||
"show",
|
||||
"-a",
|
||||
"metric_hcsv",
|
||||
"-t",
|
||||
"hcsv",
|
||||
"-x",
|
||||
"0,branch",
|
||||
]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_dir(self):
|
||||
os.mkdir("metrics_dir")
|
||||
|
||||
with self.assertRaises(DvcException):
|
||||
self.dvc.run(outs_no_cache=["metrics_dir"])
|
||||
self.dvc.metrics.add("metrics_dir")
|
||||
|
||||
def test_binary(self):
|
||||
with open("metrics_bin", "w+") as fd:
|
||||
fd.write("\0\0\0\0\0\0\0\0")
|
||||
|
||||
with self.assertRaises(DvcException):
|
||||
self.dvc.run(outs_no_cache=["metrics_bin"])
|
||||
self.dvc.metrics.add("metrics_bin")
|
||||
|
||||
def test_non_existing(self):
|
||||
ret = main(["metrics", "add", "non-existing"])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
ret = main(["metrics", "modify", "non-existing"])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
ret = main(["metrics", "remove", "non-existing"])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
def test_wrong_type_add(self):
|
||||
with open("metric.unknown", "w+") as fd:
|
||||
fd.write("unknown")
|
||||
fd.flush()
|
||||
|
||||
ret = main(["add", "metric.unknown"])
|
||||
assert ret == 0
|
||||
|
||||
self._caplog.clear()
|
||||
ret = main(["metrics", "add", "metric.unknown", "-t", "unknown"])
|
||||
assert ret == 1
|
||||
|
||||
assert (
|
||||
"failed to add metric file 'metric.unknown'"
|
||||
) in self._caplog.text
|
||||
|
||||
assert (
|
||||
"'unknown' is not supported, must be one of "
|
||||
"[raw, json, csv, tsv, hcsv, htsv]"
|
||||
) in self._caplog.text
|
||||
|
||||
ret = main(["metrics", "add", "metric.unknown", "-t", "raw"])
|
||||
assert ret == 0
|
||||
|
||||
self._caplog.clear()
|
||||
ret = main(["metrics", "show", "metric.unknown"])
|
||||
assert ret == 0
|
||||
|
||||
assert "\tmetric.unknown: unknown" in self._caplog.text
|
||||
|
||||
def test_wrong_type_modify(self):
|
||||
with open("metric.unknown", "w+") as fd:
|
||||
fd.write("unknown")
|
||||
fd.flush()
|
||||
|
||||
ret = main(["run", "-m", "metric.unknown"])
|
||||
assert ret == 0
|
||||
|
||||
self._caplog.clear()
|
||||
|
||||
ret = main(["metrics", "modify", "metric.unknown", "-t", "unknown"])
|
||||
assert ret == 1
|
||||
|
||||
assert "failed to modify metric file settings" in self._caplog.text
|
||||
|
||||
assert (
|
||||
"metric type 'unknown' is not supported, must be one of "
|
||||
"[raw, json, csv, tsv, hcsv, htsv]"
|
||||
) in self._caplog.text
|
||||
|
||||
ret = main(["metrics", "modify", "metric.unknown", "-t", "CSV"])
|
||||
assert ret == 0
|
||||
|
||||
self._caplog.clear()
|
||||
|
||||
ret = main(["metrics", "show", "metric.unknown"])
|
||||
assert ret == 0
|
||||
|
||||
assert "\tmetric.unknown: unknown" in self._caplog.text
|
||||
|
||||
def test_wrong_type_show(self):
|
||||
with open("metric.unknown", "w+") as fd:
|
||||
fd.write("unknown")
|
||||
fd.flush()
|
||||
|
||||
ret = main(["run", "-m", "metric.unknown"])
|
||||
assert ret == 0
|
||||
|
||||
self._caplog.clear()
|
||||
|
||||
ret = main(
|
||||
["metrics", "show", "metric.unknown", "-t", "unknown", "-x", "0,0"]
|
||||
)
|
||||
assert ret == 0
|
||||
assert "\tmetric.unknown: unknown" in self._caplog.text
|
||||
|
||||
|
||||
class TestNoMetrics(TestDvc):
|
||||
def test(self):
|
||||
with self.assertRaises(NoMetricsError):
|
||||
self.dvc.metrics.show()
|
||||
|
||||
def test_cli(self):
|
||||
ret = main(["metrics", "show"])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
|
||||
class TestCachedMetrics(TestDvc):
|
||||
def _do_add(self, branch):
|
||||
self.dvc.scm.checkout(branch)
|
||||
self.dvc.checkout(force=True)
|
||||
|
||||
with open("metrics.json", "w+") as fd:
|
||||
json.dump({"metrics": branch}, fd)
|
||||
|
||||
stages = self.dvc.add("metrics.json")
|
||||
self.dvc.metrics.add("metrics.json", typ="json", xpath="metrics")
|
||||
self.assertEqual(len(stages), 1)
|
||||
stage = stages[0]
|
||||
self.assertIsNotNone(stage)
|
||||
|
||||
self.dvc.scm.add([".gitignore", "metrics.json.dvc"])
|
||||
self.dvc.scm.commit(branch)
|
||||
|
||||
def _do_run(self, branch):
|
||||
self.dvc.scm.checkout(branch)
|
||||
self.dvc.checkout(force=True)
|
||||
|
||||
with open("code.py", "w+") as fobj:
|
||||
fobj.write("import sys\n")
|
||||
fobj.write("import os\n")
|
||||
fobj.write("import json\n")
|
||||
fobj.write(
|
||||
'print(json.dumps({{"metrics": "{branch}"}}))\n'.format(
|
||||
branch=branch
|
||||
)
|
||||
)
|
||||
|
||||
stage = self.dvc.run(
|
||||
deps=["code.py"],
|
||||
metrics=["metrics.json"],
|
||||
cmd="python code.py metrics.json > metrics.json",
|
||||
)
|
||||
self.assertIsNotNone(stage)
|
||||
self.assertEqual(stage.relpath, "metrics.json.dvc")
|
||||
|
||||
self.dvc.scm.add(["code.py", ".gitignore", "metrics.json.dvc"])
|
||||
self.dvc.scm.commit(branch)
|
||||
|
||||
def _test_metrics(self, func):
|
||||
self.dvc.scm.commit("init")
|
||||
|
||||
self.dvc.scm.branch("one")
|
||||
self.dvc.scm.branch("two")
|
||||
|
||||
func("master")
|
||||
func("one")
|
||||
func("two")
|
||||
|
||||
# TestDvc currently is based on TestGit, so it is safe to use
|
||||
# scm.git for now
|
||||
self.dvc.scm.git.git.clean("-fd")
|
||||
|
||||
self.dvc = DvcRepo(".")
|
||||
|
||||
res = self.dvc.metrics.show(
|
||||
"metrics.json", all_branches=True, typ="json", xpath="metrics"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
res,
|
||||
{
|
||||
"master": {"metrics.json": ["master"]},
|
||||
"one": {"metrics.json": ["one"]},
|
||||
"two": {"metrics.json": ["two"]},
|
||||
},
|
||||
)
|
||||
|
||||
res = self.dvc.metrics.show(
|
||||
"", all_branches=True, typ="json", xpath="metrics"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
res,
|
||||
{
|
||||
"master": {"metrics.json": ["master"]},
|
||||
"one": {"metrics.json": ["one"]},
|
||||
"two": {"metrics.json": ["two"]},
|
||||
},
|
||||
)
|
||||
|
||||
def test_add(self):
|
||||
self._test_metrics(self._do_add)
|
||||
|
||||
def test_run(self):
|
||||
self._test_metrics(self._do_run)
|
||||
|
||||
|
||||
class TestMetricsType(TestDvc):
|
||||
branches = ["foo", "bar", "baz"]
|
||||
files = [
|
||||
"metric",
|
||||
"metric.txt",
|
||||
"metric.json",
|
||||
"metric.tsv",
|
||||
"metric.htsv",
|
||||
"metric.csv",
|
||||
"metric.hcsv",
|
||||
]
|
||||
xpaths = [None, None, "branch", "0,0", "0,branch", "0,0", "0,branch"]
|
||||
|
||||
def setUp(self):
|
||||
super(TestMetricsType, self).setUp()
|
||||
self.dvc.scm.commit("init")
|
||||
|
||||
for branch in self.branches:
|
||||
self.dvc.scm.checkout(branch, create_new=True)
|
||||
with open("metric", "w+") as fd:
|
||||
fd.write(branch)
|
||||
with open("metric.txt", "w+") as fd:
|
||||
fd.write(branch)
|
||||
with open("metric.json", "w+") as fd:
|
||||
json.dump({"branch": branch}, fd)
|
||||
with open("metric.csv", "w+") as fd:
|
||||
fd.write(branch)
|
||||
with open("metric.hcsv", "w+") as fd:
|
||||
fd.write("branch\n")
|
||||
fd.write(branch)
|
||||
with open("metric.tsv", "w+") as fd:
|
||||
fd.write(branch)
|
||||
with open("metric.htsv", "w+") as fd:
|
||||
fd.write("branch\n")
|
||||
fd.write(branch)
|
||||
self.dvc.run(metrics_no_cache=self.files, overwrite=True)
|
||||
self.dvc.scm.add(self.files + ["metric.dvc"])
|
||||
self.dvc.scm.commit("metric")
|
||||
|
||||
self.dvc.scm.checkout("master")
|
||||
|
||||
def test_show(self):
|
||||
for file_name, xpath in zip(self.files, self.xpaths):
|
||||
self._do_show(file_name, xpath)
|
||||
|
||||
def _do_show(self, file_name, xpath):
|
||||
ret = self.dvc.metrics.show(file_name, xpath=xpath, all_branches=True)
|
||||
self.assertEqual(len(ret), 3)
|
||||
for branch in self.branches:
|
||||
if isinstance(ret[branch][file_name], list):
|
||||
self.assertSequenceEqual(ret[branch][file_name], [branch])
|
||||
else:
|
||||
self.assertSequenceEqual(ret[branch][file_name], branch)
|
||||
@@ -0,0 +1,188 @@
|
||||
import os
|
||||
|
||||
from dvc.main import main
|
||||
from dvc.exceptions import DvcException, MoveNotDataSourceError
|
||||
from dvc.stage import Stage
|
||||
from dvc.utils import load_stage_file
|
||||
|
||||
from tests.basic_env import TestDvc
|
||||
from tests.func.test_repro import TestRepro
|
||||
from tests.utils import cd
|
||||
|
||||
|
||||
class TestMove(TestDvc):
|
||||
def test(self):
|
||||
dst = self.FOO + "1"
|
||||
self.dvc.add(self.FOO)
|
||||
self.dvc.move(self.FOO, dst)
|
||||
|
||||
self.assertFalse(os.path.isfile(self.FOO))
|
||||
self.assertTrue(os.path.isfile(dst))
|
||||
|
||||
|
||||
class TestMoveNonExistentFile(TestDvc):
|
||||
def test(self):
|
||||
with self.assertRaises(DvcException):
|
||||
self.dvc.move("non_existent_file", "dst")
|
||||
|
||||
|
||||
class TestMoveDirectory(TestDvc):
|
||||
def test(self):
|
||||
dst = "dst"
|
||||
stages = self.dvc.add(self.DATA_DIR)
|
||||
self.assertEqual(len(stages), 1)
|
||||
self.assertTrue(stages[0] is not None)
|
||||
self.dvc.move(self.DATA_DIR, dst)
|
||||
self.assertFalse(os.path.exists(self.DATA_DIR))
|
||||
self.assertTrue(os.path.exists(dst))
|
||||
|
||||
|
||||
class TestCmdMove(TestDvc):
|
||||
def test(self):
|
||||
stages = self.dvc.add(self.FOO)
|
||||
self.assertEqual(len(stages), 1)
|
||||
self.assertTrue(stages[0] is not None)
|
||||
|
||||
ret = main(["move", self.FOO, self.FOO + "1"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["move", "non-existing-file", "dst"])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
|
||||
class TestMoveNotDataSource(TestRepro):
|
||||
def test(self):
|
||||
from dvc.repo import Repo as DvcRepo
|
||||
|
||||
self.dvc = DvcRepo(self._root_dir)
|
||||
with self.assertRaises(MoveNotDataSourceError):
|
||||
self.dvc.move(self.file1, "dst")
|
||||
|
||||
ret = main(["move", self.file1, "dst"])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
|
||||
class TestMoveFileWithExtension(TestDvc):
|
||||
def test(self):
|
||||
with open(os.path.join(self.dvc.root_dir, "file.csv"), "w") as fd:
|
||||
fd.write("1,2,3\n")
|
||||
|
||||
self.dvc.add("file.csv")
|
||||
|
||||
self.assertTrue(os.path.exists("file.csv"))
|
||||
self.assertTrue(os.path.exists("file.csv.dvc"))
|
||||
|
||||
ret = main(["move", "file.csv", "other_name.csv"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertFalse(os.path.exists("file.csv"))
|
||||
self.assertFalse(os.path.exists("file.csv.dvc"))
|
||||
self.assertTrue(os.path.exists("other_name.csv"))
|
||||
self.assertTrue(os.path.exists("other_name.csv.dvc"))
|
||||
|
||||
|
||||
class TestMoveFileToDirectory(TestDvc):
|
||||
def test(self):
|
||||
foo_dvc_file = self.FOO + Stage.STAGE_FILE_SUFFIX
|
||||
ret = main(["add", self.FOO])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertTrue(os.path.exists(foo_dvc_file))
|
||||
|
||||
new_foo_path = os.path.join(self.DATA_DIR, self.FOO)
|
||||
new_foo_dvc_path = new_foo_path + Stage.STAGE_FILE_SUFFIX
|
||||
ret = main(["move", self.FOO, new_foo_path])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertFalse(os.path.exists(self.FOO))
|
||||
self.assertFalse(os.path.exists(foo_dvc_file))
|
||||
self.assertTrue(os.path.exists(new_foo_path))
|
||||
self.assertTrue(os.path.exists(new_foo_dvc_path))
|
||||
|
||||
|
||||
class TestMoveFileToDirectoryWithoutSpecifiedTargetName(TestDvc):
|
||||
def test(self):
|
||||
foo_stage_file_path = self.FOO + Stage.STAGE_FILE_SUFFIX
|
||||
ret = main(["add", self.FOO])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertTrue(os.path.exists(foo_stage_file_path))
|
||||
|
||||
target_foo_path = os.path.join(self.DATA_DIR, self.FOO)
|
||||
target_foo_stage_file_path = target_foo_path + Stage.STAGE_FILE_SUFFIX
|
||||
|
||||
ret = main(["move", self.FOO, self.DATA_DIR])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertFalse(os.path.exists(self.FOO))
|
||||
self.assertFalse(os.path.exists(foo_stage_file_path))
|
||||
|
||||
self.assertTrue(os.path.exists(target_foo_path))
|
||||
self.assertTrue(os.path.exists(target_foo_stage_file_path))
|
||||
|
||||
new_stage = load_stage_file(target_foo_stage_file_path)
|
||||
self.assertEqual(self.FOO, new_stage["outs"][0]["path"])
|
||||
|
||||
|
||||
class TestMoveDirectoryShouldNotOverwriteExisting(TestDvc):
|
||||
def test(self):
|
||||
new_dir_name = "data_dir2"
|
||||
os.mkdir(new_dir_name)
|
||||
ret = main(["add", self.DATA_DIR])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["move", self.DATA_DIR, new_dir_name])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertFalse(os.path.exists(self.DATA_DIR))
|
||||
self.assertFalse(
|
||||
os.path.exists(self.DATA_DIR + Stage.STAGE_FILE_SUFFIX)
|
||||
)
|
||||
|
||||
self.assertTrue(os.path.exists(new_dir_name))
|
||||
self.assertTrue(os.path.isfile(new_dir_name + Stage.STAGE_FILE_SUFFIX))
|
||||
|
||||
self.assertEqual(os.listdir(new_dir_name), [self.DATA_DIR])
|
||||
|
||||
|
||||
class TestMoveFileBetweenDirectories(TestDvc):
|
||||
def test(self):
|
||||
data_stage_file = self.DATA + Stage.STAGE_FILE_SUFFIX
|
||||
ret = main(["add", self.DATA])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertTrue(os.path.exists(data_stage_file))
|
||||
|
||||
new_data_dir = "data_dir2"
|
||||
os.makedirs(new_data_dir)
|
||||
|
||||
ret = main(["move", self.DATA, new_data_dir])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
new_data_path = os.path.join(new_data_dir, os.path.basename(self.DATA))
|
||||
new_data_stage_file = new_data_path + Stage.STAGE_FILE_SUFFIX
|
||||
|
||||
self.assertFalse(os.path.exists(self.DATA))
|
||||
self.assertFalse(os.path.exists(data_stage_file))
|
||||
|
||||
self.assertTrue(os.path.exists(new_data_path))
|
||||
self.assertTrue(os.path.exists(new_data_stage_file))
|
||||
|
||||
new_stage_file = load_stage_file(new_data_stage_file)
|
||||
self.assertEqual(
|
||||
os.path.basename(self.DATA), new_stage_file["outs"][0]["path"]
|
||||
)
|
||||
|
||||
|
||||
class TestMoveFileInsideDirectory(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["add", self.DATA])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
with cd(self.DATA_DIR):
|
||||
ret = main(["move", os.path.basename(self.DATA), "data.txt"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertFalse(os.path.exists(self.DATA))
|
||||
|
||||
data_fullpath = os.path.join(self.DATA_DIR, "data.txt")
|
||||
dvc_fullpath = os.path.join(self.DATA_DIR, "data.txt.dvc")
|
||||
self.assertTrue(os.path.exists(data_fullpath))
|
||||
self.assertTrue(os.path.exists(dvc_fullpath))
|
||||
@@ -0,0 +1,30 @@
|
||||
from dvc.stage import Stage
|
||||
from dvc.output import _get, OUTS_MAP
|
||||
|
||||
from tests.basic_env import TestDvc
|
||||
|
||||
|
||||
class TestOutScheme(TestDvc):
|
||||
MAP = OUTS_MAP
|
||||
|
||||
TESTS = {
|
||||
"s3://bucket/path": "s3",
|
||||
"gs://bucket/path": "gs",
|
||||
"ssh://example.com:/dir/path": "ssh",
|
||||
"hdfs://example.com/dir/path": "hdfs",
|
||||
"path/to/file": "local",
|
||||
"path\\to\\file": "local",
|
||||
"file": "local",
|
||||
"./file": "local",
|
||||
".\\file": "local",
|
||||
"../file": "local",
|
||||
"..\\file": "local",
|
||||
"unknown://path": "local",
|
||||
}
|
||||
|
||||
def _get(self, path):
|
||||
return _get(Stage(self.dvc), path, None, None, None, None)
|
||||
|
||||
def test(self):
|
||||
for path, cls in self.TESTS.items():
|
||||
self.assertIsInstance(self._get(path), self.MAP[cls])
|
||||
@@ -0,0 +1,291 @@
|
||||
import logging
|
||||
|
||||
from dvc.main import main
|
||||
|
||||
from tests.basic_env import TestDvc
|
||||
from tests.func.test_repro import TestRepro, TestReproChangedDeepData
|
||||
import os
|
||||
|
||||
|
||||
class TestPipelineShowSingle(TestDvc):
|
||||
def setUp(self):
|
||||
super(TestPipelineShowSingle, self).setUp()
|
||||
self.stage = "foo.dvc"
|
||||
self.dotFile = "graph.dot"
|
||||
ret = main(["add", self.FOO])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test(self):
|
||||
ret = main(["pipeline", "show", self.stage])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_commands(self):
|
||||
ret = main(["pipeline", "show", self.stage, "--commands"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_outs(self):
|
||||
ret = main(["pipeline", "show", self.stage, "--outs"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_ascii(self):
|
||||
ret = main(["pipeline", "show", "--ascii", self.stage])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_dot(self):
|
||||
ret = main(["pipeline", "show", "--dot", self.dotFile, self.stage])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertTrue(os.path.isfile(self.dotFile))
|
||||
|
||||
def test_tree(self):
|
||||
ret = main(["pipeline", "show", "--tree", self.stage])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_ascii_commands(self):
|
||||
ret = main(["pipeline", "show", "--ascii", self.stage, "--commands"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_ascii_outs(self):
|
||||
ret = main(["pipeline", "show", "--ascii", self.stage, "--outs"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_dot_commands(self):
|
||||
ret = main(
|
||||
[
|
||||
"pipeline",
|
||||
"show",
|
||||
"--dot",
|
||||
self.dotFile,
|
||||
self.stage,
|
||||
"--commands",
|
||||
]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertTrue(os.path.isfile(self.dotFile))
|
||||
|
||||
def test_dot_outs(self):
|
||||
ret = main(
|
||||
["pipeline", "show", "--dot", self.dotFile, self.stage, "--outs"]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertTrue(os.path.isfile(self.dotFile))
|
||||
|
||||
def test_not_dvc_file(self):
|
||||
ret = main(["pipeline", "show", self.FOO])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
def test_non_existing(self):
|
||||
ret = main(["pipeline", "show", "non-existing"])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
|
||||
class TestPipelineShow(TestRepro):
|
||||
def setUp(self):
|
||||
super(TestPipelineShow, self).setUp()
|
||||
self.dotFile = "graph.dot"
|
||||
|
||||
def test(self):
|
||||
ret = main(["pipeline", "show", self.file1_stage])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_commands(self):
|
||||
ret = main(["pipeline", "show", self.file1_stage, "--commands"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_outs(self):
|
||||
ret = main(["pipeline", "show", self.file1_stage, "--outs"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_ascii(self):
|
||||
ret = main(["pipeline", "show", "--ascii", self.file1_stage])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_dot(self):
|
||||
ret = main(
|
||||
["pipeline", "show", "--dot", self.dotFile, self.file1_stage]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertTrue(os.path.isfile(self.dotFile))
|
||||
|
||||
def test_ascii_commands(self):
|
||||
ret = main(
|
||||
["pipeline", "show", "--ascii", self.file1_stage, "--commands"]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_ascii_outs(self):
|
||||
ret = main(["pipeline", "show", "--ascii", self.file1_stage, "--outs"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_dot_commands(self):
|
||||
ret = main(
|
||||
[
|
||||
"pipeline",
|
||||
"show",
|
||||
"--dot",
|
||||
self.dotFile,
|
||||
self.file1_stage,
|
||||
"--commands",
|
||||
]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertTrue(os.path.isfile(self.dotFile))
|
||||
|
||||
def test_dot_outs(self):
|
||||
ret = main(
|
||||
[
|
||||
"pipeline",
|
||||
"show",
|
||||
"--dot",
|
||||
self.dotFile,
|
||||
self.file1_stage,
|
||||
"--outs",
|
||||
]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertTrue(os.path.isfile(self.dotFile))
|
||||
|
||||
def test_not_dvc_file(self):
|
||||
ret = main(["pipeline", "show", self.file1])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
def test_non_existing(self):
|
||||
ret = main(["pipeline", "show", "non-existing"])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
def test_print_locked_stages(self):
|
||||
self.dvc.add("foo")
|
||||
self.dvc.add("bar")
|
||||
self.dvc.lock_stage("foo.dvc")
|
||||
|
||||
self._caplog.clear()
|
||||
with self._caplog.at_level(logging.INFO, logger="dvc"):
|
||||
ret = main(["pipeline", "show", "foo.dvc", "--locked"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
assert "foo.dvc" in self._caplog.text
|
||||
assert "bar.dvc" not in self._caplog.text
|
||||
|
||||
|
||||
class TestPipelineShowDeep(TestReproChangedDeepData):
|
||||
def setUp(self):
|
||||
super(TestPipelineShowDeep, self).setUp()
|
||||
self.dotFile = "graph.dot"
|
||||
|
||||
def test(self):
|
||||
ret = main(["pipeline", "show", self.file1_stage])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_commands(self):
|
||||
ret = main(["pipeline", "show", self.file1_stage, "--commands"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_outs(self):
|
||||
ret = main(["pipeline", "show", self.file1_stage, "--outs"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_ascii(self):
|
||||
ret = main(["pipeline", "show", "--ascii", self.file1_stage])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_dot(self):
|
||||
ret = main(
|
||||
["pipeline", "show", "--dot", self.dotFile, self.file1_stage]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertTrue(os.path.isfile(self.dotFile))
|
||||
|
||||
def test_ascii_commands(self):
|
||||
ret = main(
|
||||
["pipeline", "show", "--ascii", self.file1_stage, "--commands"]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_ascii_outs(self):
|
||||
ret = main(["pipeline", "show", "--ascii", self.file1_stage, "--outs"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_dot_commands(self):
|
||||
ret = main(
|
||||
[
|
||||
"pipeline",
|
||||
"show",
|
||||
"--dot",
|
||||
self.dotFile,
|
||||
self.file1_stage,
|
||||
"--commands",
|
||||
]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertTrue(os.path.isfile(self.dotFile))
|
||||
|
||||
def test_dot_outs(self):
|
||||
ret = main(
|
||||
[
|
||||
"pipeline",
|
||||
"show",
|
||||
"--dot",
|
||||
self.dotFile,
|
||||
self.file1_stage,
|
||||
"--outs",
|
||||
]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertTrue(os.path.isfile(self.dotFile))
|
||||
|
||||
def test_not_dvc_file(self):
|
||||
ret = main(["pipeline", "show", self.file1])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
def test_non_existing(self):
|
||||
ret = main(["pipeline", "show", "non-existing"])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
|
||||
class TestPipelineListEmpty(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["pipeline", "list"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
|
||||
class TestPipelineListSingle(TestPipelineShowDeep):
|
||||
def test(self):
|
||||
ret = main(["pipeline", "list"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
|
||||
class TestDvcRepoPipeline(TestDvc):
|
||||
def test_no_stages(self):
|
||||
pipelines = self.dvc.pipelines()
|
||||
self.assertEqual(len(pipelines), 0)
|
||||
|
||||
def one_pipeline(self):
|
||||
self.dvc.add("foo")
|
||||
self.dvc.run(deps=["foo"], outs=["bar"], cmd="")
|
||||
self.dvc.run(deps=["bar"], outs=["baz"], cmd="echo baz > baz")
|
||||
pipelines = self.dvc.pipelines()
|
||||
|
||||
self.assertEqual(len(pipelines), 1)
|
||||
self.assertEqual(pipelines[0].nodes, 3)
|
||||
self.assertEqual(pipelines[0].edges, 2)
|
||||
|
||||
def two_pipelines(self):
|
||||
self.dvc.add("foo")
|
||||
self.dvc.run(deps=["foo"], outs=["bar"], cmd="")
|
||||
self.dvc.run(deps=["bar"], outs=["baz"], cmd="echo baz > baz")
|
||||
|
||||
self.dvc.add("code.py")
|
||||
|
||||
pipelines = self.dvc.pipelines()
|
||||
|
||||
self.assertEqual(len(pipelines), 2)
|
||||
self.assertEqual(pipelines[0].nodes, 3)
|
||||
self.assertEqual(pipelines[0].edges, 2)
|
||||
self.assertEqual(pipelines[1].nodes, 1)
|
||||
self.assertEqual(pipelines[1].edges, 0)
|
||||
|
||||
def locked_stage(self):
|
||||
self.dvc.add("foo")
|
||||
self.dvc.lock_stage("foo.dvc")
|
||||
|
||||
pipelines = self.dvc.pipelines()
|
||||
self.assertEqual(len(pipelines), 0)
|
||||
@@ -0,0 +1,37 @@
|
||||
import os
|
||||
|
||||
from dvc.repo.pkg import Package, PackageParams
|
||||
from tests.basic_env import TestDvcPkg
|
||||
|
||||
|
||||
class TestPkgBasic(TestDvcPkg):
|
||||
NEW_PKG_DIR = "new_pkg"
|
||||
|
||||
def setUp(self):
|
||||
super(TestPkgBasic, self).setUp()
|
||||
|
||||
self.to_dir = os.path.join(self._root_dir, self.NEW_PKG_DIR)
|
||||
os.mkdir(self.to_dir)
|
||||
self.dvc.install_pkg(
|
||||
PackageParams(self.pkg_fixture._root_dir, self.to_dir)
|
||||
)
|
||||
|
||||
def test_installed_dirs_and_files(self):
|
||||
self.assertTrue(os.path.isdir(self.pkg_dir))
|
||||
|
||||
mod_dir_fullpath = os.path.join(self._root_dir, Package.MODULES_DIR)
|
||||
self.assertTrue(os.path.isdir(mod_dir_fullpath))
|
||||
|
||||
installed_pkg_dir = os.path.join(mod_dir_fullpath, self.GIT_PKG)
|
||||
for file in [self.FOO, self.BAR, self.CODE]:
|
||||
installed_file = os.path.join(installed_pkg_dir, file)
|
||||
self.assertTrue(os.path.isfile(installed_file))
|
||||
|
||||
def test_target_dir_checkout(self):
|
||||
pkg_data_file = os.path.join(self.to_dir, self.DATA)
|
||||
self.assertTrue(os.path.exists(pkg_data_file))
|
||||
self.assertTrue(os.path.isfile(pkg_data_file))
|
||||
|
||||
pkg_data_dir = os.path.join(self.to_dir, self.DATA_SUB_DIR)
|
||||
self.assertTrue(os.path.exists(pkg_data_dir))
|
||||
self.assertTrue(os.path.isdir(pkg_data_dir))
|
||||
@@ -0,0 +1,171 @@
|
||||
import os
|
||||
import configobj
|
||||
|
||||
from dvc.main import main
|
||||
from dvc.command.remote import CmdRemoteAdd
|
||||
from dvc.config import Config
|
||||
|
||||
from tests.basic_env import TestDvc
|
||||
from tests.func.test_data_cloud import get_local_storagepath
|
||||
|
||||
|
||||
class TestRemote(TestDvc):
|
||||
def test(self):
|
||||
remotes = ["a", "b", "c"]
|
||||
|
||||
self.assertEqual(main(["remote", "list"]), 0)
|
||||
self.assertNotEqual(main(["remote", "remove", remotes[0]]), 0)
|
||||
|
||||
for r in remotes:
|
||||
self.assertEqual(
|
||||
main(["remote", "add", "--default", r, "s3://bucket/name"]), 0
|
||||
)
|
||||
|
||||
self.assertEqual(main(["remote", "list"]), 0)
|
||||
|
||||
self.assertEqual(main(["remote", "remove", remotes[0]]), 0)
|
||||
self.assertEqual(
|
||||
main(["remote", "modify", remotes[0], "option", "value"]), 0
|
||||
)
|
||||
|
||||
self.assertEqual(main(["remote", "list"]), 0)
|
||||
|
||||
def test_failed_write(self):
|
||||
class A(object):
|
||||
system = False
|
||||
glob = False
|
||||
local = False
|
||||
name = "myremote"
|
||||
url = "s3://remote"
|
||||
unset = False
|
||||
|
||||
args = A()
|
||||
cmd = CmdRemoteAdd(args)
|
||||
|
||||
cmd.configobj.write = None
|
||||
ret = cmd.run()
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
def test_relative_path(self):
|
||||
dname = os.path.join("..", "path", "to", "dir")
|
||||
ret = main(["remote", "add", "mylocal", dname])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
# NOTE: we are in the repo's root and config is in .dvc/, so
|
||||
# dir path written to config should be just one level above.
|
||||
rel = os.path.join("..", dname)
|
||||
config = configobj.ConfigObj(self.dvc.config.config_file)
|
||||
self.assertEqual(config['remote "mylocal"']["url"], rel)
|
||||
|
||||
def test_overwrite(self):
|
||||
remote_name = "a"
|
||||
remote_url = "s3://bucket/name"
|
||||
self.assertEqual(main(["remote", "add", remote_name, remote_url]), 0)
|
||||
self.assertEqual(main(["remote", "add", remote_name, remote_url]), 1)
|
||||
self.assertEqual(
|
||||
main(["remote", "add", "-f", remote_name, remote_url]), 0
|
||||
)
|
||||
|
||||
def test_referencing_other_remotes(self):
|
||||
assert main(["remote", "add", "foo", "ssh://localhost/"]) == 0
|
||||
assert main(["remote", "add", "bar", "remote://foo/dvc-storage"]) == 0
|
||||
|
||||
config = configobj.ConfigObj(self.dvc.config.config_file)
|
||||
|
||||
assert config['remote "bar"']["url"] == "remote://foo/dvc-storage"
|
||||
|
||||
|
||||
class TestRemoteRemoveDefault(TestDvc):
|
||||
def test(self):
|
||||
remote = "mys3"
|
||||
self.assertEqual(
|
||||
main(["remote", "add", "--default", remote, "s3://bucket/name"]), 0
|
||||
)
|
||||
self.assertEqual(
|
||||
main(["remote", "modify", remote, "profile", "default"]), 0
|
||||
)
|
||||
self.assertEqual(main(["config", "--local", "core.remote", remote]), 0)
|
||||
|
||||
config = configobj.ConfigObj(
|
||||
os.path.join(self.dvc.dvc_dir, Config.CONFIG)
|
||||
)
|
||||
local_config = configobj.ConfigObj(
|
||||
os.path.join(self.dvc.dvc_dir, Config.CONFIG_LOCAL)
|
||||
)
|
||||
self.assertEqual(
|
||||
config[Config.SECTION_CORE][Config.SECTION_CORE_REMOTE], remote
|
||||
)
|
||||
self.assertEqual(
|
||||
local_config[Config.SECTION_CORE][Config.SECTION_CORE_REMOTE],
|
||||
remote,
|
||||
)
|
||||
|
||||
self.assertEqual(main(["remote", "remove", remote]), 0)
|
||||
config = configobj.ConfigObj(
|
||||
os.path.join(self.dvc.dvc_dir, Config.CONFIG)
|
||||
)
|
||||
local_config = configobj.ConfigObj(
|
||||
os.path.join(self.dvc.dvc_dir, Config.CONFIG_LOCAL)
|
||||
)
|
||||
section = Config.SECTION_REMOTE_FMT.format(remote)
|
||||
self.assertTrue(section not in config.keys())
|
||||
|
||||
core = config.get(Config.SECTION_CORE, None)
|
||||
if core is not None:
|
||||
self.assertTrue(Config.SECTION_CORE_REMOTE not in core.keys())
|
||||
|
||||
core = local_config.get(Config.SECTION_CORE, None)
|
||||
if core is not None:
|
||||
self.assertTrue(Config.SECTION_CORE_REMOTE not in core.keys())
|
||||
|
||||
|
||||
class TestRemoteRemove(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["config", "core.jobs", "1"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
remote = "mys3"
|
||||
ret = main(["remote", "add", remote, "s3://bucket/name"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["remote", "remove", remote])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
|
||||
class TestRemoteDefault(TestDvc):
|
||||
def test(self):
|
||||
remote = "mys3"
|
||||
ret = main(["remote", "add", "mys3", "s3://bucket/path"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["remote", "default", "mys3"])
|
||||
self.assertEqual(ret, 0)
|
||||
config_file = os.path.join(self.dvc.dvc_dir, Config.CONFIG)
|
||||
config = configobj.ConfigObj(config_file)
|
||||
default = config[Config.SECTION_CORE][Config.SECTION_CORE_REMOTE]
|
||||
self.assertEqual(default, remote)
|
||||
|
||||
ret = main(["remote", "default", "--unset"])
|
||||
self.assertEqual(ret, 0)
|
||||
config = configobj.ConfigObj(config_file)
|
||||
core = config.get(Config.SECTION_CORE)
|
||||
if core is not None:
|
||||
default = core.get(Config.SECTION_CORE_REMOTE)
|
||||
else:
|
||||
default = None
|
||||
self.assertEqual(default, None)
|
||||
|
||||
|
||||
class TestRemoteShouldHandleUppercaseRemoteName(TestDvc):
|
||||
upper_case_remote_name = "UPPERCASEREMOTE"
|
||||
|
||||
def test(self):
|
||||
remote_url = get_local_storagepath()
|
||||
ret = main(["remote", "add", self.upper_case_remote_name, remote_url])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["add", self.FOO])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["push", "-r", self.upper_case_remote_name])
|
||||
self.assertEqual(ret, 0)
|
||||
@@ -0,0 +1,97 @@
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from dvc.main import main
|
||||
from dvc.system import System
|
||||
from dvc.stage import Stage, StageFileDoesNotExistError
|
||||
from dvc.exceptions import DvcException
|
||||
|
||||
from tests.basic_env import TestDvc
|
||||
from mock import patch
|
||||
|
||||
|
||||
class TestRemove(TestDvc):
|
||||
def test(self):
|
||||
stages = self.dvc.add(self.FOO)
|
||||
self.assertEqual(len(stages), 1)
|
||||
stage = stages[0]
|
||||
self.assertTrue(stage is not None)
|
||||
stage_removed = self.dvc.remove(stage.path, outs_only=True)
|
||||
|
||||
self.assertIsInstance(stage_removed, Stage)
|
||||
self.assertEqual(stage.path, stage_removed.path)
|
||||
self.assertFalse(os.path.isfile(self.FOO))
|
||||
|
||||
stage_removed = self.dvc.remove(stage.path)
|
||||
self.assertIsInstance(stage_removed, Stage)
|
||||
self.assertEqual(stage.path, stage_removed.path)
|
||||
self.assertFalse(os.path.isfile(self.FOO))
|
||||
self.assertFalse(os.path.exists(stage.path))
|
||||
|
||||
|
||||
class TestRemoveNonExistentFile(TestDvc):
|
||||
def test(self):
|
||||
with self.assertRaises(StageFileDoesNotExistError):
|
||||
self.dvc.remove("non_existent_dvc_file")
|
||||
|
||||
|
||||
class TestRemoveBrokenSymlink(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["config", "cache.type", "symlink"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["add", self.FOO])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
shutil.rmtree(self.dvc.cache.local.cache_dir)
|
||||
|
||||
self.assertTrue(System.is_symlink(self.FOO))
|
||||
|
||||
ret = main(["remove", self.FOO + ".dvc"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertFalse(os.path.lexists(self.FOO))
|
||||
|
||||
|
||||
class TestRemoveDirectory(TestDvc):
|
||||
def test(self):
|
||||
stages = self.dvc.add(self.DATA_DIR)
|
||||
self.assertEqual(len(stages), 1)
|
||||
stage_add = stages[0]
|
||||
self.assertTrue(stage_add is not None)
|
||||
stage_removed = self.dvc.remove(stage_add.path)
|
||||
self.assertEqual(stage_add.path, stage_removed.path)
|
||||
self.assertFalse(os.path.exists(self.DATA_DIR))
|
||||
self.assertFalse(os.path.exists(stage_removed.path))
|
||||
|
||||
|
||||
class TestCmdRemove(TestDvc):
|
||||
def test(self):
|
||||
stages = self.dvc.add(self.FOO)
|
||||
self.assertEqual(len(stages), 1)
|
||||
stage = stages[0]
|
||||
self.assertTrue(stage is not None)
|
||||
ret = main(["remove", stage.path])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["remove", "non-existing-dvc-file"])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
|
||||
class TestRemovePurge(TestDvc):
|
||||
def test(self):
|
||||
dvcfile = self.dvc.add(self.FOO)[0].path
|
||||
ret = main(["remove", "--purge", "--force", dvcfile])
|
||||
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertFalse(os.path.exists(self.FOO))
|
||||
self.assertFalse(os.path.exists(dvcfile))
|
||||
|
||||
@patch("dvc.prompt.confirm", return_value=False)
|
||||
def test_force(self, mock_prompt):
|
||||
dvcfile = self.dvc.add(self.FOO)[0].path
|
||||
ret = main(["remove", "--purge", dvcfile])
|
||||
|
||||
mock_prompt.assert_called()
|
||||
self.assertEqual(ret, 1)
|
||||
self.assertRaises(DvcException)
|
||||
@@ -0,0 +1,72 @@
|
||||
from dvc.main import main
|
||||
from dvc.stage import Stage
|
||||
from tests.basic_env import TestDvc
|
||||
|
||||
from dvc.scm.git import GitTree
|
||||
from dvc.scm.tree import WorkingTree
|
||||
|
||||
|
||||
class TestCollect(TestDvc):
|
||||
def setUp(self):
|
||||
super(TestCollect, self).setUp()
|
||||
self.dvc.add(self.FOO)
|
||||
self.dvc.run(
|
||||
deps=[self.FOO],
|
||||
outs=[self.BAR],
|
||||
cmd="python code.py {} {}".format(self.FOO, self.BAR),
|
||||
)
|
||||
self.dvc.scm.add([".gitignore", self.FOO + ".dvc", self.BAR + ".dvc"])
|
||||
self.dvc.scm.commit("foo.dvc and bar.dvc")
|
||||
self.dvc.scm.checkout("new_branch", True)
|
||||
self.dvc.run(
|
||||
deps=[self.BAR],
|
||||
outs=["buzz"],
|
||||
cmd="python code.py {} {}".format(self.BAR, "buzz"),
|
||||
)
|
||||
self.dvc.scm.add([".gitignore", "buzz.dvc"])
|
||||
self.dvc.scm.commit("add buzz")
|
||||
self.dvc.scm.checkout("master")
|
||||
|
||||
def _check(self, branch, target, with_deps, expected):
|
||||
if branch:
|
||||
self.dvc.tree = GitTree(self.dvc.scm.git, branch)
|
||||
else:
|
||||
self.dvc.tree = WorkingTree()
|
||||
result = self.dvc.collect(target + ".dvc", with_deps=with_deps)
|
||||
self.assertEqual(
|
||||
[[j.rel_path for j in i.outs] for i in result], expected
|
||||
)
|
||||
return result
|
||||
|
||||
def test(self):
|
||||
self._check("", self.BAR, True, [[self.FOO], [self.BAR]])
|
||||
self._check("master", self.BAR, True, [[self.FOO], [self.BAR]])
|
||||
self._check(
|
||||
"new_branch", "buzz", True, [[self.FOO], [self.BAR], ["buzz"]]
|
||||
)
|
||||
result = self._check("new_branch", "buzz", False, [["buzz"]])
|
||||
self.assertEqual([i.rel_path for i in result[0].deps], ["bar"])
|
||||
|
||||
|
||||
class TestIgnore(TestDvc):
|
||||
def _stage_name(self, file):
|
||||
return file + Stage.STAGE_FILE_SUFFIX
|
||||
|
||||
def test_should_not_gather_stage_files_from_ignored_d(self):
|
||||
ret = main(["add", self.FOO, self.BAR, self.DATA, self.DATA_SUB])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
stages = self.dvc.stages()
|
||||
self.assertEqual(4, len(stages))
|
||||
|
||||
with open(".dvcignore", "w") as fobj:
|
||||
fobj.write("data_dir")
|
||||
|
||||
stages = self.dvc.stages()
|
||||
self.assertEqual(2, len(stages))
|
||||
|
||||
stagenames = [s.relpath for s in stages]
|
||||
self.assertIn(self._stage_name(self.FOO), stagenames)
|
||||
self.assertIn(self._stage_name(self.BAR), stagenames)
|
||||
self.assertNotIn(self._stage_name(self.DATA), stagenames)
|
||||
self.assertNotIn(self._stage_name(self.DATA_SUB), stagenames)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,953 @@
|
||||
import os
|
||||
import uuid
|
||||
|
||||
import mock
|
||||
import shutil
|
||||
import filecmp
|
||||
import subprocess
|
||||
import yaml
|
||||
|
||||
from dvc.main import main
|
||||
from dvc.output import OutputBase
|
||||
from dvc.repo import Repo as DvcRepo
|
||||
from dvc.utils import file_md5, load_stage_file
|
||||
from dvc.system import System
|
||||
from dvc.stage import Stage, StagePathNotFoundError, StagePathNotDirectoryError
|
||||
from dvc.stage import StageFileBadNameError, MissingDep
|
||||
from dvc.stage import StagePathOutsideError, StageFileAlreadyExistsError
|
||||
from dvc.exceptions import (
|
||||
OutputDuplicationError,
|
||||
CircularDependencyError,
|
||||
CyclicGraphError,
|
||||
ArgumentDuplicationError,
|
||||
StagePathAsOutputError,
|
||||
OverlappingOutputPathsError,
|
||||
)
|
||||
|
||||
from tests.basic_env import TestDvc
|
||||
|
||||
|
||||
class TestRun(TestDvc):
|
||||
def test(self):
|
||||
cmd = "python {} {} {}".format(self.CODE, self.FOO, "out")
|
||||
deps = [self.FOO, self.CODE]
|
||||
outs = [os.path.join(self.dvc.root_dir, "out")]
|
||||
outs_no_cache = []
|
||||
fname = "out.dvc"
|
||||
cwd = os.curdir
|
||||
|
||||
self.dvc.add(self.FOO)
|
||||
stage = self.dvc.run(
|
||||
cmd=cmd,
|
||||
deps=deps,
|
||||
outs=outs,
|
||||
outs_no_cache=outs_no_cache,
|
||||
fname=fname,
|
||||
cwd=cwd,
|
||||
)
|
||||
|
||||
self.assertTrue(filecmp.cmp(self.FOO, "out", shallow=False))
|
||||
self.assertTrue(os.path.isfile(stage.path))
|
||||
self.assertEqual(stage.cmd, cmd)
|
||||
self.assertEqual(len(stage.deps), len(deps))
|
||||
self.assertEqual(len(stage.outs), len(outs + outs_no_cache))
|
||||
self.assertEqual(stage.outs[0].path, outs[0])
|
||||
self.assertEqual(stage.outs[0].checksum, file_md5(self.FOO)[0])
|
||||
self.assertTrue(stage.path, fname)
|
||||
|
||||
with self.assertRaises(OutputDuplicationError):
|
||||
self.dvc.run(
|
||||
cmd=cmd,
|
||||
deps=deps,
|
||||
outs=outs,
|
||||
outs_no_cache=outs_no_cache,
|
||||
fname="duplicate" + fname,
|
||||
cwd=cwd,
|
||||
)
|
||||
|
||||
|
||||
class TestRunEmpty(TestDvc):
|
||||
def test(self):
|
||||
self.dvc.run(
|
||||
cmd="",
|
||||
deps=[],
|
||||
outs=[],
|
||||
outs_no_cache=[],
|
||||
fname="empty.dvc",
|
||||
cwd=os.curdir,
|
||||
)
|
||||
|
||||
|
||||
class TestRunMissingDep(TestDvc):
|
||||
def test(self):
|
||||
with self.assertRaises(MissingDep):
|
||||
self.dvc.run(
|
||||
cmd="",
|
||||
deps=["non-existing-dep"],
|
||||
outs=[],
|
||||
outs_no_cache=[],
|
||||
fname="empty.dvc",
|
||||
cwd=os.curdir,
|
||||
)
|
||||
|
||||
|
||||
class TestRunBadStageFilename(TestDvc):
|
||||
def test(self):
|
||||
with self.assertRaises(StageFileBadNameError):
|
||||
self.dvc.run(
|
||||
cmd="",
|
||||
deps=[],
|
||||
outs=[],
|
||||
outs_no_cache=[],
|
||||
fname="empty",
|
||||
cwd=os.curdir,
|
||||
)
|
||||
|
||||
with self.assertRaises(StageFileBadNameError):
|
||||
self.dvc.run(
|
||||
cmd="",
|
||||
deps=[],
|
||||
outs=[],
|
||||
outs_no_cache=[],
|
||||
fname=os.path.join(self.DATA_DIR, "empty.dvc"),
|
||||
cwd=os.curdir,
|
||||
)
|
||||
|
||||
|
||||
class TestRunNoExec(TestDvc):
|
||||
def test(self):
|
||||
self.dvc.run(
|
||||
cmd="python {} {} {}".format(self.CODE, self.FOO, "out"),
|
||||
no_exec=True,
|
||||
)
|
||||
self.assertFalse(os.path.exists("out"))
|
||||
|
||||
|
||||
class TestRunCircularDependency(TestDvc):
|
||||
def test(self):
|
||||
with self.assertRaises(CircularDependencyError):
|
||||
self.dvc.run(
|
||||
cmd="",
|
||||
deps=[self.FOO],
|
||||
outs=[self.FOO],
|
||||
fname="circular-dependency.dvc",
|
||||
)
|
||||
|
||||
def test_outs_no_cache(self):
|
||||
with self.assertRaises(CircularDependencyError):
|
||||
self.dvc.run(
|
||||
cmd="",
|
||||
deps=[self.FOO],
|
||||
outs_no_cache=[self.FOO],
|
||||
fname="circular-dependency.dvc",
|
||||
)
|
||||
|
||||
def test_non_normalized_paths(self):
|
||||
with self.assertRaises(CircularDependencyError):
|
||||
self.dvc.run(
|
||||
cmd="",
|
||||
deps=["./foo"],
|
||||
outs=["foo"],
|
||||
fname="circular-dependency.dvc",
|
||||
)
|
||||
|
||||
def test_graph(self):
|
||||
self.dvc.run(
|
||||
deps=[self.FOO], outs=["bar.txt"], cmd="echo bar > bar.txt"
|
||||
)
|
||||
|
||||
self.dvc.run(
|
||||
deps=["bar.txt"], outs=["baz.txt"], cmd="echo baz > baz.txt"
|
||||
)
|
||||
|
||||
with self.assertRaises(CyclicGraphError):
|
||||
self.dvc.run(
|
||||
deps=["baz.txt"], outs=[self.FOO], cmd="echo baz > foo"
|
||||
)
|
||||
|
||||
|
||||
class TestRunDuplicatedArguments(TestDvc):
|
||||
def test(self):
|
||||
with self.assertRaises(ArgumentDuplicationError):
|
||||
self.dvc.run(
|
||||
cmd="",
|
||||
deps=[],
|
||||
outs=[self.FOO, self.FOO],
|
||||
fname="circular-dependency.dvc",
|
||||
)
|
||||
|
||||
def test_outs_no_cache(self):
|
||||
with self.assertRaises(ArgumentDuplicationError):
|
||||
self.dvc.run(
|
||||
cmd="",
|
||||
outs=[self.FOO],
|
||||
outs_no_cache=[self.FOO],
|
||||
fname="circular-dependency.dvc",
|
||||
)
|
||||
|
||||
def test_non_normalized_paths(self):
|
||||
with self.assertRaises(ArgumentDuplicationError):
|
||||
self.dvc.run(
|
||||
cmd="",
|
||||
deps=[],
|
||||
outs=["foo", "./foo"],
|
||||
fname="circular-dependency.dvc",
|
||||
)
|
||||
|
||||
|
||||
class TestRunStageInsideOutput(TestDvc):
|
||||
def test_cwd(self):
|
||||
self.dvc.run(cmd="", deps=[], outs=[self.DATA_DIR])
|
||||
|
||||
with self.assertRaises(StagePathAsOutputError):
|
||||
self.dvc.run(cmd="", cwd=self.DATA_DIR, fname="inside-cwd.dvc")
|
||||
|
||||
def test_file_name(self):
|
||||
self.dvc.run(cmd="", deps=[], outs=[self.DATA_DIR])
|
||||
|
||||
with self.assertRaises(StagePathAsOutputError):
|
||||
self.dvc.run(
|
||||
cmd="",
|
||||
outs=[self.FOO],
|
||||
fname=os.path.join(self.DATA_DIR, "inside-cwd.dvc"),
|
||||
)
|
||||
|
||||
|
||||
class TestRunBadCwd(TestDvc):
|
||||
def test(self):
|
||||
with self.assertRaises(StagePathOutsideError):
|
||||
self.dvc.run(cmd="", cwd=self.mkdtemp())
|
||||
|
||||
def test_same_prefix(self):
|
||||
with self.assertRaises(StagePathOutsideError):
|
||||
path = "{}-{}".format(self._root_dir, uuid.uuid4())
|
||||
os.mkdir(path)
|
||||
self.dvc.run(cmd="", cwd=path)
|
||||
|
||||
|
||||
class TestRunBadWdir(TestDvc):
|
||||
def test(self):
|
||||
with self.assertRaises(StagePathOutsideError):
|
||||
self.dvc.run(cmd="", wdir=self.mkdtemp())
|
||||
|
||||
def test_same_prefix(self):
|
||||
with self.assertRaises(StagePathOutsideError):
|
||||
path = "{}-{}".format(self._root_dir, uuid.uuid4())
|
||||
os.mkdir(path)
|
||||
self.dvc.run(cmd="", wdir=path)
|
||||
|
||||
def test_not_found(self):
|
||||
with self.assertRaises(StagePathNotFoundError):
|
||||
path = os.path.join(self._root_dir, str(uuid.uuid4()))
|
||||
self.dvc.run(cmd="", wdir=path)
|
||||
|
||||
def test_not_dir(self):
|
||||
with self.assertRaises(StagePathNotDirectoryError):
|
||||
path = os.path.join(self._root_dir, str(uuid.uuid4()))
|
||||
os.mkdir(path)
|
||||
path = os.path.join(path, str(uuid.uuid4()))
|
||||
open(path, "a").close()
|
||||
self.dvc.run(cmd="", wdir=path)
|
||||
|
||||
|
||||
class TestRunBadName(TestDvc):
|
||||
def test(self):
|
||||
with self.assertRaises(StagePathOutsideError):
|
||||
self.dvc.run(
|
||||
cmd="",
|
||||
fname=os.path.join(
|
||||
self.mkdtemp(), self.FOO + Stage.STAGE_FILE_SUFFIX
|
||||
),
|
||||
)
|
||||
|
||||
def test_same_prefix(self):
|
||||
with self.assertRaises(StagePathOutsideError):
|
||||
path = "{}-{}".format(self._root_dir, uuid.uuid4())
|
||||
os.mkdir(path)
|
||||
self.dvc.run(
|
||||
cmd="",
|
||||
fname=os.path.join(path, self.FOO + Stage.STAGE_FILE_SUFFIX),
|
||||
)
|
||||
|
||||
def test_not_found(self):
|
||||
with self.assertRaises(StagePathNotFoundError):
|
||||
path = os.path.join(self._root_dir, str(uuid.uuid4()))
|
||||
self.dvc.run(
|
||||
cmd="",
|
||||
fname=os.path.join(path, self.FOO + Stage.STAGE_FILE_SUFFIX),
|
||||
)
|
||||
|
||||
|
||||
class TestCmdRun(TestDvc):
|
||||
def test_run(self):
|
||||
ret = main(
|
||||
[
|
||||
"run",
|
||||
"-d",
|
||||
self.FOO,
|
||||
"-d",
|
||||
self.CODE,
|
||||
"-o",
|
||||
"out",
|
||||
"-f",
|
||||
"out.dvc",
|
||||
"python",
|
||||
self.CODE,
|
||||
self.FOO,
|
||||
"out",
|
||||
]
|
||||
)
|
||||
|
||||
stage = Stage.load(self.dvc, fname="out.dvc")
|
||||
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertTrue(os.path.isfile("out"))
|
||||
self.assertTrue(os.path.isfile("out.dvc"))
|
||||
self.assertTrue(filecmp.cmp(self.FOO, "out", shallow=False))
|
||||
self.assertEqual(stage.cmd, "python code.py foo out")
|
||||
|
||||
def test_run_args_from_cli(self):
|
||||
ret = main(["run", "echo", "foo"])
|
||||
stage = Stage.load(self.dvc, fname="Dvcfile")
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertEqual(stage.cmd, "echo foo")
|
||||
|
||||
def test_run_bad_command(self):
|
||||
ret = main(["run", "non-existing-command"])
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
def test_run_args_with_spaces(self):
|
||||
ret = main(["run", "echo", "foo bar"])
|
||||
stage = Stage.load(self.dvc, fname="Dvcfile")
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertEqual(stage.cmd, 'echo "foo bar"')
|
||||
|
||||
@mock.patch.object(subprocess, "Popen", side_effect=KeyboardInterrupt)
|
||||
def test_keyboard_interrupt(self, _):
|
||||
ret = main(["run", "mycmd"])
|
||||
self.assertEqual(ret, 252)
|
||||
|
||||
|
||||
class TestRunRemoveOuts(TestDvc):
|
||||
def test(self):
|
||||
with open(self.CODE, "w+") as fobj:
|
||||
fobj.write("import sys\n")
|
||||
fobj.write("import os\n")
|
||||
fobj.write("if os.path.exists(sys.argv[1]):\n")
|
||||
fobj.write(" sys.exit(1)\n")
|
||||
fobj.write("open(sys.argv[1], 'w+').close()\n")
|
||||
|
||||
ret = main(
|
||||
[
|
||||
"run",
|
||||
"--remove-outs",
|
||||
"-d",
|
||||
self.CODE,
|
||||
"-o",
|
||||
self.FOO,
|
||||
"python",
|
||||
self.CODE,
|
||||
self.FOO,
|
||||
]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
|
||||
class TestRunUnprotectOutsCopy(TestDvc):
|
||||
def test(self):
|
||||
with open(self.CODE, "w+") as fobj:
|
||||
fobj.write("import sys\n")
|
||||
fobj.write("with open(sys.argv[1], 'a+') as fobj:\n")
|
||||
fobj.write(" fobj.write('foo')\n")
|
||||
|
||||
ret = main(["config", "cache.protected", "true"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["config", "cache.type", "copy"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(
|
||||
[
|
||||
"run",
|
||||
"-d",
|
||||
self.CODE,
|
||||
"-o",
|
||||
self.FOO,
|
||||
"python",
|
||||
self.CODE,
|
||||
self.FOO,
|
||||
]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertFalse(os.access(self.FOO, os.W_OK))
|
||||
self.assertEqual(open(self.FOO, "r").read(), "foo")
|
||||
|
||||
ret = main(
|
||||
[
|
||||
"run",
|
||||
"--overwrite-dvcfile",
|
||||
"--ignore-build-cache",
|
||||
"-d",
|
||||
self.CODE,
|
||||
"-o",
|
||||
self.FOO,
|
||||
"python",
|
||||
self.CODE,
|
||||
self.FOO,
|
||||
]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertFalse(os.access(self.FOO, os.W_OK))
|
||||
self.assertEqual(open(self.FOO, "r").read(), "foo")
|
||||
|
||||
|
||||
class TestRunUnprotectOutsSymlink(TestDvc):
|
||||
def test(self):
|
||||
with open(self.CODE, "w+") as fobj:
|
||||
fobj.write("import sys\n")
|
||||
fobj.write("import os\n")
|
||||
fobj.write("with open(sys.argv[1], 'a+') as fobj:\n")
|
||||
fobj.write(" fobj.write('foo')\n")
|
||||
|
||||
ret = main(["config", "cache.protected", "true"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["config", "cache.type", "symlink"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertEqual(ret, 0)
|
||||
ret = main(
|
||||
[
|
||||
"run",
|
||||
"-d",
|
||||
self.CODE,
|
||||
"-o",
|
||||
self.FOO,
|
||||
"python",
|
||||
self.CODE,
|
||||
self.FOO,
|
||||
]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertFalse(os.access(self.FOO, os.W_OK))
|
||||
self.assertTrue(System.is_symlink(self.FOO))
|
||||
self.assertEqual(open(self.FOO, "r").read(), "foo")
|
||||
|
||||
ret = main(
|
||||
[
|
||||
"run",
|
||||
"--overwrite-dvcfile",
|
||||
"--ignore-build-cache",
|
||||
"-d",
|
||||
self.CODE,
|
||||
"-o",
|
||||
self.FOO,
|
||||
"python",
|
||||
self.CODE,
|
||||
self.FOO,
|
||||
]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertFalse(os.access(self.FOO, os.W_OK))
|
||||
self.assertTrue(System.is_symlink(self.FOO))
|
||||
self.assertEqual(open(self.FOO, "r").read(), "foo")
|
||||
|
||||
|
||||
class TestRunUnprotectOutsHardlink(TestDvc):
|
||||
def test(self):
|
||||
with open(self.CODE, "w+") as fobj:
|
||||
fobj.write("import sys\n")
|
||||
fobj.write("import os\n")
|
||||
fobj.write("with open(sys.argv[1], 'a+') as fobj:\n")
|
||||
fobj.write(" fobj.write('foo')\n")
|
||||
|
||||
ret = main(["config", "cache.protected", "true"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["config", "cache.type", "hardlink"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertEqual(ret, 0)
|
||||
ret = main(
|
||||
[
|
||||
"run",
|
||||
"-d",
|
||||
self.CODE,
|
||||
"-o",
|
||||
self.FOO,
|
||||
"python",
|
||||
self.CODE,
|
||||
self.FOO,
|
||||
]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertFalse(os.access(self.FOO, os.W_OK))
|
||||
self.assertTrue(System.is_hardlink(self.FOO))
|
||||
self.assertEqual(open(self.FOO, "r").read(), "foo")
|
||||
|
||||
ret = main(
|
||||
[
|
||||
"run",
|
||||
"--overwrite-dvcfile",
|
||||
"--ignore-build-cache",
|
||||
"-d",
|
||||
self.CODE,
|
||||
"-o",
|
||||
self.FOO,
|
||||
"python",
|
||||
self.CODE,
|
||||
self.FOO,
|
||||
]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertFalse(os.access(self.FOO, os.W_OK))
|
||||
self.assertTrue(System.is_hardlink(self.FOO))
|
||||
self.assertEqual(open(self.FOO, "r").read(), "foo")
|
||||
|
||||
|
||||
class TestCmdRunOverwrite(TestDvc):
|
||||
def test(self):
|
||||
# NOTE: using sleep() is a workaround for filesystems
|
||||
# with low mtime resolution. We have to use mtime since
|
||||
# comparing mtime's is the only way to check that the stage
|
||||
# file didn't change(size and inode in the first test down
|
||||
# below don't change).
|
||||
import time
|
||||
|
||||
ret = main(
|
||||
[
|
||||
"run",
|
||||
"-d",
|
||||
self.FOO,
|
||||
"-d",
|
||||
self.CODE,
|
||||
"-o",
|
||||
"out",
|
||||
"-f",
|
||||
"out.dvc",
|
||||
"python",
|
||||
self.CODE,
|
||||
self.FOO,
|
||||
"out",
|
||||
]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
stage_mtime = os.path.getmtime("out.dvc")
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
ret = main(
|
||||
[
|
||||
"run",
|
||||
"-d",
|
||||
self.FOO,
|
||||
"-d",
|
||||
self.CODE,
|
||||
"-o",
|
||||
"out",
|
||||
"-f",
|
||||
"out.dvc",
|
||||
"python",
|
||||
self.CODE,
|
||||
self.FOO,
|
||||
"out",
|
||||
]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
# NOTE: check that dvcfile was NOT overwritten
|
||||
self.assertEqual(stage_mtime, os.path.getmtime("out.dvc"))
|
||||
stage_mtime = os.path.getmtime("out.dvc")
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
ret = main(
|
||||
[
|
||||
"run",
|
||||
"-d",
|
||||
self.FOO,
|
||||
"-d",
|
||||
self.CODE,
|
||||
"--overwrite-dvcfile",
|
||||
"--ignore-build-cache",
|
||||
"-o",
|
||||
"out",
|
||||
"-f",
|
||||
"out.dvc",
|
||||
"python",
|
||||
self.CODE,
|
||||
self.FOO,
|
||||
"out",
|
||||
]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
# NOTE: check that dvcfile was overwritten
|
||||
self.assertNotEqual(stage_mtime, os.path.getmtime("out.dvc"))
|
||||
stage_mtime = os.path.getmtime("out.dvc")
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
ret = main(
|
||||
["run", "--overwrite-dvcfile", "-f", "out.dvc", "-d", self.BAR]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
# NOTE: check that dvcfile was overwritten
|
||||
self.assertNotEqual(stage_mtime, os.path.getmtime("out.dvc"))
|
||||
|
||||
|
||||
class TestCmdRunCliMetrics(TestDvc):
|
||||
def test_cached(self):
|
||||
ret = main(["run", "-m", "metrics.txt", "echo test > metrics.txt"])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertEqual(open("metrics.txt", "r").read().rstrip(), "test")
|
||||
|
||||
def test_not_cached(self):
|
||||
ret = main(["run", "-M", "metrics.txt", "echo test > metrics.txt"])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertEqual(open("metrics.txt", "r").read().rstrip(), "test")
|
||||
|
||||
|
||||
class TestCmdRunWorkingDirectory(TestDvc):
|
||||
def test_default_wdir_is_written(self):
|
||||
stage = self.dvc.run(
|
||||
cmd="echo test > {}".format(self.FOO), outs=[self.FOO], wdir="."
|
||||
)
|
||||
with open(stage.relpath, "r") as fobj:
|
||||
d = yaml.safe_load(fobj)
|
||||
self.assertEqual(d[Stage.PARAM_WDIR], ".")
|
||||
|
||||
stage = self.dvc.run(
|
||||
cmd="echo test > {}".format(self.BAR), outs=[self.BAR]
|
||||
)
|
||||
with open(stage.relpath, "r") as fobj:
|
||||
d = yaml.safe_load(fobj)
|
||||
self.assertEqual(d[Stage.PARAM_WDIR], ".")
|
||||
|
||||
def test_fname_changes_path_and_wdir(self):
|
||||
dname = "dir"
|
||||
os.mkdir(os.path.join(self._root_dir, dname))
|
||||
foo = os.path.join(dname, self.FOO)
|
||||
fname = os.path.join(dname, "stage" + Stage.STAGE_FILE_SUFFIX)
|
||||
stage = self.dvc.run(
|
||||
cmd="echo test > {}".format(foo), outs=[foo], fname=fname
|
||||
)
|
||||
self.assertEqual(stage.wdir, os.path.realpath(self._root_dir))
|
||||
self.assertEqual(
|
||||
stage.path, os.path.join(os.path.realpath(self._root_dir), fname)
|
||||
)
|
||||
|
||||
# Check that it is dumped properly (relative to fname)
|
||||
with open(stage.relpath, "r") as fobj:
|
||||
d = yaml.safe_load(fobj)
|
||||
self.assertEqual(d[Stage.PARAM_WDIR], "..")
|
||||
|
||||
def test_cwd_is_ignored(self):
|
||||
dname = "dir"
|
||||
os.mkdir(os.path.join(self._root_dir, dname))
|
||||
foo = os.path.join(dname, self.FOO)
|
||||
fname = os.path.join("stage" + Stage.STAGE_FILE_SUFFIX)
|
||||
stage = self.dvc.run(
|
||||
cmd="echo test > {}".format(foo),
|
||||
outs=[foo],
|
||||
cwd=dname,
|
||||
wdir=".",
|
||||
fname=fname,
|
||||
)
|
||||
self.assertEqual(stage.wdir, os.path.realpath(self._root_dir))
|
||||
self.assertEqual(
|
||||
stage.path, os.path.join(os.path.realpath(self._root_dir), fname)
|
||||
)
|
||||
|
||||
|
||||
class TestRunDeterministicBase(TestDvc):
|
||||
def setUp(self):
|
||||
super(TestRunDeterministicBase, self).setUp()
|
||||
self.out_file = "out"
|
||||
self.stage_file = self.out_file + ".dvc"
|
||||
self.cmd = "python {} {} {}".format(self.CODE, self.FOO, self.out_file)
|
||||
self.deps = [self.FOO, self.CODE]
|
||||
self.outs = [self.out_file]
|
||||
self.overwrite = False
|
||||
self.ignore_build_cache = False
|
||||
|
||||
self._run()
|
||||
|
||||
def _run(self):
|
||||
self.stage = self.dvc.run(
|
||||
cmd=self.cmd,
|
||||
fname=self.stage_file,
|
||||
overwrite=self.overwrite,
|
||||
ignore_build_cache=self.ignore_build_cache,
|
||||
deps=self.deps,
|
||||
outs=self.outs,
|
||||
)
|
||||
|
||||
|
||||
class TestRunDeterministic(TestRunDeterministicBase):
|
||||
def test(self):
|
||||
self._run()
|
||||
|
||||
|
||||
class TestRunDeterministicOverwrite(TestRunDeterministicBase):
|
||||
def test(self):
|
||||
self.overwrite = True
|
||||
self.ignore_build_cache = True
|
||||
self._run()
|
||||
|
||||
|
||||
class TestRunDeterministicCallback(TestRunDeterministicBase):
|
||||
def test(self):
|
||||
self.stage.remove()
|
||||
self.deps = []
|
||||
self._run()
|
||||
self._run()
|
||||
|
||||
|
||||
class TestRunDeterministicChangedDep(TestRunDeterministicBase):
|
||||
def test(self):
|
||||
os.unlink(self.FOO)
|
||||
shutil.copy(self.BAR, self.FOO)
|
||||
with self.assertRaises(StageFileAlreadyExistsError):
|
||||
self._run()
|
||||
|
||||
|
||||
class TestRunDeterministicChangedDepsList(TestRunDeterministicBase):
|
||||
def test(self):
|
||||
self.deps = [self.BAR, self.CODE]
|
||||
with self.assertRaises(StageFileAlreadyExistsError):
|
||||
self._run()
|
||||
|
||||
|
||||
class TestRunDeterministicNewDep(TestRunDeterministicBase):
|
||||
def test(self):
|
||||
self.deps = [self.FOO, self.BAR, self.CODE]
|
||||
with self.assertRaises(StageFileAlreadyExistsError):
|
||||
self._run()
|
||||
|
||||
|
||||
class TestRunDeterministicRemoveDep(TestRunDeterministicBase):
|
||||
def test(self):
|
||||
self.deps = [self.CODE]
|
||||
with self.assertRaises(StageFileAlreadyExistsError):
|
||||
self._run()
|
||||
|
||||
|
||||
class TestRunDeterministicChangedOut(TestRunDeterministicBase):
|
||||
def test(self):
|
||||
os.unlink(self.out_file)
|
||||
self.out_file_mtime = None
|
||||
with self.assertRaises(StageFileAlreadyExistsError):
|
||||
self._run()
|
||||
|
||||
|
||||
class TestRunDeterministicChangedCmd(TestRunDeterministicBase):
|
||||
def test(self):
|
||||
self.cmd += " arg"
|
||||
with self.assertRaises(StageFileAlreadyExistsError):
|
||||
self._run()
|
||||
|
||||
|
||||
class TestRunCommit(TestDvc):
|
||||
def test(self):
|
||||
fname = "test"
|
||||
ret = main(
|
||||
["run", "-o", fname, "--no-commit", "echo", "test", ">", fname]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertTrue(os.path.isfile(fname))
|
||||
self.assertEqual(len(os.listdir(self.dvc.cache.local.cache_dir)), 0)
|
||||
|
||||
ret = main(["commit", fname + ".dvc"])
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertTrue(os.path.isfile(fname))
|
||||
self.assertEqual(len(os.listdir(self.dvc.cache.local.cache_dir)), 1)
|
||||
|
||||
|
||||
class TestRunPersist(TestDvc):
|
||||
@property
|
||||
def outs_command(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def _test(self):
|
||||
file = "file.txt"
|
||||
file_content = "content"
|
||||
stage_file = file + Stage.STAGE_FILE_SUFFIX
|
||||
|
||||
self.run_command(file, file_content)
|
||||
self.stage_should_contain_persist_flag(stage_file)
|
||||
|
||||
self.should_append_upon_repro(file, stage_file)
|
||||
|
||||
self.should_remove_persistent_outs(file, stage_file)
|
||||
|
||||
def run_command(self, file, file_content):
|
||||
ret = main(
|
||||
[
|
||||
"run",
|
||||
self.outs_command,
|
||||
file,
|
||||
"echo {} >> {}".format(file_content, file),
|
||||
]
|
||||
)
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
def stage_should_contain_persist_flag(self, stage_file):
|
||||
stage_file_content = load_stage_file(stage_file)
|
||||
self.assertEqual(
|
||||
True, stage_file_content["outs"][0][OutputBase.PARAM_PERSIST]
|
||||
)
|
||||
|
||||
def should_append_upon_repro(self, file, stage_file):
|
||||
ret = main(["repro", stage_file])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
with open(file, "r") as fobj:
|
||||
lines = fobj.readlines()
|
||||
self.assertEqual(2, len(lines))
|
||||
|
||||
def should_remove_persistent_outs(self, file, stage_file):
|
||||
ret = main(["remove", stage_file])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
self.assertFalse(os.path.exists(file))
|
||||
|
||||
|
||||
class TestRunPersistOuts(TestRunPersist):
|
||||
@property
|
||||
def outs_command(self):
|
||||
return "--outs-persist"
|
||||
|
||||
def test(self):
|
||||
self._test()
|
||||
|
||||
|
||||
class TestRunPersistOutsNoCache(TestRunPersist):
|
||||
@property
|
||||
def outs_command(self):
|
||||
return "--outs-persist-no-cache"
|
||||
|
||||
def test(self):
|
||||
self._test()
|
||||
|
||||
|
||||
class TestShouldRaiseOnOverlappingOutputPaths(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["add", self.DATA_DIR])
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
with self.assertRaises(OverlappingOutputPathsError) as err:
|
||||
self.dvc.run(
|
||||
outs=[self.DATA], cmd="echo data >> {}".format(self.DATA)
|
||||
)
|
||||
error_output = str(err.exception)
|
||||
|
||||
data_dir_stage = self.DATA_DIR + Stage.STAGE_FILE_SUFFIX
|
||||
data_stage = os.path.basename(self.DATA) + Stage.STAGE_FILE_SUFFIX
|
||||
|
||||
self.assertIn("Paths for outs:\n", error_output)
|
||||
self.assertIn(
|
||||
"\n'{}'('{}')\n".format(self.DATA_DIR, data_dir_stage),
|
||||
error_output,
|
||||
)
|
||||
self.assertIn(
|
||||
"\n'{}'('{}')\n".format(self.DATA, data_stage), error_output
|
||||
)
|
||||
self.assertIn(
|
||||
"\noverlap. To avoid unpredictable behaviour, rerun "
|
||||
"command with non overlapping outs paths.",
|
||||
error_output,
|
||||
)
|
||||
|
||||
|
||||
class TestRerunWithSameOutputs(TestDvc):
|
||||
def _read_content_only(self, path):
|
||||
with open(path, "r") as fobj:
|
||||
return [line.rstrip() for line in fobj]
|
||||
|
||||
@property
|
||||
def _outs_command(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def _run_twice_with_same_outputs(self):
|
||||
ret = main(
|
||||
[
|
||||
"run",
|
||||
"--outs",
|
||||
self.FOO,
|
||||
"echo {} > {}".format(self.FOO_CONTENTS, self.FOO),
|
||||
]
|
||||
)
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
output_file_content = self._read_content_only(self.FOO)
|
||||
self.assertEqual([self.FOO_CONTENTS], output_file_content)
|
||||
|
||||
ret = main(
|
||||
[
|
||||
"run",
|
||||
self._outs_command,
|
||||
self.FOO,
|
||||
"--overwrite-dvcfile",
|
||||
"echo {} >> {}".format(self.BAR_CONTENTS, self.FOO),
|
||||
]
|
||||
)
|
||||
self.assertEqual(0, ret)
|
||||
|
||||
|
||||
class TestNewRunShouldRemoveOutsOnNoPersist(TestRerunWithSameOutputs):
|
||||
def test(self):
|
||||
self._run_twice_with_same_outputs()
|
||||
|
||||
output_file_content = self._read_content_only(self.FOO)
|
||||
self.assertEqual([self.BAR_CONTENTS], output_file_content)
|
||||
|
||||
@property
|
||||
def _outs_command(self):
|
||||
return "--outs"
|
||||
|
||||
|
||||
class TestNewRunShouldNotRemoveOutsOnPersist(TestRerunWithSameOutputs):
|
||||
def test(self):
|
||||
self._run_twice_with_same_outputs()
|
||||
|
||||
output_file_content = self._read_content_only(self.FOO)
|
||||
self.assertEqual(
|
||||
[self.FOO_CONTENTS, self.BAR_CONTENTS], output_file_content
|
||||
)
|
||||
|
||||
@property
|
||||
def _outs_command(self):
|
||||
return "--outs-persist"
|
||||
|
||||
|
||||
class TestShouldNotCheckoutUponCorruptedLocalHardlinkCache(TestDvc):
|
||||
def setUp(self):
|
||||
super(
|
||||
TestShouldNotCheckoutUponCorruptedLocalHardlinkCache, self
|
||||
).setUp()
|
||||
ret = main(["config", "cache.type", "hardlink"])
|
||||
self.assertEqual(ret, 0)
|
||||
self.dvc = DvcRepo(".")
|
||||
|
||||
def test(self):
|
||||
cmd = "cp {} {}".format(self.FOO, self.BAR)
|
||||
stage = self.dvc.run(deps=[self.FOO], outs=[self.BAR], cmd=cmd)
|
||||
|
||||
with open(self.BAR, "w") as fd:
|
||||
fd.write("corrupting the output cache")
|
||||
|
||||
patch_checkout = mock.patch.object(
|
||||
stage.outs[0], "checkout", wraps=stage.outs[0].checkout
|
||||
)
|
||||
patch_run = mock.patch.object(stage, "_run", wraps=stage._run)
|
||||
|
||||
with self.dvc.state:
|
||||
with patch_checkout as mock_checkout:
|
||||
with patch_run as mock_run:
|
||||
stage.run()
|
||||
|
||||
mock_run.assert_called_once()
|
||||
mock_checkout.assert_not_called()
|
||||
@@ -0,0 +1,75 @@
|
||||
import uuid
|
||||
import posixpath
|
||||
|
||||
import boto3
|
||||
import pytest
|
||||
|
||||
from dvc.remote.s3 import RemoteS3
|
||||
from tests.func.test_data_cloud import TEST_AWS_REPO_BUCKET, _should_test_aws
|
||||
|
||||
|
||||
def _get_src_dst():
|
||||
prefix = str(uuid.uuid4())
|
||||
from_info = {
|
||||
"scheme": "s3",
|
||||
"bucket": TEST_AWS_REPO_BUCKET,
|
||||
"path": posixpath.join(prefix, "from"),
|
||||
}
|
||||
to_info = from_info.copy()
|
||||
to_info["path"] = posixpath.join(prefix, "to")
|
||||
return from_info, to_info
|
||||
|
||||
|
||||
def test_copy_singlepart_preserve_etag():
|
||||
from_info, to_info = _get_src_dst()
|
||||
|
||||
if not _should_test_aws():
|
||||
pytest.skip()
|
||||
|
||||
s3 = boto3.client("s3")
|
||||
s3.put_object(
|
||||
Bucket=from_info["bucket"], Key=from_info["path"], Body="data"
|
||||
)
|
||||
|
||||
RemoteS3._copy(s3, from_info, to_info)
|
||||
|
||||
|
||||
def _upload_multipart(s3, Bucket, Key):
|
||||
mpu = s3.create_multipart_upload(Bucket=Bucket, Key=Key)
|
||||
mpu_id = mpu["UploadId"]
|
||||
|
||||
parts = []
|
||||
n_parts = 10
|
||||
for i in range(1, n_parts + 1):
|
||||
# NOTE: Generation parts of variable size. Part size should be at
|
||||
# least 5MB:
|
||||
# https://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadComplete.html
|
||||
part_size = 10 * 1024 * 1024 + 2 ** i
|
||||
body = str(i) * part_size
|
||||
part = s3.upload_part(
|
||||
Bucket=Bucket,
|
||||
Key=Key,
|
||||
PartNumber=i,
|
||||
UploadId=mpu_id,
|
||||
Body=body,
|
||||
ContentLength=len(body),
|
||||
)
|
||||
parts.append({"PartNumber": i, "ETag": part["ETag"]})
|
||||
|
||||
s3.complete_multipart_upload(
|
||||
Bucket=Bucket,
|
||||
Key=Key,
|
||||
UploadId=mpu_id,
|
||||
MultipartUpload={"Parts": parts},
|
||||
)
|
||||
|
||||
|
||||
def test_copy_multipart_preserve_etag():
|
||||
from_info, to_info = _get_src_dst()
|
||||
|
||||
if not _should_test_aws():
|
||||
pytest.skip()
|
||||
|
||||
s3 = boto3.client("s3")
|
||||
_upload_multipart(s3, from_info["bucket"], from_info["path"])
|
||||
RemoteS3._copy(s3, from_info, to_info)
|
||||
@@ -0,0 +1,140 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
|
||||
from git import Repo
|
||||
|
||||
from dvc.utils.compat import str # noqa: F401
|
||||
from dvc.scm import SCM, NoSCM, Git
|
||||
from dvc.scm.base import FileNotInTargetSubdirError
|
||||
|
||||
from tests.basic_env import TestDir, TestGit, TestGitSubmodule
|
||||
from tests.utils import get_gitignore_content
|
||||
|
||||
|
||||
class TestSCM(TestDir):
|
||||
def test_none(self):
|
||||
self.assertIsInstance(SCM(self._root_dir), NoSCM)
|
||||
|
||||
def test_git(self):
|
||||
Repo.init(os.curdir)
|
||||
self.assertIsInstance(SCM(self._root_dir), Git)
|
||||
|
||||
|
||||
class TestSCMGit(TestGit):
|
||||
def test_is_repo(self):
|
||||
self.assertTrue(Git.is_repo(os.curdir))
|
||||
|
||||
def test_commit(self):
|
||||
G = Git(self._root_dir)
|
||||
G.add(["foo"])
|
||||
G.commit("add")
|
||||
self.assertIn("foo", self.git.git.ls_files())
|
||||
|
||||
def test_is_tracked(self):
|
||||
foo = os.path.abspath(self.FOO)
|
||||
G = Git(self._root_dir)
|
||||
G.add([self.FOO, self.UNICODE])
|
||||
self.assertTrue(G.is_tracked(foo))
|
||||
self.assertTrue(G.is_tracked(self.FOO))
|
||||
self.assertTrue(G.is_tracked(self.UNICODE))
|
||||
G.commit("add")
|
||||
self.assertTrue(G.is_tracked(foo))
|
||||
self.assertTrue(G.is_tracked(self.FOO))
|
||||
G.git.index.remove([self.FOO], working_tree=True)
|
||||
self.assertFalse(G.is_tracked(foo))
|
||||
self.assertFalse(G.is_tracked(self.FOO))
|
||||
self.assertFalse(G.is_tracked("not-existing-file"))
|
||||
|
||||
|
||||
class TestSCMGitSubmodule(TestGitSubmodule):
|
||||
def test_git_submodule(self):
|
||||
self.assertIsInstance(SCM(os.curdir), Git)
|
||||
|
||||
def test_is_submodule(self):
|
||||
self.assertTrue(Git.is_submodule(os.curdir))
|
||||
|
||||
def test_commit_in_submodule(self):
|
||||
G = Git(self._root_dir)
|
||||
G.add(["foo"])
|
||||
G.commit("add")
|
||||
self.assertTrue("foo" in self.git.git.ls_files())
|
||||
|
||||
|
||||
class TestIgnore(TestGit):
|
||||
def _count_gitignore(self):
|
||||
lines = get_gitignore_content()
|
||||
return len([i for i in lines if i == "/" + self.FOO])
|
||||
|
||||
def test_ignore(self):
|
||||
git = Git(self._root_dir)
|
||||
foo = os.path.join(self._root_dir, self.FOO)
|
||||
|
||||
git.ignore(foo)
|
||||
self.assertTrue(os.path.isfile(Git.GITIGNORE))
|
||||
self.assertEqual(self._count_gitignore(), 1)
|
||||
|
||||
git.ignore(foo)
|
||||
self.assertEqual(self._count_gitignore(), 1)
|
||||
|
||||
git.ignore_remove(foo)
|
||||
self.assertEqual(self._count_gitignore(), 0)
|
||||
|
||||
def test_get_gitignore(self):
|
||||
data_dir = os.path.join(self._root_dir, "file1")
|
||||
entry, gitignore = Git(self._root_dir)._get_gitignore(data_dir)
|
||||
self.assertEqual(entry, "/file1")
|
||||
self.assertEqual(
|
||||
gitignore, os.path.join(self._root_dir, Git.GITIGNORE)
|
||||
)
|
||||
|
||||
data_dir = os.path.join(self._root_dir, "dir")
|
||||
entry, gitignore = Git(self._root_dir)._get_gitignore(data_dir)
|
||||
self.assertEqual(entry, "/dir")
|
||||
self.assertEqual(
|
||||
gitignore, os.path.join(self._root_dir, Git.GITIGNORE)
|
||||
)
|
||||
|
||||
def test_get_gitignore_subdir(self):
|
||||
data_dir = os.path.join(self._root_dir, os.path.join("dir1", "file1"))
|
||||
entry, gitignore = Git(self._root_dir)._get_gitignore(data_dir)
|
||||
self.assertEqual(entry, "/file1")
|
||||
self.assertEqual(
|
||||
gitignore, os.path.join(self._root_dir, "dir1", Git.GITIGNORE)
|
||||
)
|
||||
|
||||
data_dir = os.path.join(self._root_dir, os.path.join("dir1", "dir2"))
|
||||
entry, gitignore = Git(self._root_dir)._get_gitignore(data_dir)
|
||||
self.assertEqual(entry, "/dir2")
|
||||
self.assertEqual(
|
||||
gitignore, os.path.join(self._root_dir, "dir1", Git.GITIGNORE)
|
||||
)
|
||||
|
||||
def test_get_gitignore_ignorefile_dir(self):
|
||||
git = Git(self._root_dir)
|
||||
|
||||
file_double_dir = os.path.join("dir1", "dir2", "file1")
|
||||
data_dir1 = os.path.join(self._root_dir, file_double_dir)
|
||||
dir1_real1 = os.path.realpath("dir1")
|
||||
entry, gitignore = git._get_gitignore(data_dir1, dir1_real1)
|
||||
self.assertEqual(entry, "/dir2/file1")
|
||||
gitignore1 = os.path.join(self._root_dir, "dir1", Git.GITIGNORE)
|
||||
self.assertEqual(gitignore, gitignore1)
|
||||
|
||||
triple_dir = os.path.join("dir1", "dir2", "dir3")
|
||||
data_dir2 = os.path.join(self._root_dir, triple_dir)
|
||||
dir1_real2 = os.path.realpath("dir1")
|
||||
entry, gitignore = git._get_gitignore(data_dir2, dir1_real2)
|
||||
self.assertEqual(entry, "/dir2/dir3")
|
||||
gitignore2 = os.path.join(self._root_dir, "dir1", Git.GITIGNORE)
|
||||
self.assertEqual(gitignore, gitignore2)
|
||||
|
||||
def test_get_gitignore_ignorefile_dir_upper_level(self):
|
||||
git = Git(self._root_dir)
|
||||
|
||||
file_double_dir = os.path.join("dir1", "dir2", "file1")
|
||||
data_dir1 = os.path.join(self._root_dir, file_double_dir)
|
||||
ignore_file_dir = os.path.realpath(os.path.join("aa", "bb"))
|
||||
|
||||
with self.assertRaises(FileNotInTargetSubdirError):
|
||||
git._get_gitignore(data_dir1, ignore_file_dir)
|
||||
@@ -0,0 +1,167 @@
|
||||
import yaml
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
from dvc.main import main
|
||||
from dvc.output.local import OutputLOCAL
|
||||
from dvc.remote.local import RemoteLOCAL
|
||||
from dvc.stage import Stage, StageFileFormatError
|
||||
from dvc.utils import load_stage_file
|
||||
|
||||
from tests.basic_env import TestDvc
|
||||
|
||||
|
||||
class TestSchema(TestDvc):
|
||||
def _validate_fail(self, d):
|
||||
with self.assertRaises(StageFileFormatError):
|
||||
Stage.validate(d)
|
||||
|
||||
|
||||
class TestSchemaCmd(TestSchema):
|
||||
def test_cmd_object(self):
|
||||
d = {Stage.PARAM_CMD: {}}
|
||||
self._validate_fail(d)
|
||||
|
||||
def test_cmd_none(self):
|
||||
d = {Stage.PARAM_CMD: None}
|
||||
Stage.validate(d)
|
||||
|
||||
def test_no_cmd(self):
|
||||
d = {}
|
||||
Stage.validate(d)
|
||||
|
||||
def test_cmd_str(self):
|
||||
d = {Stage.PARAM_CMD: "cmd"}
|
||||
Stage.validate(d)
|
||||
|
||||
|
||||
class TestSchemaDepsOuts(TestSchema):
|
||||
def test_object(self):
|
||||
d = {Stage.PARAM_DEPS: {}}
|
||||
self._validate_fail(d)
|
||||
|
||||
d = {Stage.PARAM_OUTS: {}}
|
||||
self._validate_fail(d)
|
||||
|
||||
def test_none(self):
|
||||
d = {Stage.PARAM_DEPS: None}
|
||||
Stage.validate(d)
|
||||
|
||||
d = {Stage.PARAM_OUTS: None}
|
||||
Stage.validate(d)
|
||||
|
||||
def test_empty_list(self):
|
||||
d = {Stage.PARAM_DEPS: []}
|
||||
Stage.validate(d)
|
||||
|
||||
d = {Stage.PARAM_OUTS: []}
|
||||
Stage.validate(d)
|
||||
|
||||
def test_list(self):
|
||||
lst = [
|
||||
{OutputLOCAL.PARAM_PATH: "foo", RemoteLOCAL.PARAM_CHECKSUM: "123"},
|
||||
{OutputLOCAL.PARAM_PATH: "bar", RemoteLOCAL.PARAM_CHECKSUM: None},
|
||||
{OutputLOCAL.PARAM_PATH: "baz"},
|
||||
]
|
||||
d = {Stage.PARAM_DEPS: lst}
|
||||
Stage.validate(d)
|
||||
|
||||
lst[0][OutputLOCAL.PARAM_CACHE] = True
|
||||
lst[1][OutputLOCAL.PARAM_CACHE] = False
|
||||
d = {Stage.PARAM_OUTS: lst}
|
||||
Stage.validate(d)
|
||||
|
||||
|
||||
class TestReload(TestDvc):
|
||||
def test(self):
|
||||
import yaml
|
||||
|
||||
stages = self.dvc.add(self.FOO)
|
||||
self.assertEqual(len(stages), 1)
|
||||
stage = stages[0]
|
||||
self.assertTrue(stage is not None)
|
||||
|
||||
d = load_stage_file(stage.relpath)
|
||||
|
||||
# NOTE: checking that reloaded stage didn't change its checksum
|
||||
md5 = "11111111111111111111111111111111"
|
||||
d[stage.PARAM_MD5] = md5
|
||||
|
||||
with open(stage.relpath, "w") as fobj:
|
||||
yaml.safe_dump(d, fobj, default_flow_style=False)
|
||||
|
||||
stage = Stage.load(self.dvc, stage.relpath)
|
||||
self.assertTrue(stage is not None)
|
||||
stage.dump()
|
||||
|
||||
d = load_stage_file(stage.relpath)
|
||||
self.assertEqual(d[stage.PARAM_MD5], md5)
|
||||
|
||||
|
||||
class TestDefaultWorkingDirectory(TestDvc):
|
||||
def test_ignored_in_checksum(self):
|
||||
stage = self.dvc.run(
|
||||
cmd="echo test > {}".format(self.FOO),
|
||||
deps=[self.BAR],
|
||||
outs=[self.FOO],
|
||||
)
|
||||
|
||||
d = stage.dumpd()
|
||||
self.assertEqual(d[stage.PARAM_WDIR], ".")
|
||||
|
||||
with open(stage.relpath, "r") as fobj:
|
||||
d = yaml.safe_load(fobj)
|
||||
self.assertEqual(d[stage.PARAM_WDIR], ".")
|
||||
|
||||
del d[stage.PARAM_WDIR]
|
||||
with open(stage.relpath, "w") as fobj:
|
||||
yaml.safe_dump(d, fobj, default_flow_style=False)
|
||||
|
||||
with open(stage.relpath, "r") as fobj:
|
||||
d = yaml.safe_load(fobj)
|
||||
self.assertIsNone(d.get(stage.PARAM_WDIR))
|
||||
|
||||
with self.dvc.state:
|
||||
stage = Stage.load(self.dvc, stage.relpath)
|
||||
self.assertFalse(stage.changed())
|
||||
|
||||
|
||||
class TestExternalRemoteResolution(TestDvc):
|
||||
def test_remote_output(self):
|
||||
tmp_path = tempfile.mkdtemp()
|
||||
storage = os.path.join(tmp_path, "storage")
|
||||
file_path = os.path.join(storage, "file")
|
||||
|
||||
os.makedirs(storage)
|
||||
|
||||
assert main(["remote", "add", "tmp", tmp_path]) == 0
|
||||
assert main(["remote", "add", "storage", "remote://tmp/storage"]) == 0
|
||||
assert (
|
||||
main(
|
||||
[
|
||||
"run",
|
||||
"-O",
|
||||
"remote://storage/file",
|
||||
"echo file > {path}".format(path=file_path),
|
||||
]
|
||||
)
|
||||
== 0
|
||||
)
|
||||
|
||||
assert os.path.exists(file_path)
|
||||
|
||||
def test_remote_dependency(self):
|
||||
tmp_path = tempfile.mkdtemp()
|
||||
storage = os.path.join(tmp_path, "storage")
|
||||
file_path = os.path.join(storage, "file")
|
||||
|
||||
os.makedirs(storage)
|
||||
|
||||
with open(file_path, "w") as fobj:
|
||||
fobj.write("Isle of Dogs")
|
||||
|
||||
assert main(["remote", "add", "tmp", tmp_path]) == 0
|
||||
assert main(["remote", "add", "storage", "remote://tmp/storage"]) == 0
|
||||
assert main(["import", "remote://storage/file", "movie.txt"]) == 0
|
||||
|
||||
assert os.path.exists("movie.txt")
|
||||
@@ -0,0 +1,74 @@
|
||||
import os
|
||||
import mock
|
||||
|
||||
from dvc.utils.compat import str
|
||||
|
||||
from dvc.state import State
|
||||
from dvc.utils import file_md5
|
||||
from dvc.main import main
|
||||
from dvc.utils.fs import get_inode
|
||||
|
||||
from tests.basic_env import TestDvc
|
||||
|
||||
|
||||
class TestState(TestDvc):
|
||||
def test_update(self):
|
||||
path = os.path.join(self.dvc.root_dir, self.FOO)
|
||||
md5 = file_md5(path)[0]
|
||||
|
||||
state = State(self.dvc, self.dvc.config.config)
|
||||
|
||||
with state:
|
||||
entry_md5 = state.update(path)
|
||||
self.assertEqual(entry_md5, md5)
|
||||
|
||||
os.unlink(path)
|
||||
with open(path, "a") as fd:
|
||||
fd.write("1")
|
||||
|
||||
md5 = file_md5(path)[0]
|
||||
entry_md5 = state.update(path)
|
||||
self.assertEqual(entry_md5, md5)
|
||||
|
||||
|
||||
class TestStateOverflow(TestDvc):
|
||||
def test(self):
|
||||
# NOTE: trying to add more entries than state can handle,
|
||||
# to see if it will clean up and vacuum successfully
|
||||
ret = main(["config", "state.row_limit", "10"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
dname = "dir"
|
||||
os.mkdir(dname)
|
||||
for i in range(20):
|
||||
with open(os.path.join(dname, str(i)), "w+") as fobj:
|
||||
fobj.write(str(i))
|
||||
|
||||
ret = main(["add", "dir"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
|
||||
class TestGetStateRecordForInode(TestDvc):
|
||||
@staticmethod
|
||||
def mock_get_inode(special_path, special_value):
|
||||
def get_inode_mocked(path):
|
||||
if path == special_path:
|
||||
return special_value
|
||||
else:
|
||||
return get_inode(path)
|
||||
|
||||
return get_inode_mocked
|
||||
|
||||
@mock.patch("dvc.state.get_inode", autospec=True)
|
||||
def test_transforms_inode(self, get_inode_mock):
|
||||
state = State(self.dvc, self.dvc.config.config)
|
||||
inode = state.MAX_INT + 2
|
||||
self.assertNotEqual(inode, state._to_sqlite(inode))
|
||||
|
||||
path = os.path.join(self.dvc.root_dir, self.FOO)
|
||||
get_inode_mock.side_effect = self.mock_get_inode(path, inode)
|
||||
|
||||
with state:
|
||||
state.update(path)
|
||||
ret = state.get_state_record_for_inode(inode)
|
||||
self.assertIsNotNone(ret)
|
||||
@@ -0,0 +1,25 @@
|
||||
import os
|
||||
|
||||
from dvc.main import main
|
||||
|
||||
from tests.basic_env import TestDvc
|
||||
from mock import patch
|
||||
|
||||
|
||||
class TestStatus(TestDvc):
|
||||
def test_quiet(self):
|
||||
self.dvc.add(self.FOO)
|
||||
|
||||
ret = main(["status", "--quiet"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
os.remove(self.FOO)
|
||||
os.rename(self.BAR, self.FOO)
|
||||
|
||||
ret = main(["status", "--quiet"])
|
||||
self.assertEqual(ret, 1)
|
||||
|
||||
@patch("dvc.repo.status._cloud_status", return_value=True)
|
||||
def test_implied_cloud(self, mock_status):
|
||||
main(["status", "--remote", "something"])
|
||||
mock_status.assert_called()
|
||||
@@ -0,0 +1,19 @@
|
||||
import os
|
||||
|
||||
from dvc.system import System
|
||||
from tests.basic_env import TestDir
|
||||
|
||||
|
||||
class TestSystem(TestDir):
|
||||
def test_getdirinfo(self):
|
||||
if os.name != "nt":
|
||||
return
|
||||
|
||||
# simulate someone holding an exclussive access
|
||||
locked = "locked"
|
||||
fd = os.open(locked, os.O_WRONLY | os.O_CREAT | os.O_EXCL)
|
||||
|
||||
# should not raise WinError
|
||||
System.getdirinfo(locked)
|
||||
|
||||
os.close(fd)
|
||||
@@ -0,0 +1,118 @@
|
||||
import os
|
||||
import shutil
|
||||
import filecmp
|
||||
import logging
|
||||
|
||||
from dvc.main import main
|
||||
|
||||
from tests.basic_env import TestDvc
|
||||
|
||||
|
||||
class TestTag(TestDvc):
|
||||
def test(self):
|
||||
fname = "file"
|
||||
shutil.copyfile(self.FOO, fname)
|
||||
|
||||
stages = self.dvc.add(fname)
|
||||
self.assertEqual(len(stages), 1)
|
||||
stage = stages[0]
|
||||
self.assertTrue(stage is not None)
|
||||
|
||||
ret = main(["tag", "add", "v1", stage.path])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
os.unlink(fname)
|
||||
shutil.copyfile(self.BAR, fname)
|
||||
|
||||
ret = main(["repro", stage.path])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["tag", "add", "v2", stage.path])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["tag", "list", stage.path])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["checkout", stage.path + "@v1"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertTrue(filecmp.cmp(fname, self.FOO, shallow=False))
|
||||
|
||||
ret = main(["checkout", stage.path + "@v2"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertTrue(filecmp.cmp(fname, self.BAR, shallow=False))
|
||||
|
||||
|
||||
class TestTagAll(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["add", self.FOO, self.BAR])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self._caplog.clear()
|
||||
|
||||
ret = main(["tag", "list"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertEqual("", self._caplog.text)
|
||||
|
||||
ret = main(["tag", "add", "v1"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self._caplog.clear()
|
||||
|
||||
ret = main(["tag", "list"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
expected = (
|
||||
"bar.dvc:\n"
|
||||
" bar:\n"
|
||||
" v1:\n"
|
||||
" md5: 8978c98bb5a48c2fb5f2c4c905768afa\n"
|
||||
"foo.dvc:\n"
|
||||
" foo:\n"
|
||||
" v1:\n"
|
||||
" md5: acbd18db4cc2f85cedef654fccc4a4d8\n"
|
||||
)
|
||||
|
||||
stdout = "\n".join(record.message for record in self._caplog.records)
|
||||
|
||||
assert expected == stdout
|
||||
|
||||
ret = main(["tag", "remove", "v1"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self._caplog.clear()
|
||||
|
||||
ret = main(["tag", "list"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertEqual("", self._caplog.text)
|
||||
|
||||
|
||||
class TestTagAddNoChecksumInfo(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["run", "-o", self.FOO, "--no-exec"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self._caplog.clear()
|
||||
|
||||
with self._caplog.at_level(logging.WARNING, logger="dvc"):
|
||||
ret = main(["tag", "add", "v1", "foo.dvc"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
assert "missing checksum info for 'foo'" in self._caplog.text
|
||||
|
||||
|
||||
class TestTagRemoveNoTag(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["add", self.FOO])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self._caplog.clear()
|
||||
|
||||
with self._caplog.at_level(logging.WARNING, logger="dvc"):
|
||||
ret = main(["tag", "remove", "v1", "foo.dvc"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
assert "tag 'v1' not found for 'foo'" in self._caplog.text
|
||||
@@ -0,0 +1,150 @@
|
||||
# encoding: utf-8
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from os.path import join
|
||||
|
||||
from dvc.scm import SCM
|
||||
from dvc.scm.git import GitTree
|
||||
from dvc.scm.tree import WorkingTree
|
||||
|
||||
from tests.basic_env import TestDir, TestGit
|
||||
|
||||
|
||||
class TestWorkingTree(TestDir):
|
||||
def setUp(self):
|
||||
super(TestWorkingTree, self).setUp()
|
||||
self.tree = WorkingTree()
|
||||
|
||||
def test_open(self):
|
||||
self.assertEqual(self.tree.open(self.FOO).read(), self.FOO_CONTENTS)
|
||||
self.assertEqual(
|
||||
self.tree.open(self.UNICODE).read(), self.UNICODE_CONTENTS
|
||||
)
|
||||
|
||||
def test_exists(self):
|
||||
self.assertTrue(self.tree.exists(self.FOO))
|
||||
self.assertTrue(self.tree.exists(self.UNICODE))
|
||||
self.assertFalse(self.tree.exists("not-existing-file"))
|
||||
|
||||
def test_isdir(self):
|
||||
self.assertTrue(self.tree.isdir(self.DATA_DIR))
|
||||
self.assertFalse(self.tree.isdir(self.FOO))
|
||||
self.assertFalse(self.tree.isdir("not-existing-file"))
|
||||
|
||||
def test_isfile(self):
|
||||
self.assertTrue(self.tree.isfile(self.FOO))
|
||||
self.assertFalse(self.tree.isfile(self.DATA_DIR))
|
||||
self.assertFalse(self.tree.isfile("not-existing-file"))
|
||||
|
||||
|
||||
class TestGitTree(TestGit):
|
||||
def setUp(self):
|
||||
super(TestGitTree, self).setUp()
|
||||
self.scm = SCM(self._root_dir)
|
||||
self.tree = GitTree(self.git, "master")
|
||||
|
||||
def test_open(self):
|
||||
self.scm.add([self.FOO, self.UNICODE, self.DATA_DIR])
|
||||
self.scm.commit("add")
|
||||
self.assertEqual(self.tree.open(self.FOO).read(), self.FOO_CONTENTS)
|
||||
self.assertEqual(
|
||||
self.tree.open(self.UNICODE).read(), self.UNICODE_CONTENTS
|
||||
)
|
||||
with self.assertRaises(IOError):
|
||||
self.tree.open("not-existing-file")
|
||||
with self.assertRaises(IOError):
|
||||
self.tree.open(self.DATA_DIR)
|
||||
|
||||
def test_exists(self):
|
||||
self.assertFalse(self.tree.exists(self.FOO))
|
||||
self.assertFalse(self.tree.exists(self.UNICODE))
|
||||
self.assertFalse(self.tree.exists(self.DATA_DIR))
|
||||
self.scm.add([self.FOO, self.UNICODE, self.DATA])
|
||||
self.scm.commit("add")
|
||||
self.assertTrue(self.tree.exists(self.FOO))
|
||||
self.assertTrue(self.tree.exists(self.UNICODE))
|
||||
self.assertTrue(self.tree.exists(self.DATA_DIR))
|
||||
self.assertFalse(self.tree.exists("non-existing-file"))
|
||||
|
||||
def test_isdir(self):
|
||||
self.scm.add([self.FOO, self.DATA_DIR])
|
||||
self.scm.commit("add")
|
||||
self.assertTrue(self.tree.isdir(self.DATA_DIR))
|
||||
self.assertFalse(self.tree.isdir(self.FOO))
|
||||
self.assertFalse(self.tree.isdir("non-existing-file"))
|
||||
|
||||
def test_isfile(self):
|
||||
self.scm.add([self.FOO, self.DATA_DIR])
|
||||
self.scm.commit("add")
|
||||
self.assertTrue(self.tree.isfile(self.FOO))
|
||||
self.assertFalse(self.tree.isfile(self.DATA_DIR))
|
||||
self.assertFalse(self.tree.isfile("not-existing-file"))
|
||||
|
||||
|
||||
class AssertWalkEqualMixin(object):
|
||||
def assertWalkEqual(self, actual, expected, msg=None):
|
||||
def convert_to_sets(walk_results):
|
||||
return [
|
||||
(root, set(dirs), set(nondirs))
|
||||
for root, dirs, nondirs in walk_results
|
||||
]
|
||||
|
||||
self.assertEqual(
|
||||
convert_to_sets(actual), convert_to_sets(expected), msg=msg
|
||||
)
|
||||
|
||||
|
||||
class TestWalkInNoSCM(AssertWalkEqualMixin, TestDir):
|
||||
def test(self):
|
||||
tree = WorkingTree()
|
||||
self.assertWalkEqual(
|
||||
tree.walk("."),
|
||||
[
|
||||
(".", ["data_dir"], ["code.py", "bar", "тест", "foo"]),
|
||||
(join("data_dir"), ["data_sub_dir"], ["data"]),
|
||||
(join("data_dir", "data_sub_dir"), [], ["data_sub"]),
|
||||
],
|
||||
)
|
||||
|
||||
def test_subdir(self):
|
||||
tree = WorkingTree()
|
||||
self.assertWalkEqual(
|
||||
tree.walk(join("data_dir", "data_sub_dir")),
|
||||
[(join("data_dir", "data_sub_dir"), [], ["data_sub"])],
|
||||
)
|
||||
|
||||
|
||||
class TestWalkInGit(AssertWalkEqualMixin, TestGit):
|
||||
def test_nobranch(self):
|
||||
tree = WorkingTree()
|
||||
self.assertWalkEqual(
|
||||
tree.walk("."),
|
||||
[
|
||||
(".", ["data_dir"], ["bar", "тест", "code.py", "foo"]),
|
||||
("data_dir", ["data_sub_dir"], ["data"]),
|
||||
(join("data_dir", "data_sub_dir"), [], ["data_sub"]),
|
||||
],
|
||||
)
|
||||
self.assertWalkEqual(
|
||||
tree.walk(join("data_dir", "data_sub_dir")),
|
||||
[(join("data_dir", "data_sub_dir"), [], ["data_sub"])],
|
||||
)
|
||||
|
||||
def test_branch(self):
|
||||
scm = SCM(self._root_dir)
|
||||
scm.add([self.DATA_SUB_DIR])
|
||||
scm.commit("add data_dir/data_sub_dir/data_sub")
|
||||
tree = GitTree(self.git, "master")
|
||||
self.assertWalkEqual(
|
||||
tree.walk("."),
|
||||
[
|
||||
(".", ["data_dir"], ["code.py"]),
|
||||
("data_dir", ["data_sub_dir"], []),
|
||||
(join("data_dir", "data_sub_dir"), [], ["data_sub"]),
|
||||
],
|
||||
)
|
||||
self.assertWalkEqual(
|
||||
tree.walk(join("data_dir", "data_sub_dir")),
|
||||
[(join("data_dir", "data_sub_dir"), [], ["data_sub"])],
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
import os
|
||||
import stat
|
||||
|
||||
from dvc.main import main
|
||||
|
||||
from tests.basic_env import TestDvc
|
||||
|
||||
|
||||
class TestUnprotect(TestDvc):
|
||||
def test(self):
|
||||
ret = main(["config", "cache.protected", "true"])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(["add", self.FOO])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertNotEqual(
|
||||
os.stat(self.FOO).st_mode
|
||||
& (stat.S_IREAD | stat.S_IRGRP | stat.S_IROTH),
|
||||
0,
|
||||
)
|
||||
self.assertEqual(
|
||||
os.stat(self.FOO).st_mode
|
||||
& (stat.S_IWRITE | stat.S_IWGRP | stat.S_IWOTH),
|
||||
0,
|
||||
)
|
||||
|
||||
ret = main(["unprotect", self.FOO])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.assertNotEqual(
|
||||
os.stat(self.FOO).st_mode
|
||||
& (stat.S_IREAD | stat.S_IRGRP | stat.S_IROTH | stat.S_IWRITE),
|
||||
0,
|
||||
)
|
||||
@@ -0,0 +1,43 @@
|
||||
import os
|
||||
|
||||
from tests.basic_env import TestDvc
|
||||
|
||||
|
||||
class TestUpdater(TestDvc):
|
||||
def test(self):
|
||||
# NOTE: only test on travis CRON to avoid generating too much logs
|
||||
travis = os.getenv("TRAVIS") == "true"
|
||||
if not travis:
|
||||
return
|
||||
|
||||
cron = os.getenv("TRAVIS_EVENT_TYPE") == "cron"
|
||||
if not cron:
|
||||
return
|
||||
|
||||
env = os.environ.copy()
|
||||
if os.getenv("CI"):
|
||||
del os.environ["CI"]
|
||||
|
||||
self.dvc.updater.check()
|
||||
self.dvc.updater.check()
|
||||
self.dvc.updater.check()
|
||||
|
||||
os.environ = env.copy()
|
||||
|
||||
def test_check_version_newer(self):
|
||||
self.dvc.updater.latest = "0.20.8"
|
||||
self.dvc.updater.current = "0.21.0"
|
||||
|
||||
self.assertFalse(self.dvc.updater._is_outdated())
|
||||
|
||||
def test_check_version_equal(self):
|
||||
self.dvc.updater.latest = "0.20.8"
|
||||
self.dvc.updater.current = "0.20.8"
|
||||
|
||||
self.assertFalse(self.dvc.updater._is_outdated())
|
||||
|
||||
def test_check_version_outdated(self):
|
||||
self.dvc.updater.latest = "0.21.0"
|
||||
self.dvc.updater.current = "0.20.8"
|
||||
|
||||
self.assertTrue(self.dvc.updater._is_outdated())
|
||||
@@ -0,0 +1,71 @@
|
||||
import os
|
||||
import shutil
|
||||
import filecmp
|
||||
|
||||
from dvc import utils
|
||||
from tests.basic_env import TestDvc
|
||||
|
||||
|
||||
class TestUtils(TestDvc):
|
||||
def test_copyfile(self):
|
||||
src = "file1"
|
||||
dest = "file2"
|
||||
dest_dir = "testdir"
|
||||
|
||||
with open(src, "w+") as f:
|
||||
f.write("file1contents")
|
||||
|
||||
os.mkdir(dest_dir)
|
||||
|
||||
utils.copyfile(src, dest)
|
||||
self.assertTrue(filecmp.cmp(src, dest, shallow=False))
|
||||
|
||||
utils.copyfile(src, dest_dir)
|
||||
self.assertTrue(
|
||||
filecmp.cmp(src, "{}/{}".format(dest_dir, src), shallow=False)
|
||||
)
|
||||
|
||||
shutil.rmtree(dest_dir)
|
||||
os.remove(src)
|
||||
os.remove(dest)
|
||||
|
||||
def test_file_md5_crlf(self):
|
||||
with open("cr", "wb+") as fd:
|
||||
fd.write(b"a\nb\nc")
|
||||
with open("crlf", "wb+") as fd:
|
||||
fd.write(b"a\r\nb\r\nc")
|
||||
|
||||
self.assertEqual(utils.file_md5("cr")[0], utils.file_md5("crlf")[0])
|
||||
|
||||
def test_dict_md5(self):
|
||||
d = {
|
||||
"cmd": "python code.py foo file1",
|
||||
"locked": "true",
|
||||
"outs": [
|
||||
{
|
||||
"path": "file1",
|
||||
"metric": {"type": "raw"},
|
||||
"cache": False,
|
||||
"md5": "acbd18db4cc2f85cedef654fccc4a4d8",
|
||||
}
|
||||
],
|
||||
"deps": [
|
||||
{"path": "foo", "md5": "acbd18db4cc2f85cedef654fccc4a4d8"},
|
||||
{"path": "code.py", "md5": "d05447644b89960913c7eee5fd776adb"},
|
||||
],
|
||||
}
|
||||
|
||||
md5 = "8b263fa05ede6c3145c164829be694b4"
|
||||
|
||||
self.assertEqual(md5, utils.dict_md5(d, exclude=["metric", "locked"]))
|
||||
|
||||
def test_boxify(self):
|
||||
expected = (
|
||||
"+-----------------+\n"
|
||||
"| |\n"
|
||||
"| message |\n"
|
||||
"| |\n"
|
||||
"+-----------------+\n"
|
||||
)
|
||||
|
||||
assert expected == utils.boxify("message")
|
||||
Reference in New Issue
Block a user