Skip to content

Commit 6e930d1

Browse files
Merge pull request #75 from xenserver-next/fix-MountingAccessor-writeFile
Test, cover and fix the method writeFile() of subclasses of xcp.accessor.MountingAccessor
2 parents 70b4531 + 07d5abd commit 6e930d1

File tree

3 files changed

+238
-1
lines changed

3 files changed

+238
-1
lines changed

Diff for: stubs/pyfakefs/fake_filesystem.pyi

+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
from _typeshed import Incomplete # pylint: disable=import-error
2+
3+
4+
def set_uid(uid) -> None: ...
5+
def set_gid(gid) -> None: ...
6+
def reset_ids() -> None: ...
7+
def is_root(): ...
8+
9+
10+
class FakeFilesystem:
11+
path_separator: Incomplete
12+
alternative_path_separator: Incomplete
13+
patcher: Incomplete
14+
is_windows_fs: Incomplete
15+
is_macos: Incomplete
16+
is_case_sensitive: Incomplete
17+
root: Incomplete
18+
cwd: Incomplete
19+
umask: Incomplete
20+
open_files: Incomplete
21+
mount_points: Incomplete
22+
dev_null: Incomplete
23+
def __init__(
24+
self,
25+
path_separator=...,
26+
total_size: Incomplete | None = ...,
27+
patcher: Incomplete | None = ...,
28+
) -> None: ...
29+
@property
30+
def is_linux(self): ...
31+
def reset(self, total_size: Incomplete | None = ...) -> None: ...
32+
def pause(self) -> None: ...
33+
def resume(self) -> None: ...
34+
def line_separator(self): ...
35+
def raise_os_error(
36+
self, errno, filename: Incomplete | None = ..., winerror: Incomplete | None = ...
37+
) -> None: ...
38+
def raise_io_error(self, errno, filename: Incomplete | None = ...) -> None: ...
39+
def add_mount_point(self, path, total_size: Incomplete | None = ...): ...
40+
def get_disk_usage(self, path: Incomplete | None = ...): ...
41+
def set_disk_usage(self, total_size, path: Incomplete | None = ...) -> None: ...
42+
def change_disk_usage(self, usage_change, file_path, st_dev) -> None: ...
43+
def stat(self, entry_path, follow_symlinks: bool = ...): ...
44+
def chmod(self, path, mode, follow_symlinks: bool = ...) -> None: ...
45+
def utime(
46+
self,
47+
path,
48+
times: Incomplete | None = ...,
49+
ns: Incomplete | None = ...,
50+
follow_symlinks: bool = ...,
51+
) -> None: ...
52+
def get_open_file(self, file_des): ...
53+
def has_open_file(self, file_object): ...
54+
def normcase(self, path): ...
55+
def normpath(self, path): ...
56+
def absnormpath(self, path): ...
57+
def splitpath(self, path): ...
58+
def splitdrive(self, path): ...
59+
def joinpaths(self, *paths): ...
60+
def ends_with_path_separator(self, file_path): ...
61+
def is_filepath_ending_with_separator(self, path): ...
62+
def exists(self, file_path, check_link: bool = ...): ...
63+
def resolve_path(self, file_path, allow_fd: bool = ..., raw_io: bool = ...): ...
64+
def get_object_from_normpath(self, file_path, check_read_perm: bool = ...): ...
65+
def get_object(self, file_path, check_read_perm: bool = ...): ...
66+
def resolve(
67+
self,
68+
file_path,
69+
follow_symlinks: bool = ...,
70+
allow_fd: bool = ...,
71+
check_read_perm: bool = ...,
72+
): ...
73+
def lresolve(self, path): ...
74+
def add_object(self, file_path, file_object, error_fct: Incomplete | None = ...) -> None: ...
75+
def rename(self, old_file_path, new_file_path, force_replace: bool = ...) -> None: ...
76+
def remove_object(self, file_path) -> None: ...
77+
def make_string_path(self, path): ...
78+
def create_dir(self, directory_path, perm_bits=...): ...
79+
def create_file(
80+
self,
81+
file_path,
82+
st_mode=...,
83+
contents: str = ...,
84+
st_size: Incomplete | None = ...,
85+
create_missing_dirs: bool = ...,
86+
apply_umask: bool = ...,
87+
encoding: Incomplete | None = ...,
88+
errors: Incomplete | None = ...,
89+
side_effect: Incomplete | None = ...,
90+
): ...
91+
def add_real_file(
92+
self, source_path, read_only: bool = ..., target_path: Incomplete | None = ...
93+
): ...
94+
def add_real_symlink(self, source_path, target_path: Incomplete | None = ...): ...
95+
def add_real_directory(
96+
self,
97+
source_path,
98+
read_only: bool = ...,
99+
lazy_read: bool = ...,
100+
target_path: Incomplete | None = ...,
101+
): ...
102+
def add_real_paths(
103+
self, path_list, read_only: bool = ..., lazy_dir_read: bool = ...
104+
) -> None: ...
105+
def create_file_internally(
106+
self,
107+
file_path,
108+
st_mode=...,
109+
contents: str = ...,
110+
st_size: Incomplete | None = ...,
111+
create_missing_dirs: bool = ...,
112+
apply_umask: bool = ...,
113+
encoding: Incomplete | None = ...,
114+
errors: Incomplete | None = ...,
115+
read_from_real_fs: bool = ...,
116+
raw_io: bool = ...,
117+
side_effect: Incomplete | None = ...,
118+
): ...
119+
def create_symlink(self, file_path, link_target, create_missing_dirs: bool = ...): ...
120+
def link(self, old_path, new_path, follow_symlinks: bool = ...): ...
121+
def readlink(self, path): ...
122+
def makedir(self, dir_name, mode=...) -> None: ...
123+
def makedirs(self, dir_name, mode=..., exist_ok: bool = ...) -> None: ...
124+
def isdir(self, path, follow_symlinks: bool = ...): ...
125+
def isfile(self, path, follow_symlinks: bool = ...): ...
126+
def islink(self, path): ...
127+
def confirmdir(self, target_directory): ...
128+
def remove(self, path) -> None: ...
129+
def rmdir(self, target_directory, allow_symlink: bool = ...) -> None: ...
130+
def listdir(self, target_directory): ...
131+
132+
class FakeFileOpen:
133+
__name__: str
134+
filesystem: Incomplete
135+
raw_io: Incomplete
136+
def __init__(
137+
self, filesystem, delete_on_close: bool = ..., use_io: bool = ..., raw_io: bool = ...
138+
) -> None: ...
139+
def __call__(self, *args, **kwargs): ...
140+
def call(
141+
self,
142+
file_,
143+
mode: str = ...,
144+
buffering: int = ...,
145+
encoding: Incomplete | None = ...,
146+
errors: Incomplete | None = ...,
147+
newline: Incomplete | None = ...,
148+
closefd: bool = ...,
149+
opener: Incomplete | None = ...,
150+
open_modes: Incomplete | None = ...,
151+
): ...

