170 lines
4.1 KiB
Go
170 lines
4.1 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type fakeTrayController struct {
|
|
run func(onReady func(), onExit func())
|
|
|
|
quitCh chan struct{}
|
|
quitOnce sync.Once
|
|
|
|
icon []byte
|
|
tooltip string
|
|
menuItems []*fakeTrayMenuItem
|
|
}
|
|
|
|
func newFakeTrayController() *fakeTrayController {
|
|
return &fakeTrayController{quitCh: make(chan struct{})}
|
|
}
|
|
|
|
func (c *fakeTrayController) Run(onReady func(), onExit func()) {
|
|
if c.run != nil {
|
|
c.run(onReady, onExit)
|
|
return
|
|
}
|
|
onReady()
|
|
<-c.quitCh
|
|
if onExit != nil {
|
|
onExit()
|
|
}
|
|
}
|
|
|
|
func (c *fakeTrayController) Quit() {
|
|
c.quitOnce.Do(func() { close(c.quitCh) })
|
|
}
|
|
|
|
func (c *fakeTrayController) SetIcon(icon []byte) {
|
|
c.icon = append([]byte(nil), icon...)
|
|
}
|
|
|
|
func (c *fakeTrayController) SetTooltip(tooltip string) {
|
|
c.tooltip = tooltip
|
|
}
|
|
|
|
func (c *fakeTrayController) AddMenuItem(title, tooltip string) trayMenuItem {
|
|
item := &fakeTrayMenuItem{clicked: make(chan struct{}), title: title, tooltip: tooltip}
|
|
c.menuItems = append(c.menuItems, item)
|
|
return item
|
|
}
|
|
|
|
func (c *fakeTrayController) AddSeparator() {}
|
|
|
|
type fakeTrayMenuItem struct {
|
|
clicked chan struct{}
|
|
title string
|
|
tooltip string
|
|
disabled bool
|
|
}
|
|
|
|
func (m *fakeTrayMenuItem) Disable() {
|
|
m.disabled = true
|
|
}
|
|
|
|
func (m *fakeTrayMenuItem) Clicked() <-chan struct{} {
|
|
return m.clicked
|
|
}
|
|
|
|
func TestRunSystrayReadyOpensBrowser(t *testing.T) {
|
|
controller := newFakeTrayController()
|
|
opened := make(chan string, 1)
|
|
|
|
err := runSystray(19826, trayOptions{
|
|
controller: controller,
|
|
readyTimeout: time.Second,
|
|
iconLoader: func() ([]byte, error) { return []byte("icon"), nil },
|
|
openBrowser: func(url string) error {
|
|
opened <- url
|
|
controller.Quit()
|
|
return nil
|
|
},
|
|
notify: func(string, string) {},
|
|
logger: zap.NewNop(),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("托盘 ready 成功不应返回错误: %v", err)
|
|
}
|
|
|
|
if got := <-opened; got != "http://localhost:19826" {
|
|
t.Fatalf("浏览器 URL = %s", got)
|
|
}
|
|
if string(controller.icon) != "icon" {
|
|
t.Fatalf("应设置托盘图标")
|
|
}
|
|
if controller.tooltip != appTooltip {
|
|
t.Fatalf("tooltip = %q, want %q", controller.tooltip, appTooltip)
|
|
}
|
|
}
|
|
|
|
func TestRunSystrayReadyTimeoutReturnsTrayStartupError(t *testing.T) {
|
|
controller := newFakeTrayController()
|
|
controller.run = func(_ func(), _ func()) {
|
|
<-controller.quitCh
|
|
}
|
|
|
|
err := runSystray(19826, trayOptions{
|
|
controller: controller,
|
|
readyTimeout: 10 * time.Millisecond,
|
|
iconLoader: func() ([]byte, error) { return []byte("icon"), nil },
|
|
openBrowser: func(string) error { return nil },
|
|
notify: func(string, string) {},
|
|
logger: zap.NewNop(),
|
|
})
|
|
if err == nil {
|
|
t.Fatal("托盘 ready timeout 应返回错误")
|
|
}
|
|
var startupErr *startupError
|
|
if !errors.As(err, &startupErr) || startupErr.Phase() != "tray" {
|
|
t.Fatalf("应返回 tray 阶段启动错误,实际: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRunSystrayIconLoadFailureReturnsTrayStartupError(t *testing.T) {
|
|
controller := newFakeTrayController()
|
|
|
|
err := runSystray(19826, trayOptions{
|
|
controller: controller,
|
|
readyTimeout: time.Second,
|
|
iconLoader: func() ([]byte, error) { return nil, errors.New("missing icon") },
|
|
openBrowser: func(string) error { return nil },
|
|
notify: func(string, string) {},
|
|
logger: zap.NewNop(),
|
|
})
|
|
if err == nil {
|
|
t.Fatal("托盘图标加载失败应返回错误")
|
|
}
|
|
var startupErr *startupError
|
|
if !errors.As(err, &startupErr) || startupErr.Phase() != "tray" {
|
|
t.Fatalf("应返回 tray 阶段启动错误,实际: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRunSystrayBrowserOpenFailureIsNonFatal(t *testing.T) {
|
|
controller := newFakeTrayController()
|
|
notified := make(chan string, 1)
|
|
|
|
err := runSystray(19826, trayOptions{
|
|
controller: controller,
|
|
readyTimeout: time.Second,
|
|
iconLoader: func() ([]byte, error) { return []byte("icon"), nil },
|
|
openBrowser: func(string) error { return errors.New("no browser") },
|
|
notify: func(_, message string) {
|
|
notified <- message
|
|
controller.Quit()
|
|
},
|
|
logger: zap.NewNop(),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("浏览器打开失败不应成为 fatal: %v", err)
|
|
}
|
|
if got := <-notified; got == "" {
|
|
t.Fatal("浏览器打开失败应提示用户")
|
|
}
|
|
}
|