diff --git a/Makefile b/Makefile index 47232b3b..67fb4431 100644 --- a/Makefile +++ b/Makefile @@ -36,8 +36,8 @@ protos-compile: @cd protos && protoc --gogofaster_out=. pitaya.proto rm-test-temp-files: - @rm -f cluster/127.0.0.1* - @rm -f cluster/localhost* + @rm -f cluster/127.0.0.1* 127.0.0.1* + @rm -f cluster/localhost* localhost* test: @go test $(TESTABLE_PACKAGES) diff --git a/app_test.go b/app_test.go index fbb252c6..04d191eb 100644 --- a/app_test.go +++ b/app_test.go @@ -21,6 +21,10 @@ package pitaya import ( + "fmt" + "net" + "os" + "reflect" "testing" "time" @@ -30,23 +34,50 @@ import ( "github.com/topfreegames/pitaya/acceptor" "github.com/topfreegames/pitaya/cluster" "github.com/topfreegames/pitaya/constants" + "github.com/topfreegames/pitaya/helpers" "github.com/topfreegames/pitaya/internal/codec" "github.com/topfreegames/pitaya/internal/message" "github.com/topfreegames/pitaya/route" "github.com/topfreegames/pitaya/router" "github.com/topfreegames/pitaya/serialize/json" "github.com/topfreegames/pitaya/session" + "github.com/topfreegames/pitaya/timer" ) -var tables = []struct { - isFrontend bool - serverType string - serverMode ServerMode - serverMetadata map[string]string - cfg *viper.Viper -}{ - {true, "sv1", Cluster, map[string]string{"name": "bla"}, viper.New()}, - {false, "sv2", Standalone, map[string]string{}, viper.New()}, +var ( + tables = []struct { + isFrontend bool + serverType string + serverMode ServerMode + serverMetadata map[string]string + cfg *viper.Viper + }{ + {true, "sv1", Cluster, map[string]string{"name": "bla"}, viper.New()}, + {false, "sv2", Standalone, map[string]string{}, viper.New()}, + } + typeOfetcdSD reflect.Type + typeOfNatsRPCServer reflect.Type + typeOfNatsRPCClient reflect.Type +) + +func TestMain(m *testing.M) { + setup() + exit := m.Run() + os.Exit(exit) +} + +func setup() { + initApp() + Configure(true, "testtype", Cluster, map[string]string{}, viper.New()) + + etcdSD, _ := cluster.NewEtcdServiceDiscovery(app.config, app.server) + typeOfetcdSD = reflect.TypeOf(etcdSD) + + natsRPCServer, _ := cluster.NewNatsRPCServer(app.config, app.server) + typeOfNatsRPCServer = reflect.TypeOf(natsRPCServer) + + natsRPCClient, _ := cluster.NewNatsRPCClient(app.config, app.server) + typeOfNatsRPCClient = reflect.TypeOf(natsRPCClient) } func initApp() { @@ -216,3 +247,95 @@ func TestShutdown(t *testing.T) { }() <-app.dieChan } + +func TestStartDefaultSD(t *testing.T) { + initApp() + Configure(true, "testtype", Cluster, map[string]string{}, viper.New()) + startDefaultSD() + assert.NotNil(t, app.serviceDiscovery) + assert.Equal(t, typeOfetcdSD, reflect.TypeOf(app.serviceDiscovery)) +} + +func TestStartDefaultRPCServer(t *testing.T) { + initApp() + Configure(true, "testtype", Cluster, map[string]string{}, viper.New()) + startDefaultRPCServer() + assert.NotNil(t, app.rpcServer) + assert.Equal(t, typeOfNatsRPCServer, reflect.TypeOf(app.rpcServer)) +} + +func TestStartDefaultRPCClient(t *testing.T) { + initApp() + Configure(true, "testtype", Cluster, map[string]string{}, viper.New()) + startDefaultRPCClient() + assert.NotNil(t, app.rpcClient) + assert.Equal(t, typeOfNatsRPCClient, reflect.TypeOf(app.rpcClient)) +} + +func TestStartAndListenStandalone(t *testing.T) { + initApp() + Configure(true, "testtype", Standalone, map[string]string{}, viper.New()) + + acc := acceptor.NewTCPAcceptor("0.0.0.0:0") + AddAcceptor(acc) + + go func() { + Start() + }() + helpers.ShouldEventuallyReturn(t, func() bool { + return app.running + }, true) + + assert.NotNil(t, handlerService) + assert.NotNil(t, timer.GlobalTicker) + // should be listening + assert.NotEmpty(t, acc.GetAddr()) + helpers.ShouldEventuallyReturn(t, func() error { + n, err := net.Dial("tcp", acc.GetAddr()) + defer n.Close() + return err + }, nil, 10*time.Millisecond, 100*time.Millisecond) +} + +func ConfigureClusterApp() { + +} + +func TestStartAndListenCluster(t *testing.T) { + es, cli := helpers.GetTestEtcd(t) + defer es.Terminate(t) + + ns := helpers.GetTestNatsServer(t) + nsAddr := ns.Addr().String() + + cfg := viper.New() + cfg.Set("pitaya.cluster.rpc.client.nats.connect", fmt.Sprintf("nats://%s", nsAddr)) + cfg.Set("pitaya.cluster.rpc.server.nats.connect", fmt.Sprintf("nats://%s", nsAddr)) + + initApp() + Configure(true, "testtype", Cluster, map[string]string{}, cfg) + + etcdSD, err := cluster.NewEtcdServiceDiscovery(app.config, app.server, cli) + assert.NoError(t, err) + SetServiceDiscoveryClient(etcdSD) + + acc := acceptor.NewTCPAcceptor("0.0.0.0:0") + AddAcceptor(acc) + + go func() { + Start() + }() + helpers.ShouldEventuallyReturn(t, func() bool { + return app.running + }, true) + + assert.NotNil(t, handlerService) + assert.NotNil(t, timer.GlobalTicker) + // should be listening + assert.NotEmpty(t, acc.GetAddr()) + helpers.ShouldEventuallyReturn(t, func() error { + n, err := net.Dial("tcp", acc.GetAddr()) + defer n.Close() + return err + }, nil, 10*time.Millisecond, 100*time.Millisecond) +} diff --git a/cluster/etcd_service_discovery_test.go b/cluster/etcd_service_discovery_test.go index c8ce9c11..6666f4df 100644 --- a/cluster/etcd_service_discovery_test.go +++ b/cluster/etcd_service_discovery_test.go @@ -27,7 +27,6 @@ import ( "time" "github.com/coreos/etcd/clientv3" - "github.com/coreos/etcd/integration" "github.com/spf13/viper" "github.com/stretchr/testify/assert" "github.com/topfreegames/pitaya/config" @@ -48,13 +47,6 @@ func getConfig(conf ...*viper.Viper) *config.Config { return config } -func getEtcd(t *testing.T) (*integration.ClusterV3, *clientv3.Client) { - t.Helper() - c := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) - cli := c.RandClient() - return c, cli -} - func getEtcdSD(t *testing.T, config *config.Config, server *Server, cli *clientv3.Client) *etcdServiceDiscovery { t.Helper() e, err := NewEtcdServiceDiscovery(config, server, cli) @@ -67,7 +59,7 @@ func TestNewEtcdServiceDiscovery(t *testing.T) { for _, table := range etcdSDTables { t.Run(table.server.ID, func(t *testing.T) { config := getConfig() - c, cli := getEtcd(t) + c, cli := helpers.GetTestEtcd(t) defer c.Terminate(t) e := getEtcdSD(t, config, table.server, cli) assert.NotNil(t, e) @@ -80,7 +72,7 @@ func TestEtcdSDBootstrapLease(t *testing.T) { for _, table := range etcdSDTables { t.Run(table.server.ID, func(t *testing.T) { config := getConfig() - c, cli := getEtcd(t) + c, cli := helpers.GetTestEtcd(t) defer c.Terminate(t) e := getEtcdSD(t, config, table.server, cli) err := e.bootstrapLease() @@ -95,7 +87,7 @@ func TestEtcdSDBootstrapLeaseError(t *testing.T) { for _, table := range etcdSDTables { t.Run(table.server.ID, func(t *testing.T) { config := getConfig() - c, cli := getEtcd(t) + c, cli := helpers.GetTestEtcd(t) c.Terminate(t) e := getEtcdSD(t, config, table.server, cli) err := e.bootstrapLease() @@ -109,7 +101,7 @@ func TestEtcdSDBootstrapServer(t *testing.T) { for _, table := range etcdSDTables { t.Run(table.server.ID, func(t *testing.T) { config := getConfig() - c, cli := getEtcd(t) + c, cli := helpers.GetTestEtcd(t) defer c.Terminate(t) e := getEtcdSD(t, config, table.server, cli) e.bootstrapLease() @@ -134,7 +126,7 @@ func TestEtcdSDDeleteServer(t *testing.T) { for _, table := range etcdSDTables { t.Run(table.server.ID, func(t *testing.T) { config := getConfig() - c, cli := getEtcd(t) + c, cli := helpers.GetTestEtcd(t) defer c.Terminate(t) e := getEtcdSD(t, config, table.server, cli) e.bootstrapLease() @@ -174,7 +166,7 @@ func TestEtcdSDDeleteLocalInvalidServers(t *testing.T) { for _, table := range etcdSDTables { t.Run(table.server.ID, func(t *testing.T) { config := getConfig() - _, cli := getEtcd(t) + _, cli := helpers.GetTestEtcd(t) e := getEtcdSD(t, config, table.server, cli) invalidServer := &Server{ ID: "invalid", @@ -194,7 +186,7 @@ func TestEtcdSDGetServer(t *testing.T) { for _, table := range etcdSDTables { t.Run(table.server.ID, func(t *testing.T) { config := getConfig() - c, cli := getEtcd(t) + c, cli := helpers.GetTestEtcd(t) defer c.Terminate(t) e := getEtcdSD(t, config, table.server, cli) e.bootstrapLease() @@ -214,7 +206,7 @@ func TestEtcdSDInit(t *testing.T) { conf.Set("pitaya.cluster.sd.etcd.heartbeat.interval", "30ms") conf.Set("pitaya.cluster.sd.etcd.syncservers.interval", "30ms") config := getConfig(conf) - c, cli := getEtcd(t) + c, cli := helpers.GetTestEtcd(t) defer c.Terminate(t) e := getEtcdSD(t, config, table.server, cli) e.Init() @@ -244,7 +236,7 @@ func TestEtcdShutdown(t *testing.T) { for _, table := range etcdSDTables { t.Run(table.server.ID, func(t *testing.T) { config := getConfig() - c, cli := getEtcd(t) + c, cli := helpers.GetTestEtcd(t) defer c.Terminate(t) e := getEtcdSD(t, config, table.server, cli) e.Init() @@ -264,7 +256,7 @@ func TestEtcdWatchChangesAddNewServers(t *testing.T) { conf := viper.New() conf.Set("pitaya.cluster.sd.etcd.syncservers.interval", "10ms") config := getConfig(conf) - c, cli := getEtcd(t) + c, cli := helpers.GetTestEtcd(t) defer c.Terminate(t) e := getEtcdSD(t, config, table.server, cli) e.running = true @@ -298,7 +290,7 @@ func TestEtcdWatchChangesDeleteServers(t *testing.T) { conf := viper.New() conf.Set("pitaya.cluster.sd.etcd.syncservers.interval", "10ms") config := getConfig(conf) - c, cli := getEtcd(t) + c, cli := helpers.GetTestEtcd(t) defer c.Terminate(t) e := getEtcdSD(t, config, table.server, cli) e.running = true diff --git a/component_test.go b/component_test.go new file mode 100644 index 00000000..3a8c3d02 --- /dev/null +++ b/component_test.go @@ -0,0 +1,87 @@ +// Copyright (c) nano Author and TFG Co. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package pitaya + +import ( + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" + "github.com/topfreegames/pitaya/component" +) + +type MyComp struct { + component.Base + running bool +} + +func (m *MyComp) Init() { + m.running = true +} + +func (m *MyComp) Shutdown() { + m.running = false +} + +func resetComps() { + handlerComp = make([]regComp, 0) + remoteComp = make([]regComp, 0) +} + +func TestRegister(t *testing.T) { + resetComps() + b := &component.Base{} + Register(b) + assert.Equal(t, 1, len(handlerComp)) + assert.Equal(t, regComp{b, nil}, handlerComp[0]) +} + +func TestRegisterRemote(t *testing.T) { + resetComps() + b := &component.Base{} + RegisterRemote(b) + assert.Equal(t, 1, len(remoteComp)) + assert.Equal(t, regComp{b, nil}, remoteComp[0]) +} + +func TestStartupComponents(t *testing.T) { + initApp() + resetComps() + Configure(true, "testtype", Standalone, map[string]string{}, viper.New()) + + Register(&MyComp{}) + RegisterRemote(&MyComp{}) + startupComponents() + assert.Equal(t, true, handlerComp[0].comp.(*MyComp).running) +} + +func TestShutdownComponents(t *testing.T) { + resetComps() + initApp() + Configure(true, "testtype", Standalone, map[string]string{}, viper.New()) + + Register(&MyComp{}) + RegisterRemote(&MyComp{}) + startupComponents() + + shutdownComponents() + assert.Equal(t, false, handlerComp[0].comp.(*MyComp).running) +} diff --git a/constants/errors.go b/constants/errors.go index 4ec4cd1a..e9694590 100644 --- a/constants/errors.go +++ b/constants/errors.go @@ -58,4 +58,5 @@ var ( ErrSessionNotFound = errors.New("session not found") ErrSessionOnNotify = errors.New("current session working on notify mode") ErrWrongValueType = errors.New("protobuf: convert on wrong type value") + ErrNilCondition = errors.New("pitaya/timer: nil condition") ) diff --git a/group.go b/group.go index 03256013..b7ee12b7 100644 --- a/group.go +++ b/group.go @@ -61,12 +61,10 @@ func (c *Group) Member(uid string) (*session.Session, error) { c.mu.RLock() defer c.mu.RUnlock() - for _, s := range c.sessions { - if s.UID() == uid { - return s, nil - } + s, ok := c.sessions[uid] + if ok { + return s, nil } - return nil, constants.ErrMemberNotFound } diff --git a/group_test.go b/group_test.go new file mode 100644 index 00000000..c95880d6 --- /dev/null +++ b/group_test.go @@ -0,0 +1,270 @@ +// Copyright (c) TFG Co. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package pitaya + +import ( + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "github.com/topfreegames/pitaya/constants" + "github.com/topfreegames/pitaya/session" + "github.com/topfreegames/pitaya/session/mocks" +) + +func getGroup(n string) *Group { + return NewGroup(n) +} + +func TestNewGroup(t *testing.T) { + t.Parallel() + g := getGroup("hello") + assert.NotNil(t, g) + assert.Equal(t, "hello", g.name) +} + +func TestAdd(t *testing.T) { + t.Parallel() + tables := []struct { + name string + frontend bool + UID string + err error + }{ + {"frontend_uid", true, "someuid1", nil}, + {"frontend_nouid", true, "", constants.ErrNoUIDBind}, + {"backend_nouid", false, "", constants.ErrNoUIDBind}, + {"backend_uid", false, "ola1", nil}, + } + + for _, table := range tables { + t.Run(table.name, func(t *testing.T) { + g := getGroup("testGroup") + defer g.Close() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockNetworkEntity := mocks.NewMockNetworkEntity(ctrl) + s := session.New(mockNetworkEntity, table.frontend, table.UID) + err := g.Add(s) + assert.Equal(t, table.err, err) + if err == nil { + assert.True(t, g.Contains(table.UID)) + } + }) + } +} + +func TestContains(t *testing.T) { + t.Parallel() + tables := []struct { + name string + frontend bool + UID string + err error + }{ + {"frontend_uid", true, "someuid2", nil}, + {"backend_uid", false, "ola2", nil}, + {"backend_nouid", false, "", constants.ErrNoUIDBind}, + } + + for _, table := range tables { + t.Run(table.name, func(t *testing.T) { + g := getGroup("testGroup") + defer g.Close() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockNetworkEntity := mocks.NewMockNetworkEntity(ctrl) + s := session.New(mockNetworkEntity, table.frontend, table.UID) + g.Add(s) + b := g.Contains(s.UID()) + if table.err == nil { + assert.True(t, b) + } else { + assert.False(t, b) + } + }) + } +} + +func TestLeave(t *testing.T) { + t.Parallel() + tables := []struct { + name string + frontend bool + UID string + err error + }{ + {"frontend_uid", true, "someuid2", nil}, + {"backend_uid", false, "ola2", nil}, + } + + for _, table := range tables { + t.Run(table.name, func(t *testing.T) { + g := getGroup("testGroup") + defer g.Close() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockNetworkEntity := mocks.NewMockNetworkEntity(ctrl) + s := session.New(mockNetworkEntity, table.frontend, table.UID) + g.Add(s) + err := g.Leave(s) + assert.NoError(t, err) + assert.False(t, g.Contains(table.UID)) + }) + } +} + +func TestLeaveAll(t *testing.T) { + t.Parallel() + tables := []struct { + name string + frontend bool + UID string + err error + }{ + {"frontend_uid", true, "someuid2", nil}, + {"backend_uid", false, "ola2", nil}, + } + + for _, table := range tables { + t.Run(table.name, func(t *testing.T) { + g := getGroup("testGroup") + defer g.Close() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockNetworkEntity := mocks.NewMockNetworkEntity(ctrl) + s := session.New(mockNetworkEntity, table.frontend, table.UID) + g.Add(s) + err := g.LeaveAll() + assert.NoError(t, err) + assert.False(t, g.Contains(table.UID)) + }) + } +} + +func TestCount(t *testing.T) { + t.Parallel() + tables := []struct { + name string + frontend bool + UID string + err error + }{ + {"frontend_uid", true, "someuid2", nil}, + {"backend_uid", false, "ola2", nil}, + } + + for _, table := range tables { + t.Run(table.name, func(t *testing.T) { + g := getGroup("testGroup") + defer g.Close() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockNetworkEntity := mocks.NewMockNetworkEntity(ctrl) + s := session.New(mockNetworkEntity, table.frontend, table.UID) + g.Add(s) + assert.Equal(t, 1, g.Count()) + }) + } +} + +func TestIsClosed(t *testing.T) { + t.Parallel() + tables := []struct { + name string + frontend bool + UID string + err error + }{ + {"frontend_uid", true, "someuid2", nil}, + {"backend_uid", false, "ola2", nil}, + } + + for _, table := range tables { + t.Run(table.name, func(t *testing.T) { + g := getGroup("testGroup55") + assert.False(t, g.isClosed()) + g.Close() + assert.True(t, g.isClosed()) + }) + } +} + +func TestMembers(t *testing.T) { + t.Parallel() + g := getGroup("testGroup11") + defer g.Close() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockNetworkEntity := mocks.NewMockNetworkEntity(ctrl) + s1 := session.New(mockNetworkEntity, true, "someid1") + s2 := session.New(mockNetworkEntity, true, "someid2") + g.Add(s1) + g.Add(s2) + assert.ElementsMatch(t, []string{"someid1", "someid2"}, g.Members()) +} + +func TestBroadcast(t *testing.T) { + t.Parallel() + g := getGroup("testGroup22") + defer g.Close() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockNetworkEntity := mocks.NewMockNetworkEntity(ctrl) + s1 := session.New(mockNetworkEntity, true, "someid1") + s2 := session.New(mockNetworkEntity, true, "someid2") + g.Add(s1) + g.Add(s2) + route := "some.route.bla" + data := []byte("hellow") + mockNetworkEntity.EXPECT().Push(route, data).Times(2) + err := g.Broadcast(route, data) + assert.NoError(t, err) + + g.Close() + err = g.Broadcast(route, data) + assert.EqualError(t, constants.ErrClosedGroup, err.Error()) +} + +func TestMulticast(t *testing.T) { + t.Parallel() + g := getGroup("testGroup22") + defer g.Close() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockNetworkEntity := mocks.NewMockNetworkEntity(ctrl) + s1 := session.New(mockNetworkEntity, true, "someid1") + s2 := session.New(mockNetworkEntity, true, "someid2") + g.Add(s1) + g.Add(s2) + route := "some.route.bla" + data := []byte("hellow") + filter := func(s *session.Session) bool { + return s.UID() == "someid1" + } + mockNetworkEntity.EXPECT().Push(route, data).Times(1) + err := g.Multicast(route, data, filter) + assert.NoError(t, err) + + g.Close() + err = g.Multicast(route, data, filter) + assert.EqualError(t, constants.ErrClosedGroup, err.Error()) +} diff --git a/helpers/helpers.go b/helpers/helpers.go index 82ffe263..f2dee836 100644 --- a/helpers/helpers.go +++ b/helpers/helpers.go @@ -10,6 +10,8 @@ import ( "testing" "time" + "github.com/coreos/etcd/clientv3" + "github.com/coreos/etcd/integration" "github.com/nats-io/gnatsd/server" gnatsd "github.com/nats-io/gnatsd/test" ) @@ -55,6 +57,14 @@ func GetTestNatsServer(t *testing.T) *server.Server { return s } +// GetTestEtcd gets a test in memory etcd server +func GetTestEtcd(t *testing.T) (*integration.ClusterV3, *clientv3.Client) { + t.Helper() + c := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) + cli := c.RandClient() + return c, cli +} + // WriteFile test helper func WriteFile(t *testing.T, filepath string, bytes []byte) { t.Helper() diff --git a/module_test.go b/module_test.go new file mode 100644 index 00000000..9698b6c7 --- /dev/null +++ b/module_test.go @@ -0,0 +1,94 @@ +// Copyright (c) nano Author and TFG Co. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package pitaya + +import ( + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" + "github.com/topfreegames/pitaya/component" + "github.com/topfreegames/pitaya/interfaces" +) + +type MyMod struct { + component.Base + running bool +} + +func (m *MyMod) Init() error { + m.running = true + return nil +} + +func (m *MyMod) Shutdown() error { + m.running = false + return nil +} + +func resetModules() { + modules = make(map[string]interfaces.Module) +} + +func TestRegisterModule(t *testing.T) { + resetModules() + b := &MyMod{} + err := RegisterModule(b, "mod") + assert.NoError(t, err) + assert.Equal(t, 1, len(modules)) + assert.Equal(t, b, modules["mod"]) + err = RegisterModule(b, "mod") + assert.Error(t, err) +} + +func TestGetModule(t *testing.T) { + resetModules() + b := &MyMod{} + RegisterModule(b, "mod") + m, err := GetModule("mod") + assert.NoError(t, err) + assert.Equal(t, b, m) + + m, err = GetModule("mmm") + assert.Error(t, err) +} + +func TestStartupModules(t *testing.T) { + initApp() + resetModules() + Configure(true, "testtype", Standalone, map[string]string{}, viper.New()) + + RegisterModule(&MyMod{}, "bla") + startModules() + assert.Equal(t, true, modules["bla"].(*MyMod).running) +} + +func TestShutdownModules(t *testing.T) { + resetModules() + initApp() + Configure(true, "testtype", Standalone, map[string]string{}, viper.New()) + + RegisterModule(&MyMod{}, "bla") + startModules() + + shutdownModules() + assert.Equal(t, false, modules["bla"].(*MyMod).running) +} diff --git a/pipeline_test.go b/pipeline_test.go new file mode 100644 index 00000000..5c93fdea --- /dev/null +++ b/pipeline_test.go @@ -0,0 +1,54 @@ +// Copyright (c) TFG Co. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package pitaya + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/topfreegames/pitaya/pipeline" + "github.com/topfreegames/pitaya/session" +) + +func resetPipelines() { + pipeline.BeforeHandler.Handlers = make([]pipeline.Handler, 0) + pipeline.AfterHandler.Handlers = make([]pipeline.Handler, 0) +} + +var myHandler = func(s *session.Session, in []byte) ([]byte, error) { + return []byte("test"), nil +} + +func TestBeforeHandler(t *testing.T) { + resetPipelines() + BeforeHandler(myHandler) + r, err := pipeline.BeforeHandler.Handlers[0](nil, nil) + assert.NoError(t, err) + assert.Equal(t, []byte("test"), r) +} + +func TestAfterHandler(t *testing.T) { + resetPipelines() + AfterHandler(myHandler) + r, err := pipeline.AfterHandler.Handlers[0](nil, nil) + assert.NoError(t, err) + assert.Equal(t, []byte("test"), r) +} diff --git a/timer.go b/timer.go index 33feb334..c0c946d8 100644 --- a/timer.go +++ b/timer.go @@ -24,6 +24,7 @@ import ( "math" "time" + "github.com/topfreegames/pitaya/constants" "github.com/topfreegames/pitaya/timer" ) @@ -67,14 +68,14 @@ func NewAfterTimer(duration time.Duration, fn timer.Func) *timer.Timer { // when condition satisfied that specified by the condition argument. // The duration d must be greater than zero; if not, NewCondTimer will panic. // Stop the timer to release associated resources. -func NewCondTimer(condition timer.Condition, fn timer.Func) *timer.Timer { +func NewCondTimer(condition timer.Condition, fn timer.Func) (*timer.Timer, error) { if condition == nil { - panic("pitaya/timer: nil condition") + return nil, constants.ErrNilCondition } t := NewCountTimer(time.Duration(math.MaxInt64), timer.LoopForever, fn) t.SetCondition(condition) - return t + return t, nil } // SetTimerPrecision set the ticker precision, and time precision can not less diff --git a/timer_test.go b/timer_test.go new file mode 100644 index 00000000..f2302e1c --- /dev/null +++ b/timer_test.go @@ -0,0 +1,76 @@ +// Copyright (c) TFG Co. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package pitaya + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/topfreegames/pitaya/constants" + "github.com/topfreegames/pitaya/timer" +) + +type MyCond struct{} + +func (m *MyCond) Check(now time.Time) bool { + return false +} + +func TestNewTimer(t *testing.T) { + t.Parallel() + tt := NewTimer(100*time.Millisecond, func() {}) + assert.NotNil(t, tt) +} + +func TestNewCountTimer(t *testing.T) { + t.Parallel() + tt := NewCountTimer(100*time.Millisecond, 10, func() {}) + assert.NotNil(t, tt) +} + +func TestNewAfterTimer(t *testing.T) { + t.Parallel() + tt := NewAfterTimer(100*time.Millisecond, func() {}) + assert.NotNil(t, tt) +} + +func TestNewCondTimer(t *testing.T) { + t.Parallel() + _, err := NewCondTimer(nil, func() {}) + assert.EqualError(t, constants.ErrNilCondition, err.Error()) + + tt, err := NewCondTimer(&MyCond{}, func() {}) + assert.NoError(t, err) + assert.NotNil(t, tt) +} + +func TestSetTimerPrecision(t *testing.T) { + t.Parallel() + dur := 33 * time.Millisecond + SetTimerPrecision(dur) + assert.Equal(t, dur, timer.Precision) +} + +func TestSetTimerBacklog(t *testing.T) { + backlog := 1 << 4 + SetTimerBacklog(backlog) +}