| // Copyright 2017 Google Inc. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package events_test |
| |
| import ( |
| "sync" |
| "sync/atomic" |
| |
| "github.com/30x/apid-core" |
| "github.com/30x/apid-core/events" |
| . "github.com/onsi/ginkgo" |
| . "github.com/onsi/gomega" |
| |
| "strconv" |
| ) |
| |
| var _ = Describe("Events Service", func() { |
| |
| Context("local", func() { |
| |
| var em apid.EventsService |
| |
| BeforeEach(func() { |
| em = events.CreateService() |
| }) |
| |
| AfterEach(func() { |
| if em != nil { |
| em.Close() |
| em = nil |
| } |
| }) |
| |
| It("should ignore event with no listeners", func() { |
| em.Emit("no listeners", &test_event{"test"}) |
| }) |
| |
| It("should publish an event to a listener", func(done Done) { |
| h := test_handler{ |
| "handler", |
| func(event apid.Event) { |
| close(done) |
| }, |
| } |
| |
| em.Listen("selector", &h) |
| em.Emit("selector", &test_event{"test"}) |
| }) |
| |
| It("should publish an event to a listener func", func(done Done) { |
| h := func(event apid.Event) { |
| close(done) |
| } |
| |
| em.ListenFunc("selector", h) |
| em.Emit("selector", &test_event{"test"}) |
| }) |
| |
| It("should publish multiple events to a listener", func(done Done) { |
| count := int32(0) |
| h := test_handler{ |
| "handler", |
| func(event apid.Event) { |
| defer GinkgoRecover() |
| |
| c := atomic.AddInt32(&count, 1) |
| if c > 1 { |
| close(done) |
| } |
| }, |
| } |
| |
| em.Listen("selector", &h) |
| em.Emit("selector", &test_event{"test1"}) |
| em.Emit("selector", &test_event{"test2"}) |
| }) |
| |
| It("EmitWithCallback should call the callback when done with delivery", func(done Done) { |
| delivered := func(event apid.Event) { |
| close(done) |
| } |
| |
| em.EmitWithCallback("selector", &test_event{"test1"}, delivered) |
| }) |
| |
| It("should publish only one event to a listenOnce", func(done Done) { |
| count := 0 |
| h := func(event apid.Event) { |
| defer GinkgoRecover() |
| count++ |
| } |
| |
| delivered := func(event apid.Event) { |
| defer GinkgoRecover() |
| Expect(count).To(Equal(1)) |
| close(done) |
| } |
| |
| em.ListenOnceFunc("selector", h) |
| em.Emit("selector", &test_event{"test1"}) |
| em.EmitWithCallback("selector", &test_event{"test2"}, delivered) |
| }) |
| |
| It("should publish an event to multiple listeners", func(done Done) { |
| mut := sync.Mutex{} |
| hitH1 := false |
| hitH2 := false |
| h1 := test_handler{ |
| "handler 1", |
| func(event apid.Event) { |
| defer GinkgoRecover() |
| mut.Lock() |
| defer mut.Unlock() |
| hitH1 = true |
| if hitH1 && hitH2 { |
| close(done) |
| } |
| }, |
| } |
| h2 := test_handler{ |
| "handler 2", |
| func(event apid.Event) { |
| defer GinkgoRecover() |
| mut.Lock() |
| defer mut.Unlock() |
| hitH2 = true |
| if hitH1 && hitH2 { |
| close(done) |
| } |
| }, |
| } |
| |
| em.Listen("selector", &h1) |
| em.Listen("selector", &h2) |
| em.Emit("selector", &test_event{"test"}) |
| }) |
| |
| It("should publish an event delivered event", func(done Done) { |
| testEvent := &test_event{"test"} |
| var testSelector apid.EventSelector = "selector" |
| |
| dummy := func(event apid.Event) {} |
| em.ListenFunc(testSelector, dummy) |
| |
| h := test_handler{ |
| "event delivered handler", |
| func(event apid.Event) { |
| defer GinkgoRecover() |
| |
| e, ok := event.(apid.EventDeliveryEvent) |
| |
| Expect(ok).To(BeTrue()) |
| Expect(e.Event).To(Equal(testEvent)) |
| Expect(e.Selector).To(Equal(testSelector)) |
| |
| close(done) |
| }, |
| } |
| |
| em.Listen(apid.EventDeliveredSelector, &h) |
| em.Emit(testSelector, testEvent) |
| }) |
| |
| It("should be able to remove a listener", func(done Done) { |
| event1 := &test_event{"test1"} |
| event2 := &test_event{"test2"} |
| event3 := &test_event{"test3"} |
| |
| dummy := func(event apid.Event) {} |
| em.ListenFunc("selector", dummy) |
| |
| h := test_handler{ |
| "handler", |
| func(event apid.Event) { |
| defer GinkgoRecover() |
| |
| Expect(event).NotTo(Equal(event2)) |
| if event == event3 { |
| close(done) |
| } |
| }, |
| } |
| em.Listen("selector", &h) |
| |
| // need to drive test like this because of async delivery |
| var td apid.EventHandler |
| td = &test_handler{ |
| "test driver", |
| func(event apid.Event) { |
| defer GinkgoRecover() |
| |
| e := event.(apid.EventDeliveryEvent) |
| if e.Event == event1 { |
| em.StopListening("selector", &h) |
| em.Emit("selector", event2) |
| } else if e.Event == event2 { |
| em.StopListening(apid.EventDeliveredSelector, td) |
| em.Listen("selector", &h) |
| em.Emit("selector", event3) |
| } |
| }, |
| } |
| em.Listen(apid.EventDeliveredSelector, td) |
| |
| em.Emit("selector", event1) |
| }) |
| |
| It("should deliver events according selector", func(done Done) { |
| e1 := &test_event{"test1"} |
| e2 := &test_event{"test2"} |
| |
| count := int32(0) |
| |
| h1 := test_handler{ |
| "handler1", |
| func(event apid.Event) { |
| defer GinkgoRecover() |
| |
| c := atomic.AddInt32(&count, 1) |
| Expect(event).Should(Equal(e1)) |
| if c == 2 { |
| close(done) |
| } |
| }, |
| } |
| |
| h2 := test_handler{ |
| "handler2", |
| func(event apid.Event) { |
| defer GinkgoRecover() |
| |
| c := atomic.AddInt32(&count, 1) |
| Expect(event).Should(Equal(e2)) |
| if c == 2 { |
| close(done) |
| } |
| }, |
| } |
| |
| em.Listen("selector1", &h1) |
| em.Listen("selector2", &h2) |
| |
| em.Emit("selector2", e2) |
| em.Emit("selector1", e1) |
| }) |
| }) |
| |
| Context("plugins", func() { |
| |
| BeforeEach(func() { |
| }) |
| |
| AfterEach(func() { |
| apid.Events().Close() |
| }) |
| |
| It("should publish PluginsInitialized event", func(done Done) { |
| xData := make(map[string]interface{}) |
| xData["schemaVersion"] = "1.2.3" |
| p := func(s apid.Services) (pd apid.PluginData, err error) { |
| pd = apid.PluginData{ |
| Name: "test plugin", |
| Version: "1.0.0", |
| ExtraData: xData, |
| } |
| return |
| } |
| apid.RegisterPlugin(p) |
| |
| h := func(event apid.Event) { |
| defer GinkgoRecover() |
| |
| if pie, ok := event.(apid.PluginsInitializedEvent); ok { |
| |
| Expect(len(pie.Plugins)).Should(Equal(1)) |
| p := pie.Plugins[0] |
| Expect(p.Name).To(Equal("test plugin")) |
| Expect(p.Version).To(Equal("1.0.0")) |
| Expect(p.ExtraData["schemaVersion"]).To(Equal("1.2.3")) |
| |
| close(done) |
| } |
| } |
| apid.Events().ListenFunc(apid.SystemEventsSelector, h) |
| |
| apid.InitializePlugins("") |
| }) |
| |
| It("shutdown event should be emitted and listened successfully", func(done Done) { |
| h := func(event apid.Event) { |
| defer GinkgoRecover() |
| |
| if pie, ok := event.(apid.ShutdownEvent); ok { |
| Expect(pie.Description).Should(Equal("apid is going to shutdown")) |
| } else { |
| Fail("Received wrong event") |
| } |
| } |
| apid.Events().ListenFunc(apid.ShutdownEventSelector, h) |
| <- apid.Events().Emit(apid.ShutdownEventSelector, apid.ShutdownEvent{"apid is going to shutdown"}) |
| close(done) |
| }) |
| |
| It("handlers registered by plugins should execute before apid shutdown", func(done Done) { |
| pluginNum := 10 |
| count := int32(0) |
| countP := &count |
| |
| // create and register plugins, listen to shutdown event |
| for i:=0; i<pluginNum; i++ { |
| apid.RegisterPlugin(createDummyPlugin(i)) |
| h := func(event apid.Event) { |
| if pie, ok := event.(apid.ShutdownEvent); ok { |
| Expect(pie.Description).Should(Equal("apid is going to shutdown")) |
| atomic.AddInt32(countP, 1) |
| } else { |
| Fail("Received wrong event") |
| } |
| } |
| apid.Events().ListenFunc(apid.ShutdownEventSelector, h) |
| } |
| |
| |
| apid.InitializePlugins("") |
| |
| apid.ShutdownPluginsAndWait() |
| |
| // handlers of all registered plugins have executed |
| Expect(count).Should(Equal(int32(pluginNum))) |
| close(done) |
| }) |
| |
| It("should be able to read apid version from PluginsInitialized event", func(done Done) { |
| xData := make(map[string]interface{}) |
| xData["schemaVersion"] = "1.2.3" |
| p := func(s apid.Services) (pd apid.PluginData, err error) { |
| pd = apid.PluginData{ |
| Name: "test plugin", |
| Version: "1.0.0", |
| ExtraData: xData, |
| } |
| return |
| } |
| apid.RegisterPlugin(p) |
| |
| apidVersion := "dummy_version" |
| |
| h := func(event apid.Event) { |
| defer GinkgoRecover() |
| |
| if pie, ok := event.(apid.PluginsInitializedEvent); ok { |
| Expect(pie.ApidVersion).To(Equal(apidVersion)) |
| close(done) |
| } |
| } |
| apid.Events().ListenFunc(apid.SystemEventsSelector, h) |
| |
| apid.InitializePlugins(apidVersion) |
| }) |
| }) |
| }) |
| |
| func createDummyPlugin(id int) apid.PluginInitFunc{ |
| xData := make(map[string]interface{}) |
| xData["schemaVersion"] = "1.2.3" |
| p := func(s apid.Services) (pd apid.PluginData, err error) { |
| pd = apid.PluginData{ |
| Name: "test plugin " + strconv.Itoa(id), |
| Version: "1.0.0", |
| ExtraData: xData, |
| } |
| return |
| } |
| return p |
| } |
| |
| type test_handler struct { |
| description string |
| f func(event apid.Event) |
| } |
| |
| func (t *test_handler) String() string { |
| return t.description |
| } |
| |
| func (t *test_handler) Handle(event apid.Event) { |
| t.f(event) |
| } |
| |
| type test_event struct { |
| description string |
| } |