Merge branch 'develop'
This commit is contained in:
commit
ea9e261d82
|
@ -91,6 +91,11 @@ func main() {
|
||||||
|
|
||||||
### 分流服务器
|
### 分流服务器
|
||||||
分流服务器可以将消息分流到不同的分组上,每个分组中为串行处理,不同分组之间并行处理。
|
分流服务器可以将消息分流到不同的分组上,每个分组中为串行处理,不同分组之间并行处理。
|
||||||
|
|
||||||
|
> 关于分流服务器的思考:
|
||||||
|
> - 当游戏需要以房间的形式进行时,应该确保相同房间的玩家处于同一分流中,不同房间的玩家处于不同分流中,这样可以避免不同房间的玩家之间的消息互相阻塞;
|
||||||
|
> - 这时候网络 IO 应该根据不同的游戏类型而进行不同的处理,例如回合制可以同步执行,而实时游戏应该采用异步执行;
|
||||||
|
> - 当游戏大部分时候以单人游戏进行时,应该每个玩家处于自身唯一的分流中,此时非互动的消息造成的网络 IO 采用同步执行即可,也不会阻塞到其他玩家的消息处理;
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -48,13 +48,13 @@ func (slf *Golang) Render(templates ...*pce.TmplStruct) (string, error) {
|
||||||
{{.Name}}Sign,
|
{{.Name}}Sign,
|
||||||
{{- end}}
|
{{- end}}
|
||||||
}
|
}
|
||||||
mutex sync.Mutex
|
mutex sync.RWMutex
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
{{- range .Templates}}
|
{{- range .Templates}}
|
||||||
{{- if $.HasIndex .}}
|
{{- if $.HasIndex .}}
|
||||||
{{.Name}} {{$.GetVariable .}} // {{.Desc}}
|
c{{.Name}} {{$.GetVariable .}} // {{.Desc}}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
)
|
)
|
||||||
|
@ -62,7 +62,7 @@ func (slf *Golang) Render(templates ...*pce.TmplStruct) (string, error) {
|
||||||
var (
|
var (
|
||||||
{{- range .Templates}}
|
{{- range .Templates}}
|
||||||
{{- if $.HasIndex .}}{{- else}}
|
{{- if $.HasIndex .}}{{- else}}
|
||||||
{{.Name}} *{{$.GetConfigName .}} // {{.Desc}}
|
c{{.Name}} *{{$.GetConfigName .}} // {{.Desc}}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
)
|
)
|
||||||
|
@ -169,7 +169,7 @@ func (slf *Golang) Render(templates ...*pce.TmplStruct) (string, error) {
|
||||||
{{- else}}
|
{{- else}}
|
||||||
temp := new({{$.GetConfigName .}})
|
temp := new({{$.GetConfigName .}})
|
||||||
{{- end}}
|
{{- end}}
|
||||||
if err := json.Unmarshal(data, &{{.Name}}); err != nil {
|
if err := json.Unmarshal(data, &c{{.Name}}); err != nil {
|
||||||
log.Error("Config", log.String("Name", "{{.Name}}"), log.Bool("Invalid", true), log.Err(err))
|
log.Error("Config", log.String("Name", "{{.Name}}"), log.Bool("Invalid", true), log.Err(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -185,8 +185,8 @@ func (slf *Golang) Render(templates ...*pce.TmplStruct) (string, error) {
|
||||||
cs := make(map[Sign]any)
|
cs := make(map[Sign]any)
|
||||||
|
|
||||||
{{- range .Templates}}
|
{{- range .Templates}}
|
||||||
{{.Name}} = _{{.Name}}
|
c{{.Name}} = _{{.Name}}
|
||||||
cs[{{.Name}}Sign] = {{.Name}}
|
cs[{{.Name}}Sign] = c{{.Name}}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
|
|
||||||
configs.Store(&cs)
|
configs.Store(&cs)
|
||||||
|
@ -194,8 +194,8 @@ func (slf *Golang) Render(templates ...*pce.TmplStruct) (string, error) {
|
||||||
|
|
||||||
// GetConfigs 获取所有配置
|
// GetConfigs 获取所有配置
|
||||||
func GetConfigs() map[Sign]any {
|
func GetConfigs() map[Sign]any {
|
||||||
mutex.Lock()
|
mutex.RLock()
|
||||||
defer mutex.Unlock()
|
defer mutex.RUnlock()
|
||||||
return hash.Copy(*configs.Load())
|
return hash.Copy(*configs.Load())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,6 +210,25 @@ func (slf *Golang) Render(templates ...*pce.TmplStruct) (string, error) {
|
||||||
defer mutex.Unlock()
|
defer mutex.Unlock()
|
||||||
handle(hash.Copy(*configs.Load()))
|
handle(hash.Copy(*configs.Load()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{{- range .Templates}}
|
||||||
|
{{- if $.HasIndex .}}
|
||||||
|
// {{.Name}} 获取{{.Desc}}
|
||||||
|
func {{.Name}}() {{$.GetVariable .}} {
|
||||||
|
mutex.RLock()
|
||||||
|
defer mutex.RUnlock()
|
||||||
|
return c{{.Name}}
|
||||||
|
}
|
||||||
|
{{- else}}
|
||||||
|
// {{.Name}} 获取{{.Desc}}
|
||||||
|
func {{.Name}}() *{{$.GetConfigName .}} {
|
||||||
|
mutex.RLock()
|
||||||
|
defer mutex.RUnlock()
|
||||||
|
return c{{.Name}}
|
||||||
|
}
|
||||||
|
{{- end}}
|
||||||
|
{{- end}}
|
||||||
|
|
||||||
`, slf)
|
`, slf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ type ShuntChannelClosedEventHandler func(srv *Server, guid int64)
|
||||||
type ConnectionPacketPreprocessEventHandler func(srv *Server, conn *Conn, packet []byte, abort func(), usePacket func(newPacket []byte))
|
type ConnectionPacketPreprocessEventHandler func(srv *Server, conn *Conn, packet []byte, abort func(), usePacket func(newPacket []byte))
|
||||||
type MessageExecBeforeEventHandler func(srv *Server, message *Message) bool
|
type MessageExecBeforeEventHandler func(srv *Server, message *Message) bool
|
||||||
type MessageReadyEventHandler func(srv *Server)
|
type MessageReadyEventHandler func(srv *Server)
|
||||||
|
type OnDeadlockDetectEventHandler func(srv *Server, message *Message)
|
||||||
|
|
||||||
func newEvent(srv *Server) *event {
|
func newEvent(srv *Server) *event {
|
||||||
return &event{
|
return &event{
|
||||||
|
@ -50,6 +51,7 @@ func newEvent(srv *Server) *event {
|
||||||
connectionPacketPreprocessEventHandlers: slice.NewPriority[ConnectionPacketPreprocessEventHandler](),
|
connectionPacketPreprocessEventHandlers: slice.NewPriority[ConnectionPacketPreprocessEventHandler](),
|
||||||
messageExecBeforeEventHandlers: slice.NewPriority[MessageExecBeforeEventHandler](),
|
messageExecBeforeEventHandlers: slice.NewPriority[MessageExecBeforeEventHandler](),
|
||||||
messageReadyEventHandlers: slice.NewPriority[MessageReadyEventHandler](),
|
messageReadyEventHandlers: slice.NewPriority[MessageReadyEventHandler](),
|
||||||
|
dedeadlockDetectEventHandlers: slice.NewPriority[OnDeadlockDetectEventHandler](),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +72,7 @@ type event struct {
|
||||||
connectionPacketPreprocessEventHandlers *slice.Priority[ConnectionPacketPreprocessEventHandler]
|
connectionPacketPreprocessEventHandlers *slice.Priority[ConnectionPacketPreprocessEventHandler]
|
||||||
messageExecBeforeEventHandlers *slice.Priority[MessageExecBeforeEventHandler]
|
messageExecBeforeEventHandlers *slice.Priority[MessageExecBeforeEventHandler]
|
||||||
messageReadyEventHandlers *slice.Priority[MessageReadyEventHandler]
|
messageReadyEventHandlers *slice.Priority[MessageReadyEventHandler]
|
||||||
|
dedeadlockDetectEventHandlers *slice.Priority[OnDeadlockDetectEventHandler]
|
||||||
|
|
||||||
consoleCommandEventHandlers map[string]*slice.Priority[ConsoleCommandEventHandler]
|
consoleCommandEventHandlers map[string]*slice.Priority[ConsoleCommandEventHandler]
|
||||||
consoleCommandEventHandlerInitOnce sync.Once
|
consoleCommandEventHandlerInitOnce sync.Once
|
||||||
|
@ -435,6 +438,27 @@ func (slf *event) OnMessageReadyEvent() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegDeadlockDetectEvent 在死锁检测触发时立即执行被注册的事件处理函数
|
||||||
|
func (slf *event) RegDeadlockDetectEvent(handler OnDeadlockDetectEventHandler, priority ...int) {
|
||||||
|
slf.dedeadlockDetectEventHandlers.Append(handler, slice.GetValue(priority, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *event) OnDeadlockDetectEvent(message *Message) {
|
||||||
|
if slf.dedeadlockDetectEventHandlers.Len() == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
log.Error("Server", log.String("OnDeadlockDetectEvent", fmt.Sprintf("%v", err)))
|
||||||
|
debug.PrintStack()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
slf.dedeadlockDetectEventHandlers.RangeValue(func(index int, value OnDeadlockDetectEventHandler) bool {
|
||||||
|
value(slf.Server, message)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (slf *event) check() {
|
func (slf *event) check() {
|
||||||
switch slf.network {
|
switch slf.network {
|
||||||
case NetworkHttp, NetworkGRPC, NetworkNone:
|
case NetworkHttp, NetworkGRPC, NetworkNone:
|
||||||
|
|
|
@ -695,6 +695,7 @@ func (slf *Server) dispatchMessage(dispatcher *dispatcher, msg *Message) {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
if err := ctx.Err(); err == context.DeadlineExceeded {
|
if err := ctx.Err(); err == context.DeadlineExceeded {
|
||||||
log.Warn("Server", log.String("MessageType", messageNames[msg.t]), log.String("Info", msg.String()), log.Any("SuspectedDeadlock", msg))
|
log.Warn("Server", log.String("MessageType", messageNames[msg.t]), log.String("Info", msg.String()), log.Any("SuspectedDeadlock", msg))
|
||||||
|
slf.OnDeadlockDetectEvent(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}(ctx, msg)
|
}(ctx, msg)
|
||||||
|
|
|
@ -181,12 +181,12 @@ func ReadLineWithParallel(filename string, chunkSize int64, handlerFunc func(str
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
endMutex.Lock()
|
endMutex.Lock()
|
||||||
e := chunk[1] - chunk[0]
|
e := chunk[1]
|
||||||
if e > end {
|
if e > end {
|
||||||
end = e + 1
|
end = e + 1
|
||||||
}
|
}
|
||||||
endMutex.Unlock()
|
endMutex.Unlock()
|
||||||
r := io.NewSectionReader(file, chunk[0], e)
|
r := io.NewSectionReader(file, chunk[0], e-chunk[0])
|
||||||
|
|
||||||
scanner := bufio.NewScanner(r)
|
scanner := bufio.NewScanner(r)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
|
|
|
@ -12,8 +12,10 @@ type Encoder struct {
|
||||||
conf *Config
|
conf *Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *Encoder) Split(config *lumberjack.Logger) *Encoder {
|
func (slf *Encoder) Split(config *lumberjack.Logger, level LevelEnabler) *Encoder {
|
||||||
slf.cores = append(slf.cores, zapcore.NewCore(slf.e, zapcore.AddSync(config), zapcore.DebugLevel))
|
slf.cores = append(slf.cores, zapcore.NewCore(slf.e,
|
||||||
|
zapcore.NewMultiWriteSyncer(zapcore.AddSync(config)),
|
||||||
|
level))
|
||||||
return slf
|
return slf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -204,3 +204,14 @@ func IsOdd[V generic.Integer](n V) bool {
|
||||||
func IsEven[V generic.Integer](n V) bool {
|
func IsEven[V generic.Integer](n V) bool {
|
||||||
return 0 == (int64(n) & 1)
|
return 0 == (int64(n) & 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MakeLastDigitsZero 返回一个新的数,其中 num 的最后 digits 位数被设为零。
|
||||||
|
// - 函数首先创建一个 10 的 digits 次方的遮罩,然后通过整除和乘以这个遮罩来使 num 的最后 digits 位归零。
|
||||||
|
// - 当 T 类型为浮点型时,将被向下取整后再进行转换
|
||||||
|
func MakeLastDigitsZero[T generic.Number](num T, digits int) T {
|
||||||
|
var mask int64 = 1
|
||||||
|
for i := 0; i < digits; i++ {
|
||||||
|
mask *= 10
|
||||||
|
}
|
||||||
|
return T(int64(num) / mask * mask)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package maths_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/maths"
|
||||||
|
"github.com/kercylan98/minotaur/utils/random"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMakeLastDigitsZero(t *testing.T) {
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
n := float64(random.Int64(100, 999999))
|
||||||
|
t.Log(n, 3, maths.MakeLastDigitsZero(n, 3))
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,16 +7,25 @@ import (
|
||||||
|
|
||||||
// Int64 返回一个介于min和max之间的int64类型的随机数。
|
// Int64 返回一个介于min和max之间的int64类型的随机数。
|
||||||
func Int64(min int64, max int64) int64 {
|
func Int64(min int64, max int64) int64 {
|
||||||
|
if min == max {
|
||||||
|
return min
|
||||||
|
}
|
||||||
return min + rand.Int63n(max+1-min)
|
return min + rand.Int63n(max+1-min)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Int 返回一个介于min和max之间的的int类型的随机数。
|
// Int 返回一个介于min和max之间的的int类型的随机数。
|
||||||
func Int(min int, max int) int {
|
func Int(min int, max int) int {
|
||||||
|
if min == max {
|
||||||
|
return min
|
||||||
|
}
|
||||||
return int(Int64(int64(min), int64(max)))
|
return int(Int64(int64(min), int64(max)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Duration 返回一个介于min和max之间的的Duration类型的随机数。
|
// Duration 返回一个介于min和max之间的的Duration类型的随机数。
|
||||||
func Duration(min int64, max int64) time.Duration {
|
func Duration(min int64, max int64) time.Duration {
|
||||||
|
if min == max {
|
||||||
|
return time.Duration(min)
|
||||||
|
}
|
||||||
return time.Duration(Int64(min, max))
|
return time.Duration(Int64(min, max))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,20 @@ package super
|
||||||
|
|
||||||
import "reflect"
|
import "reflect"
|
||||||
|
|
||||||
|
var (
|
||||||
|
romeThousands = []string{"", "M", "MM", "MMM"}
|
||||||
|
romeHundreds = []string{"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}
|
||||||
|
romeTens = []string{"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}
|
||||||
|
romeOnes = []string{"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}
|
||||||
|
)
|
||||||
|
|
||||||
// IsNumber 判断是否为数字
|
// IsNumber 判断是否为数字
|
||||||
func IsNumber(v any) bool {
|
func IsNumber(v any) bool {
|
||||||
kind := reflect.Indirect(reflect.ValueOf(v)).Kind()
|
kind := reflect.Indirect(reflect.ValueOf(v)).Kind()
|
||||||
return kind >= reflect.Int && kind <= reflect.Float64
|
return kind >= reflect.Int && kind <= reflect.Float64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NumberToRome 将数字转换为罗马数字
|
||||||
|
func NumberToRome(num int) string {
|
||||||
|
return romeThousands[num/1000] + romeHundreds[num%1000/100] + romeTens[num%100/10] + romeOnes[num%10]
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package super_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/super"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNumberToRome(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input int
|
||||||
|
output string
|
||||||
|
}{
|
||||||
|
{input: 1, output: "I"},
|
||||||
|
{input: 5, output: "V"},
|
||||||
|
{input: 10, output: "X"},
|
||||||
|
{input: 50, output: "L"},
|
||||||
|
{input: 100, output: "C"},
|
||||||
|
{input: 500, output: "D"},
|
||||||
|
{input: 1000, output: "M"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
result := super.NumberToRome(test.input)
|
||||||
|
if result != test.output {
|
||||||
|
t.Errorf("NumberToRome(%d) = %s; want %s", test.input, result, test.output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package super_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/super"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkCompareVersion(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
super.CompareVersion("vfe2faf.d2ad5.dd3", "afe2faf.d2ad5.dd2")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkOldVersion(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
super.OldVersion("vfe2faf.d2ad5.dd3", "vda2faf.d2ad5.dd2")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package super_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/kercylan98/minotaur/utils/super"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleCompareVersion() {
|
||||||
|
result := super.CompareVersion("1.2.3", "1.2.2")
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleOldVersion() {
|
||||||
|
result := super.OldVersion("1.2.3", "1.2.2")
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output: true
|
||||||
|
}
|
|
@ -5,9 +5,56 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCompareVersion(t *testing.T) {
|
func TestOldVersion(t *testing.T) {
|
||||||
t.Log(super.CompareVersion("1", "2"), -1)
|
testCases := []struct {
|
||||||
t.Log(super.CompareVersion("1", "vv2"), -1)
|
version1 string
|
||||||
t.Log(super.CompareVersion("1", "vv2.3.1"), -1)
|
version2 string
|
||||||
t.Log(super.CompareVersion("11", "vv2.3.1"), 1)
|
want bool
|
||||||
|
}{
|
||||||
|
{"1.2.3", "1.2.2", true},
|
||||||
|
{"1.2.1", "1.2.2", false},
|
||||||
|
{"1.2.3", "1.2.3", false},
|
||||||
|
{"v1.2.3", "v1.2.2", true},
|
||||||
|
{"v1.2.3", "v1.2.4", false},
|
||||||
|
{"v1.2.3", "1.2.3", false},
|
||||||
|
{"vxx2faf.d2ad5.dd3", "gga2faf.d2ad5.dd2", true},
|
||||||
|
{"awd2faf.d2ad4.dd3", "vsd2faf.d2ad5.dd3", false},
|
||||||
|
{"vxd2faf.d2ad5.dd3", "qdq2faf.d2ad5.dd3", false},
|
||||||
|
{"1.2.3", "vdafe2faf.d2ad5.dd3", false},
|
||||||
|
{"v1.2.3", "vdafe2faf.d2ad5.dd3", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
got := super.OldVersion(tc.version1, tc.version2)
|
||||||
|
if got != tc.want {
|
||||||
|
t.Errorf("OldVersion(%q, %q) = %v; want %v", tc.version1, tc.version2, got, tc.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompareVersion(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
version1 string
|
||||||
|
version2 string
|
||||||
|
want int
|
||||||
|
}{
|
||||||
|
{"1.2.3", "1.2.2", 1},
|
||||||
|
{"1.2.1", "1.2.2", -1},
|
||||||
|
{"1.2.3", "1.2.3", 0},
|
||||||
|
{"v1.2.3", "v1.2.2", 1},
|
||||||
|
{"v1.2.3", "v1.2.4", -1},
|
||||||
|
{"v1.2.3", "1.2.3", 0},
|
||||||
|
{"vde2faf.d2ad5.dd3", "e2faf.d2ad5.dd2", 1},
|
||||||
|
{"vde2faf.d2ad4.dd3", "vde2faf.d2ad5.dd3", -1},
|
||||||
|
{"vfe2faf.d2ad5.dd3", "ve2faf.d2ad5.dd3", 0},
|
||||||
|
{"1.2.3", "vdafe2faf.d2ad5.dd3", -1},
|
||||||
|
{"v1.2.3", "vdafe2faf.d2ad5.dd3", -1},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
got := super.CompareVersion(tc.version1, tc.version2)
|
||||||
|
if got != tc.want {
|
||||||
|
t.Errorf("CompareVersion(%q, %q) = %v; want %v", tc.version1, tc.version2, got, tc.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue