diff options
Diffstat (limited to 'cmd/krino/display_test.go')
| -rw-r--r-- | cmd/krino/display_test.go | 42 |
1 files changed, 37 insertions, 5 deletions
diff --git a/cmd/krino/display_test.go b/cmd/krino/display_test.go index 4c1cefe..0f4cf90 100644 --- a/cmd/krino/display_test.go +++ b/cmd/krino/display_test.go @@ -6,13 +6,15 @@ import ( "fmt" "strings" "testing" + "unicode" "unicode/utf8" ) -// firstUnsafe names the first thing in s a terminal could act on - a C0 -// control (a newline too, unless allowNewline), DEL, a C1 control, a -// bidirectional control, or invalid UTF-8 - or returns "" when there is -// none. +// firstUnsafe names the first thing in s a terminal could act on - a +// control character (a newline too, unless allowNewline), a Unicode +// bidirectional control, a line or paragraph separator, or invalid UTF-8 - +// or returns "" when there is none. It is built from Go's Unicode tables, +// not from display's own ranges, so it can catch a class display misses. func firstUnsafe(s string, allowNewline bool) string { if !utf8.ValidString(s) { return "invalid UTF-8" @@ -21,7 +23,7 @@ func firstUnsafe(s string, allowNewline bool) string { if r == '\n' && allowNewline { continue } - if r < 0x20 || r == 0x7f || (r >= 0x80 && r <= 0x9f) || (r >= 0x202a && r <= 0x202e) || (r >= 0x2066 && r <= 0x2069) { + if unicode.IsControl(r) || unicode.Is(unicode.Bidi_Control, r) || unicode.In(r, unicode.Zl, unicode.Zp) { return fmt.Sprintf("%U", r) } } @@ -46,6 +48,10 @@ func TestDisplayEscapesTerminalControls(t *testing.T) { "isolate\u2066x\u2069.pdf": `isolate\u2066x\u2069.pdf`, "bad\xffbyte.pdf": `bad\xffbyte.pdf`, `back\slash.pdf`: `back\slash.pdf`, + "alm\u061c.pdf": `alm\u061c.pdf`, + "lrm\u200e.pdf": `lrm\u200e.pdf`, + "rlm\u200f.pdf": `rlm\u200f.pdf`, + "ls\u2028ps\u2029.pdf": `ls\u2028ps\u2029.pdf`, "replacement\ufffd.pdf": "replacement\ufffd.pdf", } { if got := display(in); got != want { @@ -84,3 +90,29 @@ func TestReviewEscapesHostileNames(t *testing.T) { t.Errorf("undo review printed %s:\n%q", bad, undoOut) } } + +// TestSafeWriterEscapesButKeepsNewlines: everything written to stderr goes +// through display line by line, so a quoted file name cannot act on the +// terminal while messages keep their lines (review M5). +func TestSafeWriterEscapesButKeepsNewlines(t *testing.T) { + var b strings.Builder + in := "a\x1b[2J\nb\u202e\n" + n, err := safeWriter{&b}.Write([]byte(in)) + if err != nil || n != len(in) { + t.Fatalf("Write = %d, %v", n, err) + } + if got, want := b.String(), "a\\x1b[2J\nb\\u202e\n"; got != want { + t.Errorf("wrote %q, want %q", got, want) + } +} + +// TestJSONSafeEscapesTerminalRunes: in an encoded JSON document, DEL, C1 and +// bidirectional controls come out as \uXXXX escapes - the same string +// values - and everything else is untouched (review terminal F4). +func TestJSONSafeEscapesTerminalRunes(t *testing.T) { + in := "{\"rel\": \"c1\u009bx\u202ey\u007fz ż\"}" + want := `{"rel": "c1\u009bx\u202ey\u007fz ż"}` + if got := string(jsonSafe([]byte(in))); got != want { + t.Errorf("jsonSafe = %q, want %q", got, want) + } +} |
