2017-08-17 05:56:32 +03:00
|
|
|
#!/usr/bin/env python3
|
2019-06-20 02:58:25 +03:00
|
|
|
# Copyright (c) Facebook, Inc. and its affiliates.
|
2017-08-17 05:56:32 +03:00
|
|
|
#
|
2019-06-20 02:58:25 +03:00
|
|
|
# This software may be used and distributed according to the terms of the
|
|
|
|
# GNU General Public License version 2.
|
2017-08-17 05:56:32 +03:00
|
|
|
|
|
|
|
import os
|
2018-03-24 04:17:05 +03:00
|
|
|
import stat
|
2018-03-01 23:25:12 +03:00
|
|
|
import time
|
2017-08-17 05:56:32 +03:00
|
|
|
|
2018-05-10 07:33:49 +03:00
|
|
|
from facebook.eden.ttypes import TimeSpec
|
|
|
|
|
|
|
|
from .lib import edenclient, testcase
|
|
|
|
|
2018-04-18 22:27:18 +03:00
|
|
|
|
2017-08-17 05:56:32 +03:00
|
|
|
@testcase.eden_repo_test
|
2018-04-05 03:31:25 +03:00
|
|
|
class DebugGetPathTest(testcase.EdenRepoTest):
|
2018-04-05 03:31:28 +03:00
|
|
|
def populate_repo(self) -> None:
|
2018-05-10 07:33:49 +03:00
|
|
|
self.repo.write_file("hello", "hola\n")
|
|
|
|
self.repo.commit("Initial commit.")
|
2017-08-17 05:56:32 +03:00
|
|
|
|
2018-04-05 03:31:28 +03:00
|
|
|
def test_getpath_root_inode(self) -> None:
|
2018-05-10 07:33:49 +03:00
|
|
|
"""
|
2017-08-25 18:24:43 +03:00
|
|
|
Test that calling `eden debug getpath 1` returns the path to the eden
|
2017-08-17 05:56:32 +03:00
|
|
|
mount, and indicates that the inode is loaded.
|
2018-05-10 07:33:49 +03:00
|
|
|
"""
|
|
|
|
output = self.eden.run_cmd("debug", "getpath", "1", cwd=self.mount)
|
2017-08-17 05:56:32 +03:00
|
|
|
|
2018-05-10 07:33:49 +03:00
|
|
|
self.assertEqual("loaded " + self.mount + "\n", output)
|
2017-08-17 05:56:32 +03:00
|
|
|
|
2018-04-05 03:31:28 +03:00
|
|
|
def test_getpath_dot_eden_inode(self) -> None:
|
2018-05-10 07:33:49 +03:00
|
|
|
"""
|
2018-04-05 03:31:28 +03:00
|
|
|
Test that calling `eden debug getpath ${ino}` returns the path to the
|
|
|
|
.eden directory, and indicates that the inode is loaded.
|
2018-05-10 07:33:49 +03:00
|
|
|
"""
|
|
|
|
st = os.lstat(os.path.join(self.mount, ".eden"))
|
2018-03-24 04:17:05 +03:00
|
|
|
self.assertTrue(stat.S_ISDIR(st.st_mode))
|
|
|
|
ino = st.st_ino
|
|
|
|
|
2018-05-10 07:33:49 +03:00
|
|
|
output = self.eden.run_cmd("debug", "getpath", str(ino), cwd=self.mount)
|
2017-08-17 05:56:32 +03:00
|
|
|
|
2018-05-10 07:33:49 +03:00
|
|
|
self.assertEqual("loaded " + os.path.join(self.mount, ".eden") + "\n", output)
|
2017-08-17 05:56:32 +03:00
|
|
|
|
2018-04-05 03:31:28 +03:00
|
|
|
def test_getpath_invalid_inode(self) -> None:
|
2018-05-10 07:33:49 +03:00
|
|
|
"""
|
2017-08-25 18:24:43 +03:00
|
|
|
Test that calling `eden debug getpath 1234` raises an error since
|
2017-08-17 05:56:32 +03:00
|
|
|
1234 is not a valid inode number
|
2018-05-10 07:33:49 +03:00
|
|
|
"""
|
2017-08-17 05:56:32 +03:00
|
|
|
with self.assertRaises(edenclient.EdenCommandError) as context:
|
2018-05-10 07:33:49 +03:00
|
|
|
self.eden.run_cmd("debug", "getpath", "1234", cwd=self.mount)
|
|
|
|
self.assertIn(
|
2019-07-11 00:27:04 +03:00
|
|
|
"unknown inode number 1234", context.exception.stderr.decode()
|
2018-05-10 07:33:49 +03:00
|
|
|
)
|
2017-08-25 18:24:43 +03:00
|
|
|
|
2018-04-05 03:31:28 +03:00
|
|
|
def test_getpath_unloaded_inode(self) -> None:
|
2018-05-10 07:33:49 +03:00
|
|
|
"""
|
2018-02-09 21:54:26 +03:00
|
|
|
Test that calling `eden debug getpath` on an unloaded inode returns the
|
2017-08-25 18:24:43 +03:00
|
|
|
correct path and indicates that it is unloaded
|
2018-05-10 07:33:49 +03:00
|
|
|
"""
|
|
|
|
dirpath = os.path.join(self.mount, "dir")
|
|
|
|
filepath = os.path.join(dirpath, "file")
|
2018-03-01 23:25:12 +03:00
|
|
|
|
2017-08-25 18:24:43 +03:00
|
|
|
# Create the file
|
2018-05-10 07:33:49 +03:00
|
|
|
self.write_file(os.path.join("dir", "file"), "blah")
|
2017-08-25 18:24:43 +03:00
|
|
|
# Get the inodeNumber
|
2018-03-01 23:25:12 +03:00
|
|
|
stat = os.stat(filepath)
|
2018-05-10 07:33:49 +03:00
|
|
|
self.unload_one_inode_under("dir")
|
2018-03-01 23:25:12 +03:00
|
|
|
|
2017-08-25 18:24:43 +03:00
|
|
|
# Get the path for dir/file from its inodeNumber
|
2018-05-10 07:33:49 +03:00
|
|
|
output = self.eden.run_cmd("debug", "getpath", str(stat.st_ino), cwd=self.mount)
|
2017-08-25 18:24:43 +03:00
|
|
|
|
2018-05-10 07:33:49 +03:00
|
|
|
self.assertEqual(f"unloaded {filepath}\n", output)
|
2017-08-25 18:24:43 +03:00
|
|
|
|
2018-04-05 03:31:28 +03:00
|
|
|
def test_getpath_unloaded_inode_rename_parent(self) -> None:
|
2018-05-10 07:33:49 +03:00
|
|
|
"""
|
2017-08-25 18:24:43 +03:00
|
|
|
Test that when an unloaded inode has one of its parents renamed,
|
|
|
|
`eden debug getpath` returns the new path
|
2018-05-10 07:33:49 +03:00
|
|
|
"""
|
2017-08-25 18:24:43 +03:00
|
|
|
# Create the file
|
2018-05-10 07:33:49 +03:00
|
|
|
self.write_file(os.path.join("foo", "bar", "test.txt"), "blah")
|
|
|
|
dirpath = os.path.join(self.mount, "foo", "bar")
|
2017-08-25 18:24:43 +03:00
|
|
|
# Get the inodeNumber
|
2018-05-10 07:33:49 +03:00
|
|
|
stat = os.stat(os.path.join(dirpath, "test.txt"))
|
2018-03-01 23:25:12 +03:00
|
|
|
|
2018-05-10 07:33:49 +03:00
|
|
|
self.unload_one_inode_under(os.path.join("foo", "bar"))
|
2018-03-01 23:25:12 +03:00
|
|
|
|
2017-08-25 18:24:43 +03:00
|
|
|
# Rename the foo directory
|
2018-05-10 07:33:49 +03:00
|
|
|
os.rename(os.path.join(self.mount, "foo"), os.path.join(self.mount, "newname"))
|
2017-08-25 18:24:43 +03:00
|
|
|
# Get the new path for the file from its inodeNumber
|
2018-05-10 07:33:49 +03:00
|
|
|
output = self.eden.run_cmd("debug", "getpath", str(stat.st_ino), cwd=self.mount)
|
2017-08-25 18:24:43 +03:00
|
|
|
|
|
|
|
self.assertEqual(
|
2018-05-10 07:33:49 +03:00
|
|
|
"unloaded " + os.path.join(self.mount, "newname", "bar", "test.txt") + "\n",
|
|
|
|
output,
|
|
|
|
)
|
2017-08-25 18:24:43 +03:00
|
|
|
|
2018-04-05 03:31:28 +03:00
|
|
|
def unload_one_inode_under(self, path: str) -> None:
|
2018-03-01 23:25:12 +03:00
|
|
|
# TODO: To support unloading more than one inode, sum the return value
|
|
|
|
# until count is reached our the attempt limit has been reached.
|
|
|
|
remaining_attempts = 5
|
|
|
|
while True:
|
|
|
|
age = TimeSpec() # zero
|
|
|
|
with self.eden.get_thrift_client() as client:
|
2018-11-14 23:13:46 +03:00
|
|
|
count = client.unloadInodeForPath(
|
|
|
|
os.fsencode(self.mount), os.fsencode(path), age
|
|
|
|
)
|
2018-03-01 23:25:12 +03:00
|
|
|
if remaining_attempts == 1:
|
|
|
|
self.assertEqual(1, count)
|
|
|
|
elif count == 1:
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
remaining_attempts -= 1
|
|
|
|
time.sleep(1)
|
|
|
|
continue
|
|
|
|
|
2018-04-05 03:31:28 +03:00
|
|
|
def test_getpath_unlinked_inode(self) -> None:
|
2018-05-10 07:33:49 +03:00
|
|
|
"""
|
2017-08-25 18:24:43 +03:00
|
|
|
Test that when an inode is unlinked, `eden debug getpath` indicates
|
|
|
|
that it is unlinked
|
2018-05-10 07:33:49 +03:00
|
|
|
"""
|
2017-08-25 18:24:43 +03:00
|
|
|
# Create the file
|
2018-05-10 07:33:49 +03:00
|
|
|
self.write_file(os.path.join("foo", "bar", "test.txt"), "blah")
|
2017-08-25 18:24:43 +03:00
|
|
|
# Keep an open file handle so that the inode doesn't become invalid
|
2018-05-10 07:33:49 +03:00
|
|
|
f = open(os.path.join(self.mount, "foo", "bar", "test.txt"))
|
2017-08-25 18:24:43 +03:00
|
|
|
# Get the inodeNumber
|
2018-05-10 07:33:49 +03:00
|
|
|
stat = os.stat(os.path.join(self.mount, "foo", "bar", "test.txt"))
|
2017-08-25 18:24:43 +03:00
|
|
|
# Unlink the file
|
2018-05-10 07:33:49 +03:00
|
|
|
os.unlink(os.path.join(self.mount, "foo", "bar", "test.txt"))
|
|
|
|
output = self.eden.run_cmd("debug", "getpath", str(stat.st_ino), cwd=self.mount)
|
2017-08-25 18:24:43 +03:00
|
|
|
# Close the file handle
|
|
|
|
f.close()
|
|
|
|
|
2018-08-22 21:05:44 +03:00
|
|
|
self.assertEqual("loaded [unlinked]\n", output)
|