package attach
import (
"bytes"
"encoding/hex"
"errors"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
)
// fakeDesktop stands in for whatever helper this machine would have used.
// "cat" is the helper here: it exists everywhere this runs and it writes the
// bytes it is given, which is all any of the real ones do.
func fakeDesktop(t *testing.T, gs []grabber) {
t.Helper()
if runtime.GOOS == "windows" {
t.Skip("the stand-in helper is a unix one")
}
old := grabbers
grabbers = gs
t.Cleanup(func() { grabbers = old })
}
func writeTemp(t *testing.T, name string, data []byte) string {
t.Helper()
p := filepath.Join(t.TempDir(), name)
if err := os.WriteFile(p, data, 0o644); err != nil {
t.Fatal(err)
}
return p
}
func TestClipboardReadsWhatTheHelperWrites(t *testing.T) {
want := tinyPNG(t)
fakeDesktop(t, []grabber{{cmd: "cat", args: []string{writeTemp(t, "clip.png", want)}}})
im, err := Clipboard()
if err != nil {
t.Fatal(err)
}
if im.Media != "image/png" || !bytes.Equal(im.Data, want) {
t.Errorf("got %s, %d bytes; want image/png, %d", im.Media, len(im.Data), len(want))
}
if im.Origin != "clipboard" {
t.Errorf("origin = %q", im.Origin)
}
}
// Text on the clipboard is what ctrl-V usually finds, and it has to come back
// as "no image" rather than as an error worth interrupting anyone over.
func TestClipboardOfTextIsNoImage(t *testing.T) {
fakeDesktop(t, []grabber{{cmd: "cat", args: []string{writeTemp(t, "clip.txt", []byte("some copied words"))}}})
if _, err := Clipboard(); !errors.Is(err, ErrNoImage) {
t.Fatalf("err = %v; want ErrNoImage", err)
}
}
// A helper that is not installed is not a failure: the next one in the list
// may be the one this desktop uses.
func TestClipboardFallsThroughToTheNextHelper(t *testing.T) {
want := tinyPNG(t)
fakeDesktop(t, []grabber{
{cmd: "codey-no-such-clipboard-helper"},
{cmd: "cat", args: []string{writeTemp(t, "clip.png", want)}},
})
im, err := Clipboard()
if err != nil || !bytes.Equal(im.Data, want) {
t.Fatalf("second helper not reached: %v", err)
}
}
func TestClipboardWithNoHelperInstalledSaysSo(t *testing.T) {
fakeDesktop(t, []grabber{{cmd: "codey-no-such-clipboard-helper"}})
_, err := Clipboard()
if err == nil {
t.Fatal("a machine with no helper reported success")
}
// The message has to name the fix; "no image" would send someone looking
// at their clipboard instead of at their packages.
if !strings.Contains(err.Error(), "helper") {
t.Errorf("unhelpful message: %v", err)
}
}
// An image found and too big is worth saying out loud, unlike an empty
// clipboard: the person can see the screenshot they copied.
func TestClipboardReportsAnOversizeImage(t *testing.T) {
big := append(tinyPNG(t), make([]byte, MaxBytes)...)
fakeDesktop(t, []grabber{{cmd: "cat", args: []string{writeTemp(t, "big.png", big)}}})
_, err := Clipboard()
if !errors.Is(err, ErrTooBig) {
t.Fatalf("err = %v; want ErrTooBig", err)
}
}
func TestDecodeAppleScriptUnwrapsHexData(t *testing.T) {
want := tinyPNG(t)
out := "«data PNGf" + hex.EncodeToString(want) + "»\n"
got, err := decodeAppleScript([]byte(out))
if err != nil || !bytes.Equal(got, want) {
t.Fatalf("decode = %d bytes, %v; want %d bytes", len(got), err, len(want))
}
}
func TestDecodeAppleScriptOnAnEmptyClipboard(t *testing.T) {
// osascript answers a clipboard with no PNG on it by failing, but an
// empty or surprising line must not decode into rubbish either.
if _, err := decodeAppleScript([]byte("\n")); err == nil {
t.Error("nothing decoded into something")
}
}
func TestDecodeHexLine(t *testing.T) {
want := tinyPNG(t)
got, err := decodeHexLine([]byte(strings.ToUpper(hex.EncodeToString(want)) + "\r\n"))
if err != nil || !bytes.Equal(got, want) {
t.Fatalf("decode = %d bytes, %v", len(got), err)
}
}
// Whatever this machine is, it must offer some way to try.
func TestDefaultGrabbersExist(t *testing.T) {
if len(defaultGrabbers()) == 0 {
t.Fatal("no clipboard helpers are even attempted on " + runtime.GOOS)
}
}