2025-06-05 10:02:19 +08:00
|
|
|
|
package gocmdDaemon
|
2025-06-05 00:18:07 +08:00
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
|
|
|
|
"net"
|
|
|
|
|
"os"
|
|
|
|
|
"testing"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestWrite(t *testing.T) {
|
|
|
|
|
// 创建一个配对的连接
|
|
|
|
|
conn1, conn2 := net.Pipe()
|
|
|
|
|
defer conn1.Close()
|
|
|
|
|
defer conn2.Close()
|
|
|
|
|
|
|
|
|
|
// 要写入的数据
|
|
|
|
|
data := map[string]string{"test": "data"}
|
|
|
|
|
|
|
|
|
|
// 在一个goroutine中执行写操作
|
|
|
|
|
go func() {
|
|
|
|
|
err := Write(conn1, data)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("Write failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
// 读取数据并验证
|
|
|
|
|
var received map[string]string
|
|
|
|
|
err := json.NewDecoder(conn2).Decode(&received)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Read failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for k, v := range data {
|
|
|
|
|
if received[k] != v {
|
|
|
|
|
t.Errorf("Received data does not match. Expected %v:%v, Got %v:%v", k, v, received[k], received[k])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestRead(t *testing.T) {
|
|
|
|
|
// 创建一个配对的连接
|
|
|
|
|
conn1, conn2 := net.Pipe()
|
|
|
|
|
defer conn1.Close()
|
|
|
|
|
defer conn2.Close()
|
|
|
|
|
|
|
|
|
|
// 准备要读取的数据
|
|
|
|
|
data := map[string]string{"test": "data"}
|
|
|
|
|
|
|
|
|
|
// 在一个goroutine中执行写操作
|
|
|
|
|
go func() {
|
|
|
|
|
encoder := json.NewEncoder(conn1)
|
|
|
|
|
err := encoder.Encode(data)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("Write failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
// 读取数据并验证
|
|
|
|
|
received, err := Read[map[string]string](conn2)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Read failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for k, v := range data {
|
|
|
|
|
if (*received)[k] != v {
|
|
|
|
|
t.Errorf("Received data does not match. Expected %v:%v, Got %v:%v", k, v, (*received)[k], (*received)[k])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestCmdDaemonListen(t *testing.T) {
|
|
|
|
|
socketPath := "/tmp/test.sock"
|
|
|
|
|
daemon := &CmdDaemon{
|
|
|
|
|
SocketPath: socketPath,
|
|
|
|
|
cmds: make(map[string]CmdHandler),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 注册一个简单的命令处理程序
|
|
|
|
|
daemon.RegisterCmd("test", &SimpleCmdHandler{})
|
|
|
|
|
|
|
|
|
|
// 启动守护进程
|
|
|
|
|
go func() {
|
|
|
|
|
err := daemon.Listen()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("Listen failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
// 等待服务器启动
|
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
|
|
|
|
|
|
// 模拟客户端连接
|
|
|
|
|
conn, err := net.Dial("unix", socketPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Client connect failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
defer conn.Close()
|
|
|
|
|
|
|
|
|
|
// 发送请求
|
|
|
|
|
req := CmdRequest{
|
|
|
|
|
Id: "1",
|
|
|
|
|
Cmd: "test",
|
|
|
|
|
Args: "arg1",
|
|
|
|
|
IsDebug: false,
|
|
|
|
|
}
|
|
|
|
|
err = Write(conn, req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("Write failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 读取响应
|
|
|
|
|
resp, err := Read[CmdResponse](conn)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Read failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if resp.Data != "Handled test with args: arg1" || resp.Error != "" {
|
|
|
|
|
t.Errorf("Unexpected response. Data: %v, Error: %v", resp.Data, resp.Error)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestCmdDaemonRun(t *testing.T) {
|
|
|
|
|
// 这里我们假设有一个正在运行的服务器来处理请求
|
|
|
|
|
// 因此我们需要首先启动一个简单的服务器来测试Run方法
|
|
|
|
|
socketPath := "/tmp/test_run.sock"
|
|
|
|
|
daemon := &CmdDaemon{
|
|
|
|
|
SocketPath: socketPath,
|
|
|
|
|
cmds: make(map[string]CmdHandler),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 注册一个简单的命令处理程序
|
|
|
|
|
daemon.RegisterCmd("test", &SimpleCmdHandler{})
|
|
|
|
|
|
|
|
|
|
// 创建一个同步通道来等待守护进程开始监听
|
|
|
|
|
listening := make(chan struct{})
|
|
|
|
|
|
|
|
|
|
// 启动守护进程
|
|
|
|
|
go startTestDaemon(daemon, listening)
|
|
|
|
|
|
|
|
|
|
// 等待直到监听开始
|
|
|
|
|
// for循环中检查socket文件是否存在,如果存在退出循环,否则sleepeep 1秒
|
|
|
|
|
// 使用一个错误channel来传递错误
|
|
|
|
|
errChan := make(chan error)
|
|
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
|
listened := false
|
|
|
|
|
for {
|
|
|
|
|
|
|
|
|
|
if listened {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if _, err := os.Stat(socketPath); err == nil {
|
|
|
|
|
listened = true
|
|
|
|
|
}
|
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 设置命令行参数
|
|
|
|
|
os.Args = []string{"cmd", "--debug", "test", "arg1"}
|
|
|
|
|
ndaemon := &CmdDaemon{
|
|
|
|
|
SocketPath: socketPath,
|
|
|
|
|
}
|
|
|
|
|
// 执行Run方法
|
|
|
|
|
err := ndaemon.Run()
|
|
|
|
|
if err != nil {
|
|
|
|
|
errChan <- err
|
|
|
|
|
} else {
|
|
|
|
|
t.Log("Run completed successfully")
|
|
|
|
|
close(errChan)
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
err := <-errChan
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// startTestDaemon 启动守护进程并在后台运行
|
|
|
|
|
func startTestDaemon(daemon *CmdDaemon, ready chan struct{}) {
|
|
|
|
|
err := daemon.Listen()
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(fmt.Sprintf("Listen failed: %v", err))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SimpleCmdHandler 是一个简单的CmdHandler实现用于测试
|
|
|
|
|
type SimpleCmdHandler struct{}
|
|
|
|
|
|
|
|
|
|
func (h *SimpleCmdHandler) Handle(conn *CmdConn, req *CmdRequest) error {
|
|
|
|
|
err := conn.End(fmt.Sprintf("Handled %s with args: %s", req.Cmd, req.Args))
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (h *SimpleCmdHandler) Description() string {
|
|
|
|
|
return "Simple command handler for testing"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (h *SimpleCmdHandler) Usage() string {
|
|
|
|
|
return "Usage: test [args]"
|
|
|
|
|
}
|
2025-06-05 09:24:16 +08:00
|
|
|
|
|
|
|
|
|
// EmptyCmdHandler 是一个简单的CmdHandler实现用于测试空输入
|
|
|
|
|
type EmptyCmdHandler struct{}
|
|
|
|
|
|
|
|
|
|
func (h *EmptyCmdHandler) Handle(conn *CmdConn, req *CmdRequest) error {
|
|
|
|
|
// 返回一个空响应
|
|
|
|
|
err := conn.End("")
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (h *EmptyCmdHandler) Description() string {
|
|
|
|
|
return "Simple command handler for testing empty input"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (h *EmptyCmdHandler) Usage() string {
|
|
|
|
|
return "Usage: empty"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UnknownCmdHandler 是一个简单的CmdHandler实现用于测试未知命令
|
|
|
|
|
// 它会返回一个错误响应
|
|
|
|
|
type UnknownCmdHandler struct{}
|
|
|
|
|
|
|
|
|
|
func (h *UnknownCmdHandler) Handle(conn *CmdConn, req *CmdRequest) error {
|
|
|
|
|
// 返回一个错误响应
|
|
|
|
|
err := conn.WriteError(fmt.Errorf("unknown command"), false)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (h *UnknownCmdHandler) Description() string {
|
|
|
|
|
return "Simple command handler for testing unknown commands"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (h *UnknownCmdHandler) Usage() string {
|
|
|
|
|
return "Usage: unknown"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestEmptyCommand(t *testing.T) {
|
|
|
|
|
socketPath := "/tmp/test_empty.sock"
|
|
|
|
|
daemon := &CmdDaemon{
|
|
|
|
|
SocketPath: socketPath,
|
|
|
|
|
cmds: make(map[string]CmdHandler),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 注册一个简单的命令处理程序
|
|
|
|
|
daemon.RegisterCmd("empty", &EmptyCmdHandler{})
|
|
|
|
|
|
|
|
|
|
// 创建一个同步通道来等待守护进程开始监听
|
|
|
|
|
listening := make(chan struct{})
|
|
|
|
|
|
|
|
|
|
// 启动守护进程
|
|
|
|
|
go startTestDaemon(daemon, listening)
|
|
|
|
|
|
|
|
|
|
// 等待直到监听开始
|
|
|
|
|
// for循环中检查socket文件是否存在,如果存在退出循环,否则sleepeep 1秒
|
|
|
|
|
// 使用一个错误channel来传递错误
|
|
|
|
|
errChan := make(chan error)
|
|
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
|
listened := false
|
|
|
|
|
for {
|
|
|
|
|
|
|
|
|
|
if listened {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if _, err := os.Stat(socketPath); err == nil {
|
|
|
|
|
listened = true
|
|
|
|
|
}
|
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 设置命令行参数
|
|
|
|
|
os.Args = []string{"cmd", "empty"}
|
|
|
|
|
ndaemon := &CmdDaemon{
|
|
|
|
|
SocketPath: socketPath,
|
|
|
|
|
}
|
|
|
|
|
// 执行Run方法
|
|
|
|
|
err := ndaemon.Run()
|
|
|
|
|
if err != nil {
|
|
|
|
|
errChan <- err
|
|
|
|
|
} else {
|
|
|
|
|
t.Log("Run completed successfully")
|
|
|
|
|
close(errChan)
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
err := <-errChan
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestUnknownCommand(t *testing.T) {
|
|
|
|
|
socketPath := "/tmp/test_unknown.sock"
|
|
|
|
|
daemon := &CmdDaemon{
|
|
|
|
|
SocketPath: socketPath,
|
|
|
|
|
cmds: make(map[string]CmdHandler),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 注册一个简单的命令处理程序
|
|
|
|
|
daemon.RegisterCmd("unknown", &UnknownCmdHandler{})
|
|
|
|
|
|
|
|
|
|
// 启动守护进程
|
|
|
|
|
go func() {
|
|
|
|
|
err := daemon.Listen()
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(fmt.Sprintf("Listen failed: %v", err))
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
// 等待 socket 文件创建
|
|
|
|
|
waitForSocket(t, socketPath)
|
|
|
|
|
|
|
|
|
|
// 设置命令行参数
|
|
|
|
|
os.Args = []string{"cmd", "unknown"}
|
|
|
|
|
ndaemon := &CmdDaemon{
|
|
|
|
|
SocketPath: socketPath,
|
|
|
|
|
}
|
|
|
|
|
// 执行Run方法
|
|
|
|
|
err := ndaemon.Run()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Run failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// waitForSocket 等待直到指定路径的 socket 文件被创建
|
|
|
|
|
func waitForSocket(t *testing.T, path string) {
|
|
|
|
|
timeout := time.After(5 * time.Second)
|
|
|
|
|
ticker := time.Tick(500 * time.Millisecond)
|
|
|
|
|
|
|
|
|
|
for {
|
|
|
|
|
select {
|
|
|
|
|
case <-timeout:
|
|
|
|
|
t.Fatal("Timed out waiting for socket")
|
|
|
|
|
case <-ticker:
|
|
|
|
|
if _, err := os.Stat(path); err == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|