Fixed a couple sneaky bugs with prompts.
This commit is contained in:
1 parent
c842a1e3db
commit
21cecd1e71
3 files changed
+55
-6
No files matched your search
@@ -259,9 +259,11 @@ func (a *application) observe_override(next screen.Snapshot) (applicationoverrid
|
|||||||
}
|
}
|
||||||
frame := selected.adapter.Observe(next)
|
frame := selected.adapter.Observe(next)
|
||||||
freshShell := selected.shell.Observe(next)
|
freshShell := selected.shell.Observe(next)
|
||||||
if !frame.Recognized {
|
// A fresh shell prompt ends the override even if an adapter still recognizes
|
||||||
|
// stale application content or its bridge has not reported closure yet.
|
||||||
|
selected.exitPending = selected.exitPending || freshShell
|
||||||
|
if !frame.Recognized || selected.exitPending {
|
||||||
selected.recognized = false
|
selected.recognized = false
|
||||||
selected.exitPending = selected.exitPending || freshShell
|
|
||||||
if a.overrideCurrent == selected {
|
if a.overrideCurrent == selected {
|
||||||
if !a.restore_override(func() {}) {
|
if !a.restore_override(func() {}) {
|
||||||
return applicationoverrides.Frame{}, false
|
return applicationoverrides.Frame{}, false
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
@@ -149,6 +150,45 @@ func TestExitWaitsForPendingSpeechChangeAndRestoresBeforeShellOutput(t *testing.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCodexShellExitWithoutBridgeClosure(t *testing.T) {
|
||||||
|
for _, pendingSpeech := range []bool{false, true} {
|
||||||
|
t.Run(fmt.Sprint(pendingSpeech), func(t *testing.T) {
|
||||||
|
a, voice, _ := selection_application(t, true)
|
||||||
|
a.prepareSpeech = func(config.Speech) (speech.Transport, error) { return &preparedVoice{}, nil }
|
||||||
|
composer := override_screen(1, "› hello")
|
||||||
|
composer.X = 7
|
||||||
|
override_read(t, a, composer)
|
||||||
|
a.select_override("codex")
|
||||||
|
override_read(t, a, composer)
|
||||||
|
baseline := a.overrideCurrent.underlying.settings.live
|
||||||
|
if pendingSpeech {
|
||||||
|
if response := request_selection(t, a, "SET speech#voice=Temporary;speech#module=fixture"); response != "" {
|
||||||
|
t.Fatal(response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shell := override_screen(1, "[Username@host directory] $")
|
||||||
|
copy(shell.Line(20), []rune("› previous composer"))
|
||||||
|
shell.X = len([]rune(shell.Text(23))) + 1
|
||||||
|
override_read(t, a, shell)
|
||||||
|
if pendingSpeech {
|
||||||
|
finish_selection(t, a)
|
||||||
|
override_read(t, a, shell)
|
||||||
|
}
|
||||||
|
if a.overrideCurrent != nil || a.overrides[1] != nil || !reflect.DeepEqual(a.settings.live, baseline) {
|
||||||
|
t.Fatal("shell retained Codex settings without a bridge close event")
|
||||||
|
}
|
||||||
|
next := shell
|
||||||
|
next.X--
|
||||||
|
start := len(voice.messages)
|
||||||
|
a.engine.Note_input(true, false, false)
|
||||||
|
override_read(t, a, next)
|
||||||
|
if len(voice.messages) == start {
|
||||||
|
t.Fatal("shell arrow navigation remained silent")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func override_read(t *testing.T, a *application, snapshot screen.Snapshot) {
|
func override_read(t *testing.T, a *application, snapshot screen.Snapshot) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
a.source.(*fakeSource).snapshot = snapshot
|
a.source.(*fakeSource).snapshot = snapshot
|
||||||
|
|||||||
+11
-4
@@ -21,6 +21,9 @@ def fixture_shell():
|
|||||||
data = os.read(0, 4096)
|
data = os.read(0, 4096)
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
|
if data == b"\x04":
|
||||||
|
os.write(1, b"\x1b[24;1H\x1b[2K[Username@host directory] $ ")
|
||||||
|
continue
|
||||||
os.write(1, data)
|
os.write(1, data)
|
||||||
|
|
||||||
|
|
||||||
@@ -128,13 +131,17 @@ def main():
|
|||||||
bridge.write_status(statusPath, "done")
|
bridge.write_status(statusPath, "done")
|
||||||
time.sleep(0.4)
|
time.sleep(0.4)
|
||||||
action("Insert+0", "Codex finished")
|
action("Insert+0", "Codex finished")
|
||||||
action("Insert+a", "Application Codex")
|
# Leave old Codex/composer text visible and keep the bridge alive:
|
||||||
action("d", "Default")
|
# the fresh shell prompt itself must restore ordinary behavior.
|
||||||
action("Return", "Default settings restored")
|
action("ctrl+d", "Default settings restored")
|
||||||
|
keyboard.key("h", "e", "l", "l", "o")
|
||||||
|
action("Left", "o")
|
||||||
|
action("Left", "l")
|
||||||
|
action("Right", "o")
|
||||||
keyboard.key("Insert+q")
|
keyboard.key("Insert+q")
|
||||||
wait_for(lambda: (root / "finished").exists(), "reader exit")
|
wait_for(lambda: (root / "finished").exists(), "reader exit")
|
||||||
assert not server.errors, server.errors
|
assert not server.errors, server.errors
|
||||||
print("PASS: real private PTY/override/socket bridge, composer Left/Right feedback, baseline history, completed messages only, one announcement, long replies, replay and status")
|
print("PASS: real private PTY/override/socket bridge, composer Left/Right feedback, baseline history, completed messages only, one announcement, long replies, replay, status, shell exit without bridge closure and restored shell arrows")
|
||||||
except BaseException:
|
except BaseException:
|
||||||
print("Recent private speech:", server.events[-15:], file=sys.stderr)
|
print("Recent private speech:", server.events[-15:], file=sys.stderr)
|
||||||
if (root / "status.json").exists():
|
if (root / "status.json").exists():
|
||||||
|
|||||||
Reference in new issue
Block a user