From f47a2c75855ea2fe5aaa97a17efdb3a7cd20c13f Mon Sep 17 00:00:00 2001 From: Ruslan Kuprieiev Date: Mon, 1 Jan 2018 15:00:48 +0200 Subject: [PATCH] dvc: initial unittests Initial unittests for new design. Signed-off-by: Ruslan Kuprieiev --- .travis.yml | 3 +- dvc/cache.py | 2 +- dvc/command/common/traverse.py | 91 --------------- dvc/config.py | 2 +- dvc/project.py | 49 +++++++- dvc/repository_change.py | 129 --------------------- dvc/scm.py | 29 +++-- dvc/settings.py | 67 ----------- dvc/stage.py | 40 ++++++- dvc/utils.py | 9 -- tests/basic_env.py | 145 +++++++---------------- tests/test_add.py | 45 ++++++++ tests/test_cache.py | 24 ++++ tests/test_checkout.py | 30 +++++ tests/test_cmd_remove.py | 159 -------------------------- tests/test_cmd_run.py | 100 ---------------- tests/test_config.py | 25 ++-- tests/test_data_file_obj.py | 196 -------------------------------- tests/test_dvc_path.py | 70 ------------ tests/test_gc.py | 32 ++++++ tests/test_import.py | 119 ------------------- tests/test_lock.py | 19 ++++ tests/test_remove.py | 28 +++++ tests/test_repository_change.py | 98 ---------------- tests/test_repro.py | 122 ++++++++------------ tests/test_run.py | 35 ++++++ tests/test_scm.py | 32 ++++++ tests/test_state.py | 31 +++++ tests/test_utils.py | 21 +--- 29 files changed, 481 insertions(+), 1271 deletions(-) delete mode 100644 dvc/command/common/traverse.py delete mode 100644 dvc/repository_change.py delete mode 100644 dvc/settings.py create mode 100644 tests/test_add.py create mode 100644 tests/test_cache.py create mode 100644 tests/test_checkout.py delete mode 100644 tests/test_cmd_remove.py delete mode 100644 tests/test_cmd_run.py delete mode 100644 tests/test_data_file_obj.py delete mode 100644 tests/test_dvc_path.py create mode 100644 tests/test_gc.py delete mode 100644 tests/test_import.py create mode 100644 tests/test_lock.py create mode 100644 tests/test_remove.py delete mode 100644 tests/test_repository_change.py create mode 100644 tests/test_run.py create mode 100644 tests/test_scm.py create mode 100644 tests/test_state.py diff --git a/.travis.yml b/.travis.yml index 347e22eeb..5ac87f4ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,8 +44,7 @@ install: - printf "[default]\naws_access_key_id = $AWS_ACCESS_KEY_ID\naws_secret_access_key = $AWS_SECRET_ACCESS_KEY\n" > ~/.aws/credentials - printf "[default]\n" > ~/.aws/config script: -# Temporarily disabled untill dvc is stable -# - ./unittests.sh + - ./unittests.sh - ./functests.sh - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ./build_macos.sh; else ./build_linux.sh; fi - ./build_package.sh diff --git a/dvc/cache.py b/dvc/cache.py index 8a9b4a7d6..dbb8417e9 100644 --- a/dvc/cache.py +++ b/dvc/cache.py @@ -5,7 +5,7 @@ class Cache(object): CACHE_DIR = 'cache' def __init__(self, dvc_dir): - self.cache_dir = os.path.join(dvc_dir, self.CACHE_DIR) + self.cache_dir = os.path.abspath(os.path.realpath(os.path.join(dvc_dir, self.CACHE_DIR))) @staticmethod def init(dvc_dir): diff --git a/dvc/command/common/traverse.py b/dvc/command/common/traverse.py deleted file mode 100644 index 2d7df39ad..000000000 --- a/dvc/command/common/traverse.py +++ /dev/null @@ -1,91 +0,0 @@ -import os - -from dvc.command.common.base import CmdBase -from dvc.exceptions import DvcException -from dvc.logger import Logger -from dvc.path.data_item import DataItemError - - -class TraverseError(DvcException): - def __init__(self, msg): - DvcException.__init__(self, 'Traverse file tree error: {}'.format(msg)) - - -class Traverse(CmdBase): - def __init__(self, settings, cmd_name, do_not_start_from_root=True): - super(Traverse, self).__init__(settings) - self._cmd_name = cmd_name - self._do_not_start_from_root = do_not_start_from_root - - def run(self): - if not self._traverse_all(): - return 1 - - return 0 - - def _traverse_all(self): - if not self.no_git_actions and not self.git.is_ready_to_go(): - return False - - error = False - for target in self.parsed_args.target: - if not self._traverse(target): - error = True - - message = 'DVC {}: {}'.format(self._cmd_name, ' '.join(self.parsed_args.target)) - self.commit_if_needed(message, error) - - return error == 0 - - def _traverse(self, target): - try: - if os.path.isdir(target): - if not self.is_recursive: - msg = '[TraverseFileTree] Directory "%s" cannot be traversed. Use --recurcive flag.' - raise TraverseError(msg % target) - - self._traverse_dir(target) - else: - self.process_file(target) - return True - except DvcException as ex: - Logger.error('[TraverseFileTree] Unable to {} data item "{}": {}'.format(self._cmd_name, target, ex)) - return False - - def _traverse_dir(self, target): - if target == self.settings.config.CONFIG_DIR: - return - - for f in os.listdir(target): - file = os.path.join(target, f) - if os.path.isdir(file): - self._traverse_dir(file) - else: - self.process_file(file) - - self.traverse_dir_finalize(target) - pass - - # Common helpers - def _get_data_item(self, target): - try: - data_item = self.settings.path_factory.existing_data_item(target) - except DataItemError: - Logger.warn(u'[TraverseFileTree] Data file {} does not have existing state file'.format(target)) - data_item = self.settings.path_factory.data_item(target) - - return data_item - - def _remove_cloud_cache(self, data_item): - if not self.parsed_args.keep_in_cloud: - self.settings.cloud.remove(data_item) - - ## API: - def is_recursive(self): - return True - - def process_file(self, target): - pass - - def traverse_dir_finalize(self, target): - pass diff --git a/dvc/config.py b/dvc/config.py index 786833361..105d94bcf 100644 --- a/dvc/config.py +++ b/dvc/config.py @@ -57,7 +57,7 @@ ProjectName = ''' def __init__(self, dvc_dir): - self.dvc_dir = dvc_dir + self.dvc_dir = os.path.abspath(os.path.realpath(dvc_dir)) self.config_file = os.path.join(dvc_dir, self.CONFIG) self._config = configparser.SafeConfigParser() diff --git a/dvc/project.py b/dvc/project.py index d5eb2b23b..28685dde9 100644 --- a/dvc/project.py +++ b/dvc/project.py @@ -60,7 +60,7 @@ class Project(object): DVC_DIR = '.dvc' def __init__(self, root_dir): - self.root_dir = os.path.abspath(root_dir) + self.root_dir = os.path.abspath(os.path.realpath(root_dir)) self.dvc_dir = os.path.join(self.root_dir, self.DVC_DIR) self.scm = SCM(self.root_dir) @@ -73,6 +73,18 @@ class Project(object): @staticmethod def init(root_dir): + """ + Initiate dvc project in directory. + + Args: + root_dir: Path to project's root directory. + + Returns: + Project instance. + + Raises: + KeyError: Raises an exception. + """ root_dir = os.path.abspath(root_dir) dvc_dir = os.path.join(root_dir, Project.DVC_DIR) os.mkdir(dvc_dir) @@ -106,12 +118,23 @@ class Project(object): locked=True) stage.save() stage.dump() + return stage def remove(self, fname): - path = os.path.abspath(fname) + stages = [] + output = Output.loads(self, fname) for out in self.outs(): - if out.path == path: - out.stage().remove() + if out.path == output.path: + stage = out.stage() + stages.append(stage) + + if len(stages) == 0: + raise StageNotFoundError(fname) + + for stage in stages: + stage.remove() + + return stages def _add_orphans(self, deps): outs = [out.path for out in self.outs()] @@ -120,7 +143,15 @@ class Project(object): continue self.add(dep.path) - def run(self, cmd, deps, deps_no_cache, outs, outs_no_cache, locked, fname, cwd): + def run(self, + cmd=None, + deps=[], + deps_no_cache=[], + outs=[], + outs_no_cache=[], + locked=False, + fname=Stage.STAGE_FILE, + cwd=os.curdir): cwd = os.path.abspath(cwd) path = os.path.join(cwd, fname) outputs = Output.loads_from(self, outs, use_cache=True, cwd=cwd) @@ -140,8 +171,10 @@ class Project(object): locked=locked) stage.run() stage.dump() + return stage - def reproduce(self, targets, recursive=False, force=False): + def reproduce(self, targets, recursive=True, force=False): + reproduced = [] stages = nx.get_node_attributes(self.graph(), 'stage') for target in targets: node = os.path.relpath(os.path.abspath(target), self.root_dir) @@ -152,9 +185,13 @@ class Project(object): for n in nx.dfs_postorder_nodes(self.graph(), node): stages[n].reproduce(force=force) stages[n].dump() + reproduced.append(stages[n]) stages[node].reproduce(force=force) stages[node].dump() + reproduced.append(stages[node]) + + return reproduced def checkout(self): for stage in self.stages(): diff --git a/dvc/repository_change.py b/dvc/repository_change.py deleted file mode 100644 index 1a487fede..000000000 --- a/dvc/repository_change.py +++ /dev/null @@ -1,129 +0,0 @@ -import os - -from dvc.exceptions import DvcException -from dvc.git_wrapper import GitWrapper -from dvc.executor import Executor -from dvc.logger import Logger -from dvc.path.data_item import DataDirError, DataItemInConfigDirError -from dvc.path.stated_data_item import StatedDataItem -from dvc.utils import cached_property -from dvc.config import ConfigI - -class RepositoryChangeError(DvcException): - def __init__(self, msg): - DvcException.__init__(self, 'Repository change error: {}'.format(msg)) - - -class RepositoryChange(object): - """Pre-condition: git repository has no changes""" - - def __init__(self, command, settings, stdout, stderr, shell=False): - self._settings = settings - - stemps_before = self.data_file_timesteps() - - Logger.debug(u'[Repository change] Exec command: {}. stdout={}, stderr={}, shell={}'.format( - command, - stdout, - stderr, - shell)) - Executor.exec_cmd_only_success(command.split(' '), stdout, stderr, shell=shell) - - stemps_after = self.data_file_timesteps() - - sym_diff = stemps_after ^ stemps_before - self._modified_content_filenames = set([filename for filename, timestemp in sym_diff]) - - Logger.debug(u'[Repository change] Identified modifications: {}'.format( - u', '.join(self._modified_content_filenames))) - - self._stated_data_items = [] - self._externally_created_files = [] - self._created_status_files = [] - self._init_file_states() - - @property - def modified_content_data_items(self): - return [self._settings.path_factory.data_item(file) for file in self._modified_content_filenames] - - @cached_property - def removed_data_items(self): - return [x for x in self._stated_data_items if x.is_removed] - - @cached_property - def modified_data_items(self): - return [x for x in self._stated_data_items if x.is_modified] - - @cached_property - def new_data_items(self): - return [x for x in self._stated_data_items if x.is_new] - - @cached_property - def unusual_data_items(self): - return [x for x in self._stated_data_items if x.is_unusual] - - @property - def changed_data_items(self): - res = set(self.new_data_items + self.modified_data_items + self.modified_content_data_items) - return list(res) - - def _add_stated_data_item(self, state, file): - try: - item = self._settings.path_factory.stated_data_item(state, file) - self._stated_data_items.append(item) - Logger.debug('[Repository change] Add status: {} {}'.format( - item.status, - item.data.dvc)) - except DataItemInConfigDirError: - self._created_status_files.append(file) - except DataDirError: - self._externally_created_files.append(file) - pass - - def _init_file_states(self): - statuses = GitWrapper.git_file_statuses() - - for status, file in statuses: - file_path = os.path.join(self._settings.git.git_dir_abs, file) - - if not os.path.isdir(file_path): - self._add_stated_data_item(status, file_path) - else: - files = [] - self.get_all_files_from_dir(file_path, files) - state = StatedDataItem.STATUS_UNTRACKED + StatedDataItem.STATUS_UNTRACKED - for f in files: - self._add_stated_data_item(state, f) - pass - - def get_all_files_from_dir(self, dir, result): - files = os.listdir(dir) - for f in files: - path = os.path.join(dir, f) - if os.path.isfile(path): - result.append(path) - else: - self.get_all_files_from_dir(path, result) - pass - - @property - def externally_created_files(self): - return self._externally_created_files - - @property - def created_status_files(self): - return self._created_status_files - - def data_file_timesteps(self): - res = set() - for root, dirs, files in os.walk(self._settings.git.git_dir_abs): - if root.startswith(os.path.join(self._settings.git.git_dir_abs, ConfigI.CONFIG_DIR)): - continue - - for file in files: - filename = os.path.join(root, file) - if os.path.exists(filename): - timestemp = os.path.getmtime(filename) - res.add((filename, timestemp)) - - return res diff --git a/dvc/scm.py b/dvc/scm.py index 439a8d574..cad50ed6f 100644 --- a/dvc/scm.py +++ b/dvc/scm.py @@ -5,11 +5,18 @@ from dvc.exceptions import DvcException from dvc.command.common.branch_changer import BranchChanger +class SCMError(DvcException): + pass + + class FileNotInRepoError(DvcException): pass class Base(object): + def __init__(self, root_dir=os.curdir): + self.root_dir = os.path.abspath(os.path.realpath(root_dir)) + @staticmethod def is_repo(root_dir): return True @@ -23,6 +30,9 @@ class Base(object): def ignore_list(self, p_list): return [self.ignore(path) for path in p_list] + def ignore_file(self): + return self.GITIGNORE + def add(self, paths): pass @@ -46,9 +56,13 @@ class Git(Base): GITIGNORE = '.gitignore' GIT_DIR = '.git' - def __init__(self, root_dir): - self.root_dir = root_dir - self.repo = git.Repo(root_dir) + def __init__(self, root_dir=os.curdir): + super(Git, self).__init__(root_dir) + + try: + self.repo = git.Repo(root_dir) + except InvalidGitRepositoryError: + raise SCMError('{} is not a git repository'.format(root_dir)) @staticmethod def is_repo(root_dir): @@ -57,7 +71,9 @@ class Git(Base): def ignore(self, path): entry = os.path.basename(path) - gitignore = os.path.join(os.path.dirname(path), self.GITIGNORE) + gitignore = os.path.join(os.path.abspath(os.path.dirname(path)), + self.GITIGNORE) + gitignore = os.path.abspath(os.path.realpath(gitignore)) if not gitignore.startswith(self.root_dir): raise FileNotInRepoError() @@ -74,9 +90,6 @@ class Git(Base): open(gitignore, 'a').write(content) - def ignore_file(self): - return self.GITIGNORE - def add(self, paths): self.repo.index.add(paths) @@ -96,7 +109,7 @@ class Git(Base): return self.repo.untracked_files -def SCM(root_dir): +def SCM(root_dir=os.curdir): if Git.is_repo(root_dir): return Git(root_dir) diff --git a/dvc/settings.py b/dvc/settings.py deleted file mode 100644 index eceb42e6a..000000000 --- a/dvc/settings.py +++ /dev/null @@ -1,67 +0,0 @@ -import os - -from dvc.git_wrapper import GitWrapperI, GitWrapper -from dvc.config import ConfigI, Config -from dvc.exceptions import DvcException -from dvc.path.factory import PathFactory -from dvc.cli import parse_args -from dvc.data_cloud import DataCloud - -class SettingsError(DvcException): - def __init__(self, msg): - DvcException.__init__(self, msg) - - -class Settings(object): - def __init__(self, argv=None, git=None, config=None): - self._args = None - args = None - - if argv is not None and len(argv) != 0: - args = parse_args(argv) - self._args = argv[2:] - - if git is None: - git = GitWrapper() - - if config is None: - if args is not None and args.cmd != 'init': - config = Config() - else: - config = ConfigI() - - self._git = git - self._config = config - self._path_factory = PathFactory(self) - self._parsed_args = args - self._cloud = DataCloud(self) - - @property - def args(self): - return self._args - - def set_args(self, args): - self._args = args - - @property - def parsed_args(self): - return self._parsed_args - - def parse_args(self, args): - self._parsed_args = parse_args(args) - - @property - def git(self): - return self._git - - @property - def config(self): - return self._config - - @property - def path_factory(self): - return self._path_factory - - @property - def cloud(self): - return self._cloud diff --git a/dvc/stage.py b/dvc/stage.py index 22e0a4417..24b60dbbe 100644 --- a/dvc/stage.py +++ b/dvc/stage.py @@ -8,9 +8,29 @@ from dvc.data_cloud import file_md5 from dvc.exceptions import DvcException from dvc.executor import Executor +class OutputError(DvcException): + def __init__(self, path, msg): + super(OutputError, self).__init__('Output file \'{}\' error: {}'.format(path, msg)) -class OutputNoCacheError(DvcException): - pass + +class OutputNoCacheError(OutputError): + def __init__(self, path): + super(OutputNoCacheError, self).__init__(path, 'no cache') + + +class OutputOutsideOfRepoError(OutputError): + def __init__(self, path): + super(OutputOutsideOfRepoError, self).__init__(path, 'outside of repository') + + +class OutputDoesNotExistError(OutputError): + def __init__(self, path): + super(OutputDoesNotExistError, self).__init__(path, 'does not exist') + + +class OutputIsNotFileError(OutputError): + def __init__(self, path): + super(OutputIsNotFileError, self).__init__(path, 'not a file') class Output(object): @@ -20,7 +40,11 @@ class Output(object): def __init__(self, project, path, md5=None, use_cache=False): self.project = project - self.path = path + self.path = os.path.abspath(os.path.realpath(path)) + + if not self.path.startswith(self.project.root_dir): + raise OutputOutsideOfRepoError(self.path) + self.md5 = md5 self.use_cache = use_cache @@ -46,10 +70,10 @@ class Output(object): def link(self, checkout=False): if not self.use_cache: - raise OutputNoCacheError() + raise OutputNoCacheError(self.path) if not os.path.exists(self.path) and not os.path.exists(self.cache): - raise OutputNoCacheError() + raise OutputNoCacheError(self.path) if os.path.exists(self.path) and os.path.exists(self.cache) and os.path.samefile(self.path, self.cache): return @@ -66,7 +90,7 @@ class Output(object): src = self.path link = self.cache else: - raise OutputNoCacheError() + raise OutputNoCacheError(self.path) System.hardlink(src, link) @@ -81,6 +105,10 @@ class Output(object): def update(self, md5=None): self.md5 = md5 if not self.md5: + if not os.path.exists(self.path): + raise OutputDoesNotExistError(self.path) + if not os.path.isfile(self.path): + raise OutputIsNotFileError(self.path) self.md5 = file_md5(self.path)[0] self.project.state.update(self.path, self.md5, self.mtime()) diff --git a/dvc/utils.py b/dvc/utils.py index 0e5cb5294..c2ceffe0c 100644 --- a/dvc/utils.py +++ b/dvc/utils.py @@ -32,15 +32,6 @@ def cached_property(func): return property(get) -def rmtree(directory): - '''Cross platform rmtree()''' - if os.name == 'nt': - if os.path.exists(directory) and not os.access(directory, os.W_OK): - os.chmod(directory, stat.S_IWUSR) - - shutil.rmtree(directory, ignore_errors=True) - - def copyfile(src, dest): '''Copy file with progress bar''' copied = 0 diff --git a/tests/basic_env.py b/tests/basic_env.py index 1d01f1c0d..116b10086 100644 --- a/tests/basic_env.py +++ b/tests/basic_env.py @@ -1,124 +1,55 @@ import os +import shutil import tempfile +from git import Repo from unittest import TestCase -from dvc.config import ConfigI -from dvc.git_wrapper import GitWrapperI -from dvc.path.data_item import DataItem -from dvc.path.factory import PathFactory -from dvc.settings import Settings from dvc.system import System -from dvc.utils import rmtree +from dvc.project import Project -class BasicEnvironment(TestCase): - def init_environment(self, - test_dir=System.get_long_path(tempfile.mkdtemp()), - curr_dir=None): - self._test_dir = System.realpath(test_dir) - self._proj_dir = 'proj' - self._test_git_dir = os.path.join(self._test_dir, self._proj_dir) - self._old_curr_dir_abs = System.realpath(os.curdir) - if os.path.exists(self._test_dir): - rmtree(self._test_dir) +class TestDir(TestCase): + FOO = 'foo' + FOO_CONTENTS = FOO + BAR = 'bar' + BAR_CONTENTS = BAR + CODE = 'code.py' + CODE_CONTENTS = 'import sys\nimport shutil\nshutil.copyfile(sys.argv[1], sys.argv[2])' - if curr_dir: - self._curr_dir = System.realpath(curr_dir) - else: - self._curr_dir = self._test_git_dir + def _pushd(self, d): + self._saved_dir = System.realpath(os.curdir) + os.chdir(d) - if not os.path.exists(self._curr_dir): - os.makedirs(self._curr_dir) + def _popd(self): + os.chdir(self._saved_dir) + self._saved_dir = None - if not os.path.isdir(self._test_git_dir): - os.makedirs(self._test_git_dir) + def create(self, name, contents): + with open(name, 'a') as f: + f.write(contents) - data_dir = os.path.join(self._test_git_dir, 'data') - cache_dir = os.path.join(self._test_git_dir, ConfigI.CONFIG_DIR, ConfigI.CACHE_DIR_NAME) - state_dir = os.path.join(self._test_git_dir, ConfigI.CONFIG_DIR, ConfigI.STATE_DIR_NAME, 'data') - - os.makedirs(data_dir) - os.makedirs(cache_dir) - os.makedirs(state_dir) - - os.chdir(self._curr_dir) + def setUp(self): + self._root_dir = System.get_long_path(tempfile.mkdtemp()) + self._pushd(self._root_dir) + self.create(self.FOO, self.FOO_CONTENTS) + self.create(self.BAR, self.BAR_CONTENTS) + self.create(self.CODE, self.CODE_CONTENTS) def tearDown(self): - if self._old_curr_dir_abs: - os.chdir(self._old_curr_dir_abs) - if self._test_git_dir: - rmtree(self._test_dir) - pass + self._popd() + shutil.rmtree(self._root_dir) -class DirHierarchyEnvironment(BasicEnvironment): - def init_environment(self, - test_dir=os.path.join(os.path.sep, 'tmp', 'ntx_unit_test'), - curr_dir=None, - commit='abc12345'): - ''' Creates data environment with data, cache and state dirs. - data/ - file1.txt --> ../cache/file1.txt_abc123 - dir1/ - file2.txt --> ../../cache/dir1/file2.txt_abc123 - file3.txt --> ../../cache/dir1/file3.txt_abc123 - dir11/ - file4.txt --> ../../../cache/dir1/dir11/file4.txt_abc123 - dir2/ - file5.txt --> ../../cache/dir2/file5.txt_abc123 - file6.txt --> an actual file - ''' +class TestGit(TestDir): + def setUp(self): + super(TestGit, self).setUp() + self.git = Repo.init() + self.git.index.add([self.CODE]) + self.git.index.commit('add code') - BasicEnvironment.init_environment(self, test_dir, curr_dir) - self._commit = commit - - self._git = GitWrapperI(git_dir=self._test_git_dir, commit=self._commit) - self._config = ConfigI('data') - self.path_factory = PathFactory(self._git, self._config) - self.settings = Settings([], self._git, self._config) - - self.dir1 = 'dir1' - self.dir11 = os.path.join('dir1', 'dir11') - self.dir2 = 'dir2' - - self.create_dirs(self.dir1) - self.create_dirs(self.dir11) - self.create_dirs(self.dir2) - - self.file1, self.cache1, self.state1 = self.crate_data_item('file1.txt') - self.file2, self.cache2, self.state2 = self.crate_data_item(os.path.join(self.dir1, 'file2.txt')) - self.file3, self.cache3, self.state3 = self.crate_data_item(os.path.join(self.dir1, 'file3.txt')) - self.file4, self.cache4, self.state4 = self.crate_data_item(os.path.join(self.dir11, 'file4.txt')) - self.file5, self.cache5, self.state5 = self.crate_data_item(os.path.join(self.dir2, 'file5.txt')) - self.file6, self.cache6, self.state6 = self.crate_data_item(os.path.join(self.dir2, 'file6.txt'), - cache_file=False) - pass - - @staticmethod - def create_content_file(file, content='some test'): - fd = open(file, 'w+') - fd.write(content) - fd.close() - - def crate_data_item(self, data_file, cache_file=True, content='random text'): - file_result = os.path.join('data', data_file) - state_result = os.path.join(ConfigI.CONFIG_DIR, ConfigI.STATE_DIR_NAME, file_result + DataItem.STATE_FILE_SUFFIX) - - dummy_md5 = os.path.basename(data_file) + DataItem.CACHE_FILE_SEP + self._commit - self.create_content_file(state_result, '{"Md5" : "' + dummy_md5 + '", "Command" : "run", "Cwd" : "dir"}') - - if cache_file: - cache_result = os.path.join(ConfigI.CONFIG_DIR, ConfigI.CACHE_DIR_NAME, dummy_md5) - self.create_content_file(cache_result, content) - - System.hardlink(cache_result, file_result) - else: - cache_result = None - self.create_content_file(file_result, content) - - return file_result, cache_result, state_result - - def create_dirs(self, dir): - os.makedirs(os.path.join('data', dir)) - os.makedirs(os.path.join(ConfigI.CONFIG_DIR, ConfigI.STATE_DIR_NAME, 'data', dir)) +class TestDvc(TestGit): + def setUp(self): + super(TestDvc, self).setUp() + self.dvc = Project.init(self._root_dir) + self.dvc.logger.be_verbose() diff --git a/tests/test_add.py b/tests/test_add.py new file mode 100644 index 000000000..6d230b9a4 --- /dev/null +++ b/tests/test_add.py @@ -0,0 +1,45 @@ +import os +import shutil +import filecmp + +from dvc.data_cloud import file_md5 +from dvc.stage import Stage, OutputNoCacheError, OutputOutsideOfRepoError +from dvc.stage import OutputDoesNotExistError, OutputIsNotFileError +from dvc.project import StageNotFoundError + +from tests.basic_env import TestDvc + + +class TestAdd(TestDvc): + def test(self): + md5 = file_md5(self.FOO)[0] + + stage = self.dvc.add(self.FOO) + + self.assertIsInstance(stage, Stage) + self.assertTrue(os.path.isfile(stage.path)) + self.assertEqual(len(stage.outs), 1) + self.assertEqual(len(stage.deps), 0) + self.assertTrue(stage.locked) + self.assertEqual(stage.cmd, None) + self.assertEqual(stage.outs[0].md5, md5) + + +class TestAddNonExistentFile(TestDvc): + def test(self): + with self.assertRaises(OutputDoesNotExistError) as cx: + self.dvc.add('non_existent_file') + + +class TestAddFileOutsideOfRepo(TestDvc): + def test(self): + with self.assertRaises(OutputOutsideOfRepoError) as cx: + self.dvc.add(os.path.join(os.path.dirname(self.dvc.root_dir), self.FOO)) + + +class TestAddDirectory(TestDvc): + def test(self): + dname = 'directory' + os.mkdir(dname) + with self.assertRaises(OutputIsNotFileError) as cx: + self.dvc.add(dname) diff --git a/tests/test_cache.py b/tests/test_cache.py new file mode 100644 index 000000000..efa2dff29 --- /dev/null +++ b/tests/test_cache.py @@ -0,0 +1,24 @@ +import os + +from dvc.cache import Cache + +from tests.basic_env import TestDvc + + +class TestCache(TestDvc): + def setUp(self): + super(TestCache, self).setUp() + self.cache1 = os.path.join(self.dvc.cache.cache_dir, '1') + self.cache2 = os.path.join(self.dvc.cache.cache_dir, '2') + self.create(self.cache1, '1') + self.create(self.cache2, '2') + + def test_all(self): + flist = Cache(self.dvc.dvc_dir).all() + self.assertEquals(len(flist), 2) + self.assertTrue(self.cache1 in flist) + self.assertTrue(self.cache2 in flist) + + def test_get(self): + cache = Cache(self.dvc.dvc_dir).get(os.path.basename(self.cache1)) + self.assertEquals(cache, self.cache1) diff --git a/tests/test_checkout.py b/tests/test_checkout.py new file mode 100644 index 000000000..aec522671 --- /dev/null +++ b/tests/test_checkout.py @@ -0,0 +1,30 @@ +import os +import shutil +import filecmp + +from dvc.data_cloud import file_md5 +from dvc.stage import Stage, OutputNoCacheError, OutputOutsideOfRepoError +from dvc.stage import OutputDoesNotExistError, OutputIsNotFileError +from dvc.project import StageNotFoundError + +from tests.basic_env import TestDvc + + +class TestCheckout(TestDvc): + def test_checkout(self): + cmd = 'python {} {} {}'.format(self.CODE, self.FOO, 'out') + deps = [self.FOO] + deps_no_cache = [self.CODE] + outs = [os.path.join(self.dvc.root_dir, 'out')] + outs_no_cache = [] + locked = False + fname = os.path.join(self.dvc.root_dir, 'out.dvc') + cwd = os.curdir + + stage = self.dvc.run(cmd, deps, deps_no_cache, outs, outs_no_cache, locked, fname, cwd) + + shutil.copy(self.FOO, 'orig') + os.unlink(self.FOO) + self.dvc.checkout() + self.assertTrue(os.path.isfile(self.FOO)) + self.assertTrue(filecmp.cmp(self.FOO, 'orig')) diff --git a/tests/test_cmd_remove.py b/tests/test_cmd_remove.py deleted file mode 100644 index bbd24454d..000000000 --- a/tests/test_cmd_remove.py +++ /dev/null @@ -1,159 +0,0 @@ -import os - -from dvc.command.remove import CmdRemove -from tests.basic_env import DirHierarchyEnvironment - -class TestCmdRemove(DirHierarchyEnvironment): - def setUp(self): - DirHierarchyEnvironment.init_environment(self) - - def test_file_by_file_removal(self): - self.settings.parse_args('remove --keep-in-cloud') - cmd = CmdRemove(self.settings) - - dir1_dvc_name = os.path.join('data', self.dir1) - self.assertTrue(os.path.exists(dir1_dvc_name)) - - cmd.remove_dir(dir1_dvc_name) - self.assertFalse(os.path.exists(dir1_dvc_name)) - - def test_remove_deep_dir(self): - self.settings.parse_args('remove --keep-in-cloud --recursive') - cmd = CmdRemove(self.settings) - - dir1_dvc_name = os.path.join('data', self.dir1) - self.assertTrue(os.path.exists(dir1_dvc_name)) - - cmd.remove_dir(dir1_dvc_name) - self.assertFalse(os.path.exists(dir1_dvc_name)) - - def test_not_recursive_removal(self): - self.settings.parse_args('remove --keep-in-cloud') - cmd = CmdRemove(self.settings) - - dir1_dvc_name = os.path.join('data', self.dir1) - self.assertTrue(os.path.exists(dir1_dvc_name)) - - cmd.remove_dir(dir1_dvc_name) - pass - - def test_data_dir_removal(self): - self.settings.parse_args('remove --keep-in-cloud --recursive') - cmd = CmdRemove(self.settings) - - data_dir = 'data' - self.assertTrue(os.path.exists(data_dir)) - - cmd.remove_dir(data_dir) - pass - - -class TestRemoveDataItem(DirHierarchyEnvironment): - def setUp(self): - DirHierarchyEnvironment.init_environment(self) - - def test_remove_data_instance(self): - self.settings.parse_args('remove --keep-in-cloud') - cmd = CmdRemove(self.settings) - - self.assertTrue(os.path.isfile(self.file1)) - self.assertTrue(os.path.isfile(self.cache1)) - self.assertTrue(os.path.isfile(self.state1)) - - cmd.remove_file(self.file1) - self.assertFalse(os.path.exists(self.file1)) - self.assertFalse(os.path.exists(self.cache1)) - self.assertFalse(os.path.exists(self.state1)) - - def test_keep_cache(self): - self.settings.parse_args('remove --keep-in-cloud --keep-in-cache') - cmd = CmdRemove(self.settings) - - self.assertTrue(os.path.isfile(self.file1)) - self.assertTrue(os.path.isfile(self.cache1)) - self.assertTrue(os.path.isfile(self.state1)) - - cmd.remove_file(self.file1) - self.assertFalse(os.path.exists(self.file1)) - self.assertTrue(os.path.exists(self.cache1)) - self.assertFalse(os.path.exists(self.state1)) - - def test_remove_data_instance_without_cache(self): - self.settings.parse_args('remove --keep-in-cloud') - cmd = CmdRemove(self.settings) - - self.assertTrue(os.path.isfile(self.file6)) - self.assertIsNone(self.cache6) - self.assertTrue(os.path.exists(self.state6)) - self.assertTrue(os.path.isfile(self.state6)) - - cmd.remove_file(self.file6) - self.assertFalse(os.path.exists(self.file6)) - - -class TestRemoveEndToEnd(DirHierarchyEnvironment): - def setUp(self): - DirHierarchyEnvironment.init_environment(self) - - def test_end_to_end_with_no_error(self): - dir11_full = os.path.join('data', self.dir11) - dir2_full = os.path.join('data', self.dir2) - - self.settings.parse_args('remove --no-git-actions --keep-in-cloud --recursive {} {}'.format(dir11_full, self.file5)) - cmd = CmdRemove(self.settings) - - self.assertTrue(os.path.exists(dir11_full)) - self.assertTrue(os.path.exists(self.file5)) - self.assertTrue(os.path.exists(dir2_full)) - self.assertTrue(os.path.exists(self.file1)) - - self.assertTrue(cmd.remove_all()) - - self.assertFalse(os.path.exists(dir11_full)) - self.assertFalse(os.path.exists(self.file5)) - self.assertTrue(os.path.exists(dir2_full)) - self.assertTrue(os.path.exists(self.file1)) - - def test_run(self): - dir11_full = os.path.join('data', self.dir11) - dir2_full = os.path.join('data', self.dir2) - - self.settings.parse_args('remove --no-git-actions --keep-in-cloud --recursive {} {}'.format(dir11_full, self.file5)) - cmd = CmdRemove(self.settings) - - self.assertTrue(os.path.exists(dir11_full)) - self.assertTrue(os.path.exists(self.file5)) - self.assertTrue(os.path.exists(dir2_full)) - self.assertTrue(os.path.exists(self.file1)) - - self.assertEqual(cmd.run(), 0) - - self.assertFalse(os.path.exists(dir11_full)) - self.assertFalse(os.path.exists(self.file5)) - self.assertTrue(os.path.exists(dir2_full)) - self.assertTrue(os.path.exists(self.file1)) - - -class TestRemoveEndToEndWithAnError(DirHierarchyEnvironment): - def setUp(self): - DirHierarchyEnvironment.init_environment(self) - - def test(self): - dir11_full = os.path.join('data', self.dir11) - dir2_full = os.path.join('data', self.dir2) - - self.settings.parse_args('remove --no-git-actions --keep-in-cloud --recursive {} {}'.format(dir11_full, self.file5)) - cmd = CmdRemove(self.settings) - - self.assertTrue(os.path.exists(dir11_full)) - self.assertTrue(os.path.exists(self.file5)) - self.assertTrue(os.path.exists(dir2_full)) - self.assertTrue(os.path.exists(self.file1)) - self.assertTrue(os.path.exists(self.file6)) - - self.assertTrue(cmd.remove_all()) - - self.assertFalse(os.path.exists(dir11_full)) - self.assertFalse(os.path.exists(self.file5)) - self.assertTrue(os.path.exists(dir2_full)) - self.assertTrue(os.path.exists(self.file1)) diff --git a/tests/test_cmd_run.py b/tests/test_cmd_run.py deleted file mode 100644 index cda6a5a9f..000000000 --- a/tests/test_cmd_run.py +++ /dev/null @@ -1,100 +0,0 @@ -import os -import tempfile -from unittest import TestCase - -from dvc.command.run import CmdRun, RunError -from dvc.config import ConfigI -from dvc.executor import Executor -from dvc.path.data_item import DataItem -from dvc.path.factory import PathFactory -from dvc.git_wrapper import GitWrapper -from dvc.settings import Settings -from dvc.system import System -from dvc.utils import rmtree - - -class RunBasicTest(TestCase): - def setUp(self): - self.test_dir = System.get_long_path(tempfile.mkdtemp()) - self._old_curr_dir_abs = System.realpath(os.curdir) - - self.tearDown() - os.mkdir(self.test_dir) - os.chdir(self.test_dir) - os.mkdir('data') - os.mkdir(ConfigI.CONFIG_DIR) - os.mkdir(os.path.join(ConfigI.CONFIG_DIR, ConfigI.CACHE_DIR_NAME)) - os.mkdir(os.path.join(ConfigI.CONFIG_DIR, ConfigI.STATE_DIR_NAME)) - - self.init_git_repo() - self.git = GitWrapper() - - self.config = ConfigI('data') - self.path_factory = PathFactory(self.git, self.config) - - self.settings = Settings('run cmd', self.git, self.config) - pass - - def init_git_repo(self): - Executor.exec_cmd_only_success(['git', 'init']) - msg = os.path.join(ConfigI.CONFIG_DIR, ConfigI.CACHE_DIR_NAME) - msg += '\n' - msg += os.path.join(ConfigI.CONFIG_DIR, '.' + ConfigI.CONFIG + '.lock') - self.create_file('.gitignore', msg) - Executor.exec_cmd_only_success(['git', 'add', '.gitignore']) - Executor.exec_cmd_only_success(['git', 'commit', '-m', '"Init test repo"']) - - @staticmethod - def create_file(file2, content='random text'): - fd = open(file2, 'w+') - fd.write(content) - fd.close() - - def tearDown(self): - rmtree(self.test_dir) - os.chdir(self._old_curr_dir_abs) - - -class RunTwoFilesBase(RunBasicTest): - def setUp(self): - super(RunTwoFilesBase, self).setUp() - - self.input_param_file = os.path.join('data', 'extra_input') - self.extra_output_file = os.path.join('data', 'extra_output') - - self.settings.parse_args('run --input {} --output {} cmd'.format(self.input_param_file, self.extra_output_file)) - self.settings._args = [] - cmd_run = CmdRun(self.settings) - - self.file_name1 = os.path.join('data', 'file1') - self.file_name2 = os.path.join('data', 'file2') - self.state_objs = cmd_run.run_command(['echo', 'test'], [], [], shell=True, - stdout=self.file_name1, - stderr=self.file_name2) - - self.state_file_name1 = os.path.join(ConfigI.CONFIG_DIR, ConfigI.STATE_DIR_NAME, self.file_name1 + DataItem.STATE_FILE_SUFFIX) - self.state_file_name2 = os.path.join(ConfigI.CONFIG_DIR, ConfigI.STATE_DIR_NAME, self.file_name2 + DataItem.STATE_FILE_SUFFIX) - - self.state_file1 = None - self.state_file2 = None - for s in self.state_objs: - if s.file == self.state_file_name1: - self.state_file1 = s - elif s.file == self.state_file_name2: - self.state_file2 = s - pass - - -class TestRunStateFiles(RunTwoFilesBase): - def test(self): - names = [x.file for x in self.state_objs] - self.assertEqual({self.state_file_name1, self.state_file_name2}, set(names)) - - -class TestRunExtraInExtraOutFiles(RunTwoFilesBase): - def test(self): - self.assertIsNotNone(self.state_file1) - self.assertEqual(self.state_file1.input_files, [self.input_param_file]) - - output_set = {self.file_name1, self.file_name2, self.extra_output_file} - self.assertEqual(set(self.state_file1.output_files), output_set) diff --git a/tests/test_config.py b/tests/test_config.py index 8868a6006..46f6b44df 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,4 +1,5 @@ import os +import configparser from unittest import TestCase import mock @@ -15,15 +16,18 @@ except ImportError: # Python 3 builtin_module_name = 'builtins' -from dvc.config import Config from dvc.data_cloud import DataCloud -from dvc.settings import Settings class TestConfigTest(TestCase): def setUp(self): pass + def _conf(self, s): + c = configparser.SafeConfigParser() + c.readfp(s) + return c + def storage_path_hierarchy_test(self): """ StoragePath should be read first from global and then from cloud section """ @@ -41,9 +45,8 @@ class TestConfigTest(TestCase): "StoragePath = googlesb/google_storage_path" ) s = StringIO('\n'.join(c)) - conf = Config(s, conf_pseudo_file = s) - settings = Settings(None, None, conf) - cloud = DataCloud(settings) + conf = self._conf(s) + cloud = DataCloud(conf) self.assertEqual(cloud.typ, 'AWS') self.assertEqual(cloud._cloud.storage_bucket, 'globalsb') self.assertEqual(cloud._cloud.storage_prefix, 'global_storage_path') @@ -61,8 +64,8 @@ class TestConfigTest(TestCase): "StoragePath = googlesb/google_storage_path" ) s = StringIO('\n'.join(c)) - conf = Config(s, conf_pseudo_file=s) - cloud = DataCloud(Settings(None, None, conf)) + conf = self._conf(s) + cloud = DataCloud(conf) self.assertEqual(cloud.typ, 'AWS') self.assertEqual(cloud._cloud.storage_bucket, 'awssb') self.assertEqual(cloud._cloud.storage_prefix, 'aws_storage_path') @@ -100,8 +103,8 @@ class TestConfigTest(TestCase): patcher = mock.patch(builtin_module_name + '.open', side_effect=self.mocked_open_aws_default_credentials) # patcher.start() - conf = Config(s, conf_pseudo_file=s) - cloud = DataCloud(Settings(None, None, conf)) + conf = self._conf(s) + cloud = DataCloud(conf) aws_creds = cloud._cloud._get_credentials() patcher.stop() @@ -131,8 +134,8 @@ class TestConfigTest(TestCase): patcher = mock.patch(builtin_module_name + '.open', side_effect=self.mocked_open_aws_default_credentials) patcher.start() - conf = Config(s, conf_pseudo_file=s) - cloud = DataCloud(Settings(None, None, conf)) + conf = self._conf(s) + cloud = DataCloud(conf) aws_creds = cloud._cloud._get_credentials() patcher.stop() diff --git a/tests/test_data_file_obj.py b/tests/test_data_file_obj.py deleted file mode 100644 index 1b8c748b6..000000000 --- a/tests/test_data_file_obj.py +++ /dev/null @@ -1,196 +0,0 @@ -import os - -from dvc.config import ConfigI -from dvc.git_wrapper import GitWrapperI -from dvc.path.data_item import DataDirError, DataItemError, DataItem -from dvc.path.factory import PathFactory -from dvc.system import System -from tests.basic_env import BasicEnvironment - - -class TestDataFileObjBasic(BasicEnvironment): - def setUp(self): - BasicEnvironment.init_environment(self, test_dir=os.path.join(os.path.sep, 'tmp', 'ntx_unit_test')) - - git = GitWrapperI(git_dir=self._test_git_dir, commit='ad45ba8') - config = ConfigI() - - file = os.path.join('data', 'file.txt') - self.cache_file_full_path = os.path.join(self._test_git_dir, ConfigI.CONFIG_DIR, ConfigI.CACHE_DIR_NAME, 'file.txt_ad45ba8') - self._data_path = DataItem(file, git, config, cache_file=self.cache_file_full_path) - pass - - def test_data_file(self): - data_file_full_path = os.path.join(self._test_git_dir, 'data', 'file.txt') - self.assertEqual(self._data_path.data.abs, data_file_full_path) - - def test_cache_file(self): - self.assertEqual(self._data_path.cache.abs, self.cache_file_full_path) - - def test_state_file(self): - state_file_full_path = os.path.join(self._test_git_dir, ConfigI.CONFIG_DIR, ConfigI.STATE_DIR_NAME, 'data', 'file.txt.state') - self.assertEqual(self._data_path.state.abs, state_file_full_path) - - -class TestDataItemWithGivenCache(BasicEnvironment): - def setUp(self): - BasicEnvironment.init_environment(self, test_dir=os.path.join(os.path.sep, 'tmp', 'ntx_unit_test')) - - self._git = GitWrapperI(git_dir=self._test_git_dir, commit='ad45ba8') - self._config = ConfigI('data') - - self._file = os.path.join('data', 'file.txt') - self._cache = os.path.join(ConfigI.CONFIG_DIR, ConfigI.CACHE_DIR_NAME, 'file.txt_abcd') - - self._data_item = DataItem(self._file, self._git, self._config, self._cache) - pass - - def test_basic(self): - self.assertEqual(self._data_item.data.relative, self._file) - self.assertEqual(self._data_item.cache.relative, self._cache) - - -class TestDeepDataItemWithGivenCache(BasicEnvironment): - def setUp(self): - BasicEnvironment.init_environment(self, test_dir=os.path.join(os.path.sep, 'tmp', 'ntx_unit_test')) - - self._git = GitWrapperI(git_dir=self._test_git_dir, commit='ad45ba8') - self._config = ConfigI('data') - - self._file = os.path.join('data', 'dir1', 'file.txt') - self._cache = os.path.join(ConfigI.CONFIG_DIR, ConfigI.CACHE_DIR_NAME, 'dir1', 'file.txt_abcd') - - self._data_item = DataItem(self._file, self._git, self._config, self._cache) - pass - - def test_basic(self): - self.assertEqual(self._data_item.data.relative, self._file) - self.assertEqual(self._data_item.cache.relative, self._cache) - - -class TestPathFactory(BasicEnvironment): - def setUp(self): - BasicEnvironment.init_environment(self, test_dir=os.path.join(os.path.sep, 'tmp', 'ntx_unit_test')) - - git = GitWrapperI(git_dir=self._test_git_dir, commit='ad45ba8') - config = ConfigI() - self.path_factory = PathFactory(git, config) - - self.data_file = os.path.join('data', 'file.txt') - dummy_md5 = 'fsymlinc.txt' - self.cache_file = os.path.join(ConfigI.CONFIG_DIR, ConfigI.CACHE_DIR_NAME, dummy_md5) - self.state_file = os.path.join(ConfigI.CONFIG_DIR, ConfigI.STATE_DIR_NAME, 'data', 'file.txt' + DataItem.STATE_FILE_SUFFIX) - - with open(self.cache_file, 'w+') as fd: - fd.write('some text') - - with open(self.state_file, 'w+') as fd: - fd.write('{"Md5" : "' + dummy_md5 + '", "Command" : "run", "Cwd" : "dir"}') - - os.chdir('data') - System.hardlink(os.path.join('..', self.cache_file), 'file.txt') - os.chdir('..') - pass - - def test_data_file_factory(self): - data_path = self.path_factory.data_item(self.data_file) - self.assertEqual(data_path.data.dvc, self.data_file) - - indirect_file_path = os.path.join('..', self._proj_dir, self.data_file) - data_path_indirect = self.path_factory.data_item(indirect_file_path) - self.assertEqual(data_path_indirect.data.dvc, self.data_file) - pass - - def test_data_hardlink_factory(self): - data_path = self.path_factory.existing_data_item(self.data_file) - self.assertEqual(data_path.cache.dvc, self.cache_file) - pass - - def test_data_hardlink_factory_cache(self): - data_path = self.path_factory.existing_data_item(self.data_file) - self.assertEqual(data_path.data.dvc, self.data_file) - pass - - def test_data_hardlink_factory_exception(self): - with self.assertRaises(DataItemError): - self.path_factory.existing_data_item(self.cache_file) - pass - - def test_data_path(self): - file = os.path.join('data', 'file1') - path = self.path_factory.path(file) - self.assertEqual(path.dvc, file) - self.assertEqual(path.relative, file) - self.assertTrue(path.abs.endswith(file)) - - -class TestDataPathInDataDir(BasicEnvironment): - def setUp(self): - BasicEnvironment.init_environment(self, os.path.join(os.path.sep, 'tmp', 'ntx_unit_test')) - - git = GitWrapperI(git_dir=self._test_git_dir, commit='eeeff8f') - self.data_dir = 'da' - config = ConfigI() - self.path_factory = PathFactory(git, config) - - deep_path = os.path.join(self.data_dir, 'dir1', 'd2', 'file.txt') - self.cache_file = os.path.join(self._test_git_dir, ConfigI.CONFIG_DIR, ConfigI.CACHE_DIR_NAME, 'dir1', 'd2', 'file.txt_eeeff8f') - self.data_path = self.path_factory.data_item(deep_path, cache_file=self.cache_file) - pass - - def test_data_file(self): - target = os.path.join(self._test_git_dir, 'da', 'dir1', 'd2', 'file.txt') - self.assertEqual(self.data_path.data.abs, target) - - def test_cache_file(self): - self.assertEqual(self.data_path.cache.abs, self.cache_file) - - def test_state_file(self): - target = os.path.join(self._test_git_dir, ConfigI.CONFIG_DIR, ConfigI.STATE_DIR_NAME, 'da', 'dir1', 'd2', 'file.txt.state') - self.assertEqual(self.data_path.state.abs, target) - - -class TestDataFileObjLongPath(BasicEnvironment): - def setUp(self): - curr_dir = os.path.join(os.path.sep, 'tmp', 'ntx_unit_test', 'proj', 'mydir', 'dd3') - - BasicEnvironment.init_environment(self, - os.path.join(os.path.sep, 'tmp', 'ntx_unit_test'), - curr_dir) - - self._git = GitWrapperI(git_dir=self._test_git_dir, commit='123ed8') - self._config = ConfigI('data') - self.path_factory = PathFactory(self._git, self._config) - - self.cache_file = os.path.join(self._test_git_dir, ConfigI.CONFIG_DIR, ConfigI.CACHE_DIR_NAME, 'file1.txt_123ed8') - self.data_path = self.path_factory.data_item(os.path.join('..', '..', 'data', 'file1.txt'), cache_file=self.cache_file) - pass - - def test_data_file(self): - target = os.path.join(self._test_git_dir, 'data', 'file1.txt') - self.assertEqual(self.data_path.data.abs, target) - - def test_cache_file(self): - self.assertEqual(self.data_path.cache.abs, self.cache_file) - - def test_state_file(self): - target = os.path.join(self._test_git_dir, ConfigI.CONFIG_DIR, ConfigI.STATE_DIR_NAME, 'data', 'file1.txt.state') - self.assertEqual(self.data_path.state.abs, target) - - -class RunOutsideGitRepoTest(BasicEnvironment): - def setUp(self): - curr_dir = os.path.join(os.path.sep, 'tmp') - - BasicEnvironment.init_environment(self, - os.path.join(os.path.sep, 'tmp', 'ntx_unit_test'), - curr_dir) - - def test_outside_git_dir(self): - git = GitWrapperI(git_dir=self._test_git_dir, commit='123ed8') - config = ConfigI('data') - path_factory = PathFactory(git, config) - - with self.assertRaises(DataDirError): - path_factory.data_item('file.txt') - pass diff --git a/tests/test_dvc_path.py b/tests/test_dvc_path.py deleted file mode 100644 index 5f2cd7149..000000000 --- a/tests/test_dvc_path.py +++ /dev/null @@ -1,70 +0,0 @@ -import os -import shutil -from unittest import TestCase - -from dvc.path.path import Path -from dvc.git_wrapper import GitWrapperI -from dvc.system import System - - -class TestDvcPathTest(TestCase): - def setUp(self): - self.curr_dir = System.realpath('.') - self.test_dir = System.realpath('/tmp/ntx_unit_test/dvc_path') - self.tearDown() - os.makedirs(os.path.join(self.test_dir, 'data')) - os.makedirs(os.path.join(self.test_dir, 'code', 'lib')) - os.makedirs(os.path.join(self.test_dir, 'd1', 'd2', 'dir3', 'd4', 'dir5')) - - self._git = GitWrapperI(self.test_dir) - - def tearDown(self): - os.chdir(self.curr_dir) - shutil.rmtree(self.test_dir, ignore_errors=True) - - def _validate_dvc_path(self, path, dvc_file_name, relative_file_name): - self.assertEqual(path.dvc, dvc_file_name) - self.assertEqual(path.filename, os.path.basename(dvc_file_name)) - self.assertEqual(path.relative, relative_file_name) - self.assertTrue(os.path.isabs(path.abs)) - self.assertTrue(path.abs.endswith(dvc_file_name)) - - def basic_test(self): - os.chdir(self.test_dir) - - file = os.path.join('data', 'file.txt') - path = Path(file, self._git) - self._validate_dvc_path(path, file, file) - pass - - def from_dir_test(self): - os.chdir(os.path.join(self.test_dir, 'code')) - - file_dvc_path = os.path.join('data', 'file1.txt') - file_relative_path = os.path.join('..', file_dvc_path) - - path = Path(file_relative_path, self._git) - self._validate_dvc_path(path, file_dvc_path, file_relative_path) - pass - - def from_deep_dirs_test(self): - deep_dir = os.path.join('d1', 'd2', 'dir3') - os.chdir(os.path.join(self.test_dir, deep_dir)) - - file_dvc = os.path.join('code', 'lib', 'context_switcher_structs.asm') - file_relative = os.path.join('..', '..', '..', file_dvc) - - path = Path(file_relative, self._git) - self._validate_dvc_path(path, file_dvc, file_relative) - pass - - def go_deeper_test(self): - deep_dir = os.path.join('d1', 'd2', 'dir3') - os.chdir(os.path.join(self.test_dir, deep_dir)) - - file_relative_path = os.path.join(deep_dir, 'd4', 'dir5', 'rawdata.tsv') - file_dvc_path = os.path.join(deep_dir, file_relative_path) - - path = Path(file_relative_path, self._git) - self._validate_dvc_path(path, file_dvc_path, file_relative_path) - pass diff --git a/tests/test_gc.py b/tests/test_gc.py new file mode 100644 index 000000000..c61ec9993 --- /dev/null +++ b/tests/test_gc.py @@ -0,0 +1,32 @@ +import os +import shutil +import filecmp + +from dvc.data_cloud import file_md5 +from dvc.stage import Stage, OutputNoCacheError, OutputOutsideOfRepoError +from dvc.stage import OutputDoesNotExistError, OutputIsNotFileError +from dvc.project import StageNotFoundError + +from tests.basic_env import TestDvc + + +class TestGC(TestDvc): + def test_gc(self): + stage = self.dvc.add(self.FOO) + good_cache = self.dvc.cache.all() + + bad_cache = [] + for i in ['1', '2', '3']: + path = os.path.join(self.dvc.cache.cache_dir, i) + self.create(path, i) + bad_cache.append(path) + + self.dvc.gc() + + self.assertTrue(os.path.isdir(self.dvc.cache.cache_dir)) + for c in bad_cache: + self.assertFalse(os.path.exists(c)) + + for c in good_cache: + self.assertTrue(os.path.exists(c)) + self.assertTrue(os.path.isfile(c)) diff --git a/tests/test_import.py b/tests/test_import.py deleted file mode 100644 index 8cec2ef39..000000000 --- a/tests/test_import.py +++ /dev/null @@ -1,119 +0,0 @@ -import os -import tempfile -import filecmp -from copy import copy - -from dvc.command.import_file import CmdImportFile, ImportFileError -from tests.basic_env import DirHierarchyEnvironment - - -class TestCmdRemove(DirHierarchyEnvironment): - def setUp(self): - DirHierarchyEnvironment.init_environment(self) - - def test_import_file_to_dir(self): - with tempfile.NamedTemporaryFile(mode='w') as temp: - content = 'Some data' - temp.write(content) - temp.flush() - - dir = os.path.join('data', self.dir1) - settings = copy(self.settings) - settings.parse_args('import {} {}'.format(temp.name, dir)) - cmd = CmdImportFile(settings) - cmd.run() - - dvc_name = os.path.join(dir, os.path.basename(temp.name)) - data_item = self.path_factory.existing_data_item(dvc_name) - - self.assertTrue(os.path.exists(data_item.data.relative)) - self.assertTrue(filecmp.cmp(data_item.data.relative, data_item.cache.relative)) - self.assertEqual(open(data_item.data.relative).read(), content) - - self.assertTrue(os.path.exists(data_item.cache.relative)) - self.assertEqual(open(data_item.cache.relative).read(), content) - - self.assertTrue(os.path.exists(data_item.state.relative)) - pass - - def test_import_file_to_file(self): - with tempfile.NamedTemporaryFile(mode='w') as temp: - content = 'Some data' - temp.write(content) - temp.flush() - - target_file_name = 'targ.txt' - target_file = os.path.join('data', self.dir1, target_file_name) - settings = copy(self.settings) - settings.parse_args('import {} {}'.format(temp.name, target_file)) - cmd = CmdImportFile(settings) - cmd.run() - - self.assertTrue(os.path.exists(target_file)) - self.assertTrue(open(target_file).read(), content) - pass - - def test_import_to_existing_dir(self): - with tempfile.NamedTemporaryFile(mode='w') as temp: - content = 'Some data' - temp.write(content) - temp.flush() - - dir = os.path.join('data', self.dir11) - settings = copy(self.settings) - settings.parse_args('import {} {}'.format(temp.name, dir)) - cmd = CmdImportFile(settings) - cmd.run() - - dvc_name = os.path.join(dir, os.path.basename(temp.name)) - - self.assertTrue(os.path.exists(dvc_name)) - self.assertTrue(open(dvc_name).read(), content) - pass - - def test_import_to_not_existing_dir(self): - with tempfile.NamedTemporaryFile(mode='w') as temp: - content = 'Some data' - temp.write(content) - temp.flush() - - dir = os.path.join('data', self.dir1, 'not_existing_dir/') - settings = copy(self.settings) - settings.parse_args('import {} {}'.format(temp.name, dir)) - cmd = CmdImportFile(settings) - - with self.assertRaises(ImportFileError): - cmd.run() - - dvc_name = os.path.join(dir, os.path.basename(temp.name)) - - self.assertFalse(os.path.exists(dir)) - self.assertFalse(os.path.exists(dvc_name)) - pass - - def test_multiple_import(self): - with tempfile.NamedTemporaryFile(mode='w') as temp1,\ - tempfile.NamedTemporaryFile(mode='w') as temp2: - content = 'Some data' - - temp1.write(content) - temp1.flush() - - temp2.write(content) - temp2.flush() - - dir = os.path.join('data', self.dir11) - settings = copy(self.settings) - settings.parse_args('import {} {} {}'.format(temp1.name, temp2.name, dir)) - cmd = CmdImportFile(settings) - cmd.run() - - dvc_name1 = os.path.join(dir, os.path.basename(temp1.name)) - dvc_name2 = os.path.join(dir, os.path.basename(temp2.name)) - - self.assertTrue(os.path.exists(dvc_name1)) - self.assertTrue(open(dvc_name1).read(), content) - - self.assertTrue(os.path.exists(dvc_name2)) - self.assertTrue(open(dvc_name2).read(), content) - pass diff --git a/tests/test_lock.py b/tests/test_lock.py new file mode 100644 index 000000000..f0963ff0a --- /dev/null +++ b/tests/test_lock.py @@ -0,0 +1,19 @@ +from dvc.lock import Lock + +from tests.basic_env import TestDvc + + +class TestLock(TestDvc): + def test_lock(self): + lock = Lock(self.dvc.dvc_dir) + lock.lock() + self.assertTrue(lock._lock.exists()) + self.assertTrue(lock._lock.acquired) + lock.unlock() + self.assertFalse(lock._lock.acquired) + + def test_with(self): + lock = Lock(self.dvc.dvc_dir) + with lock: + self.assertTrue(lock._lock.acquired) + self.assertFalse(lock._lock.acquired) diff --git a/tests/test_remove.py b/tests/test_remove.py new file mode 100644 index 000000000..d0fac4d2c --- /dev/null +++ b/tests/test_remove.py @@ -0,0 +1,28 @@ +import os + +from dvc.stage import Stage, OutputOutsideOfRepoError +from dvc.project import StageNotFoundError + +from tests.basic_env import TestDvc + + +class TestRemove(TestDvc): + def test(self): + stage = self.dvc.add(self.FOO) + stages_removed = self.dvc.remove(self.FOO) + + self.assertEqual(len(stages_removed), 1) + self.assertIsInstance(stages_removed[0], Stage) + self.assertEqual(stage.path, stages_removed[0].path) + + +class TestRemoveNonExistentFile(TestDvc): + def test(self): + with self.assertRaises(StageNotFoundError) as cx: + self.dvc.remove('non_existent_file') + + +class TestRemoveFileOutsideOfRepo(TestDvc): + def test(self): + with self.assertRaises(OutputOutsideOfRepoError) as cx: + self.dvc.remove(os.path.join(os.path.dirname(self.dvc.root_dir), self.FOO)) diff --git a/tests/test_repository_change.py b/tests/test_repository_change.py deleted file mode 100644 index cbce53338..000000000 --- a/tests/test_repository_change.py +++ /dev/null @@ -1,98 +0,0 @@ -import os -import shutil -import subprocess -import tempfile -from unittest import TestCase - -from dvc.config import ConfigI -from dvc.path.factory import PathFactory -from dvc.git_wrapper import GitWrapperI -from dvc.repository_change import RepositoryChange -from dvc.settings import Settings -from dvc.system import System - - -class BasicTestRepositoryChange(TestCase): - def setUp(self): - self.test_dir = System.get_long_path(tempfile.mkdtemp()) - self._old_curr_dir_abs = System.realpath(os.curdir) - - self.tearDown() - os.mkdir(self.test_dir) - os.chdir(self.test_dir) - os.mkdir('data') - - self._devnull = open(os.devnull, 'w') - subprocess.Popen(['git', 'init'], stdout=self._devnull, stderr=None).wait() - - self.git = GitWrapperI(self.test_dir) - self.config = ConfigI() - self.path_factory = PathFactory(self.git, self.config) - self.settings = Settings([], self.git, self.config) - - def tearDown(self): - shutil.rmtree(self.test_dir, ignore_errors=True) - os.chdir(self._old_curr_dir_abs) - - @staticmethod - def create_file(file2): - fd = open(file2, 'w+') - fd.write('random text') - fd.close() - - -class TestRepositoryChange(BasicTestRepositoryChange): - def test_all(self): - file_to_modify = os.path.join('data', 'file1.txt') - file_created_before_run = os.path.join('data', 'file2.txt') - file_created_by_run = os.path.join('data', 'out.txt') - file_to_remove = os.path.join('data', 'file3.txt') - - self.create_file(file_to_modify) - self.create_file(file_created_before_run) - self.create_file(file_to_remove) - - subprocess.Popen(['git', 'add', file_to_modify, file_to_remove], - stdout=self._devnull, stderr=None).wait() - subprocess.Popen(['git', 'commit', '-m', '"Adding one file3"'], - stdout=self._devnull, stderr=None).wait() - os.remove(file_to_remove) - - change = RepositoryChange(['echo', 'test'], - self.settings, - file_created_by_run, - file_to_modify, - shell=True) - - expected_new = [file_created_by_run, file_created_before_run] - new_file_abs = [x.data.dvc for x in change.new_data_items] - self.assertEqual(set(new_file_abs), set(expected_new)) - - self.assertEqual(len(change.removed_data_items), 1) - self.assertEqual(change.removed_data_items[0].data.dvc, file_to_remove) - self.assertEqual(len(change.modified_data_items), 1) - self.assertEqual(change.modified_data_items[0].data.dvc, file_to_modify) - - expected_to_change_set = set(expected_new + [file_to_modify]) - changed_file_names = [x.data.dvc for x in change.changed_data_items] - self.assertEqual(set(changed_file_names), expected_to_change_set) - - self.assertEqual([], change.externally_created_files) - pass - - -class TestRepositoryChangeDeepInDirectory(BasicTestRepositoryChange): - def test(self): - os.mkdir(os.path.join('data', 'dir32')) - deep_file = os.path.join('data', 'dir32', 'file1.txt') - self.create_file(deep_file) - - change = RepositoryChange(['echo', 'text'], - self.settings, - deep_file, - None, - shell=True) - - self.assertEqual([x.data.dvc for x in change.new_data_items], [deep_file]) - - diff --git a/tests/test_repro.py b/tests/test_repro.py index 8a3175296..7a7359197 100644 --- a/tests/test_repro.py +++ b/tests/test_repro.py @@ -1,90 +1,73 @@ import os -from dvc.command.remove import CmdRemove -from dvc.command.repro import CmdRepro -from dvc.command.run import CmdRun -from dvc.executor import Executor -from tests.test_cmd_run import RunBasicTest +from tests.basic_env import TestDvc -class ReproBasicEnv(RunBasicTest): +class ReproBasicEnv(TestDvc): def setUp(self): super(ReproBasicEnv, self).setUp() + os.mkdir('data') + self.file_name1 = os.path.join('data', 'file1') self.file1_code_file = 'file1.py' - self.create_file_and_commit(self.file1_code_file, 'print("Hello")' + os.linesep + 'print("Mary")') - self.settings.parse_args(['run', - '--stdout', self.file_name1, - '--code', self.file1_code_file, - 'python', self.file1_code_file, '--not-repro']) - cmd_file1 = CmdRun(self.settings) - self.assertEqual(cmd_file1.code_dependencies, [self.file1_code_file]) - cmd_file1.run() + self.create(self.file1_code_file, 'print("Hello")' + os.linesep + 'print("Mary")') + stage = self.dvc.run(fname='file1.dvc', + outs=[self.file_name1], + deps_no_cache=[self.file1_code_file], + cmd='python {} --not-repro > {}'.format(self.file1_code_file, + self.file_name1)) self.file_name11 = os.path.join('data', 'file11') self.file11_code_file = 'file11.py' - self.create_file_and_commit(self.file11_code_file, - 'import sys' + os.linesep + 'print(open(sys.argv[1]).readline().strip())') + self.create(self.file11_code_file, + 'import sys' + os.linesep + 'print(open(sys.argv[1]).readline().strip())') - self.settings.parse_args(['run', - '--stdout', self.file_name11, - '--code', self.file11_code_file, - 'python', self.file11_code_file, self.file_name1]) - CmdRun(self.settings).run() + stage = self.dvc.run(fname='file11.dvc', + outs=[self.file_name11], + deps=[self.file_name1], + deps_no_cache=[self.file11_code_file], + cmd='python {} {} > {}'.format(self.file11_code_file, + self.file_name1, + self.file_name11)) + self.file_name11_stage_name = stage.path self.file_name2 = os.path.join('data', 'file2') self.file2_code_file = 'file2.py' - self.create_file_and_commit(self.file2_code_file, - 'print("Bobby")') - self.settings.parse_args(['run', - '--stdout', self.file_name2, - 'python', self.file2_code_file, '--not-repro']) - CmdRun(self.settings).run() + self.create(self.file2_code_file, 'print("Bobby")') + self.dvc.run(fname='file2.dvc', + outs=[self.file_name2], + cmd='python {} --not-repro > {}'.format(self.file2_code_file, + self.file_name2)) self.file_res_code_file = 'code_res.py' - self.create_file_and_commit(self.file_res_code_file, - 'import sys' + os.linesep + - 'text1 = open(sys.argv[1]).read()' + os.linesep + - 'text2 = open(sys.argv[2]).read()' + os.linesep + - 'print(text1 + text2)') + self.create(self.file_res_code_file, + 'import sys' + os.linesep + + 'text1 = open(sys.argv[1]).read()' + os.linesep + + 'text2 = open(sys.argv[2]).read()' + os.linesep + + 'print(text1 + text2)') self.file_name_res = os.path.join('data', 'file_res') - self.settings.parse_args(['run', - '--stdout', self.file_name_res, - '--code', self.file_res_code_file, - 'python', self.file_res_code_file, - self.file_name11, - self.file_name2]) + stage = self.dvc.run(fname='file_res.dvc', + outs=[self.file_name_res], + deps_no_cache=[self.file_res_code_file], + deps=[self.file_name11, self.file_name2], + cmd='python {} {} {} > {}'.format(self.file_res_code_file, + self.file_name11, + self.file_name2, + self.file_name_res)) + self.file_name_res_stage_name = stage.path - cmd_res = CmdRun(self.settings) - self.assertEqual(cmd_res.code_dependencies, [self.file_res_code_file]) - cmd_res.run() - - lines = list(filter(None, map(str.strip, open(self.file_name_res).readlines()))) - self.assertEqual(lines, ['Hello', 'Bobby']) - - def create_file_and_commit(self, file_name, content='Any', message='Just a commit'): - self.create_file(file_name, content) - self.commit_file(file_name, message) - - @staticmethod - def commit_file(file_name, message='Just a commit'): - Executor.exec_cmd_only_success(['git', 'add', file_name]) - Executor.exec_cmd_only_success(['git', 'commit', '-m', message]) - - def modify_file_and_commit(self, filename, content_to_add=' '): + def modify_file(self, filename, content_to_add=' '): fd = open(filename, 'a') fd.write(content_to_add) fd.close() - self.commit_file(filename) class ReproCodeDependencyTest(ReproBasicEnv): def test(self): - self.modify_file_and_commit(self.file_res_code_file) + self.modify_file(self.file_res_code_file) - self.settings.parse_args('repro {}'.format(self.file_name_res)) - CmdRepro(self.settings).run() + self.dvc.reproduce([self.file_name_res_stage_name]) self.assertEqual(open(self.file_name_res).read().strip(), 'Hello\nBobby') @@ -93,30 +76,25 @@ class ReproChangedDependency(ReproBasicEnv): def test(self): self.recreate_file1() - self.settings.parse_args('repro {}'.format(self.file_name11)) - CmdRepro(self.settings).run() + self.dvc.reproduce([self.file_name11_stage_name]) self.assertEqual(open(self.file_name11).read(), 'Goodbye\n') def recreate_file1(self): - self.settings.parse_args('remove {} --keep-in-cloud'.format(self.file_name1)) - CmdRemove(self.settings).run() + self.dvc.remove(self.file_name1) file1_code_file = 'file1_2.py' - self.create_file_and_commit(file1_code_file, 'print("Goodbye")' + os.linesep + 'print("Jack")') - self.settings.parse_args(['run', - '--stdout', self.file_name1, - '--code', file1_code_file, - 'python', file1_code_file, '--not-repro']) - - CmdRun(self.settings).run() + self.create(file1_code_file, 'print("Goodbye")' + os.linesep + 'print("Jack")') + self.dvc.run(fname='file1.dvc', + outs=[self.file_name1], + deps_no_cache=[file1_code_file], + cmd='python {} --not-repro > {}'.format(file1_code_file, self.file_name1)) class ReproChangedDeepDependency(ReproChangedDependency): def test(self): self.recreate_file1() - self.settings.parse_args('repro {}'.format(self.file_name_res)) - CmdRepro(self.settings).run() + self.dvc.reproduce([self.file_name_res_stage_name]) self.assertEqual(open(self.file_name_res).read().strip(), 'Goodbye\nBobby') diff --git a/tests/test_run.py b/tests/test_run.py new file mode 100644 index 000000000..11c9666b8 --- /dev/null +++ b/tests/test_run.py @@ -0,0 +1,35 @@ +import os +import filecmp + +from dvc.data_cloud import file_md5 + +from tests.basic_env import TestDvc + + +class TestRun(TestDvc): + def test(self): + cmd = 'python {} {} {}'.format(self.CODE, self.FOO, 'out') + deps = [self.FOO] + deps_no_cache = [self.CODE] + outs = [os.path.join(self.dvc.root_dir, 'out')] + outs_no_cache = [] + locked = False + fname = os.path.join(self.dvc.root_dir, 'out.dvc') + cwd = os.curdir + + stage = self.dvc.run(cmd, deps, deps_no_cache, outs, outs_no_cache, locked, fname, cwd) + + self.assertTrue(filecmp.cmp(self.FOO, 'out')) + self.assertTrue(os.path.isfile(stage.path)) + self.assertEqual(stage.cmd, cmd) + self.assertEqual(len(stage.deps), len(deps + deps_no_cache)) + self.assertEqual(len(stage.outs), len(outs + outs_no_cache)) + self.assertEqual(stage.outs[0].path, outs[0]) + self.assertEqual(stage.outs[0].md5, file_md5(self.FOO)[0]) + self.assertEqual(stage.locked, locked) + self.assertTrue(stage.path, fname) + + +class TestRunEmpty(TestDvc): + def test(self): + self.dvc.run('', [], [], [], [], False, 'empty.dvc', os.curdir) diff --git a/tests/test_scm.py b/tests/test_scm.py new file mode 100644 index 000000000..70fb50bb6 --- /dev/null +++ b/tests/test_scm.py @@ -0,0 +1,32 @@ +import os + +from git import Repo + +from dvc.scm import SCM, Base, Git + +from tests.basic_env import TestDir, TestGit + + +class TestSCM(TestDir): + def test_none(self): + self.assertIsInstance(SCM(self._root_dir), Base) + + 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_ignore(self): + Git(self._root_dir).ignore('foo') + self.assertTrue(os.path.isfile(Git.GITIGNORE)) + self.assertTrue('foo' in open(Git.GITIGNORE, 'r').readlines()) + + def test_commit(self): + G = Git(self._root_dir) + G.add(['foo']) + G.commit('add') + self.assertTrue('foo' in self.git.git.ls_files()) diff --git a/tests/test_state.py b/tests/test_state.py new file mode 100644 index 000000000..642bed0a8 --- /dev/null +++ b/tests/test_state.py @@ -0,0 +1,31 @@ +import os + +from dvc.state import State, StateEntry + +from tests.basic_env import TestDvc + + +class TestState(TestDvc): + def test_update(self): + path = os.path.join(self.dvc.root_dir, '1') + md5_1 = '1' + md5_2 = '2' + mtime_1 = 1 + mtime_2 = 2 + + state = State(self.dvc.root_dir, self.dvc.dvc_dir) + self.assertIsNone(state.get(path)) + + state.add(path, md5_1, mtime_1) + entry = state.get(path) + self.assertIsInstance(entry, StateEntry) + self.assertEqual(entry.path, path) + self.assertEqual(entry.md5, md5_1) + self.assertEqual(entry.mtime, mtime_1) + + state.update(path, md5_2, mtime_2) + entry = state.get(path) + self.assertIsInstance(entry, StateEntry) + self.assertEqual(entry.path, path) + self.assertEqual(entry.md5, md5_2) + self.assertEqual(entry.mtime, mtime_2) diff --git a/tests/test_utils.py b/tests/test_utils.py index 549ae9e86..b1344d89c 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -3,27 +3,10 @@ import shutil import filecmp from dvc import utils -from tests.basic_env import DirHierarchyEnvironment +from tests.basic_env import TestDvc -class TestExecutor(DirHierarchyEnvironment): - def setUp(self): - DirHierarchyEnvironment.init_environment(self) - - def tearDown(self): - pass - - def test_rmtree(self): - root = 'testdir' - - os.makedirs(root + '/subdir') - with open(root + '/file1', 'w+') as f: - f.write('file1contents') - with open(root + '/subdir/file2', 'w+') as f: - f.write('file2contents') - - utils.rmtree(root) - self.assertFalse(os.path.exists(root)) +class TestUtils(TestDvc): def test_copyfile(self): src = 'file1' dest = 'file2'