diff --git a/Volo.Abp.sln b/Volo.Abp.sln
index 6e5a711457..ba0cb91ba9 100644
--- a/Volo.Abp.sln
+++ b/Volo.Abp.sln
@@ -278,6 +278,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Caching", "src\Vol
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EventBus", "src\Volo.Abp.EventBus\Volo.Abp.EventBus.csproj", "{D9455AE7-2E0C-4647-9880-F5831BCEE3D8}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EventBus.Tests", "test\Volo.Abp.EventBus.Tests\Volo.Abp.EventBus.Tests.csproj", "{8C327AA0-BBED-4F8B-A88E-1DD97B04E58F}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -728,6 +730,10 @@ Global
{D9455AE7-2E0C-4647-9880-F5831BCEE3D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D9455AE7-2E0C-4647-9880-F5831BCEE3D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D9455AE7-2E0C-4647-9880-F5831BCEE3D8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8C327AA0-BBED-4F8B-A88E-1DD97B04E58F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8C327AA0-BBED-4F8B-A88E-1DD97B04E58F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8C327AA0-BBED-4F8B-A88E-1DD97B04E58F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8C327AA0-BBED-4F8B-A88E-1DD97B04E58F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -863,6 +869,7 @@ Global
{90197499-CBB6-4C8C-90E6-8718CD755C04} = {055F4AC0-46C4-4E99-89CD-0F30D4A97382}
{A5B650AB-A67F-4A4C-9F81-7B5471CA1331} = {4C753F64-0C93-4D65-96C2-A40893AFC1E8}
{D9455AE7-2E0C-4647-9880-F5831BCEE3D8} = {4C753F64-0C93-4D65-96C2-A40893AFC1E8}
+ {8C327AA0-BBED-4F8B-A88E-1DD97B04E58F} = {37087D1B-3693-4E96-983D-A69F210BDE53}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}
diff --git a/test/Volo.Abp.EventBus.Tests/Volo.Abp.EventBus.Tests.csproj b/test/Volo.Abp.EventBus.Tests/Volo.Abp.EventBus.Tests.csproj
new file mode 100644
index 0000000000..b57069aaaf
--- /dev/null
+++ b/test/Volo.Abp.EventBus.Tests/Volo.Abp.EventBus.Tests.csproj
@@ -0,0 +1,25 @@
+
+
+
+ netcoreapp2.0
+ latest
+ Volo.Abp.EventBus.Tests
+ Volo.Abp.EventBus.Tests
+ true
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/ActionBasedEventHandlerTest.cs b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/ActionBasedEventHandlerTest.cs
new file mode 100644
index 0000000000..954b134c73
--- /dev/null
+++ b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/ActionBasedEventHandlerTest.cs
@@ -0,0 +1,115 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Volo.Abp.EventBus
+{
+ public class ActionBasedEventHandlerTest : EventBusTestBase
+ {
+ [Fact]
+ public void Should_Call_Action_On_Event_With_Correct_Source()
+ {
+ var totalData = 0;
+
+ EventBus.Register(
+ eventData =>
+ {
+ totalData += eventData.Value;
+ });
+
+ EventBus.Trigger(new MySimpleEventData(1));
+ EventBus.Trigger(new MySimpleEventData(2));
+ EventBus.Trigger(new MySimpleEventData(3));
+ EventBus.Trigger(new MySimpleEventData(4));
+
+ Assert.Equal(10, totalData);
+ }
+
+ [Fact]
+ public void Should_Call_Handler_With_Non_Generic_Trigger()
+ {
+ var totalData = 0;
+
+ EventBus.Register(
+ eventData =>
+ {
+ totalData += eventData.Value;
+ });
+
+ EventBus.Trigger(typeof(MySimpleEventData), new MySimpleEventData(1));
+ EventBus.Trigger(typeof(MySimpleEventData), new MySimpleEventData(2));
+ EventBus.Trigger(typeof(MySimpleEventData), new MySimpleEventData(3));
+ EventBus.Trigger(typeof(MySimpleEventData), new MySimpleEventData(4));
+
+ Assert.Equal(10, totalData);
+ }
+
+ [Fact]
+ public void Should_Not_Call_Action_After_Unregister_1()
+ {
+ var totalData = 0;
+
+ var registerDisposer = EventBus.Register(
+ eventData =>
+ {
+ totalData += eventData.Value;
+ });
+
+ EventBus.Trigger(new MySimpleEventData(1));
+ EventBus.Trigger(new MySimpleEventData(2));
+ EventBus.Trigger(new MySimpleEventData(3));
+
+ registerDisposer.Dispose();
+
+ EventBus.Trigger(new MySimpleEventData(4));
+
+ Assert.Equal(6, totalData);
+ }
+
+ [Fact]
+ public void Should_Not_Call_Action_After_Unregister_2()
+ {
+ var totalData = 0;
+
+ var action = new Action(
+ eventData =>
+ {
+ totalData += eventData.Value;
+ });
+
+ EventBus.Register(action);
+
+ EventBus.Trigger(new MySimpleEventData(1));
+ EventBus.Trigger(new MySimpleEventData(2));
+ EventBus.Trigger(new MySimpleEventData(3));
+
+ EventBus.Unregister(action);
+
+ EventBus.Trigger(new MySimpleEventData(4));
+
+ Assert.Equal(6, totalData);
+ }
+
+ [Fact]
+ public async Task Should_Call_Action_On_Event_With_Correct_Source_Async()
+ {
+ int totalData = 0;
+
+ EventBus.AsyncRegister(
+ async eventData =>
+ {
+ await Task.Delay(20);
+ Interlocked.Add(ref totalData, eventData.Value);
+ await Task.Delay(20);
+ });
+
+ await EventBus.TriggerAsync(new MySimpleEventData(1));
+ await EventBus.TriggerAsync(new MySimpleEventData(2));
+ await EventBus.TriggerAsync(new MySimpleEventData(3));
+ await EventBus.TriggerAsync(new MySimpleEventData(4));
+
+ Assert.Equal(10, totalData);
+ }
+ }
+}
diff --git a/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventBusTestBase.cs b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventBusTestBase.cs
new file mode 100644
index 0000000000..ccd280c26a
--- /dev/null
+++ b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventBusTestBase.cs
@@ -0,0 +1,12 @@
+namespace Volo.Abp.EventBus
+{
+ public abstract class EventBusTestBase
+ {
+ protected IEventBus EventBus;
+
+ protected EventBusTestBase()
+ {
+ EventBus = new EventBus();
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventBus_Exception_Test.cs b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventBus_Exception_Test.cs
new file mode 100644
index 0000000000..31d0b4dfbe
--- /dev/null
+++ b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventBus_Exception_Test.cs
@@ -0,0 +1,42 @@
+using System;
+using Shouldly;
+using Xunit;
+
+namespace Volo.Abp.EventBus
+{
+ public class EventBus_Exception_Test : EventBusTestBase
+ {
+ [Fact]
+ public void Should_Throw_Single_Exception_If_Only_One_Of_Handlers_Fails()
+ {
+ EventBus.Register(
+ eventData => throw new Exception("This exception is intentionally thrown!"));
+
+ var appException = Assert.Throws(() =>
+ {
+ EventBus.Trigger(new MySimpleEventData(1));
+ });
+
+ appException.Message.ShouldBe("This exception is intentionally thrown!");
+ }
+
+ [Fact]
+ public void Should_Throw_Aggregate_Exception_If_More_Than_One_Of_Handlers_Fail()
+ {
+ EventBus.Register(
+ eventData => throw new Exception("This exception is intentionally thrown #1!"));
+
+ EventBus.Register(
+ eventData => throw new Exception("This exception is intentionally thrown #2!"));
+
+ var aggrException = Assert.Throws(() =>
+ {
+ EventBus.Trigger(new MySimpleEventData(1));
+ });
+
+ aggrException.InnerExceptions.Count.ShouldBe(2);
+ aggrException.InnerExceptions[0].Message.ShouldBe("This exception is intentionally thrown #1!");
+ aggrException.InnerExceptions[1].Message.ShouldBe("This exception is intentionally thrown #2!");
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventBus_MultipleHandle_Test.cs b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventBus_MultipleHandle_Test.cs
new file mode 100644
index 0000000000..8a87ead49f
--- /dev/null
+++ b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventBus_MultipleHandle_Test.cs
@@ -0,0 +1,77 @@
+using System.Threading.Tasks;
+using Shouldly;
+using Volo.Abp.Domain.Entities;
+using Volo.Abp.Domain.Entities.Events;
+using Volo.Abp.EventBus.Handlers;
+using Xunit;
+
+namespace Volo.Abp.EventBus
+{
+ public class EventBus_EntityEvents_Test : EventBusTestBase
+ {
+ [Fact]
+ public void Should_Call_Created_And_Changed_Once()
+ {
+ var handler = new MyEventHandler();
+
+ EventBus.Register>(handler);
+ EventBus.Register>(handler);
+
+ var asyncHandler = new MyAsyncEventHandler();
+
+ EventBus.AsyncRegister>(asyncHandler);
+ EventBus.AsyncRegister>(asyncHandler);
+
+ EventBus.Trigger(new EntityCreatedEventData(new MyEntity()));
+
+ handler.EntityCreatedEventCount.ShouldBe(1);
+ handler.EntityChangedEventCount.ShouldBe(1);
+
+ asyncHandler.EntityCreatedEventCount.ShouldBe(1);
+ asyncHandler.EntityChangedEventCount.ShouldBe(1);
+ }
+
+ public class MyEntity : Entity
+ {
+
+ }
+
+ public class MyEventHandler :
+ IEventHandler>,
+ IEventHandler>
+ {
+ public int EntityChangedEventCount { get; set; }
+ public int EntityCreatedEventCount { get; set; }
+
+ public void HandleEvent(EntityChangedEventData eventData)
+ {
+ EntityChangedEventCount++;
+ }
+
+ public void HandleEvent(EntityCreatedEventData eventData)
+ {
+ EntityCreatedEventCount++;
+ }
+ }
+
+ public class MyAsyncEventHandler :
+ IAsyncEventHandler>,
+ IAsyncEventHandler>
+ {
+ public int EntityChangedEventCount { get; set; }
+ public int EntityCreatedEventCount { get; set; }
+
+ public Task HandleEventAsync(EntityChangedEventData eventData)
+ {
+ EntityChangedEventCount++;
+ return Task.FromResult(0);
+ }
+
+ public Task HandleEventAsync(EntityCreatedEventData eventData)
+ {
+ EntityCreatedEventCount++;
+ return Task.FromResult(0);
+ }
+ }
+ }
+}
diff --git a/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/GenericInheritanceTest.cs b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/GenericInheritanceTest.cs
new file mode 100644
index 0000000000..d59a29b648
--- /dev/null
+++ b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/GenericInheritanceTest.cs
@@ -0,0 +1,55 @@
+using Shouldly;
+using Volo.Abp.Domain.Entities;
+using Volo.Abp.Domain.Entities.Events;
+using Xunit;
+
+namespace Volo.Abp.EventBus
+{
+ public class GenericInheritanceTest : EventBusTestBase
+ {
+ [Fact]
+ public void Should_Trigger_For_Inherited_Generic_1()
+ {
+ var triggeredEvent = false;
+
+ EventBus.Register>(
+ eventData =>
+ {
+ eventData.Entity.Id.ShouldBe(42);
+ triggeredEvent = true;
+ });
+
+ EventBus.Trigger(new EntityUpdatedEventData(new Person { Id = 42 }));
+
+ triggeredEvent.ShouldBe(true);
+ }
+
+ [Fact]
+ public void Should_Trigger_For_Inherited_Generic_2()
+ {
+ var triggeredEvent = false;
+
+ EventBus.Register>(
+ eventData =>
+ {
+ eventData.Entity.Id.ShouldBe(42);
+ triggeredEvent = true;
+ });
+
+ EventBus.Trigger(new EntityChangedEventData(new Student { Id = 42 }));
+
+ triggeredEvent.ShouldBe(true);
+ }
+
+
+ public class Person : Entity
+ {
+
+ }
+
+ public class Student : Person
+ {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/InheritanceTest.cs b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/InheritanceTest.cs
new file mode 100644
index 0000000000..9f6a26f89b
--- /dev/null
+++ b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/InheritanceTest.cs
@@ -0,0 +1,45 @@
+using Xunit;
+
+namespace Volo.Abp.EventBus
+{
+ public class InheritanceTest : EventBusTestBase
+ {
+ [Fact]
+ public void Should_Handle_Events_For_Derived_Classes()
+ {
+ var totalData = 0;
+
+ EventBus.Register(
+ eventData =>
+ {
+ totalData += eventData.Value;
+ });
+
+ EventBus.Trigger(new MySimpleEventData(1)); //Should handle directly registered class
+ EventBus.Trigger(new MySimpleEventData(2)); //Should handle directly registered class
+ EventBus.Trigger(new MyDerivedEventData(3)); //Should handle derived class too
+ EventBus.Trigger(new MyDerivedEventData(4)); //Should handle derived class too
+
+ Assert.Equal(10, totalData);
+ }
+
+ [Fact]
+ public void Should_Not_Handle_Events_For_Base_Classes()
+ {
+ var totalData = 0;
+
+ EventBus.Register(
+ eventData =>
+ {
+ totalData += eventData.Value;
+ });
+
+ EventBus.Trigger(new MySimpleEventData(1)); //Should not handle
+ EventBus.Trigger(new MySimpleEventData(2)); //Should not handle
+ EventBus.Trigger(new MyDerivedEventData(3)); //Should handle
+ EventBus.Trigger(new MyDerivedEventData(4)); //Should handle
+
+ Assert.Equal(7, totalData);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/MyDerivedEventData.cs b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/MyDerivedEventData.cs
new file mode 100644
index 0000000000..19de222834
--- /dev/null
+++ b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/MyDerivedEventData.cs
@@ -0,0 +1,10 @@
+namespace Volo.Abp.EventBus
+{
+ public class MyDerivedEventData : MySimpleEventData
+ {
+ public MyDerivedEventData(int value)
+ : base(value)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/MySimpleEventData.cs b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/MySimpleEventData.cs
new file mode 100644
index 0000000000..08142b3355
--- /dev/null
+++ b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/MySimpleEventData.cs
@@ -0,0 +1,12 @@
+namespace Volo.Abp.EventBus
+{
+ public class MySimpleEventData
+ {
+ public int Value { get; set; }
+
+ public MySimpleEventData(int value)
+ {
+ Value = value;
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/MySimpleTransientAsyncEventHandler.cs b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/MySimpleTransientAsyncEventHandler.cs
new file mode 100644
index 0000000000..9223a92537
--- /dev/null
+++ b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/MySimpleTransientAsyncEventHandler.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.EventBus.Handlers;
+
+namespace Volo.Abp.EventBus
+{
+ public class MySimpleTransientAsyncEventHandler : IAsyncEventHandler, IDisposable
+ {
+ public static int HandleCount { get; set; }
+
+ public static int DisposeCount { get; set; }
+
+ public Task HandleEventAsync(MySimpleEventData eventData)
+ {
+ ++HandleCount;
+ return Task.FromResult(0);
+ }
+
+ public void Dispose()
+ {
+ ++DisposeCount;
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/MySimpleTransientEventHandler.cs b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/MySimpleTransientEventHandler.cs
new file mode 100644
index 0000000000..02235476f2
--- /dev/null
+++ b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/MySimpleTransientEventHandler.cs
@@ -0,0 +1,22 @@
+using System;
+using Volo.Abp.EventBus.Handlers;
+
+namespace Volo.Abp.EventBus
+{
+ public class MySimpleTransientEventHandler : IEventHandler, IDisposable
+ {
+ public static int HandleCount { get; set; }
+
+ public static int DisposeCount { get; set; }
+
+ public void HandleEvent(MySimpleEventData eventData)
+ {
+ ++HandleCount;
+ }
+
+ public void Dispose()
+ {
+ ++DisposeCount;
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/TransientDisposableEventHandlerTest.cs b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/TransientDisposableEventHandlerTest.cs
new file mode 100644
index 0000000000..4b62c62363
--- /dev/null
+++ b/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/TransientDisposableEventHandlerTest.cs
@@ -0,0 +1,24 @@
+using Xunit;
+
+namespace Volo.Abp.EventBus
+{
+ public class TransientDisposableEventHandlerTest : EventBusTestBase
+ {
+ [Fact]
+ public void Should_Call_Handler_AndDispose()
+ {
+ EventBus.Register();
+ EventBus.Register();
+
+ EventBus.Trigger(new MySimpleEventData(1));
+ EventBus.Trigger(new MySimpleEventData(2));
+ EventBus.Trigger(new MySimpleEventData(3));
+
+ Assert.Equal(3, MySimpleTransientEventHandler.HandleCount);
+ Assert.Equal(3, MySimpleTransientEventHandler.DisposeCount);
+
+ Assert.Equal(3, MySimpleTransientAsyncEventHandler.HandleCount);
+ Assert.Equal(3, MySimpleTransientAsyncEventHandler.DisposeCount);
+ }
+ }
+}
\ No newline at end of file