Diff for: tests/test_mountingaccessor.py

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
"""pytest tests testing subclasses of xcp.accessor.MountingAccessor using pyfakefs"""
2+
import sys
3+
from io import BytesIO
4+
from typing import cast
5+
6+
from mock import patch
7+
from pyfakefs.fake_filesystem import FakeFileOpen, FakeFilesystem
8+
9+
import xcp.accessor
10+
11+
binary_data = b"\x00\x1b\x5b\x95\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xcc\xdd\xee\xff"
12+
13+
14+
def test_device_accessor(fs):
15+
# type: (FakeFilesystem) -> None
16+
accessor = xcp.accessor.createAccessor("dev:///dev/device", False)
17+
check_mounting_accessor(accessor, fs)
18+
19+
20+
def test_nfs_accessor(fs):
21+
# type: (FakeFilesystem) -> None
22+
accessor = xcp.accessor.createAccessor("nfs://server/path", False)
23+
check_mounting_accessor(accessor, fs)
24+
25+
26+
def check_mounting_accessor(accessor, fs):
27+
# type: (xcp.accessor.MountingAccessor, FakeFilesystem) -> None
28+
"""Test subclasses of MountingAccessor (with xcp.cmd.runCmd in xcp.mount mocked)"""
29+
30+
with patch("xcp.cmd.runCmd") as mount_runcmd:
31+
mount_runcmd.return_value = (0, "", "")
32+
accessor.start()
33+
34+
assert accessor.location
35+
assert fs.isdir(accessor.location)
36+
37+
location = accessor.location
38+
39+
if sys.version_info.major >= 3:
40+
fs.add_mount_point(location)
41+
42+
assert check_binary_read(accessor, location, fs)
43+
assert check_binary_write(accessor, location, fs)
44+
45+
if sys.version_info.major >= 3:
46+
fs.mount_points.pop(location)
47+
48+
with patch("xcp.cmd.runCmd"):
49+
accessor.finish()
50+
51+
assert not fs.exists(location)
52+
53+
assert not accessor.location
54+
55+
56+
def check_binary_read(accessor, location, fs):
57+
# type: (xcp.accessor.MountingAccessor, str, FakeFilesystem) -> bool
58+
"""Test the openAddress() method of subclasses of xcp.accessor.MountingAccessor"""
59+
60+
name = "binary_file"
61+
path = location + "/" + name
62+
63+
assert fs.create_file(path, contents=cast(str, binary_data))
64+
65+
assert accessor.access(name)
66+
67+
binary_file = accessor.openAddress(name)
68+
assert not isinstance(binary_file, bool)
69+
70+
fs.remove(path)
71+
return cast(bytes, binary_file.read()) == binary_data
72+
73+
74+
def check_binary_write(accessor, location, fs):
75+
# type: (xcp.accessor.MountingAccessor, str, FakeFilesystem) -> bool
76+
"""Test the writeFile() method of subclasses of xcp.accessor.MountingAccessor"""
77+
78+
name = "binary_file_written_by_accessor"
79+
accessor.writeFile(BytesIO(binary_data), name)
80+
81+
assert accessor.access(name)
82+
83+
with FakeFileOpen(fs, delete_on_close=True)(location + "/" + name, "rb") as written:
84+
return cast(bytes, written.read()) == binary_data

Diff for: xcp/accessor.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,15 @@ def finish(self):
155155
return
156156
self.start_count -= 1
157157
if self.start_count == 0:
158+
assert self.location
158159
mount.umount(self.location)
159160
os.rmdir(self.location)
160161
self.location = None
161162

162163
def writeFile(self, in_fh, out_name):
164+
assert self.location
163165
logger.info("Copying to %s" % os.path.join(self.location, out_name))
164-
out_fh = open(os.path.join(self.location, out_name), 'w')
166+
out_fh = open(os.path.join(self.location, out_name), "wb")
165167
return self._writeFile(in_fh, out_fh)
166168

167169
def __del__(self):

0 commit comments

Comments
 (0)