using System; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Threading.Tasks; using Newtonsoft.Json; using Orleans; using Orleans.Runtime; using Orleans.Serialization; using Orleans.Storage; using TestExtensions; using UnitTests.GrainInterfaces; using Xunit; namespace DefaultCluster.Tests.General { using Microsoft.Extensions.DependencyInjection; /// /// Summary description for GrainReferenceTest /// [TestCategory("BVT"), TestCategory("Functional"), TestCategory("GrainReference")] public class GrainReferenceTest : HostedTestClusterEnsureDefaultStarted { public GrainReferenceTest(DefaultClusterFixture fixture) : base(fixture) { } [Fact] public void GrainReferenceComparison_DifferentReference() { ISimpleGrain ref1 = this.GrainFactory.GetGrain(random.Next(), UnitTests.Grains.SimpleGrain.SimpleGrainNamePrefix); ISimpleGrain ref2 = this.GrainFactory.GetGrain(random.Next(), UnitTests.Grains.SimpleGrain.SimpleGrainNamePrefix); Assert.True(ref1 != ref2); Assert.True(ref2 != ref1); Assert.False(ref1 == ref2); Assert.False(ref2 == ref1); Assert.False(ref1.Equals(ref2)); Assert.False(ref2.Equals(ref1)); } [Fact, TestCategory("BVT"), TestCategory("Functional"), TestCategory("AsynchronyPrimitives")] public void TaskCompletionSource_Resolve() { string str = "Hello TaskCompletionSource"; TaskCompletionSource tcs = new TaskCompletionSource(); Task task = tcs.Task; Assert.False(task.IsCompleted, "TCS.Task not yet completed"); tcs.SetResult(str); Assert.True(task.IsCompleted, "TCS.Task is now completed"); Assert.False(task.IsFaulted, "TCS.Task should not be in faulted state: " + task.Exception); Assert.Equal(str, tcs.Task.Result); } [Fact] public void GrainReference_Pass_this() { IChainedGrain g1 = this.GrainFactory.GetGrain(GetRandomGrainId()); IChainedGrain g2 = this.GrainFactory.GetGrain(GetRandomGrainId()); g1.PassThis(g2).Wait(); } [Fact] public void GrainReference_Pass_this_Nested() { IChainedGrain g1 = this.GrainFactory.GetGrain(GetRandomGrainId()); IChainedGrain g2 = this.GrainFactory.GetGrain(GetRandomGrainId()); g1.PassThisNested(new ChainGrainHolder { Next = g2 }).Wait(); } [Fact] public async Task GrainReference_Pass_Null() { IChainedGrain g1 = this.GrainFactory.GetGrain(GetRandomGrainId()); IChainedGrain g2 = this.GrainFactory.GetGrain(GetRandomGrainId()); // g1 will pass a null reference to g2 await g1.PassNullNested(new ChainGrainHolder { Next = g2 }); Assert.Null(await g2.GetNext()); await g1.PassNull(g2); Assert.Null(await g2.GetNext()); } [Fact, TestCategory("Serialization")] public void GrainReference_DotNet_Serialization() { int id = random.Next(); TestGrainReferenceSerialization(id, false, false); } [Fact, TestCategory("Serialization")] public void GrainReference_DotNet_Serialization_Unresolved() { int id = random.Next(); TestGrainReferenceSerialization(id, false, false); } [Fact, TestCategory("Serialization"), TestCategory("JSON")] public void GrainReference_Json_Serialization() { int id = random.Next(); TestGrainReferenceSerialization(id, true, true); } [Fact, TestCategory("Serialization"), TestCategory("JSON")] public async Task GrainReference_Json_Serialization_Nested() { var typeResolver = this.HostedCluster.Client.ServiceProvider.GetRequiredService(); var settings = OrleansJsonSerializer.GetDefaultSerializerSettings(typeResolver, HostedCluster.GrainFactory); var grain = HostedCluster.GrainFactory.GetGrain(GetRandomGrainId()); await grain.SetA(56820); var input = new GenericGrainReferenceHolder { Reference = grain as GrainReference }; var json = JsonConvert.SerializeObject(input, settings); var output = JsonConvert.DeserializeObject(json, settings); Assert.Equal(input.Reference, output.Reference); var reference = output.Reference; Assert.Equal(56820, await ((ISimpleGrain)reference).GetA()); } [Serializable] public class GenericGrainReferenceHolder { [JsonProperty] public GrainReference Reference { get; set; } } [Fact, TestCategory("Serialization"), TestCategory("JSON")] public void GrainReference_Json_Serialization_Unresolved() { int id = random.Next(); TestGrainReferenceSerialization(id, false, true); } [Fact(Skip = "GrainReference interning is not currently implemented."), TestCategory("Serialization"), TestCategory("Interner")] public void GrainReference_Interning() { var grainId = GrainId.GetGrainIdForTesting(new Guid()); var g1 = GrainReference.FromGrainId(grainId, null); var g2 = GrainReference.FromGrainId(grainId, null); Assert.Equal(g1, g2); // Should be equal GrainReferences Assert.Same(g1, g2); // Should be same / interned GrainReference object // Round-trip through Serializer var g3 = this.HostedCluster.SerializationManager.RoundTripSerializationForTesting(g1); Assert.Equal(g3, g1); Assert.Equal(g3, g2); Assert.Same(g3, g1); Assert.Same(g3, g2); } [Fact(Skip = "GrainReference interning is not currently implemented."), TestCategory("Serialization"), TestCategory("Interner")] public void GrainReference_Interning_Sys_DirectoryGrain() { var g1 = GrainReference.FromGrainId(Constants.DirectoryServiceId, null); var g2 = GrainReference.FromGrainId(Constants.DirectoryServiceId, null); Assert.Equal(g1, g2); // Should be equal GrainReferences. Assert.Same(g1, g2); // Should be same / interned GrainReference object // Round-trip through Serializer var g3 = this.HostedCluster.SerializationManager.RoundTripSerializationForTesting(g1); Assert.Equal(g3, g1); Assert.Equal(g3, g2); Assert.Same(g3, g1); Assert.Same(g3, g2); } [Fact(Skip = "GrainReference interning is not currently implemented."), TestCategory("Serialization"), TestCategory("Interner")] public void GrainReference_Interning_Sys_StoreGrain() { var g1 = (GrainReference)this.GrainFactory.GetGrain(0); var g2 = (GrainReference)this.GrainFactory.GetGrain(0); Assert.Equal(g1, g2); // Should be equal GrainReferences. Assert.Same(g1, g2); // Should be same / interned GrainReference object // Round-trip through Serializer var g3 = this.HostedCluster.SerializationManager.RoundTripSerializationForTesting(g1); Assert.Equal(g3, g1); Assert.Equal(g3, g2); Assert.Same(g3, g1); Assert.Same(g3, g2); } private void TestGrainReferenceSerialization(int id, bool resolveBeforeSerialize, bool useJson) { // Make sure grain references serialize well through .NET serializer. var grain = this.GrainFactory.GetGrain(random.Next(), UnitTests.Grains.SimpleGrain.SimpleGrainNamePrefix); if (resolveBeforeSerialize) { grain.SetA(id).Wait(); // Resolve GR } ISimpleGrain other; if (useJson) { // Serialize + Deserialize through Json serializer other = NewtonsoftJsonSerializeRoundtrip(grain); } else { // Serialize + Deserialize through .NET serializer other = DotNetSerializeRoundtrip(grain); } if (!resolveBeforeSerialize) { grain.SetA(id).Wait(); // Resolve GR } Assert.IsAssignableFrom(grain.GetType(), other); Assert.NotNull(other); Assert.Equal(grain, other); // "Deserialized grain reference equality is preserved" int res = other.GetA().Result; Assert.Equal(id, res); // "Returned values from call to deserialized grain reference" } private T DotNetSerializeRoundtrip(T obj) { T other; using (var memoryStream = new MemoryStream()) { var formatter = new BinaryFormatter { Context = new StreamingContext(StreamingContextStates.All, new SerializationContext(this.HostedCluster.SerializationManager)) }; formatter.Serialize(memoryStream, obj); memoryStream.Flush(); memoryStream.Position = 0; // Reset to start other = (T)formatter.Deserialize(memoryStream); } return other; } private T NewtonsoftJsonSerializeRoundtrip(T obj) { var typeResolver = this.HostedCluster.Client.ServiceProvider.GetRequiredService(); var settings = OrleansJsonSerializer.GetDefaultSerializerSettings(typeResolver, this.GrainFactory); // http://james.newtonking.com/json/help/index.html?topic=html/T_Newtonsoft_Json_JsonConvert.htm string json = JsonConvert.SerializeObject(obj, settings); object other = JsonConvert.DeserializeObject(json, typeof(T), settings); return (T)other; } } }