test: move func tests to tests/func

Signed-off-by: Ruslan Kuprieiev <[email protected]>
This commit is contained in:
Ruslan Kuprieiev
2019-04-16 20:19:22 +03:00
committed by Ruslan Kuprieiev
parent 6c730dd5cb
commit 6f11f1f51f
39 changed files with 10 additions and 10 deletions
View File
+490
View File
@@ -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)
+68
View File
@@ -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)
+175
View File
@@ -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"])
+575
View File
@@ -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),
)
+178
View File
@@ -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)
+89
View File
@@ -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)
+104
View File
@@ -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)
+797
View File
@@ -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
+15
View File
@@ -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)
+21
View File
@@ -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))
+189
View File
@@ -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)
+175
View 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)
+68
View File
@@ -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)
+158
View File
@@ -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)
+66
View File
@@ -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()
+33
View File
@@ -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))
+21
View File
@@ -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)
+697
View File
@@ -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)
+188
View File
@@ -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))
+30
View File
@@ -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])
+291
View File
@@ -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)
+37
View File
@@ -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))
+171
View File
@@ -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)
+97
View File
@@ -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)
+72
View File
@@ -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
+953
View File
@@ -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()
+75
View File
@@ -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)
+140
View File
@@ -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)
+167
View File
@@ -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")
+74
View File
@@ -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)
+25
View File
@@ -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()
+19
View File
@@ -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)
+118
View File
@@ -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
+150
View File
@@ -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"])],
)
+35
View File
@@ -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,
)
+43
View File
@@ -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())
+71
View File
@@ -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")