diff --git a/kittens/dnd/drop.go b/kittens/dnd/drop.go index d17192d74..b90e7e6e2 100644 --- a/kittens/dnd/drop.go +++ b/kittens/dnd/drop.go @@ -44,12 +44,15 @@ func open_file_for_writing(path string) (*os.File, error) { func (d *drop_dest) write(chunk []byte) (err error) { if d.dest == nil { - d.dest, err = open_file_for_writing(d.path) - d.close_on_finish = true - if err != nil { - return + if d.path == "" { + d.dest = &bufferWriteCloser{&bytes.Buffer{}} + } else { + d.dest, err = open_file_for_writing(d.path) + d.close_on_finish = true + if err != nil { + return + } } - } _, err = d.dest.Write(chunk) return @@ -205,6 +208,23 @@ func (d *drop_status) reset() { *d = drop_status{cell_x: -1, cell_y: -1} } +func (d *drop_dest) reset() { + if d.dest != nil && d.dest != os.Stdout { + d.dest.Close() + d.dest = nil + } + d.completed = false + d.close_on_finish = false + d.b64_decoder = streaming_base64.StreamingBase64Decoder{} +} + +func (dnd *dnd) reset_drop() { + dnd.drop_status.reset() + for _, x := range dnd.drop_dests { + x.reset() + } +} + func (root *remote_dir_entry) close_tree() { if root.base_dir != nil { root.base_dir = root.base_dir.unref() @@ -220,7 +240,7 @@ func (dnd *dnd) end_drop() { dnd.drop_status.root_remote_dir.close_tree() dnd.drop_status.root_remote_dir = nil } - dnd.drop_status.reset() + dnd.reset_drop() dnd.render_screen() } @@ -239,7 +259,7 @@ func (dnd *dnd) all_mime_data_dropped() (err error) { } } if len(drop_status.uri_list) == 0 { - dnd.drop_status.reset() + dnd.reset_drop() dnd.data_has_been_dropped = true dnd.render_screen() return @@ -328,7 +348,7 @@ func (dnd *dnd) on_drop_move(cell_x, cell_y int, has_more bool, offered_mimes st } dnd.drop_status.in_window = cell_x > -1 && cell_y > -1 if !dnd.drop_status.in_window || dnd.drag_started { // disallow self drag and drop - dnd.drop_status.reset() + dnd.reset_drop() } mimes_changed := !slices.Equal(prev_status.accepted_mimes, dnd.drop_status.accepted_mimes) needs_rerender = prev_status.action != dnd.drop_status.action || mimes_changed diff --git a/kittens/dnd/main.go b/kittens/dnd/main.go index 4b9ca61b5..ff674d632 100644 --- a/kittens/dnd/main.go +++ b/kittens/dnd/main.go @@ -98,39 +98,50 @@ func (dnd *dnd) send_test_response(payload string) { dnd.lp.DebugPrintln(payload) } -func (dnd *dnd) run_loop() (err error) { - base_dir, err := os.Getwd() - if err != nil { - return err - } +func (dnd *dnd) setup_base_dir(base_dir string) error { base_tdir, err := os.MkdirTemp(base_dir, ".dnd-kitten-drop-*") if err != nil { return err } - var base_tdir_f *os.File - defer func() { - if base_tdir_f != nil { - utils.RemoveChildren(base_tdir_f) - base_tdir_f.Close() - } - if terr := os.RemoveAll(base_tdir); terr != nil && err == nil { - err = terr - } - }() - base_tdir_f, err = os.Open(base_tdir) + bf, err := os.Open(base_tdir) if err != nil { - return + os.RemoveAll(base_tdir) + return err } - dnd.base_tempdir = base_tdir_f + dnd.base_tempdir = bf if _, serr := os.Stat(filepath.Join(base_dir, strings.ToUpper(filepath.Base(base_tdir)))); serr == nil { dnd.is_case_sensitive_filesystem = false } + return nil +} + +func (dnd *dnd) remove_tdir() error { + path := dnd.base_tempdir.Name() + dnd.base_tempdir.Close() + dnd.base_tempdir = nil + return os.RemoveAll(path) +} + +func (dnd *dnd) run_loop() (err error) { + defer func() { + if dnd.in_test_mode && err != nil { + debugprintln("dnd kitten exiting with error: ", err) + } + }() + base_dir, err := os.Getwd() + if err != nil { + return err + } + if err = dnd.setup_base_dir(base_dir); err != nil { + return err + } + defer dnd.remove_tdir() dnd.allow_drops, dnd.allow_drags = len(dnd.drop_dests) > 0, len(dnd.drag_sources) > 0 if dnd.lp, err = loop.New(); err != nil { return err } - dnd.drop_status.reset() + dnd.reset_drop() dnd.lp.OnInitialize = func() (string, error) { dnd.lp.AllowLineWrapping(false) @@ -205,6 +216,8 @@ func (dnd *dnd) run_loop() (err error) { dnd.drop_status.reset() dnd.lp.StopAcceptingDrops() dnd.lp.StopOfferingDrags() + dnd.remove_tdir() + dnd.setup_base_dir(base_dir) machine_id := "" if string(cmd.Payload) == "SETUP_REMOTE" { machine_id = "remote-client-for-test" diff --git a/kitty_tests/dnd_kitten.py b/kitty_tests/dnd_kitten.py index 14e4b9f3a..394a5d731 100644 --- a/kitty_tests/dnd_kitten.py +++ b/kitty_tests/dnd_kitten.py @@ -121,13 +121,21 @@ class TestDnDKitten(BaseTest): self.capture.pty = self.pty self.pty.callbacks.printbuf = self self.screen = self.pty.screen - self.send_dnd_command_to_kitten('SETUP_REMOTE' if remote_client else 'SETUP_LOCAL') - self.wait_for_responses('SETUP_DONE') + self.reset_kitten(remote_client, clear_tdir=False) self.assertTrue(self.probe_state('drop_wanted')) self.assertEqual(remote_client, self.probe_state('drop_is_remote_client')) if self.probe_state('drag_can_offer'): self.assertEqual(remote_client, self.probe_state('drag_is_remote_client')) + def reset_kitten(self, remote_client: bool, clear_tdir=True): + if clear_tdir: + shutil.rmtree(self.kitten_wd) + os.mkdir(self.kitten_wd) + shutil.rmtree(self.src_data_dir) + os.mkdir(self.src_data_dir) + self.send_dnd_command_to_kitten('SETUP_REMOTE' if remote_client else 'SETUP_LOCAL') + self.wait_for_responses('SETUP_DONE') + def get_button_geometry(self, are_present: bool = True): self.send_dnd_command_to_kitten('GEOMETRY') self.pty.wait_till(lambda: bool(self.messages_from_kitten)) @@ -152,7 +160,7 @@ class TestDnDKitten(BaseTest): def wait_till(): return q == self.messages_from_kitten.strip() try: - self.pty.wait_till(wait_till, timeout, lambda: f'Responses so far: {self.messages_from_kitten!r}') + self.pty.wait_till(wait_till, timeout, lambda: f'Responses so far: Expected:\n{q!r}\nActual:\n{self.messages_from_kitten.strip()!r} != {q!r}') finally: self.messages_from_kitten = '' @@ -166,6 +174,10 @@ class TestDnDKitten(BaseTest): self.send_dnd_command_to_kitten('PING') self.wait_for_responses('PONG') + def exit_kitten(self): + self.pty.write_to_child('\x1b[27u') # ] + self.pty.wait_till_child_exits(require_exit_code=0) + def tearDown(self): dnd_set_test_write_func(None) dnd_test_cleanup_fake_window(self.capture.os_window_id) @@ -174,14 +186,14 @@ class TestDnDKitten(BaseTest): self.pty = None def test_dnd_kitten_drop(self): - self.dnd_kitten_drop(False) - - def test_dnd_kitten_drop_remote(self): - self.dnd_kitten_drop(True) - - def dnd_kitten_drop(self, remote_client): img_drop_path = 'images/image.png' - self.finish_setup(remote_client=remote_client, cli_args=(f'--drop=image/png:{img_drop_path}',)) + self.finish_setup(cli_args=(f'--drop=image/png:{img_drop_path}',)) + self.dnd_kitten_drop(False, img_drop_path) + self.reset_kitten(True) + self.dnd_kitten_drop(True, img_drop_path) + self.exit_kitten() + + def dnd_kitten_drop(self, remote_client, img_drop_path): copy, move = self.get_button_geometry() all_mimes = 'text/uri-list a/b c/d' for b, expected in ((copy, GLFW_DRAG_OPERATION_COPY), (move, GLFW_DRAG_OPERATION_MOVE)): @@ -215,7 +227,7 @@ class TestDnDKitten(BaseTest): self.assertEqual('text/uri-list', self.probe_state('drop_getting_data_for_mime')) create_fs(self.src_data_dir) uri_list, path_list = [], [] - for x in os.listdir(self.src_data_dir): + for x in sorted(os.listdir(self.src_data_dir)): uri_list.append(as_file_url(self.src_data_dir, x)) uri_list = ['moose:cow', '# file:///frog/march'] + uri_list uri_list.insert(3, 'ignore://me')