Add more integration testing for the transfer kitten

This commit is contained in:
Kovid Goyal 2023-07-26 16:24:31 +05:30
parent e24dd7be2c
commit c11d1d3430
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
5 changed files with 107 additions and 45 deletions

View File

@ -48,7 +48,9 @@
:code:`/path/to/dir/` on the local computer. :code:`/path/to/dir/` on the local computer.
Note that when copying multiple files or directories, the destination Note that when copying multiple files or directories, the destination
must be an existing directory on the receiving computer. must be an existing directory on the receiving computer. Relative file
paths are resolved with respect to the current directory on the computer
running the kitten and the home directory on the other computer.
''' '''

View File

@ -26,6 +26,7 @@ import (
"kitty/tools/utils/humanize" "kitty/tools/utils/humanize"
"kitty/tools/wcswidth" "kitty/tools/wcswidth"
"golang.org/x/exp/slices"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@ -683,6 +684,8 @@ func files_for_receive(opts *Options, dest string, files []*remote_file, remote_
} }
spec_paths := make([]string, len(specs)) spec_paths := make([]string, len(specs))
for i := range specs { for i := range specs {
// use the shortest path as the path for the spec
slices.SortStableFunc(spec_map[i], func(a, b *remote_file) bool { return len(a.remote_path) < len(b.remote_path) })
spec_paths[i] = spec_map[i][0].remote_path spec_paths[i] = spec_map[i][0].remote_path
} }
if opts.Mode == "mirror" { if opts.Mode == "mirror" {

View File

@ -177,14 +177,14 @@ func process(opts *Options, paths []string, remote_base string, counter *int) (a
func process_mirrored_files(opts *Options, args []string) (ans []*File, err error) { func process_mirrored_files(opts *Options, args []string) (ans []*File, err error) {
paths := utils.Map(func(x string) string { return abspath(expand_home(x)) }, args) paths := utils.Map(func(x string) string { return abspath(expand_home(x)) }, args)
common_path := utils.Commonpath(paths...) home := strings.TrimRight(home_path(), string(filepath.Separator)) + string(filepath.Separator)
home := strings.TrimRight(home_path(), string(filepath.Separator)) paths = utils.Map(func(path string) string {
if common_path != "" && strings.HasPrefix(common_path, home+string(filepath.Separator)) { if strings.HasPrefix(path, home) {
paths = utils.Map(func(x string) string { r, _ := filepath.Rel(home, path)
r, _ := filepath.Rel(home, x)
return filepath.Join("~", r) return filepath.Join("~", r)
}, paths) }
} return path
}, paths)
counter := 0 counter := 0
return process(opts, paths, "", &counter) return process(opts, paths, "", &counter)
} }
@ -232,7 +232,7 @@ func files_for_send(opts *Options, args []string) (files []*File, err error) {
// detect symlinks to other transferred files // detect symlinks to other transferred files
for i, f := range files { for i, f := range files {
if f.file_type == FileType_symlink { if f.file_type == FileType_symlink {
link_dest, err := os.Readlink(f.local_path) link_dest, err := os.Readlink(f.expanded_local_path)
if err != nil { if err != nil {
remove = append(remove, i) remove = append(remove, i)
continue continue
@ -241,7 +241,7 @@ func files_for_send(opts *Options, args []string) (files []*File, err error) {
is_abs := filepath.IsAbs(link_dest) is_abs := filepath.IsAbs(link_dest)
q := link_dest q := link_dest
if !is_abs { if !is_abs {
q = filepath.Join(filepath.Dir(f.local_path), link_dest) q = filepath.Join(filepath.Dir(f.expanded_local_path), link_dest)
} }
st, err := os.Stat(q) st, err := os.Stat(q)
if err == nil { if err == nil {

View File

@ -447,9 +447,9 @@ class DestFile:
def __init__(self, ftc: FileTransmissionCommand) -> None: def __init__(self, ftc: FileTransmissionCommand) -> None:
self.name = ftc.name self.name = ftc.name
if not os.path.isabs(self.name): if not os.path.isabs(self.name):
self.name = os.path.expanduser(self.name) self.name = expand_home(self.name)
if not os.path.isabs(self.name): if not os.path.isabs(self.name):
self.name = os.path.join(tempfile.gettempdir(), self.name) self.name = abspath(self.name, use_home=True)
try: try:
self.existing_stat: Optional[os.stat_result] = os.stat(self.name, follow_symlinks=False) self.existing_stat: Optional[os.stat_result] = os.stat(self.name, follow_symlinks=False)
except OSError: except OSError:

View File

@ -181,6 +181,7 @@ class TestFileTransmission(BaseTest):
def setUp(self): def setUp(self):
self.direction_receive = False self.direction_receive = False
self.kitty_home = self.kitty_cwd = self.kitten_home = self.kitten_cwd = ''
super().setUp() super().setUp()
self.tdir = os.path.realpath(tempfile.mkdtemp()) self.tdir = os.path.realpath(tempfile.mkdtemp())
self.responses = [] self.responses = []
@ -196,8 +197,12 @@ def tearDown(self):
super().tearDown() super().tearDown()
def clean_tdir(self): def clean_tdir(self):
shutil.rmtree(self.tdir) for x in os.listdir(self.tdir):
self.tdir = os.path.realpath(tempfile.mkdtemp()) x = os.path.join(self.tdir, x)
if os.path.isdir(x):
shutil.rmtree(x)
else:
os.remove(x)
self.responses = [] self.responses = []
def cr(self, a, b): def cr(self, a, b):
@ -324,22 +329,18 @@ def test_rsync_hashers(self):
self.assertEqual(h128.hexdigest(), '8d6b60383dfa90c21be79eecd1b1353d') self.assertEqual(h128.hexdigest(), '8d6b60383dfa90c21be79eecd1b1353d')
@contextmanager @contextmanager
def run_kitten(self, cmd, home_dir='', allow=True): def run_kitten(self, cmd, home_dir='', allow=True, cwd=''):
cwd = os.path.realpath(tempfile.mkdtemp(suffix='-cwd', dir=self.tdir)) cwd = cwd or self.kitten_cwd or self.tdir
cmd = [kitten_exe(), 'transfer'] + (['--direction=receive'] if self.direction_receive else []) + cmd cmd = [kitten_exe(), 'transfer'] + (['--direction=receive'] if self.direction_receive else []) + cmd
env = {'PWD': cwd} env = {'PWD': cwd}
if home_dir: env['HOME'] = home_dir or self.kitten_home or self.tdir
env['HOME'] = home_dir with set_paths(home=self.kitty_home, cwd=self.kitty_cwd):
try:
pty = TransferPTY(cmd, cwd=cwd, allow=allow, env=env) pty = TransferPTY(cmd, cwd=cwd, allow=allow, env=env)
i = 10 i = 10
while i > 0 and not pty.screen_contents().strip(): while i > 0 and not pty.screen_contents().strip():
pty.process_input_from_child() pty.process_input_from_child()
i -= 1 i -= 1
yield pty yield pty
finally:
if os.path.exists(cwd):
shutil.rmtree(cwd)
def basic_transfer_tests(self): def basic_transfer_tests(self):
src = os.path.join(self.tdir, 'src') src = os.path.join(self.tdir, 'src')
@ -445,19 +446,62 @@ def de(path):
multiple_files('--transmit-deltas') multiple_files('--transmit-deltas')
multiple_files('--transmit-deltas') multiple_files('--transmit-deltas')
def setup_dirs(self):
self.clean_tdir()
self.kitty_home = os.path.join(self.tdir, 'kitty-home')
self.kitty_cwd = os.path.join(self.tdir, 'kitty-cwd')
self.kitten_home = os.path.join(self.tdir, 'kitten-home')
self.kitten_cwd = os.path.join(self.tdir, 'kitten-cwd')
tuple(map(os.mkdir, (self.kitty_home, self.kitty_cwd, self.kitten_home, self.kitten_cwd)))
def create_src(self, base):
src = os.path.join(base, 'src')
with open(src, 'wb') as s:
s.write(self.src_data)
return src
def mirror_test(self, src, dest, prefix=''):
self.create_src(src)
os.symlink('/', os.path.join(src, 'sym'))
os.mkdir(os.path.join(src, 'sub'))
os.link(os.path.join(src, 'src'), os.path.join(src, 'sub', 'hardlink'))
with self.run_kitten(['--mode=mirror', f'{prefix}src', f'{prefix}sym', f'{prefix}sub']) as pty:
pty.wait_till_child_exits(require_exit_code=0)
os.remove(os.path.join(dest, 'src'))
os.remove(os.path.join(dest, 'sym'))
shutil.rmtree(os.path.join(dest, 'sub'))
def test_transfer_receive(self): def test_transfer_receive(self):
self.direction_receive = True self.direction_receive = True
self.basic_transfer_tests() self.basic_transfer_tests()
src = os.path.join(self.tdir, 'src')
with open(src, 'wb') as s:
s.write(self.src_data)
# home dir expansion
fname = 'tstest-file'
home = os.path.dirname(src)
with set_paths(home=home), self.run_kitten(['~/'+os.path.basename(src), f'~/{fname}'], home_dir=home) as pty:
pty.wait_till_child_exits(require_exit_code=0)
os.remove(os.path.join(home, fname))
self.setup_dirs()
self.create_src(self.kitty_home)
# dir expansion with single transfer
with self.run_kitten(['~/src', '~/src']) as pty:
pty.wait_till_child_exits(require_exit_code=0)
os.remove(os.path.join(self.kitten_home, 'src'))
with self.run_kitten(['src', 'src']) as pty:
pty.wait_till_child_exits(require_exit_code=0)
os.remove(os.path.join(self.kitten_cwd, 'src'))
# dir expansion with multiple transfers
os.symlink('/', os.path.join(self.kitty_home, 'sym'))
with self.run_kitten(['~/src', '~/sym', '~']) as pty:
pty.wait_till_child_exits(require_exit_code=0)
os.remove(os.path.join(self.kitten_home, 'src'))
os.remove(os.path.join(self.kitten_home, 'sym'))
with self.run_kitten(['src', 'sym', '.']) as pty:
pty.wait_till_child_exits(require_exit_code=0)
os.remove(os.path.join(self.kitten_cwd, 'src'))
os.remove(os.path.join(self.kitten_cwd, 'sym'))
# mirroring
self.setup_dirs()
self.mirror_test(self.kitty_home, self.kitten_home)
def test_transfer_send(self): def test_transfer_send(self):
self.basic_transfer_tests() self.basic_transfer_tests()
@ -465,19 +509,32 @@ def test_transfer_send(self):
with open(src, 'wb') as s: with open(src, 'wb') as s:
s.write(self.src_data) s.write(self.src_data)
# home dir expansion self.setup_dirs()
fname = 'tstest-file' self.create_src(self.kitten_home)
home = os.path.dirname(src)
with set_paths(home=home), self.run_kitten(['~/'+os.path.basename(src), '~/'+fname], home_dir=home) as pty:
pty.wait_till_child_exits(require_exit_code=0)
os.remove(os.path.expanduser('~/'+fname))
# mirror mode # dir expansion with single transfer
src_home = os.path.join(self.tdir, 'misrc') with self.run_kitten(['~/src', '~/src']) as pty:
os.mkdir(src_home)
open(os.path.join(src_home, fname), 'w').close()
with self.run_kitten(['--mode=mirror', '~/'+fname], home_dir=src_home) as pty:
pty.wait_till_child_exits(require_exit_code=0) pty.wait_till_child_exits(require_exit_code=0)
with open(os.path.expanduser('~/'+fname)) as f: os.remove(os.path.join(self.kitty_home, 'src'))
self.assertEqual('', f.read())
os.remove(f.name) self.create_src(self.kitten_cwd)
with self.run_kitten(['src', 'src']) as pty:
pty.wait_till_child_exits(require_exit_code=0)
os.remove(os.path.join(self.kitty_home, 'src'))
# dir expansion with multiple transfers
os.symlink('/', os.path.join(self.kitten_home, 'sym'))
with self.run_kitten(['~/src', '~/sym', '~']) as pty:
pty.wait_till_child_exits(require_exit_code=0)
os.remove(os.path.join(self.kitty_home, 'src'))
os.remove(os.path.join(self.kitty_home, 'sym'))
os.symlink('/', os.path.join(self.kitten_cwd, 'sym'))
with self.run_kitten(['src', 'sym', '.']) as pty:
pty.wait_till_child_exits(require_exit_code=0)
os.remove(os.path.join(self.kitty_home, 'src'))
os.remove(os.path.join(self.kitty_home, 'sym'))
# mirroring
self.setup_dirs()
self.mirror_test(self.kitten_home, self.kitty_home, prefix='~/')