1//go:build linux
2
3package tui
4
5import (
6 "bytes"
7 "context"
8 "os"
9 "path/filepath"
10 "testing"
11)
12
13func TestReadClipboardImageWaylandUsesMIMEPriority(t *testing.T) {
14 dir := t.TempDir()
15 state := filepath.Join(dir, "listed")
16 writeClipboardTestHelper(t, dir, "wl-paste", `#!/bin/sh
17if [ "$1" = "--list-types" ]; then
18 : > "$CLIPBOARD_TEST_STATE"
19 printf 'image/jpeg\nimage/png\nimage/bmp\n'
20 exit 0
21fi
22if [ "$1" = "--type" ]; then
23 if [ ! -f "$CLIPBOARD_TEST_STATE" ]; then
24 exit 1
25 fi
26 case "$2" in
27 image/png) printf 'png-bytes' ;;
28 image/jpeg) printf 'jpeg-bytes' ;;
29 *) exit 1 ;;
30 esac
31 exit 0
32fi
33exit 1
34`)
35 t.Setenv("PATH", dir)
36 t.Setenv("WAYLAND_DISPLAY", "wayland-test")
37 t.Setenv("DISPLAY", "")
38 t.Setenv("CLIPBOARD_TEST_STATE", state)
39
40 image, ok, err := ReadClipboardImage(context.Background())
41 if err != nil {
42 t.Fatalf("ReadClipboardImage() error = %v", err)
43 }
44 if !ok {
45 t.Fatal("ReadClipboardImage() reported no image")
46 }
47 if image.MimeType != "image/png" {
48 t.Fatalf("MIME type = %q, want image/png", image.MimeType)
49 }
50 if string(image.Data) != "png-bytes" {
51 t.Fatalf("image data = %q, want png-bytes", image.Data)
52 }
53}
54
55func TestReadClipboardImageFallsBackToX11Helpers(t *testing.T) {
56 dir := t.TempDir()
57 writeClipboardTestHelper(t, dir, "wl-paste", `#!/bin/sh
58if [ "$1" = "--list-types" ]; then
59 printf 'text/plain\n'
60fi
61`)
62 writeClipboardTestHelper(t, dir, "xclip", `#!/bin/sh
63case "$*" in
64 *"image/gif"*) printf 'gif-bytes' ;;
65 *) exit 1 ;;
66esac
67`)
68 t.Setenv("PATH", dir)
69 t.Setenv("WAYLAND_DISPLAY", "wayland-test")
70 t.Setenv("DISPLAY", ":test")
71
72 image, ok, err := ReadClipboardImage(context.Background())
73 if err != nil {
74 t.Fatalf("ReadClipboardImage() error = %v", err)
75 }
76 if !ok {
77 t.Fatal("ReadClipboardImage() reported no image")
78 }
79 if image.MimeType != "image/gif" {
80 t.Fatalf("MIME type = %q, want image/gif", image.MimeType)
81 }
82 if string(image.Data) != "gif-bytes" {
83 t.Fatalf("image data = %q, want gif-bytes", image.Data)
84 }
85}
86
87func TestReadClipboardImageUnavailableIsNoImage(t *testing.T) {
88 dir := t.TempDir()
89 writeClipboardTestHelper(t, dir, "wl-paste", `#!/bin/sh
90printf 'clipboard helper failure' >&2
91exit 1
92`)
93 t.Setenv("PATH", dir)
94 t.Setenv("WAYLAND_DISPLAY", "wayland-test")
95 t.Setenv("DISPLAY", "")
96
97 image, ok, err := ReadClipboardImage(context.Background())
98 if err != nil {
99 t.Fatalf("ReadClipboardImage() error = %v, want nil", err)
100 }
101 if ok {
102 t.Fatalf("ReadClipboardImage() returned image %#v", image)
103 }
104 if image.MimeType != "" || len(image.Data) != 0 {
105 t.Fatalf("empty image = %#v", image)
106 }
107
108 path, data, ok, err := ReadClipboardImagePNG()
109 if err != nil || ok || path != "" || data != nil {
110 t.Fatalf("ReadClipboardImagePNG() = (%q, %v, %v, %v), want empty result", path, data, ok, err)
111 }
112}
113
114func TestWriteClipboardTextWaylandUsesWlCopy(t *testing.T) {
115 dir := t.TempDir()
116 payload := filepath.Join(dir, "clipboard-payload")
117 writeClipboardTestHelper(t, dir, "wl-copy", `#!/bin/sh
118if [ "$1" != "--type" ] || [ "$2" != "text/plain" ]; then
119 exit 1
120fi
121exec /bin/cat > "$CLIPBOARD_TEST_PAYLOAD"
122`)
123 t.Setenv("PATH", dir)
124 t.Setenv("WAYLAND_DISPLAY", "wayland-test")
125 t.Setenv("DISPLAY", "")
126 t.Setenv("CLIPBOARD_TEST_PAYLOAD", payload)
127
128 if err := WriteClipboardText(context.Background(), "https://example.com/oauth?state=xyz"); err != nil {
129 t.Fatal(err)
130 }
131 got, err := os.ReadFile(payload)
132 if err != nil {
133 t.Fatal(err)
134 }
135 if string(got) != "https://example.com/oauth?state=xyz" {
136 t.Fatalf("clipboard payload = %q", got)
137 }
138}
139
140func TestRunClipboardImageCommandEnforcesSizeLimit(t *testing.T) {
141 dir := t.TempDir()
142 payload := filepath.Join(dir, "payload")
143 writeClipboardTestHelper(t, dir, "clipboard-test-helper", `#!/bin/sh
144exec /bin/cat "$CLIPBOARD_TEST_PAYLOAD"
145`)
146 t.Setenv("PATH", dir)
147 t.Setenv("CLIPBOARD_TEST_PAYLOAD", payload)
148
149 if err := os.WriteFile(payload, bytes.Repeat([]byte{'x'}, maxClipboardImageBytes), 0o600); err != nil {
150 t.Fatal(err)
151 }
152 data, ok := runClipboardImageCommand(context.Background(), clipboardImageCommand{name: "clipboard-test-helper"})
153 if !ok || len(data) != maxClipboardImageBytes {
154 t.Fatalf("exact-limit command result = (%d bytes, %v), want (%d bytes, true)", len(data), ok, maxClipboardImageBytes)
155 }
156
157 if err := os.WriteFile(payload, bytes.Repeat([]byte{'x'}, maxClipboardImageBytes+1), 0o600); err != nil {
158 t.Fatal(err)
159 }
160 data, ok = runClipboardImageCommand(context.Background(), clipboardImageCommand{name: "clipboard-test-helper"})
161 if ok || data != nil {
162 t.Fatalf("over-limit command result = (%d bytes, %v), want (0 bytes, false)", len(data), ok)
163 }
164}
165
166func writeClipboardTestHelper(t *testing.T, dir, name, script string) {
167 t.Helper()
168 path := filepath.Join(dir, name)
169 if err := os.WriteFile(path, []byte(script), 0o700); err != nil {
170 t.Fatal(err)
171 }
172}