test: server 包完善部分测试用例
This commit is contained in:
parent
22449ff5c3
commit
bbf70fab02
|
@ -16,6 +16,22 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// Network 服务器运行的网络模式
|
||||
// - 根据不同的网络模式,服务器将会产生不同的行为,该类型将在服务器创建时候指定
|
||||
//
|
||||
// 服务器支持的网络模式如下:
|
||||
// - NetworkNone 该模式下不监听任何网络端口,仅开启消息队列,适用于纯粹的跨服服务器等情况
|
||||
// - NetworkTcp 该模式下将会监听 TCP 协议的所有地址,包括 IPv4 和 IPv6
|
||||
// - NetworkTcp4 该模式下将会监听 TCP 协议的 IPv4 地址
|
||||
// - NetworkTcp6 该模式下将会监听 TCP 协议的 IPv6 地址
|
||||
// - NetworkUdp 该模式下将会监听 UDP 协议的所有地址,包括 IPv4 和 IPv6
|
||||
// - NetworkUdp4 该模式下将会监听 UDP 协议的 IPv4 地址
|
||||
// - NetworkUdp6 该模式下将会监听 UDP 协议的 IPv6 地址
|
||||
// - NetworkUnix 该模式下将会监听 Unix 协议的地址
|
||||
// - NetworkHttp 该模式下将会监听 HTTP 协议的地址
|
||||
// - NetworkWebsocket 该模式下将会监听 Websocket 协议的地址
|
||||
// - NetworkKcp 该模式下将会监听 KCP 协议的地址
|
||||
// - NetworkGRPC 该模式下将会监听 GRPC 协议的地址
|
||||
type Network string
|
||||
|
||||
const (
|
||||
|
@ -321,7 +337,16 @@ func (n Network) websocketMode(state chan<- error, srv *Server) {
|
|||
}((&listener{srv: srv, Listener: l, state: state}).init(), mux)
|
||||
}
|
||||
|
||||
// IsSocket 返回当前服务器的网络模式是否为 Socket 模式
|
||||
// IsSocket 返回当前服务器的网络模式是否为 Socket 模式,目前为止仅有如下几种模式为 Socket 模式:
|
||||
// - NetworkTcp
|
||||
// - NetworkTcp4
|
||||
// - NetworkTcp6
|
||||
// - NetworkUdp
|
||||
// - NetworkUdp4
|
||||
// - NetworkUdp6
|
||||
// - NetworkUnix
|
||||
// - NetworkKcp
|
||||
// - NetworkWebsocket
|
||||
func (n Network) IsSocket() bool {
|
||||
return collection.KeyInMap(socketNetworks, n)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package server_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/server"
|
||||
"github.com/kercylan98/minotaur/utils/times"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 服务器在启动时将阻塞 1s,模拟了慢消息的过程,这时候如果通过 RegMessageLowExecEvent 函数注册过慢消息事件,将会收到该事件的消息
|
||||
// - 该示例中,将在收到慢消息时关闭服务器
|
||||
func ExampleWithLowMessageDuration() {
|
||||
srv := server.New(server.NetworkNone,
|
||||
server.WithLowMessageDuration(time.Second),
|
||||
)
|
||||
srv.RegStartFinishEvent(func(srv *server.Server) {
|
||||
time.Sleep(time.Second)
|
||||
})
|
||||
srv.RegMessageLowExecEvent(func(srv *server.Server, message *server.Message, cost time.Duration) {
|
||||
srv.Shutdown()
|
||||
fmt.Println(times.GetSecond(cost))
|
||||
})
|
||||
if err := srv.RunNone(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Output:
|
||||
// 1
|
||||
}
|
||||
|
||||
// 服务器在启动时将发布一条阻塞 1s 的异步消息,模拟了慢消息的过程,这时候如果通过 RegMessageLowExecEvent 函数注册过慢消息事件,将会收到该事件的消息
|
||||
// - 该示例中,将在收到慢消息时关闭服务器
|
||||
func ExampleWithAsyncLowMessageDuration() {
|
||||
srv := server.New(server.NetworkNone,
|
||||
server.WithAsyncLowMessageDuration(time.Second),
|
||||
)
|
||||
srv.RegStartFinishEvent(func(srv *server.Server) {
|
||||
srv.PushAsyncMessage(func() error {
|
||||
time.Sleep(time.Second)
|
||||
return nil
|
||||
}, nil)
|
||||
})
|
||||
srv.RegMessageLowExecEvent(func(srv *server.Server, message *server.Message, cost time.Duration) {
|
||||
srv.Shutdown()
|
||||
fmt.Println(times.GetSecond(cost))
|
||||
})
|
||||
if err := srv.RunNone(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Output:
|
||||
// 1
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
package server_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/server"
|
||||
"github.com/kercylan98/minotaur/utils/random"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestWithLowMessageDuration(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
duration time.Duration
|
||||
}{
|
||||
{name: "TestWithLowMessageDuration", duration: server.DefaultLowMessageDuration},
|
||||
{name: "TestWithLowMessageDuration_Zero", duration: 0},
|
||||
{name: "TestWithLowMessageDuration_Negative", duration: -server.DefaultAsyncLowMessageDuration},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
c := c
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
networks := server.GetNetworks()
|
||||
for i := 0; i < len(networks); i++ {
|
||||
low := false
|
||||
network := networks[i]
|
||||
srv := server.New(network,
|
||||
server.WithLowMessageDuration(c.duration),
|
||||
)
|
||||
srv.RegMessageLowExecEvent(func(srv *server.Server, message *server.Message, cost time.Duration) {
|
||||
low = true
|
||||
srv.Shutdown()
|
||||
})
|
||||
srv.RegStartFinishEvent(func(srv *server.Server) {
|
||||
if c.duration <= 0 {
|
||||
srv.Shutdown()
|
||||
return
|
||||
}
|
||||
time.Sleep(server.DefaultLowMessageDuration)
|
||||
})
|
||||
var lis string
|
||||
switch network {
|
||||
case server.NetworkNone, server.NetworkUnix:
|
||||
lis = "addr"
|
||||
default:
|
||||
lis = fmt.Sprintf(":%d", random.UsablePort())
|
||||
}
|
||||
if err := srv.Run(lis); err != nil {
|
||||
t.Fatalf("%s run error: %s", network, err)
|
||||
}
|
||||
if !low && c.duration > 0 {
|
||||
t.Fatalf("%s low message not exec", network)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithAsyncLowMessageDuration(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
duration time.Duration
|
||||
}{
|
||||
{name: "TestWithAsyncLowMessageDuration", duration: time.Millisecond * 100},
|
||||
{name: "TestWithAsyncLowMessageDuration_Zero", duration: 0},
|
||||
{name: "TestWithAsyncLowMessageDuration_Negative", duration: -server.DefaultAsyncLowMessageDuration},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
networks := server.GetNetworks()
|
||||
for i := 0; i < len(networks); i++ {
|
||||
low := false
|
||||
network := networks[i]
|
||||
srv := server.New(network,
|
||||
server.WithAsyncLowMessageDuration(c.duration),
|
||||
)
|
||||
srv.RegMessageLowExecEvent(func(srv *server.Server, message *server.Message, cost time.Duration) {
|
||||
low = true
|
||||
srv.Shutdown()
|
||||
})
|
||||
srv.RegStartFinishEvent(func(srv *server.Server) {
|
||||
if c.duration <= 0 {
|
||||
srv.Shutdown()
|
||||
return
|
||||
}
|
||||
srv.PushAsyncMessage(func() error {
|
||||
time.Sleep(c.duration)
|
||||
return nil
|
||||
}, nil)
|
||||
})
|
||||
var lis string
|
||||
switch network {
|
||||
case server.NetworkNone, server.NetworkUnix:
|
||||
lis = fmt.Sprintf("%s%d", "addr", random.Int(0, 9999))
|
||||
default:
|
||||
lis = fmt.Sprintf(":%d", random.UsablePort())
|
||||
}
|
||||
if err := srv.Run(lis); err != nil {
|
||||
t.Fatalf("%s run error: %s", network, err)
|
||||
}
|
||||
if !low && c.duration > 0 {
|
||||
t.Fatalf("%s low message not exec", network)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -149,7 +149,7 @@ func (srv *Server) Run(addr string) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
// IsSocket 是否是 Socket 模式
|
||||
// IsSocket 通过执行 Network.IsSocket 函数检查该服务器是否是 Socket 模式
|
||||
func (srv *Server) IsSocket() bool {
|
||||
return srv.network.IsSocket()
|
||||
}
|
||||
|
|
|
@ -1,30 +1,53 @@
|
|||
package server_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/server"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 该案例将创建一个简单的 WebSocket 服务器,如果需要更多的服务器类型可参考 [` Network `](#struct_Network) 部分
|
||||
// - server.WithLimitLife(time.Millisecond) 通常不是在正常开发应该使用的,在这里只是为了让服务器在启动完成后的 1 毫秒后自动关闭
|
||||
//
|
||||
// 该案例的输出结果为 true
|
||||
func ExampleNew() {
|
||||
srv := server.New(server.NetworkWebsocket, server.WithLimitLife(time.Millisecond))
|
||||
srv.RegConnectionReceivePacketEvent(func(srv *server.Server, conn *server.Conn, packet []byte) {
|
||||
conn.Write(packet)
|
||||
})
|
||||
if err := srv.Run(":9999"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println(srv != nil)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
// 该案例将创建两个不同类型的服务器,其中 WebSocket 是一个 Socket 服务器,而 Http 是一个非 Socket 服务器
|
||||
//
|
||||
// 可知案例输出结果为:
|
||||
// - true
|
||||
// - false
|
||||
func ExampleServer_IsSocket() {
|
||||
srv1 := server.New(server.NetworkWebsocket)
|
||||
fmt.Println(srv1.IsSocket())
|
||||
srv2 := server.New(server.NetworkHttp)
|
||||
fmt.Println(srv2.IsSocket())
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
// 该案例将创建一个简单的 WebSocket 服务器并启动监听 `:9999/` 作为 WebSocket 监听地址,如果需要更多的服务器类型可参考 [` Network `](#struct_Network) 部分
|
||||
// - 当服务器启动失败后,将会返回错误信息并触发 panic
|
||||
// - server.WithLimitLife(time.Millisecond) 通常不是在正常开发应该使用的,在这里只是为了让服务器在启动完成后的 1 毫秒后自动关闭
|
||||
func ExampleServer_Run() {
|
||||
srv := server.New(server.NetworkWebsocket, server.WithLimitLife(time.Millisecond))
|
||||
srv.RegConnectionReceivePacketEvent(func(srv *server.Server, conn *server.Conn, packet []byte) {
|
||||
conn.Write(packet)
|
||||
})
|
||||
if err := srv.Run(":9999"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
}
|
||||
|
||||
// RunNone 函数并没有特殊的意义,该函数内部调用了 `srv.Run("")` 函数,仅是一个语法糖,用来表示服务器不需要监听任何地址
|
||||
func ExampleServer_RunNone() {
|
||||
srv := server.New(server.NetworkNone)
|
||||
if err := srv.RunNone(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Output:
|
||||
}
|
||||
|
|
|
@ -1,81 +1,101 @@
|
|||
package server_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/server"
|
||||
"github.com/kercylan98/minotaur/server/client"
|
||||
"github.com/kercylan98/minotaur/utils/times"
|
||||
"github.com/kercylan98/minotaur/utils/super"
|
||||
"runtime/debug"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 该单元测试用于测试以不同的基本参数创建服务器是否存在异常
|
||||
func TestNew(t *testing.T) {
|
||||
srv := server.New(server.NetworkWebsocket, server.WithPProf())
|
||||
srv.RegStartBeforeEvent(func(srv *server.Server) {
|
||||
fmt.Println("启动前")
|
||||
})
|
||||
srv.RegStartFinishEvent(func(srv *server.Server) {
|
||||
fmt.Println("启动完成")
|
||||
})
|
||||
srv.RegConnectionClosedEvent(func(srv *server.Server, conn *server.Conn, err any) {
|
||||
fmt.Println("关闭", conn.GetID(), err, "IncrCount", srv.GetOnlineCount())
|
||||
})
|
||||
|
||||
srv.RegConnectionReceivePacketEvent(func(srv *server.Server, conn *server.Conn, packet []byte) {
|
||||
conn.Write(packet)
|
||||
})
|
||||
if err := srv.Run(":9999"); err != nil {
|
||||
panic(err)
|
||||
var cases = []struct {
|
||||
name string
|
||||
network server.Network
|
||||
addr string
|
||||
shouldPanic bool
|
||||
}{
|
||||
{name: "TestNew_Unknown", addr: "", network: "Unknown", shouldPanic: true},
|
||||
{name: "TestNew_None", addr: "", network: server.NetworkNone, shouldPanic: false},
|
||||
{name: "TestNew_None_Addr", addr: "addr", network: server.NetworkNone, shouldPanic: false},
|
||||
{name: "TestNew_Tcp_AddrEmpty", addr: "", network: server.NetworkTcp, shouldPanic: true},
|
||||
{name: "TestNew_Tcp_AddrIllegal", addr: "addr", network: server.NetworkTcp, shouldPanic: true},
|
||||
{name: "TestNew_Tcp_Addr", addr: ":9999", network: server.NetworkTcp, shouldPanic: false},
|
||||
{name: "TestNew_Tcp4_AddrEmpty", addr: "", network: server.NetworkTcp4, shouldPanic: true},
|
||||
{name: "TestNew_Tcp4_AddrIllegal", addr: "addr", network: server.NetworkTcp4, shouldPanic: true},
|
||||
{name: "TestNew_Tcp4_Addr", addr: ":9999", network: server.NetworkTcp4, shouldPanic: false},
|
||||
{name: "TestNew_Tcp6_AddrEmpty", addr: "", network: server.NetworkTcp6, shouldPanic: true},
|
||||
{name: "TestNew_Tcp6_AddrIllegal", addr: "addr", network: server.NetworkTcp6, shouldPanic: true},
|
||||
{name: "TestNew_Tcp6_Addr", addr: ":9999", network: server.NetworkTcp6, shouldPanic: false},
|
||||
{name: "TestNew_Udp_AddrEmpty", addr: "", network: server.NetworkUdp, shouldPanic: true},
|
||||
{name: "TestNew_Udp_AddrIllegal", addr: "addr", network: server.NetworkUdp, shouldPanic: true},
|
||||
{name: "TestNew_Udp_Addr", addr: ":9999", network: server.NetworkUdp, shouldPanic: false},
|
||||
{name: "TestNew_Udp4_AddrEmpty", addr: "", network: server.NetworkUdp4, shouldPanic: true},
|
||||
{name: "TestNew_Udp4_AddrIllegal", addr: "addr", network: server.NetworkUdp4, shouldPanic: true},
|
||||
{name: "TestNew_Udp4_Addr", addr: ":9999", network: server.NetworkUdp4, shouldPanic: false},
|
||||
{name: "TestNew_Udp6_AddrEmpty", addr: "", network: server.NetworkUdp6, shouldPanic: true},
|
||||
{name: "TestNew_Udp6_AddrIllegal", addr: "addr", network: server.NetworkUdp6, shouldPanic: true},
|
||||
{name: "TestNew_Udp6_Addr", addr: ":9999", network: server.NetworkUdp6, shouldPanic: false},
|
||||
{name: "TestNew_Unix_AddrEmpty", addr: "", network: server.NetworkUnix, shouldPanic: true},
|
||||
{name: "TestNew_Unix_AddrIllegal", addr: "addr", network: server.NetworkUnix, shouldPanic: true},
|
||||
{name: "TestNew_Unix_Addr", addr: "addr", network: server.NetworkUnix, shouldPanic: false},
|
||||
{name: "TestNew_Websocket_AddrEmpty", addr: "", network: server.NetworkWebsocket, shouldPanic: true},
|
||||
{name: "TestNew_Websocket_AddrIllegal", addr: "addr", network: server.NetworkWebsocket, shouldPanic: true},
|
||||
{name: "TestNew_Websocket_Addr", addr: ":9999/ws", network: server.NetworkWebsocket, shouldPanic: false},
|
||||
{name: "TestNew_Http_AddrEmpty", addr: "", network: server.NetworkHttp, shouldPanic: true},
|
||||
{name: "TestNew_Http_AddrIllegal", addr: "addr", network: server.NetworkHttp, shouldPanic: true},
|
||||
{name: "TestNew_Http_Addr", addr: ":9999", network: server.NetworkHttp, shouldPanic: false},
|
||||
{name: "TestNew_Kcp_AddrEmpty", addr: "", network: server.NetworkKcp, shouldPanic: true},
|
||||
{name: "TestNew_Kcp_AddrIllegal", addr: "addr", network: server.NetworkKcp, shouldPanic: true},
|
||||
{name: "TestNew_Kcp_Addr", addr: ":9999", network: server.NetworkKcp, shouldPanic: false},
|
||||
{name: "TestNew_GRPC_AddrEmpty", addr: "", network: server.NetworkGRPC, shouldPanic: true},
|
||||
{name: "TestNew_GRPC_AddrIllegal", addr: "addr", network: server.NetworkGRPC, shouldPanic: true},
|
||||
{name: "TestNew_GRPC_Addr", addr: ":9999", network: server.NetworkGRPC, shouldPanic: false},
|
||||
}
|
||||
}
|
||||
|
||||
func TestNew2(t *testing.T) {
|
||||
srv := server.New(server.NetworkWebsocket, server.WithPProf())
|
||||
srv.RegStartBeforeEvent(func(srv *server.Server) {
|
||||
fmt.Println("启动前")
|
||||
})
|
||||
srv.RegStartFinishEvent(func(srv *server.Server) {
|
||||
fmt.Println("启动完成")
|
||||
})
|
||||
srv.RegConnectionClosedEvent(func(srv *server.Server, conn *server.Conn, err any) {
|
||||
fmt.Println("关闭", conn.GetID(), err, "IncrCount", srv.GetOnlineCount())
|
||||
})
|
||||
|
||||
srv.RegConnectionReceivePacketEvent(func(srv *server.Server, conn *server.Conn, packet []byte) {
|
||||
conn.Write(packet)
|
||||
})
|
||||
if err := srv.Run(":9999"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewClient(t *testing.T) {
|
||||
count := 500
|
||||
for i := 0; i < count; i++ {
|
||||
fmt.Println("启动", i+1)
|
||||
cli := client.NewWebsocket("ws://172.29.5.138:9999")
|
||||
cli.RegConnectionReceivePacketEvent(func(conn *client.Client, wst int, packet []byte) {
|
||||
fmt.Println(time.Now().Unix(), "收到", string(packet))
|
||||
})
|
||||
cli.RegConnectionClosedEvent(func(conn *client.Client, err any) {
|
||||
fmt.Println("关闭", err)
|
||||
})
|
||||
cli.RegConnectionOpenedEvent(func(conn *client.Client) {
|
||||
go func() {
|
||||
for i < count {
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
for {
|
||||
for i := 0; i < 10; i++ {
|
||||
cli.WriteWS(2, []byte("hello"))
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
defer func() {
|
||||
if err := super.RecoverTransform(recover()); err != nil && !c.shouldPanic {
|
||||
debug.PrintStack()
|
||||
t.Fatal("not should panic, err:", err)
|
||||
}
|
||||
}()
|
||||
})
|
||||
if err := cli.Run(); err != nil {
|
||||
if err := server.New(c.network, server.WithLimitLife(time.Millisecond*10)).Run(""); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 这个测试检查了各个类型的服务器是否为 Socket 模式。如需查看为 Socket 模式的网络类型,请参考 [` Network.IsSocket` ](#struct_Network_IsSocket)
|
||||
func TestServer_IsSocket(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
network server.Network
|
||||
expect bool
|
||||
}{
|
||||
{name: "TestServer_IsSocket_None", network: server.NetworkNone, expect: false},
|
||||
{name: "TestServer_IsSocket_Tcp", network: server.NetworkTcp, expect: true},
|
||||
{name: "TestServer_IsSocket_Tcp4", network: server.NetworkTcp4, expect: true},
|
||||
{name: "TestServer_IsSocket_Tcp6", network: server.NetworkTcp6, expect: true},
|
||||
{name: "TestServer_IsSocket_Udp", network: server.NetworkUdp, expect: true},
|
||||
{name: "TestServer_IsSocket_Udp4", network: server.NetworkUdp4, expect: true},
|
||||
{name: "TestServer_IsSocket_Udp6", network: server.NetworkUdp6, expect: true},
|
||||
{name: "TestServer_IsSocket_Unix", network: server.NetworkUnix, expect: true},
|
||||
{name: "TestServer_IsSocket_Http", network: server.NetworkHttp, expect: false},
|
||||
{name: "TestServer_IsSocket_Websocket", network: server.NetworkWebsocket, expect: true},
|
||||
{name: "TestServer_IsSocket_Kcp", network: server.NetworkKcp, expect: true},
|
||||
{name: "TestServer_IsSocket_GRPC", network: server.NetworkGRPC, expect: false},
|
||||
}
|
||||
|
||||
time.Sleep(times.Week)
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
s := server.New(c.network)
|
||||
if s.IsSocket() != c.expect {
|
||||
t.Fatalf("expect: %v, got: %v", c.expect, s.IsSocket())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@ import (
|
|||
"reflect"
|
||||
)
|
||||
|
||||
// Service 兼容传统 service 设计模式的接口
|
||||
// Service 兼容传统 service 设计模式的接口,通过该接口可以实现更简洁、更具有可读性的服务绑定
|
||||
// - 在这之前,我们在实现功能上会将 Server 进行全局存储,之后通过 init 函数进行初始化,这样的顺序是不可控的。
|
||||
type Service interface {
|
||||
// OnInit 初始化服务,该方法将会在 Server 初始化时执行
|
||||
// - 通常来说,该阶段发生任何错误都应该 panic 以阻止 Server 启动
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package server_test
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/server"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 这个案例中我们将 `TestService` 绑定到了 `srv` 服务器中,当服务器启动时,将会对 `TestService` 进行初始化
|
||||
//
|
||||
// 其中 `TestService` 的定义如下:
|
||||
// ```go
|
||||
//
|
||||
// type TestService struct{}
|
||||
//
|
||||
// func (ts *TestService) OnInit(srv *server.Server) {
|
||||
// srv.RegStartFinishEvent(onStartFinish)
|
||||
//
|
||||
// srv.RegStopEvent(func(srv *server.Server) {
|
||||
// fmt.Println("server stop")
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// func (ts *TestService) onStartFinish(srv *server.Server) {
|
||||
// fmt.Println("server start finish")
|
||||
// }
|
||||
//
|
||||
// ```
|
||||
//
|
||||
// 可以看出,在服务初始化时,该服务向服务器注册了启动完成事件及停止事件。这是我们推荐的编码方式,这样编码有以下好处:
|
||||
// - 具备可控制的初始化顺序,避免 init 产生的各种顺序导致的问题,如配置还未加载完成,即开始进行数据库连接等操作
|
||||
// - 可以方便的将不同的服务拆分到不同的包中进行管理
|
||||
// - 当不需要某个服务时,可以直接删除该服务的绑定,而不需要修改其他代码
|
||||
// - ...
|
||||
func ExampleBindService() {
|
||||
srv := server.New(server.NetworkNone, server.WithLimitLife(time.Second))
|
||||
server.BindService(srv, new(TestService))
|
||||
|
||||
if err := srv.RunNone(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Output:
|
||||
// server start finish
|
||||
// server stop
|
||||
}
|
|
@ -20,24 +20,19 @@ func (ts *TestService) OnInit(srv *server.Server) {
|
|||
}
|
||||
|
||||
func TestBindService(t *testing.T) {
|
||||
srv := server.New(server.NetworkNone, server.WithLimitLife(time.Second))
|
||||
var cases = []struct {
|
||||
name string
|
||||
}{
|
||||
{name: "TestBindService"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
srv := server.New(server.NetworkNone, server.WithLimitLife(time.Millisecond))
|
||||
server.BindService(srv, new(TestService))
|
||||
|
||||
if err := srv.RunNone(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleBindService() {
|
||||
srv := server.New(server.NetworkNone, server.WithLimitLife(time.Second))
|
||||
server.BindService(srv, new(TestService))
|
||||
|
||||
if err := srv.RunNone(); err != nil {
|
||||
panic(err)
|
||||
})
|
||||
}
|
||||
|
||||
// Output:
|
||||
// server start finish
|
||||
// server stop
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
func newComment(cg *ast.CommentGroup) *Comment {
|
||||
func newComment(name string, cg *ast.CommentGroup) *Comment {
|
||||
c := &Comment{}
|
||||
if cg == nil {
|
||||
return c
|
||||
|
@ -14,9 +14,10 @@ func newComment(cg *ast.CommentGroup) *Comment {
|
|||
c.Comments = append(c.Comments, comment.Text)
|
||||
cc := strings.TrimPrefix(strings.Replace(comment.Text, "// ", "//", 1), "//")
|
||||
if i == 0 {
|
||||
s := strings.SplitN(cc, " ", 2)
|
||||
if len(s) == 2 {
|
||||
cc = s[1]
|
||||
tsc := strings.TrimSpace(cc)
|
||||
if strings.HasPrefix(tsc, name) {
|
||||
s := strings.TrimSpace(strings.TrimPrefix(tsc, name))
|
||||
cc = s
|
||||
}
|
||||
}
|
||||
c.Clear = append(c.Clear, cc)
|
||||
|
|
|
@ -10,7 +10,7 @@ func newField(field *ast.Field) []*Field {
|
|||
return []*Field{{
|
||||
Anonymous: true,
|
||||
Type: newType(field.Type),
|
||||
Comments: newComment(field.Comment),
|
||||
Comments: newComment("", field.Comment),
|
||||
}}
|
||||
} else {
|
||||
var fs []*Field
|
||||
|
@ -22,7 +22,7 @@ func newField(field *ast.Field) []*Field {
|
|||
Anonymous: false,
|
||||
Name: name.String(),
|
||||
Type: newType(field.Type),
|
||||
Comments: newComment(field.Comment),
|
||||
Comments: newComment(name.String(), field.Comment),
|
||||
})
|
||||
}
|
||||
return fs
|
||||
|
|
|
@ -16,7 +16,7 @@ func newFile(owner *Package, filePath string) (*File, error) {
|
|||
af: af,
|
||||
owner: owner,
|
||||
FilePath: filePath,
|
||||
Comment: newComment(af.Doc),
|
||||
Comment: newComment("Package", af.Doc),
|
||||
}
|
||||
for _, decl := range af.Decls {
|
||||
switch typ := decl.(type) {
|
||||
|
|
|
@ -12,7 +12,7 @@ func newFunction(astFunc *ast.FuncDecl) *Function {
|
|||
f := &Function{
|
||||
decl: astFunc,
|
||||
Name: astFunc.Name.String(),
|
||||
Comments: newComment(astFunc.Doc),
|
||||
Comments: newComment(astFunc.Name.String(), astFunc.Doc),
|
||||
}
|
||||
f.IsTest = strings.HasPrefix(f.Name, "Test")
|
||||
f.IsBenchmark = strings.HasPrefix(f.Name, "Benchmark")
|
||||
|
|
|
@ -91,7 +91,7 @@ func (p *Package) Structs() []*Struct {
|
|||
}
|
||||
|
||||
func (p *Package) FileComments() *Comment {
|
||||
var comment = newComment(nil)
|
||||
var comment = newComment("", nil)
|
||||
for _, file := range p.Files {
|
||||
for _, c := range file.Comment.Comments {
|
||||
comment.Comments = append(comment.Comments, c)
|
||||
|
|
|
@ -8,7 +8,7 @@ func newStruct(astGen *ast.GenDecl) *Struct {
|
|||
astTypeSpec := astGen.Specs[0].(*ast.TypeSpec)
|
||||
s := &Struct{
|
||||
Name: astTypeSpec.Name.String(),
|
||||
Comments: newComment(astGen.Doc),
|
||||
Comments: newComment(astTypeSpec.Name.String(), astGen.Doc),
|
||||
}
|
||||
s.Internal = s.Name[0] >= 97 && s.Name[0] <= 122
|
||||
if astTypeSpec.TypeParams != nil {
|
||||
|
|
|
@ -288,6 +288,7 @@ func (b *Builder) genStructs() {
|
|||
if function.Internal || function.Test {
|
||||
continue
|
||||
}
|
||||
b.newLine(fmt.Sprintf(`<span id="struct_%s_%s"></span>`, structInfo.Name, function.Name)).newLine()
|
||||
b.title(4, strings.TrimSpace(fmt.Sprintf("func (%s%s) %s%s %s",
|
||||
super.If(function.Struct.Type.IsPointer, "*", ""),
|
||||
structInfo.Name,
|
||||
|
@ -311,6 +312,7 @@ func (b *Builder) genStructs() {
|
|||
for _, comment := range function.Comments.Clear {
|
||||
b.quote(comment)
|
||||
}
|
||||
b.newLine()
|
||||
if example := b.p.GetExampleTest(function); example != nil {
|
||||
b.newLine("**示例代码:**").newLine()
|
||||
if len(example.Comments.Clear) > 0 {
|
||||
|
|
|
@ -15,6 +15,20 @@ func Port() int {
|
|||
return Int(1, 65535)
|
||||
}
|
||||
|
||||
// UsablePort 随机返回一个可用的端口号,如果没有可用端口号则返回 -1
|
||||
func UsablePort() int {
|
||||
addr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
cli, err := net.ListenTCP("tcp", addr)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
defer func() { _ = cli.Close() }()
|
||||
return cli.Addr().(*net.TCPAddr).Port
|
||||
}
|
||||
|
||||
// IPv4 返回一个随机产生的IPv4地址。
|
||||
func IPv4() string {
|
||||
return fmt.Sprintf("%d.%d.%d.%d", Int(1, 255), Int(0, 255), Int(0, 255), Int(0, 255))
|
||||
|
|
Loading…
Reference in New Issue