From 9c10a643a753d54451f908c7ad0b360b0c3b9a7f Mon Sep 17 00:00:00 2001 From: Ruslan Kuprieiev Date: Tue, 2 Jan 2018 09:48:03 +0200 Subject: [PATCH] win: enable unit tests Signed-off-by: Ruslan Kuprieiev --- appveyor.yml | 14 +++++++++++++- dvc/stage.py | 12 +++++++++--- dvc/system.py | 22 ++++++++++++++++++---- dvc/utils.py | 2 +- requirements.txt | 1 + setup.py | 1 + tests/basic_env.py | 1 - tests/test_checkout.py | 2 ++ 8 files changed, 45 insertions(+), 10 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index dd3dd6b73..b0f30e223 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -29,7 +29,19 @@ environment: PYTHON_VERSION: "3.4.x" # currently 3.4.3 PYTHON_ARCH: "64" -build_script: +install: + - pip install -r requirements.txt + - python setup.py install + +build: false + +before_test: + - pip install -r test-requirements.txt + +test_script: + - nosetests --cover-inclusive --cover-erase --cover-package=dvc --with-coverage + +after_test: - .\build_windows.cmd artifacts: diff --git a/dvc/stage.py b/dvc/stage.py index 24b60dbbe..ec95cb1fd 100644 --- a/dvc/stage.py +++ b/dvc/stage.py @@ -57,7 +57,7 @@ class Output(object): else: changed = os.path.exists(self.path) \ and os.path.exists(self.cache) \ - and not os.path.samefile(self.path, self.cache) + and not System.samefile(self.path, self.cache) if changed: self.project.logger.debug('{} changed'.format(self.path)) @@ -75,7 +75,10 @@ class Output(object): if not os.path.exists(self.path) and not os.path.exists(self.cache): raise OutputNoCacheError(self.path) - if os.path.exists(self.path) and os.path.exists(self.cache) and os.path.samefile(self.path, self.cache): + if os.path.exists(self.path) and \ + os.path.exists(self.cache) and \ + System.samefile(self.path, self.cache) and \ + os.stat(self.cache).st_mode & stat.S_IREAD: return if os.path.exists(self.cache): @@ -94,6 +97,8 @@ class Output(object): System.hardlink(src, link) + os.chmod(self.path, stat.S_IREAD) + def checkout(self): if not self.use_cache: return @@ -118,7 +123,6 @@ class Output(object): self.project.scm.ignore(self.path) self.link() - os.chmod(self.path, stat.S_IREAD) def dumpd(self, cwd): return { @@ -205,7 +209,9 @@ class Stage(object): for out in self.outs: if os.path.exists(out.path): self.project.logger.debug("Removing '{}'".format(out.path)) + os.chmod(out.path, stat.S_IWUSR) os.unlink(out.path) + os.chmod(out.cache, stat.S_IREAD) def remove(self): self.remove_outs() diff --git a/dvc/system.py b/dvc/system.py index 55f61ccc9..99c662e9a 100644 --- a/dvc/system.py +++ b/dvc/system.py @@ -5,11 +5,13 @@ from builtins import str if os.name == 'nt': from ctypes import create_unicode_buffer, windll + import ntfsutils.hardlink as winlink from dvc.executor import Executor class System(object): + SYMLINK_OUTPUT = '' LONG_PATH_BUFFER_SIZE = 1024 @staticmethod @@ -21,14 +23,26 @@ class System(object): if System.is_unix(): return os.link(source, link_name) - raise Exception('Not Implemented Yet') + return winlink.create(source, link_name) @staticmethod def samefile(path1, path2): if System.is_unix(): return os.path.samefile(path1, path2) - raise Exception('Not Implemented Yet') + return winlink.samefile(path1, path2) + + @staticmethod + def _get_symlink_string(path): + code, output, _ = Executor.exec_cmd(["dir", path], shell=True) + if code != 0: + return None + + lines = output.split('\n') + for line in lines: + if System.SYMLINK_OUTPUT in line: + return line + return None @staticmethod def realpath(path): @@ -54,8 +68,8 @@ class System(object): if System.is_unix(): return path - buffer = create_unicode_buffer(System.LONG_PATH_BUFFER_SIZE) - get_long_path_name = windll.kernel32.GetLongPathNameW + buffer = ctypes.create_unicode_buffer(System.LONG_PATH_BUFFER_SIZE) + get_long_path_name = ctypes.windll.kernel32.GetLongPathNameW result = get_long_path_name(u'%s' % str(path), buffer, System.LONG_PATH_BUFFER_SIZE) if result == 0 or result > System.LONG_PATH_BUFFER_SIZE: return path diff --git a/dvc/utils.py b/dvc/utils.py index c2ceffe0c..a0182732b 100644 --- a/dvc/utils.py +++ b/dvc/utils.py @@ -12,7 +12,7 @@ from dvc.progress import progress from dvc.logger import Logger -LOCAL_CHUNK_SIZE = 1024*1024*1024 +LOCAL_CHUNK_SIZE = 1024*1024 def cached_property(func): diff --git a/requirements.txt b/requirements.txt index 8a630af32..80ba6217b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -93,3 +93,4 @@ xmltodict==0.11.0 pyyaml==3.12 tinydb==3.7.0 gitpython==2.1.8 +ntfsutils==0.1.3 diff --git a/setup.py b/setup.py index 1a005398b..2d6fd037f 100644 --- a/setup.py +++ b/setup.py @@ -98,6 +98,7 @@ install_requires = [ 'pyyaml==3.12', 'tinydb==3.7.0', 'gitpython==2.1.8', + 'ntfsutils==0.1.3', ] if platform.system() == 'Darwin': diff --git a/tests/basic_env.py b/tests/basic_env.py index 116b10086..60c1bc798 100644 --- a/tests/basic_env.py +++ b/tests/basic_env.py @@ -37,7 +37,6 @@ class TestDir(TestCase): def tearDown(self): self._popd() - shutil.rmtree(self._root_dir) class TestGit(TestDir): diff --git a/tests/test_checkout.py b/tests/test_checkout.py index aec522671..7d4954f3a 100644 --- a/tests/test_checkout.py +++ b/tests/test_checkout.py @@ -1,4 +1,5 @@ import os +import stat import shutil import filecmp @@ -24,6 +25,7 @@ class TestCheckout(TestDvc): stage = self.dvc.run(cmd, deps, deps_no_cache, outs, outs_no_cache, locked, fname, cwd) shutil.copy(self.FOO, 'orig') + os.chmod(self.FOO, stat.S_IWRITE) os.unlink(self.FOO) self.dvc.checkout() self.assertTrue(os.path.isfile(self.FOO))