You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sb...@apache.org on 2015/09/04 18:27:29 UTC
[15/55] [abbrv] ignite git commit: IGNITE-1348: Moved GridGain's .Net
module to Ignite.
http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Process/IgniteProcess.cs
----------------------------------------------------------------------
diff --git a/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Process/IgniteProcess.cs b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Process/IgniteProcess.cs
new file mode 100644
index 0000000..9e332fe
--- /dev/null
+++ b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Process/IgniteProcess.cs
@@ -0,0 +1,292 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+namespace Apache.Ignite.Core.Tests.Process
+{
+ using System;
+ using System.Diagnostics;
+ using System.IO;
+ using System.Linq;
+ using System.Text;
+ using System.Threading;
+ using Apache.Ignite.Core.Impl;
+
+ /// <summary>
+ /// Defines forked Ignite node.
+ /// </summary>
+ public class IgniteProcess
+ {
+ /** Executable file name. */
+ private static readonly string ExeName = "Ignite.exe";
+
+ /** Executable process name. */
+ private static readonly string ExeProcName = ExeName.Substring(0, ExeName.IndexOf('.'));
+
+ /** Executable configuration file name. */
+ private static readonly string ExeCfgName = ExeName + ".config";
+
+ /** Executable backup configuration file name. */
+ private static readonly string ExeCfgBakName = ExeCfgName + ".bak";
+
+ /** Directory where binaries are stored. */
+ private static readonly string ExeDir;
+
+ /** Full path to executable. */
+ private static readonly string ExePath;
+
+ /** Full path to executable configuration file. */
+ private static readonly string ExeCfgPath;
+
+ /** Full path to executable configuration file backup. */
+ private static readonly string ExeCfgBakPath;
+
+ /** Default process output reader. */
+ private static readonly IIgniteProcessOutputReader DfltOutReader = new IgniteProcessConsoleOutputReader();
+
+ /** Process. */
+ private readonly Process _proc;
+
+ /// <summary>
+ /// Static initializer.
+ /// </summary>
+ static IgniteProcess()
+ {
+ // 1. Locate executable file and related stuff.
+ DirectoryInfo dir = new FileInfo(new Uri(typeof(IgniteProcess).Assembly.CodeBase).LocalPath).Directory;
+
+ // ReSharper disable once PossibleNullReferenceException
+ ExeDir = dir.FullName;
+
+ FileInfo[] exe = dir.GetFiles(ExeName);
+
+
+ // TODO: IGNITE-1367
+ /*
+ if (exe.Length == 0)
+ throw new Exception(ExeName + " is not found in test output directory: " + dir.FullName);
+
+ ExePath = exe[0].FullName;
+
+ FileInfo[] exeCfg = dir.GetFiles(ExeCfgName);
+
+ if (exeCfg.Length == 0)
+ throw new Exception(ExeCfgName + " is not found in test output directory: " + dir.FullName);
+
+ ExeCfgPath = exeCfg[0].FullName;
+
+ ExeCfgBakPath = Path.Combine(ExeDir, ExeCfgBakName);
+
+ File.Delete(ExeCfgBakPath);*/
+ }
+
+ /// <summary>
+ /// Save current configuration to backup.
+ /// </summary>
+ public static void SaveConfigurationBackup()
+ {
+ File.Copy(ExeCfgPath, ExeCfgBakPath, true);
+ }
+
+ /// <summary>
+ /// Restore configuration from backup.
+ /// </summary>
+ public static void RestoreConfigurationBackup()
+ {
+ File.Copy(ExeCfgBakPath, ExeCfgPath, true);
+ }
+
+ /// <summary>
+ /// Replace application configuration with another one.
+ /// </summary>
+ /// <param name="relPath">Path to config relative to executable directory.</param>
+ public static void ReplaceConfiguration(string relPath)
+ {
+ File.Copy(Path.Combine(ExeDir, relPath), ExeCfgPath, true);
+ }
+
+ /// <summary>
+ /// Kill all GridGain processes.
+ /// </summary>
+ public static void KillAll()
+ {
+ foreach (Process proc in Process.GetProcesses())
+ {
+ if (proc.ProcessName.Equals(ExeProcName))
+ {
+ proc.Kill();
+
+ proc.WaitForExit();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Construector.
+ /// </summary>
+ /// <param name="args">Arguments</param>
+ public IgniteProcess(params string[] args) : this(DfltOutReader, args) { }
+
+ /// <summary>
+ /// Construector.
+ /// </summary>
+ /// <param name="outReader">Output reader.</param>
+ /// <param name="args">Arguments.</param>
+ public IgniteProcess(IIgniteProcessOutputReader outReader, params string[] args)
+ {
+ // Add test dll path
+ args = args.Concat(new[] {"-assembly=" + GetType().Assembly.Location}).ToArray();
+
+ _proc = Start(ExePath, IgniteManager.GetIgniteHome(null), outReader, args);
+ }
+
+ /// <summary>
+ /// Starts a grid process.
+ /// </summary>
+ /// <param name="exePath">Exe path.</param>
+ /// <param name="ggHome">GridGain home.</param>
+ /// <param name="outReader">Output reader.</param>
+ /// <param name="args">Arguments.</param>
+ /// <returns>Started process.</returns>
+ public static Process Start(string exePath, string ggHome, IIgniteProcessOutputReader outReader = null,
+ params string[] args)
+ {
+ Debug.Assert(!string.IsNullOrEmpty(exePath));
+ Debug.Assert(!string.IsNullOrEmpty(ggHome));
+
+ // 1. Define process start configuration.
+ var sb = new StringBuilder();
+
+ foreach (string arg in args)
+ sb.Append('\"').Append(arg).Append("\" ");
+
+ var procStart = new ProcessStartInfo
+ {
+ FileName = exePath,
+ Arguments = sb.ToString()
+ };
+
+ if (!string.IsNullOrEmpty(ggHome))
+ procStart.EnvironmentVariables[IgniteManager.EnvIgniteHome] = ggHome;
+
+ procStart.EnvironmentVariables["GRIDGAIN_NATIVE_TEST_CLASSPATH"] = "true";
+
+ procStart.CreateNoWindow = true;
+ procStart.UseShellExecute = false;
+
+ procStart.RedirectStandardOutput = true;
+ procStart.RedirectStandardError = true;
+
+ var workDir = Path.GetDirectoryName(exePath);
+
+ if (workDir != null)
+ procStart.WorkingDirectory = workDir;
+
+ Console.WriteLine("About to run Ignite.exe process [exePath=" + exePath + ", arguments=" + sb + ']');
+
+ // 2. Start.
+ var proc = Process.Start(procStart);
+
+ Debug.Assert(proc != null);
+
+ // 3. Attach output readers to avoid hangs.
+ outReader = outReader ?? DfltOutReader;
+
+ Attach(proc, proc.StandardOutput, outReader, false);
+ Attach(proc, proc.StandardError, outReader, true);
+
+ return proc;
+ }
+
+ /// <summary>
+ /// Whether the process is still alive.
+ /// </summary>
+ public bool Alive
+ {
+ get
+ {
+ return !_proc.HasExited;
+ }
+ }
+
+ /// <summary>
+ /// Kill process.
+ /// </summary>
+ public void Kill()
+ {
+ _proc.Kill();
+ }
+
+ /// <summary>
+ /// Join process.
+ /// </summary>
+ /// <returns>Exit code.</returns>
+ public int Join()
+ {
+ _proc.WaitForExit();
+
+ return _proc.ExitCode;
+ }
+
+ /// <summary>
+ /// Join process with timeout.
+ /// </summary>
+ /// <param name="timeout">Timeout in milliseconds.</param>
+ /// <returns><c>True</c> if process exit occurred before timeout.</returns>
+ public bool Join(int timeout)
+ {
+ return _proc.WaitForExit(timeout);
+ }
+
+ /// <summary>
+ /// Join process with timeout.
+ /// </summary>
+ /// <param name="timeout">Timeout in milliseconds.</param>
+ /// <param name="exitCode">Exit code.</param>
+ /// <returns><c>True</c> if process exit occurred before timeout.</returns>
+ public bool Join(int timeout, out int exitCode)
+ {
+ if (_proc.WaitForExit(timeout))
+ {
+ exitCode = _proc.ExitCode;
+
+ return true;
+ }
+ exitCode = 0;
+
+ return false;
+ }
+
+ /// <summary>
+ /// Attach output reader to the process.
+ /// </summary>
+ /// <param name="proc">Process.</param>
+ /// <param name="reader">Process stream reader.</param>
+ /// <param name="outReader">Output reader.</param>
+ /// <param name="err">Whether this is error stream.</param>
+ private static void Attach(Process proc, StreamReader reader, IIgniteProcessOutputReader outReader, bool err)
+ {
+ Thread thread = new Thread(() =>
+ {
+ while (!proc.HasExited)
+ outReader.OnOutput(proc, reader.ReadLine(), err);
+ }) {IsBackground = true};
+
+
+ thread.Start();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Process/IgniteProcessConsoleOutputReader.cs
----------------------------------------------------------------------
diff --git a/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Process/IgniteProcessConsoleOutputReader.cs b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Process/IgniteProcessConsoleOutputReader.cs
new file mode 100644
index 0000000..00cc040
--- /dev/null
+++ b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Process/IgniteProcessConsoleOutputReader.cs
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+namespace Apache.Ignite.Core.Tests.Process
+{
+ using System;
+ using System.Diagnostics;
+
+ /// <summary>
+ /// Output reader pushing data to the console.
+ /// </summary>
+ public class IgniteProcessConsoleOutputReader : IIgniteProcessOutputReader
+ {
+ /** Out message format. */
+ private static readonly string OutFormat = ">>> {0} OUT: {1}";
+
+ /** Error message format. */
+ private static readonly string ErrFormat = ">>> {0} ERR: {1}";
+
+ /** <inheritDoc /> */
+ public void OnOutput(Process proc, string data, bool err)
+ {
+ Console.WriteLine(err ? ErrFormat : OutFormat, proc.Id, data);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Query/ImplicitPortablePerson.cs
----------------------------------------------------------------------
diff --git a/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Query/ImplicitPortablePerson.cs b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Query/ImplicitPortablePerson.cs
new file mode 100644
index 0000000..f80c4eb
--- /dev/null
+++ b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Query/ImplicitPortablePerson.cs
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+namespace Apache.Ignite.Core.Tests.Query
+{
+ /// <summary>
+ /// Test person.
+ /// </summary>
+ internal class ImplicitPortablePerson
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ImplicitPortablePerson"/> class.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ /// <param name="age">The age.</param>
+ public ImplicitPortablePerson(string name, int age)
+ {
+ Name = name;
+ Age = age;
+ }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the age.
+ /// </summary>
+ public int Age { get; set; }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Query/NoDefPortablePerson.cs
----------------------------------------------------------------------
diff --git a/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Query/NoDefPortablePerson.cs b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Query/NoDefPortablePerson.cs
new file mode 100644
index 0000000..16bd07d
--- /dev/null
+++ b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Query/NoDefPortablePerson.cs
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+namespace Apache.Ignite.Core.Tests.Query
+{
+ /// <summary>
+ /// Test person.
+ /// </summary>
+ internal class NoDefPortablePerson
+ {
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the age.
+ /// </summary>
+ public int Age { get; set; }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Query/PortablePerson.cs
----------------------------------------------------------------------
diff --git a/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Query/PortablePerson.cs b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Query/PortablePerson.cs
new file mode 100644
index 0000000..1e11001
--- /dev/null
+++ b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Query/PortablePerson.cs
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+namespace Apache.Ignite.Core.Tests.Query
+{
+ using Apache.Ignite.Core.Portable;
+
+ /// <summary>
+ /// Test person.
+ /// </summary>
+ internal class PortablePerson : IPortableMarshalAware
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PortablePerson"/> class.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ /// <param name="age">The age.</param>
+ public PortablePerson(string name, int age)
+ {
+ Name = name;
+ Age = age;
+ }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the address.
+ /// </summary>
+ public string Address { get; set; }
+
+ /// <summary>
+ /// Gets or sets the age.
+ /// </summary>
+ public int Age { get; set; }
+
+ /** <ineritdoc /> */
+ public void WritePortable(IPortableWriter writer)
+ {
+ writer.WriteString("name", Name);
+ writer.WriteString("address", Address);
+ writer.WriteInt("age", Age);
+ }
+
+ /** <ineritdoc /> */
+ public void ReadPortable(IPortableReader reader)
+ {
+ Name = reader.ReadString("name");
+ Address = reader.ReadString("address");
+ Age = reader.ReadInt("age");
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/SerializationTest.cs
----------------------------------------------------------------------
diff --git a/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/SerializationTest.cs b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/SerializationTest.cs
new file mode 100644
index 0000000..8ed2899
--- /dev/null
+++ b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/SerializationTest.cs
@@ -0,0 +1,240 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+namespace Apache.Ignite.Core.Tests
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Reflection;
+ using System.Reflection.Emit;
+ using System.Runtime.Serialization;
+ using System.Xml;
+ using Apache.Ignite.Core.Cluster;
+ using Apache.Ignite.Core.Compute;
+ using Apache.Ignite.Core.Impl;
+ using NUnit.Framework;
+
+ /// <summary>
+ /// Tests for native serialization.
+ /// </summary>
+ public class SerializationTest
+ {
+ /** Grid name. */
+ private const string GridName = "SerializationTest";
+
+ /// <summary>
+ /// Set up routine.
+ /// </summary>
+ [TestFixtureSetUp]
+ public void SetUp()
+ {
+ var cfg = new IgniteConfigurationEx
+ {
+ GridName = GridName,
+ JvmClasspath = TestUtils.CreateTestClasspath(),
+ JvmOptions = TestUtils.TestJavaOptions(),
+ SpringConfigUrl = "config\\native-client-test-cache.xml"
+ };
+
+ Ignition.Start(cfg);
+ }
+
+ /// <summary>
+ /// Tear down routine.
+ /// </summary>
+ [TestFixtureTearDown]
+ public void TearDown()
+ {
+ Ignition.StopAll(true);
+ }
+
+ /// <summary>
+ /// Test complex file serialization.
+ /// </summary>
+ [Test]
+ public void TestSerializableXmlDoc()
+ {
+ var grid = Ignition.GetIgnite(GridName);
+ var cache = grid.Cache<int, SerializableXmlDoc>("replicated");
+
+ var doc = new SerializableXmlDoc();
+
+ doc.LoadXml("<document><test1>val</test1><test2 attr=\"x\" /></document>");
+
+ for (var i = 0; i < 50; i++)
+ {
+ // Test cache
+ cache.Put(i, doc);
+
+ var resultDoc = cache.Get(i);
+
+ Assert.AreEqual(doc.OuterXml, resultDoc.OuterXml);
+
+ // Test task with document arg
+ CheckTask(grid, doc);
+ }
+ }
+
+ /// <summary>
+ /// Checks task execution.
+ /// </summary>
+ /// <param name="grid">Grid.</param>
+ /// <param name="arg">Task arg.</param>
+ private static void CheckTask(IIgnite grid, object arg)
+ {
+ var jobResult = grid.Compute().Execute(new CombineStringsTask(), arg);
+
+ var nodeCount = grid.Cluster.Nodes().Count;
+
+ var expectedRes =
+ CombineStringsTask.CombineStrings(Enumerable.Range(0, nodeCount).Select(x => arg.ToString()));
+
+ Assert.AreEqual(expectedRes, jobResult.InnerXml);
+ }
+
+ /// <summary>
+ /// Tests custom serialization binder.
+ /// </summary>
+ [Test]
+ public void TestSerializationBinder()
+ {
+ const int count = 50;
+
+ var cache = Ignition.GetIgnite(GridName).Cache<int, object>("local");
+
+ // Put multiple objects from muliple same-named assemblies to cache
+ for (var i = 0; i < count; i++)
+ {
+ dynamic val = Activator.CreateInstance(GenerateDynamicType());
+
+ val.Id = i;
+ val.Name = "Name_" + i;
+
+ cache.Put(i, val);
+ }
+
+ // Verify correct deserialization
+ for (var i = 0; i < count; i++)
+ {
+ dynamic val = cache.Get(i);
+
+ Assert.AreEqual(val.Id, i);
+ Assert.AreEqual(val.Name, "Name_" + i);
+ }
+ }
+
+ /// <summary>
+ /// Generates a Type in runtime, puts it into a dynamic assembly.
+ /// </summary>
+ /// <returns></returns>
+ public static Type GenerateDynamicType()
+ {
+ var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
+ new AssemblyName("GridSerializationTestDynamicAssembly"), AssemblyBuilderAccess.Run);
+
+ var moduleBuilder = asmBuilder.DefineDynamicModule("GridSerializationTestDynamicModule");
+
+ var typeBuilder = moduleBuilder.DefineType("GridSerializationTestDynamicType",
+ TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Serializable);
+
+ typeBuilder.DefineField("Id", typeof (int), FieldAttributes.Public);
+
+ typeBuilder.DefineField("Name", typeof (string), FieldAttributes.Public);
+
+ return typeBuilder.CreateType();
+ }
+ }
+
+ [Serializable]
+ [DataContract]
+ public sealed class SerializableXmlDoc : XmlDocument, ISerializable
+ {
+ /// <summary>
+ /// Default ctor.
+ /// </summary>
+ public SerializableXmlDoc()
+ {
+ // No-op
+ }
+
+ /// <summary>
+ /// Serialization ctor.
+ /// </summary>
+ private SerializableXmlDoc(SerializationInfo info, StreamingContext context)
+ {
+ LoadXml(info.GetString("xmlDocument"));
+ }
+
+ /** <inheritdoc /> */
+ public void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ info.AddValue("xmlDocument", OuterXml, typeof(string));
+ }
+ }
+
+ [Serializable]
+ public class CombineStringsTask : IComputeTask<object, string, SerializableXmlDoc>
+ {
+ public IDictionary<IComputeJob<string>, IClusterNode> Map(IList<IClusterNode> subgrid, object arg)
+ {
+ return subgrid.ToDictionary(x => (IComputeJob<string>) new ToStringJob {Arg = arg}, x => x);
+ }
+
+ public ComputeJobResultPolicy Result(IComputeJobResult<string> res, IList<IComputeJobResult<string>> rcvd)
+ {
+ return ComputeJobResultPolicy.Wait;
+ }
+
+ public SerializableXmlDoc Reduce(IList<IComputeJobResult<string>> results)
+ {
+ var result = new SerializableXmlDoc();
+
+ result.LoadXml(CombineStrings(results.Select(x => x.Data())));
+
+ return result;
+ }
+
+ public static string CombineStrings(IEnumerable<string> strings)
+ {
+ var text = string.Concat(strings.Select(x => string.Format("<val>{0}</val>", x)));
+
+ return string.Format("<document>{0}</document>", text);
+ }
+ }
+
+ [Serializable]
+ public class ToStringJob : IComputeJob<string>
+ {
+ /// <summary>
+ /// Job argument.
+ /// </summary>
+ public object Arg { get; set; }
+
+ /** <inheritdoc /> */
+ public string Execute()
+ {
+ return Arg.ToString();
+ }
+
+ /** <inheritdoc /> */
+ public void Cancel()
+ {
+ // No-op.
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Services/ServiceProxyTest.cs
----------------------------------------------------------------------
diff --git a/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Services/ServiceProxyTest.cs b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Services/ServiceProxyTest.cs
new file mode 100644
index 0000000..44e1d71
--- /dev/null
+++ b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Services/ServiceProxyTest.cs
@@ -0,0 +1,741 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+namespace Apache.Ignite.Core.Tests.Services
+{
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.IO;
+ using System.Linq;
+ using System.Reflection;
+ using Apache.Ignite.Core.Impl.Memory;
+ using Apache.Ignite.Core.Impl.Portable;
+ using Apache.Ignite.Core.Impl.Services;
+ using Apache.Ignite.Core.Portable;
+ using Apache.Ignite.Core.Services;
+ using NUnit.Framework;
+
+ /// <summary>
+ /// Tests <see cref="ServiceProxySerializer"/> functionality.
+ /// </summary>
+ public class ServiceProxyTest
+ {
+ /** */
+ private TestIgniteService _svc;
+
+ /** */
+ private readonly PortableMarshaller _marsh = new PortableMarshaller(new PortableConfiguration
+ {
+ TypeConfigurations = new[]
+ {
+ new PortableTypeConfiguration(typeof (TestPortableClass)),
+ new PortableTypeConfiguration(typeof (CustomExceptionPortable))
+ }
+ });
+
+ /** */
+ protected readonly IPortables Portables;
+
+ /** */
+ private readonly PlatformMemoryManager _memory = new PlatformMemoryManager(1024);
+
+ /** */
+ protected bool KeepPortable;
+
+ /** */
+ protected bool SrvKeepPortable;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ServiceProxyTest"/> class.
+ /// </summary>
+ public ServiceProxyTest()
+ {
+ Portables = new PortablesImpl(_marsh);
+ }
+
+ /// <summary>
+ /// Tests object class methods proxying.
+ /// </summary>
+ [Test]
+ public void TestObjectClassMethods()
+ {
+ var prx = GetProxy();
+
+ prx.IntProp = 12345;
+
+ Assert.AreEqual("12345", prx.ToString());
+ Assert.AreEqual("12345", _svc.ToString());
+ Assert.AreEqual(12345, prx.GetHashCode());
+ Assert.AreEqual(12345, _svc.GetHashCode());
+ }
+
+ /// <summary>
+ /// Tests properties proxying.
+ /// </summary>
+ [Test]
+ [SuppressMessage("ReSharper", "PossibleNullReferenceException")]
+ public void TestProperties()
+ {
+ var prx = GetProxy();
+
+ prx.IntProp = 10;
+ Assert.AreEqual(10, prx.IntProp);
+ Assert.AreEqual(10, _svc.IntProp);
+
+ _svc.IntProp = 15;
+ Assert.AreEqual(15, prx.IntProp);
+ Assert.AreEqual(15, _svc.IntProp);
+
+ prx.ObjProp = "prop1";
+ Assert.AreEqual("prop1", prx.ObjProp);
+ Assert.AreEqual("prop1", _svc.ObjProp);
+
+ prx.ObjProp = null;
+ Assert.IsNull(prx.ObjProp);
+ Assert.IsNull(_svc.ObjProp);
+
+ prx.ObjProp = new TestClass {Prop = "prop2"};
+ Assert.AreEqual("prop2", ((TestClass)prx.ObjProp).Prop);
+ Assert.AreEqual("prop2", ((TestClass)_svc.ObjProp).Prop);
+ }
+
+ /// <summary>
+ /// Tests void methods proxying.
+ /// </summary>
+ [Test]
+ public void TestVoidMethods()
+ {
+ var prx = GetProxy();
+
+ prx.VoidMethod();
+ Assert.AreEqual("VoidMethod", prx.InvokeResult);
+ Assert.AreEqual("VoidMethod", _svc.InvokeResult);
+
+ prx.VoidMethod(10);
+ Assert.AreEqual(_svc.InvokeResult, prx.InvokeResult);
+
+ prx.VoidMethod(10, "string");
+ Assert.AreEqual(_svc.InvokeResult, prx.InvokeResult);
+
+ prx.VoidMethod(10, "string", "arg");
+ Assert.AreEqual(_svc.InvokeResult, prx.InvokeResult);
+
+ prx.VoidMethod(10, "string", "arg", "arg1", 2, 3, "arg4");
+ Assert.AreEqual(_svc.InvokeResult, prx.InvokeResult);
+ }
+
+ /// <summary>
+ /// Tests object methods proxying.
+ /// </summary>
+ [Test]
+ public void TestObjectMethods()
+ {
+ var prx = GetProxy();
+
+ Assert.AreEqual("ObjectMethod", prx.ObjectMethod());
+ Assert.AreEqual("ObjectMethod987", prx.ObjectMethod(987));
+ Assert.AreEqual("ObjectMethod987str123", prx.ObjectMethod(987, "str123"));
+ Assert.AreEqual("ObjectMethod987str123TestClass", prx.ObjectMethod(987, "str123", new TestClass()));
+ Assert.AreEqual("ObjectMethod987str123TestClass34arg5arg6",
+ prx.ObjectMethod(987, "str123", new TestClass(), 3, 4, "arg5", "arg6"));
+ }
+
+ /// <summary>
+ /// Tests methods that exist in proxy interface, but do not exist in the actual service.
+ /// </summary>
+ [Test]
+ public void TestMissingMethods()
+ {
+ var prx = GetProxy();
+
+ var ex = Assert.Throws<InvalidOperationException>(() => prx.MissingMethod());
+
+ Assert.AreEqual("Failed to invoke proxy: there is no method 'MissingMethod'" +
+ " in type 'Apache.Ignite.Core.Tests.Services.ServiceProxyTest+TestIgniteService'", ex.Message);
+ }
+
+ /// <summary>
+ /// Tests ambiguous methods handling (multiple methods with the same signature).
+ /// </summary>
+ [Test]
+ public void TestAmbiguousMethods()
+ {
+ var prx = GetProxy();
+
+ var ex = Assert.Throws<InvalidOperationException>(() => prx.AmbiguousMethod(1));
+
+ Assert.AreEqual("Failed to invoke proxy: there are 2 methods 'AmbiguousMethod' in type " +
+ "'Apache.Ignite.Core.Tests.Services.ServiceProxyTest+TestIgniteService' with (Int32) arguments, " +
+ "can't resolve ambiguity.", ex.Message);
+ }
+
+ [Test]
+ public void TestException()
+ {
+ var prx = GetProxy();
+
+ var err = Assert.Throws<ServiceInvocationException>(prx.ExceptionMethod);
+ Assert.AreEqual("Expected exception", err.InnerException.Message);
+
+ var ex = Assert.Throws<ServiceInvocationException>(() => prx.CustomExceptionMethod());
+ Assert.IsTrue(ex.ToString().Contains("+CustomException"));
+ }
+
+ [Test]
+ public void TestPortableMarshallingException()
+ {
+ var prx = GetProxy();
+
+ var ex = Assert.Throws<ServiceInvocationException>(() => prx.CustomExceptionPortableMethod(false, false));
+
+ if (KeepPortable)
+ {
+ Assert.AreEqual("Proxy method invocation failed with a portable error. " +
+ "Examine PortableCause for details.", ex.Message);
+
+ Assert.IsNotNull(ex.PortableCause);
+ Assert.IsNull(ex.InnerException);
+ }
+ else
+ {
+ Assert.AreEqual("Proxy method invocation failed with an exception. " +
+ "Examine InnerException for details.", ex.Message);
+
+ Assert.IsNull(ex.PortableCause);
+ Assert.IsNotNull(ex.InnerException);
+ }
+
+ ex = Assert.Throws<ServiceInvocationException>(() => prx.CustomExceptionPortableMethod(true, false));
+ Assert.IsTrue(ex.ToString().Contains(
+ "Call completed with error, but error serialization failed [errType=CustomExceptionPortable, " +
+ "serializationErrMsg=Expected exception in CustomExceptionPortable.WritePortable]"));
+
+ ex = Assert.Throws<ServiceInvocationException>(() => prx.CustomExceptionPortableMethod(true, true));
+ Assert.IsTrue(ex.ToString().Contains(
+ "Call completed with error, but error serialization failed [errType=CustomExceptionPortable, " +
+ "serializationErrMsg=Expected exception in CustomExceptionPortable.WritePortable]"));
+ }
+
+ /// <summary>
+ /// Creates the proxy.
+ /// </summary>
+ protected ITestIgniteServiceProxyInterface GetProxy()
+ {
+ return GetProxy<ITestIgniteServiceProxyInterface>();
+ }
+
+ /// <summary>
+ /// Creates the proxy.
+ /// </summary>
+ protected T GetProxy<T>()
+ {
+ _svc = new TestIgniteService(Portables);
+
+ var prx = new ServiceProxy<T>(InvokeProxyMethod).GetTransparentProxy();
+
+ Assert.IsFalse(ReferenceEquals(_svc, prx));
+
+ return prx;
+ }
+
+ /// <summary>
+ /// Invokes the proxy.
+ /// </summary>
+ /// <param name="method">Method.</param>
+ /// <param name="args">Arguments.</param>
+ /// <returns>
+ /// Invocation result.
+ /// </returns>
+ private object InvokeProxyMethod(MethodBase method, object[] args)
+ {
+ using (var inStream = new PlatformMemoryStream(_memory.Allocate()))
+ using (var outStream = new PlatformMemoryStream(_memory.Allocate()))
+ {
+ // 1) Write to a stream
+ inStream.WriteBool(SrvKeepPortable); // WriteProxyMethod does not do this, but Java does
+
+ ServiceProxySerializer.WriteProxyMethod(_marsh.StartMarshal(inStream), method, args);
+
+ inStream.SynchronizeOutput();
+
+ inStream.Seek(0, SeekOrigin.Begin);
+
+ // 2) call InvokeServiceMethod
+ string mthdName;
+ object[] mthdArgs;
+
+ ServiceProxySerializer.ReadProxyMethod(inStream, _marsh, out mthdName, out mthdArgs);
+
+ var result = ServiceProxyInvoker.InvokeServiceMethod(_svc, mthdName, mthdArgs);
+
+ ServiceProxySerializer.WriteInvocationResult(outStream, _marsh, result.Key, result.Value);
+
+ _marsh.StartMarshal(outStream).WriteString("unused"); // fake Java exception details
+
+ outStream.SynchronizeOutput();
+
+ outStream.Seek(0, SeekOrigin.Begin);
+
+ return ServiceProxySerializer.ReadInvocationResult(outStream, _marsh, KeepPortable);
+ }
+ }
+
+ /// <summary>
+ /// Test service interface.
+ /// </summary>
+ protected interface ITestIgniteServiceProperties
+ {
+ /** */
+ int IntProp { get; set; }
+
+ /** */
+ object ObjProp { get; set; }
+
+ /** */
+ string InvokeResult { get; }
+ }
+
+ /// <summary>
+ /// Test service interface to check ambiguity handling.
+ /// </summary>
+ protected interface ITestIgniteServiceAmbiguity
+ {
+ /** */
+ int AmbiguousMethod(int arg);
+ }
+
+ /// <summary>
+ /// Test service interface.
+ /// </summary>
+ protected interface ITestIgniteService : ITestIgniteServiceProperties
+ {
+ /** */
+ void VoidMethod();
+
+ /** */
+ void VoidMethod(int arg);
+
+ /** */
+ void VoidMethod(int arg, string arg1, object arg2 = null);
+
+ /** */
+ void VoidMethod(int arg, string arg1, object arg2 = null, params object[] args);
+
+ /** */
+ object ObjectMethod();
+
+ /** */
+ object ObjectMethod(int arg);
+
+ /** */
+ object ObjectMethod(int arg, string arg1, object arg2 = null);
+
+ /** */
+ object ObjectMethod(int arg, string arg1, object arg2 = null, params object[] args);
+
+ /** */
+ void ExceptionMethod();
+
+ /** */
+ void CustomExceptionMethod();
+
+ /** */
+ void CustomExceptionPortableMethod(bool throwOnWrite, bool throwOnRead);
+
+ /** */
+ TestPortableClass PortableArgMethod(int arg1, IPortableObject arg2);
+
+ /** */
+ IPortableObject PortableResultMethod(int arg1, TestPortableClass arg2);
+
+ /** */
+ IPortableObject PortableArgAndResultMethod(int arg1, IPortableObject arg2);
+
+ /** */
+ int AmbiguousMethod(int arg);
+ }
+
+ /// <summary>
+ /// Test service interface. Does not derive from actual interface, but has all the same method signatures.
+ /// </summary>
+ protected interface ITestIgniteServiceProxyInterface
+ {
+ /** */
+ int IntProp { get; set; }
+
+ /** */
+ object ObjProp { get; set; }
+
+ /** */
+ string InvokeResult { get; }
+
+ /** */
+ void VoidMethod();
+
+ /** */
+ void VoidMethod(int arg);
+
+ /** */
+ void VoidMethod(int arg, string arg1, object arg2 = null);
+
+ /** */
+ void VoidMethod(int arg, string arg1, object arg2 = null, params object[] args);
+
+ /** */
+ object ObjectMethod();
+
+ /** */
+ object ObjectMethod(int arg);
+
+ /** */
+ object ObjectMethod(int arg, string arg1, object arg2 = null);
+
+ /** */
+ object ObjectMethod(int arg, string arg1, object arg2 = null, params object[] args);
+
+ /** */
+ void ExceptionMethod();
+
+ /** */
+ void CustomExceptionMethod();
+
+ /** */
+ void CustomExceptionPortableMethod(bool throwOnWrite, bool throwOnRead);
+
+ /** */
+ TestPortableClass PortableArgMethod(int arg1, IPortableObject arg2);
+
+ /** */
+ IPortableObject PortableResultMethod(int arg1, TestPortableClass arg2);
+
+ /** */
+ IPortableObject PortableArgAndResultMethod(int arg1, IPortableObject arg2);
+
+ /** */
+ void MissingMethod();
+
+ /** */
+ int AmbiguousMethod(int arg);
+ }
+
+ /// <summary>
+ /// Test service.
+ /// </summary>
+ [Serializable]
+ private class TestIgniteService : ITestIgniteService, ITestIgniteServiceAmbiguity
+ {
+ /** */
+ private readonly IPortables _portables;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TestIgniteService"/> class.
+ /// </summary>
+ /// <param name="portables">The portables.</param>
+ public TestIgniteService(IPortables portables)
+ {
+ _portables = portables;
+ }
+
+ /** <inheritdoc /> */
+ public int IntProp { get; set; }
+
+ /** <inheritdoc /> */
+ public object ObjProp { get; set; }
+
+ /** <inheritdoc /> */
+ public string InvokeResult { get; private set; }
+
+ /** <inheritdoc /> */
+ public void VoidMethod()
+ {
+ InvokeResult = "VoidMethod";
+ }
+
+ /** <inheritdoc /> */
+ public void VoidMethod(int arg)
+ {
+ InvokeResult = "VoidMethod" + arg;
+ }
+
+ /** <inheritdoc /> */
+ public void VoidMethod(int arg, string arg1, object arg2 = null)
+ {
+ InvokeResult = "VoidMethod" + arg + arg1 + arg2;
+ }
+
+ /** <inheritdoc /> */
+ public void VoidMethod(int arg, string arg1, object arg2 = null, params object[] args)
+ {
+ InvokeResult = "VoidMethod" + arg + arg1 + arg2 + string.Concat(args.Select(x => x.ToString()));
+ }
+
+ /** <inheritdoc /> */
+ public object ObjectMethod()
+ {
+ return "ObjectMethod";
+ }
+
+ /** <inheritdoc /> */
+ public object ObjectMethod(int arg)
+ {
+ return "ObjectMethod" + arg;
+ }
+
+ /** <inheritdoc /> */
+ public object ObjectMethod(int arg, string arg1, object arg2 = null)
+ {
+ return "ObjectMethod" + arg + arg1 + arg2;
+ }
+
+ /** <inheritdoc /> */
+ public object ObjectMethod(int arg, string arg1, object arg2 = null, params object[] args)
+ {
+ return "ObjectMethod" + arg + arg1 + arg2 + string.Concat(args.Select(x => x.ToString()));
+ }
+
+ /** <inheritdoc /> */
+ public void ExceptionMethod()
+ {
+ throw new ArithmeticException("Expected exception");
+ }
+
+ /** <inheritdoc /> */
+ public void CustomExceptionMethod()
+ {
+ throw new CustomException();
+ }
+
+ /** <inheritdoc /> */
+ public void CustomExceptionPortableMethod(bool throwOnWrite, bool throwOnRead)
+ {
+ throw new CustomExceptionPortable {ThrowOnRead = throwOnRead, ThrowOnWrite = throwOnWrite};
+ }
+
+ /** <inheritdoc /> */
+ public TestPortableClass PortableArgMethod(int arg1, IPortableObject arg2)
+ {
+ return arg2.Deserialize<TestPortableClass>();
+ }
+
+ /** <inheritdoc /> */
+ public IPortableObject PortableResultMethod(int arg1, TestPortableClass arg2)
+ {
+ return _portables.ToPortable<IPortableObject>(arg2);
+ }
+
+ /** <inheritdoc /> */
+ public IPortableObject PortableArgAndResultMethod(int arg1, IPortableObject arg2)
+ {
+ return _portables.ToPortable<IPortableObject>(arg2.Deserialize<TestPortableClass>());
+ }
+
+ /** <inheritdoc /> */
+ public override string ToString()
+ {
+ return IntProp.ToString();
+ }
+
+ /** <inheritdoc /> */
+ public override int GetHashCode()
+ {
+ return IntProp.GetHashCode();
+ }
+
+ /** <inheritdoc /> */
+ int ITestIgniteService.AmbiguousMethod(int arg)
+ {
+ return arg;
+ }
+
+ /** <inheritdoc /> */
+ int ITestIgniteServiceAmbiguity.AmbiguousMethod(int arg)
+ {
+ return -arg;
+ }
+ }
+
+ /// <summary>
+ /// Test serializable class.
+ /// </summary>
+ [Serializable]
+ private class TestClass
+ {
+ /** */
+ public string Prop { get; set; }
+
+ /** <inheritdoc /> */
+ public override string ToString()
+ {
+ return "TestClass" + Prop;
+ }
+ }
+
+ /// <summary>
+ /// Custom non-serializable exception.
+ /// </summary>
+ private class CustomException : Exception
+ {
+
+ }
+
+ /// <summary>
+ /// Custom non-serializable exception.
+ /// </summary>
+ private class CustomExceptionPortable : Exception, IPortableMarshalAware
+ {
+ /** */
+ public bool ThrowOnWrite { get; set; }
+
+ /** */
+ public bool ThrowOnRead { get; set; }
+
+ /** <inheritdoc /> */
+ public void WritePortable(IPortableWriter writer)
+ {
+ writer.WriteBoolean("ThrowOnRead", ThrowOnRead);
+
+ if (ThrowOnWrite)
+ throw new Exception("Expected exception in CustomExceptionPortable.WritePortable");
+ }
+
+ /** <inheritdoc /> */
+ public void ReadPortable(IPortableReader reader)
+ {
+ ThrowOnRead = reader.ReadBoolean("ThrowOnRead");
+
+ if (ThrowOnRead)
+ throw new Exception("Expected exception in CustomExceptionPortable.ReadPortable");
+ }
+ }
+
+ /// <summary>
+ /// Portable object for method argument/result.
+ /// </summary>
+ protected class TestPortableClass : IPortableMarshalAware
+ {
+ /** */
+ public string Prop { get; set; }
+
+ /** */
+ public bool ThrowOnWrite { get; set; }
+
+ /** */
+ public bool ThrowOnRead { get; set; }
+
+ /** <inheritdoc /> */
+ public void WritePortable(IPortableWriter writer)
+ {
+ writer.WriteString("Prop", Prop);
+ writer.WriteBoolean("ThrowOnRead", ThrowOnRead);
+
+ if (ThrowOnWrite)
+ throw new Exception("Expected exception in TestPortableClass.WritePortable");
+ }
+
+ /** <inheritdoc /> */
+ public void ReadPortable(IPortableReader reader)
+ {
+ Prop = reader.ReadString("Prop");
+ ThrowOnRead = reader.ReadBoolean("ThrowOnRead");
+
+ if (ThrowOnRead)
+ throw new Exception("Expected exception in TestPortableClass.ReadPortable");
+ }
+ }
+ }
+
+ /// <summary>
+ /// Tests <see cref="ServiceProxySerializer"/> functionality with keepPortable mode enabled on client.
+ /// </summary>
+ public class ServiceProxyTestKeepPortableClient : ServiceProxyTest
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ServiceProxyTestKeepPortableClient"/> class.
+ /// </summary>
+ public ServiceProxyTestKeepPortableClient()
+ {
+ KeepPortable = true;
+ }
+
+ [Test]
+ public void TestPortableMethods()
+ {
+ var prx = GetProxy();
+
+ var obj = new TestPortableClass { Prop = "PropValue" };
+
+ var result = prx.PortableResultMethod(1, obj);
+
+ Assert.AreEqual(obj.Prop, result.Deserialize<TestPortableClass>().Prop);
+ }
+ }
+
+ /// <summary>
+ /// Tests <see cref="ServiceProxySerializer"/> functionality with keepPortable mode enabled on server.
+ /// </summary>
+ public class ServiceProxyTestKeepPortableServer : ServiceProxyTest
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ServiceProxyTestKeepPortableServer"/> class.
+ /// </summary>
+ public ServiceProxyTestKeepPortableServer()
+ {
+ SrvKeepPortable = true;
+ }
+
+ [Test]
+ public void TestPortableMethods()
+ {
+ var prx = GetProxy();
+
+ var obj = new TestPortableClass { Prop = "PropValue" };
+ var portObj = Portables.ToPortable<IPortableObject>(obj);
+
+ var result = prx.PortableArgMethod(1, portObj);
+
+ Assert.AreEqual(obj.Prop, result.Prop);
+ }
+ }
+
+ /// <summary>
+ /// Tests <see cref="ServiceProxySerializer"/> functionality with keepPortable mode enabled on client and on server.
+ /// </summary>
+ public class ServiceProxyTestKeepPortableClientServer : ServiceProxyTest
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ServiceProxyTestKeepPortableClientServer"/> class.
+ /// </summary>
+ public ServiceProxyTestKeepPortableClientServer()
+ {
+ KeepPortable = true;
+ SrvKeepPortable = true;
+ }
+
+ [Test]
+ public void TestPortableMethods()
+ {
+ var prx = GetProxy();
+
+ var obj = new TestPortableClass { Prop = "PropValue" };
+ var portObj = Portables.ToPortable<IPortableObject>(obj);
+
+ var result = prx.PortableArgAndResultMethod(1, portObj);
+
+ Assert.AreEqual(obj.Prop, result.Deserialize<TestPortableClass>().Prop);
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Services/ServicesAsyncWrapper.cs
----------------------------------------------------------------------
diff --git a/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Services/ServicesAsyncWrapper.cs b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Services/ServicesAsyncWrapper.cs
new file mode 100644
index 0000000..ba45dbd
--- /dev/null
+++ b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Services/ServicesAsyncWrapper.cs
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+namespace Apache.Ignite.Core.Tests.Services
+{
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using Apache.Ignite.Core.Cluster;
+ using Apache.Ignite.Core.Common;
+ using Apache.Ignite.Core.Services;
+
+ /// <summary>
+ /// Services async wrapper to simplify testing.
+ /// </summary>
+ public class ServicesAsyncWrapper : IServices
+ {
+ /** Wrapped async services. */
+ private readonly IServices _services;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ServicesAsyncWrapper"/> class.
+ /// </summary>
+ /// <param name="services">Services to wrap.</param>
+ public ServicesAsyncWrapper(IServices services)
+ {
+ _services = services.WithAsync();
+ }
+
+ /** <inheritDoc /> */
+ public IServices WithAsync()
+ {
+ return this;
+ }
+
+ /** <inheritDoc /> */
+ public bool IsAsync
+ {
+ get { return true; }
+ }
+
+ /** <inheritDoc /> */
+ public IFuture GetFuture()
+ {
+ Debug.Fail("ServicesAsyncWrapper.Future() should not be called. It always returns null.");
+ return null;
+ }
+
+ /** <inheritDoc /> */
+ public IFuture<TResult> GetFuture<TResult>()
+ {
+ Debug.Fail("ServicesAsyncWrapper.Future() should not be called. It always returns null.");
+ return null;
+ }
+
+ /** <inheritDoc /> */
+ public IClusterGroup ClusterGroup
+ {
+ get { return _services.ClusterGroup; }
+ }
+
+ /** <inheritDoc /> */
+ public void DeployClusterSingleton(string name, IService service)
+ {
+ _services.DeployClusterSingleton(name, service);
+ WaitResult();
+ }
+
+ /** <inheritDoc /> */
+ public void DeployNodeSingleton(string name, IService service)
+ {
+ _services.DeployNodeSingleton(name, service);
+ WaitResult();
+ }
+
+ /** <inheritDoc /> */
+ public void DeployKeyAffinitySingleton<TK>(string name, IService service, string cacheName, TK affinityKey)
+ {
+ _services.DeployKeyAffinitySingleton(name, service, cacheName, affinityKey);
+ WaitResult();
+ }
+
+ /** <inheritDoc /> */
+ public void DeployMultiple(string name, IService service, int totalCount, int maxPerNodeCount)
+ {
+ _services.DeployMultiple(name, service, totalCount, maxPerNodeCount);
+ WaitResult();
+ }
+
+ /** <inheritDoc /> */
+ public void Deploy(ServiceConfiguration configuration)
+ {
+ _services.Deploy(configuration);
+ WaitResult();
+ }
+
+ /** <inheritDoc /> */
+ public void Cancel(string name)
+ {
+ _services.Cancel(name);
+ WaitResult();
+ }
+
+ /** <inheritDoc /> */
+ public void CancelAll()
+ {
+ _services.CancelAll();
+ WaitResult();
+ }
+
+ /** <inheritDoc /> */
+ public ICollection<IServiceDescriptor> GetServiceDescriptors()
+ {
+ return _services.GetServiceDescriptors();
+ }
+
+ /** <inheritDoc /> */
+ public T GetService<T>(string name)
+ {
+ return _services.GetService<T>(name);
+ }
+
+ /** <inheritDoc /> */
+ public ICollection<T> GetServices<T>(string name)
+ {
+ return _services.GetServices<T>(name);
+ }
+
+ /** <inheritDoc /> */
+ public T GetServiceProxy<T>(string name) where T : class
+ {
+ return _services.GetServiceProxy<T>(name);
+ }
+
+ /** <inheritDoc /> */
+ public T GetServiceProxy<T>(string name, bool sticky) where T : class
+ {
+ return _services.GetServiceProxy<T>(name, sticky);
+ }
+
+ /** <inheritDoc /> */
+ public IServices WithKeepPortable()
+ {
+ return new ServicesAsyncWrapper(_services.WithKeepPortable());
+ }
+
+ /** <inheritDoc /> */
+ public IServices WithServerKeepPortable()
+ {
+ return new ServicesAsyncWrapper(_services.WithServerKeepPortable());
+ }
+
+ /// <summary>
+ /// Waits for the async result.
+ /// </summary>
+ private void WaitResult()
+ {
+ _services.GetFuture().Get();
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
----------------------------------------------------------------------
diff --git a/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
new file mode 100644
index 0000000..7f5aa44
--- /dev/null
+++ b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
@@ -0,0 +1,823 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+namespace Apache.Ignite.Core.Tests.Services
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Threading;
+ using Apache.Ignite.Core.Cluster;
+ using Apache.Ignite.Core.Common;
+ using Apache.Ignite.Core.Portable;
+ using Apache.Ignite.Core.Resource;
+ using Apache.Ignite.Core.Services;
+ using NUnit.Framework;
+
+ /// <summary>
+ /// Services tests.
+ /// </summary>
+ public class ServicesTest
+ {
+ /** */
+ private const string SvcName = "Service1";
+
+ /** */
+ private const string CacheName = "cache1";
+
+ /** */
+ private const int AffKey = 25;
+
+ /** */
+ protected IIgnite Grid1;
+
+ /** */
+ protected IIgnite Grid2;
+
+ /** */
+ protected IIgnite Grid3;
+
+ /** */
+ protected IIgnite[] Grids;
+
+ [TestFixtureTearDown]
+ public void FixtureTearDown()
+ {
+ StopGrids();
+ }
+
+ /// <summary>
+ /// Executes before each test.
+ /// </summary>
+ [SetUp]
+ public void SetUp()
+ {
+ StartGrids();
+ EventsTestHelper.ListenResult = true;
+ }
+
+ /// <summary>
+ /// Executes after each test.
+ /// </summary>
+ [TearDown]
+ public void TearDown()
+ {
+ try
+ {
+ Services.Cancel(SvcName);
+
+ TestUtils.AssertHandleRegistryIsEmpty(1000, Grid1, Grid2, Grid3);
+ }
+ catch (Exception)
+ {
+ // Restart grids to cleanup
+ StopGrids();
+
+ throw;
+ }
+ finally
+ {
+ EventsTestHelper.AssertFailures();
+
+ if (TestContext.CurrentContext.Test.Name.StartsWith("TestEventTypes"))
+ StopGrids(); // clean events for other tests
+ }
+ }
+
+ /// <summary>
+ /// Tests deployment.
+ /// </summary>
+ [Test]
+ public void TestDeploy([Values(true, false)] bool portable)
+ {
+ var cfg = new ServiceConfiguration
+ {
+ Name = SvcName,
+ MaxPerNodeCount = 3,
+ TotalCount = 3,
+ NodeFilter = new NodeFilter {NodeId = Grid1.Cluster.LocalNode.Id},
+ Service = portable ? new TestIgniteServicePortable() : new TestIgniteServiceSerializable()
+ };
+
+ Services.Deploy(cfg);
+
+ CheckServiceStarted(Grid1, 3);
+ }
+
+ /// <summary>
+ /// Tests cluster singleton deployment.
+ /// </summary>
+ [Test]
+ public void TestDeployClusterSingleton()
+ {
+ var svc = new TestIgniteServiceSerializable();
+
+ Services.DeployClusterSingleton(SvcName, svc);
+
+ var svc0 = Services.GetServiceProxy<ITestIgniteService>(SvcName);
+
+ // Check that only one node has the service.
+ foreach (var grid in Grids)
+ {
+ if (grid.Cluster.LocalNode.Id == svc0.NodeId)
+ CheckServiceStarted(grid);
+ else
+ Assert.IsNull(grid.Services().GetService<TestIgniteServiceSerializable>(SvcName));
+ }
+ }
+
+ /// <summary>
+ /// Tests node singleton deployment.
+ /// </summary>
+ [Test]
+ public void TestDeployNodeSingleton()
+ {
+ var svc = new TestIgniteServiceSerializable();
+
+ Services.DeployNodeSingleton(SvcName, svc);
+
+ Assert.AreEqual(1, Grid1.Services().GetServices<ITestIgniteService>(SvcName).Count);
+ Assert.AreEqual(1, Grid2.Services().GetServices<ITestIgniteService>(SvcName).Count);
+ Assert.AreEqual(1, Grid3.Services().GetServices<ITestIgniteService>(SvcName).Count);
+ }
+
+ /// <summary>
+ /// Tests key affinity singleton deployment.
+ /// </summary>
+ [Test]
+ public void TestDeployKeyAffinitySingleton()
+ {
+ var svc = new TestIgniteServicePortable();
+
+ Services.DeployKeyAffinitySingleton(SvcName, svc, CacheName, AffKey);
+
+ var affNode = Grid1.Affinity(CacheName).MapKeyToNode(AffKey);
+
+ var prx = Services.GetServiceProxy<ITestIgniteService>(SvcName);
+
+ Assert.AreEqual(affNode.Id, prx.NodeId);
+ }
+
+ /// <summary>
+ /// Tests key affinity singleton deployment.
+ /// </summary>
+ [Test]
+ public void TestDeployKeyAffinitySingletonPortable()
+ {
+ var services = Services.WithKeepPortable();
+
+ var svc = new TestIgniteServicePortable();
+
+ var affKey = new PortableObject {Val = AffKey};
+
+ services.DeployKeyAffinitySingleton(SvcName, svc, CacheName, affKey);
+
+ var prx = services.GetServiceProxy<ITestIgniteService>(SvcName);
+
+ Assert.IsTrue(prx.Initialized);
+ }
+
+ /// <summary>
+ /// Tests multiple deployment.
+ /// </summary>
+ [Test]
+ public void TestDeployMultiple()
+ {
+ var svc = new TestIgniteServiceSerializable();
+
+ Services.DeployMultiple(SvcName, svc, Grids.Length * 5, 5);
+
+ foreach (var grid in Grids)
+ CheckServiceStarted(grid, 5);
+ }
+
+ /// <summary>
+ /// Tests cancellation.
+ /// </summary>
+ [Test]
+ public void TestCancel()
+ {
+ for (var i = 0; i < 10; i++)
+ {
+ Services.DeployNodeSingleton(SvcName + i, new TestIgniteServicePortable());
+ Assert.IsNotNull(Services.GetService<ITestIgniteService>(SvcName + i));
+ }
+
+ Services.Cancel(SvcName + 0);
+ Services.Cancel(SvcName + 1);
+
+ Assert.IsNull(Services.GetService<ITestIgniteService>(SvcName + 0));
+ Assert.IsNull(Services.GetService<ITestIgniteService>(SvcName + 1));
+
+ for (var i = 2; i < 10; i++)
+ Assert.IsNotNull(Services.GetService<ITestIgniteService>(SvcName + i));
+
+ Services.CancelAll();
+
+ for (var i = 0; i < 10; i++)
+ Assert.IsNull(Services.GetService<ITestIgniteService>(SvcName + i));
+ }
+
+ /// <summary>
+ /// Tests service proxy.
+ /// </summary>
+ [Test]
+ public void TestGetServiceProxy([Values(true, false)] bool portable)
+ {
+ // Test proxy without a service
+ var prx = Services.GetServiceProxy<ITestIgniteService>(SvcName);
+
+ Assert.IsTrue(prx != null);
+
+ var ex = Assert.Throws<ServiceInvocationException>(() => Assert.IsTrue(prx.Initialized)).InnerException;
+ Assert.AreEqual("Failed to find deployed service: " + SvcName, ex.Message);
+
+ // Deploy to grid2 & grid3
+ var svc = portable
+ ? new TestIgniteServicePortable {TestProperty = 17}
+ : new TestIgniteServiceSerializable {TestProperty = 17};
+
+ Grid1.Cluster.ForNodeIds(Grid2.Cluster.LocalNode.Id, Grid3.Cluster.LocalNode.Id).Services()
+ .DeployNodeSingleton(SvcName,
+ svc);
+
+ // Make sure there is no local instance on grid1
+ Assert.IsNull(Services.GetService<ITestIgniteService>(SvcName));
+
+ // Get proxy
+ prx = Services.GetServiceProxy<ITestIgniteService>(SvcName);
+
+ // Check proxy properties
+ Assert.IsNotNull(prx);
+ Assert.AreEqual(prx.GetType(), svc.GetType());
+ Assert.AreEqual(prx.ToString(), svc.ToString());
+ Assert.AreEqual(17, prx.TestProperty);
+ Assert.IsTrue(prx.Initialized);
+ Assert.IsTrue(prx.Executed);
+ Assert.IsFalse(prx.Cancelled);
+ Assert.AreEqual(SvcName, prx.LastCallContextName);
+
+ // Check err method
+ Assert.Throws<ServiceInvocationException>(() => prx.ErrMethod(123));
+
+ // Check local scenario (proxy should not be created for local instance)
+ Assert.IsTrue(ReferenceEquals(Grid2.Services().GetService<ITestIgniteService>(SvcName),
+ Grid2.Services().GetServiceProxy<ITestIgniteService>(SvcName)));
+
+ // Check sticky = false: call multiple times, check that different nodes get invoked
+ var invokedIds = Enumerable.Range(1, 100).Select(x => prx.NodeId).Distinct().ToList();
+ Assert.AreEqual(2, invokedIds.Count);
+
+ // Check sticky = true: all calls should be to the same node
+ prx = Services.GetServiceProxy<ITestIgniteService>(SvcName, true);
+ invokedIds = Enumerable.Range(1, 100).Select(x => prx.NodeId).Distinct().ToList();
+ Assert.AreEqual(1, invokedIds.Count);
+
+ // Proxy does not work for cancelled service.
+ Services.CancelAll();
+
+ Assert.Throws<ServiceInvocationException>(() => { Assert.IsTrue(prx.Cancelled); });
+ }
+
+ /// <summary>
+ /// Tests the duck typing: proxy interface can be different from actual service interface,
+ /// only called method signature should be compatible.
+ /// </summary>
+ [Test]
+ public void TestDuckTyping([Values(true, false)] bool local)
+ {
+ var svc = new TestIgniteServicePortable {TestProperty = 33};
+
+ // Deploy locally or to the remote node
+ var nodeId = (local ? Grid1 : Grid2).Cluster.LocalNode.Id;
+
+ var cluster = Grid1.Cluster.ForNodeIds(nodeId);
+
+ cluster.Services().DeployNodeSingleton(SvcName, svc);
+
+ // Get proxy
+ var prx = Services.GetServiceProxy<ITestIgniteServiceProxyInterface>(SvcName);
+
+ // NodeId signature is the same as in service
+ Assert.AreEqual(nodeId, prx.NodeId);
+
+ // Method signature is different from service signature (object -> object), but is compatible.
+ Assert.AreEqual(15, prx.Method(15));
+
+ // TestProperty is object in proxy and int in service, getter works..
+ Assert.AreEqual(33, prx.TestProperty);
+
+ // .. but setter does not
+ var ex = Assert.Throws<ServiceInvocationException>(() => { prx.TestProperty = new object(); });
+ Assert.AreEqual("Object of type 'System.Object' cannot be converted to type 'System.Int32'.",
+ ex.InnerException.Message);
+ }
+
+ /// <summary>
+ /// Tests service descriptors.
+ /// </summary>
+ [Test]
+ public void TestServiceDescriptors()
+ {
+ Services.DeployKeyAffinitySingleton(SvcName, new TestIgniteServiceSerializable(), CacheName, 1);
+
+ var descriptors = Services.GetServiceDescriptors();
+
+ Assert.AreEqual(1, descriptors.Count);
+
+ var desc = descriptors.Single();
+
+ Assert.AreEqual(SvcName, desc.Name);
+ Assert.AreEqual(CacheName, desc.CacheName);
+ Assert.AreEqual(1, desc.AffinityKey);
+ Assert.AreEqual(1, desc.MaxPerNodeCount);
+ Assert.AreEqual(1, desc.TotalCount);
+ Assert.AreEqual(typeof(TestIgniteServiceSerializable), desc.Type);
+ Assert.AreEqual(Grid1.Cluster.LocalNode.Id, desc.OriginNodeId);
+
+ var top = desc.TopologySnapshot;
+ var prx = Services.GetServiceProxy<ITestIgniteService>(SvcName);
+
+ Assert.AreEqual(1, top.Count);
+ Assert.AreEqual(prx.NodeId, top.Keys.Single());
+ Assert.AreEqual(1, top.Values.Single());
+ }
+
+ /// <summary>
+ /// Tests the client portable flag.
+ /// </summary>
+ [Test]
+ public void TestWithKeepPortableClient()
+ {
+ var svc = new TestIgniteServicePortable();
+
+ // Deploy to grid2
+ Grid1.Cluster.ForNodeIds(Grid2.Cluster.LocalNode.Id).Services().WithKeepPortable()
+ .DeployNodeSingleton(SvcName, svc);
+
+ // Get proxy
+ var prx = Services.WithKeepPortable().GetServiceProxy<ITestIgniteService>(SvcName);
+
+ var obj = new PortableObject {Val = 11};
+
+ var res = (IPortableObject) prx.Method(obj);
+ Assert.AreEqual(11, res.Deserialize<PortableObject>().Val);
+
+ res = (IPortableObject) prx.Method(Grid1.Portables().ToPortable<IPortableObject>(obj));
+ Assert.AreEqual(11, res.Deserialize<PortableObject>().Val);
+ }
+
+ /// <summary>
+ /// Tests the server portable flag.
+ /// </summary>
+ [Test]
+ public void TestWithKeepPortableServer()
+ {
+ var svc = new TestIgniteServicePortable();
+
+ // Deploy to grid2
+ Grid1.Cluster.ForNodeIds(Grid2.Cluster.LocalNode.Id).Services().WithServerKeepPortable()
+ .DeployNodeSingleton(SvcName, svc);
+
+ // Get proxy
+ var prx = Services.WithServerKeepPortable().GetServiceProxy<ITestIgniteService>(SvcName);
+
+ var obj = new PortableObject { Val = 11 };
+
+ var res = (PortableObject) prx.Method(obj);
+ Assert.AreEqual(11, res.Val);
+
+ res = (PortableObject)prx.Method(Grid1.Portables().ToPortable<IPortableObject>(obj));
+ Assert.AreEqual(11, res.Val);
+ }
+
+ /// <summary>
+ /// Tests server and client portable flag.
+ /// </summary>
+ [Test]
+ public void TestWithKeepPortableBoth()
+ {
+ var svc = new TestIgniteServicePortable();
+
+ // Deploy to grid2
+ Grid1.Cluster.ForNodeIds(Grid2.Cluster.LocalNode.Id).Services().WithKeepPortable().WithServerKeepPortable()
+ .DeployNodeSingleton(SvcName, svc);
+
+ // Get proxy
+ var prx = Services.WithKeepPortable().WithServerKeepPortable().GetServiceProxy<ITestIgniteService>(SvcName);
+
+ var obj = new PortableObject { Val = 11 };
+
+ var res = (IPortableObject)prx.Method(obj);
+ Assert.AreEqual(11, res.Deserialize<PortableObject>().Val);
+
+ res = (IPortableObject)prx.Method(Grid1.Portables().ToPortable<IPortableObject>(obj));
+ Assert.AreEqual(11, res.Deserialize<PortableObject>().Val);
+ }
+
+ /// <summary>
+ /// Tests exception in Initialize.
+ /// </summary>
+ [Test]
+ public void TestInitException()
+ {
+ var svc = new TestIgniteServiceSerializable { ThrowInit = true };
+
+ var ex = Assert.Throws<IgniteException>(() => Services.DeployMultiple(SvcName, svc, Grids.Length, 1));
+ Assert.AreEqual("Expected exception", ex.Message);
+
+ var svc0 = Services.GetService<TestIgniteServiceSerializable>(SvcName);
+
+ Assert.IsNull(svc0);
+ }
+
+ /// <summary>
+ /// Tests exception in Execute.
+ /// </summary>
+ [Test]
+ public void TestExecuteException()
+ {
+ var svc = new TestIgniteServiceSerializable { ThrowExecute = true };
+
+ Services.DeployMultiple(SvcName, svc, Grids.Length, 1);
+
+ var svc0 = Services.GetService<TestIgniteServiceSerializable>(SvcName);
+
+ // Execution failed, but service exists.
+ Assert.IsNotNull(svc0);
+ Assert.IsFalse(svc0.Executed);
+ }
+
+ /// <summary>
+ /// Tests exception in Cancel.
+ /// </summary>
+ [Test]
+ public void TestCancelException()
+ {
+ var svc = new TestIgniteServiceSerializable { ThrowCancel = true };
+
+ Services.DeployMultiple(SvcName, svc, Grids.Length, 1);
+
+ CheckServiceStarted(Grid1);
+
+ Services.CancelAll();
+
+ // Cancellation failed, but service is removed.
+ foreach (var grid in Grids)
+ Assert.IsNull(grid.Services().GetService<ITestIgniteService>(SvcName));
+ }
+
+ [Test]
+ public void TestMarshalExceptionOnRead()
+ {
+ var svc = new TestIgniteServicePortableErr();
+
+ var ex = Assert.Throws<IgniteException>(() => Services.DeployMultiple(SvcName, svc, Grids.Length, 1));
+ Assert.AreEqual("Expected exception", ex.Message);
+
+ var svc0 = Services.GetService<TestIgniteServiceSerializable>(SvcName);
+
+ Assert.IsNull(svc0);
+ }
+
+ [Test]
+ public void TestMarshalExceptionOnWrite()
+ {
+ var svc = new TestIgniteServicePortableErr {ThrowOnWrite = true};
+
+ var ex = Assert.Throws<Exception>(() => Services.DeployMultiple(SvcName, svc, Grids.Length, 1));
+ Assert.AreEqual("Expected exception", ex.Message);
+
+ var svc0 = Services.GetService<TestIgniteServiceSerializable>(SvcName);
+
+ Assert.IsNull(svc0);
+ }
+
+ /// <summary>
+ /// Starts the grids.
+ /// </summary>
+ private void StartGrids()
+ {
+ if (Grid1 != null)
+ return;
+
+ Grid1 = Ignition.Start(Configuration("config\\compute\\compute-grid1.xml"));
+ Grid2 = Ignition.Start(Configuration("config\\compute\\compute-grid2.xml"));
+ Grid3 = Ignition.Start(Configuration("config\\compute\\compute-grid3.xml"));
+
+ Grids = new[] { Grid1, Grid2, Grid3 };
+ }
+
+ /// <summary>
+ /// Stops the grids.
+ /// </summary>
+ private void StopGrids()
+ {
+ Grid1 = Grid2 = Grid3 = null;
+ Grids = null;
+
+ Ignition.StopAll(true);
+ }
+
+ /// <summary>
+ /// Checks that service has started on specified grid.
+ /// </summary>
+ private static void CheckServiceStarted(IIgnite grid, int count = 1)
+ {
+ var services = grid.Services().GetServices<TestIgniteServiceSerializable>(SvcName);
+
+ Assert.AreEqual(count, services.Count);
+
+ var svc = services.First();
+
+ Assert.IsNotNull(svc);
+
+ Assert.IsTrue(svc.Initialized);
+
+ Thread.Sleep(100); // Service runs in a separate thread, wait for it to execute.
+
+ Assert.IsTrue(svc.Executed);
+ Assert.IsFalse(svc.Cancelled);
+
+ Assert.AreEqual(grid.Cluster.LocalNode.Id, svc.NodeId);
+ }
+
+ /// <summary>
+ /// Gets the Ignite configuration.
+ /// </summary>
+ private static IgniteConfiguration Configuration(string springConfigUrl)
+ {
+ return new IgniteConfiguration
+ {
+ SpringConfigUrl = springConfigUrl,
+ JvmClasspath = TestUtils.CreateTestClasspath(),
+ JvmOptions = TestUtils.TestJavaOptions(),
+ PortableConfiguration = new PortableConfiguration
+ {
+ TypeConfigurations = new List<PortableTypeConfiguration>
+ {
+ new PortableTypeConfiguration(typeof(TestIgniteServicePortable)),
+ new PortableTypeConfiguration(typeof(TestIgniteServicePortableErr)),
+ new PortableTypeConfiguration(typeof(PortableObject))
+ }
+ }
+ };
+ }
+
+ /// <summary>
+ /// Gets the services.
+ /// </summary>
+ protected virtual IServices Services
+ {
+ get { return Grid1.Services(); }
+ }
+
+ /// <summary>
+ /// Test service interface for proxying.
+ /// </summary>
+ private interface ITestIgniteService
+ {
+ int TestProperty { get; set; }
+
+ /** */
+ bool Initialized { get; }
+
+ /** */
+ bool Cancelled { get; }
+
+ /** */
+ bool Executed { get; }
+
+ /** */
+ Guid NodeId { get; }
+
+ /** */
+ string LastCallContextName { get; }
+
+ /** */
+ object Method(object arg);
+
+ /** */
+ object ErrMethod(object arg);
+ }
+
+ /// <summary>
+ /// Test service interface for proxy usage.
+ /// Has some of the original interface members with different signatures.
+ /// </summary>
+ private interface ITestIgniteServiceProxyInterface
+ {
+ /** */
+ Guid NodeId { get; }
+
+ /** */
+ object TestProperty { get; set; }
+
+ /** */
+ int Method(int arg);
+ }
+
+ #pragma warning disable 649
+
+ /// <summary>
+ /// Test serializable service.
+ /// </summary>
+ [Serializable]
+ private class TestIgniteServiceSerializable : IService, ITestIgniteService
+ {
+ /** */
+ [InstanceResource]
+ private IIgnite _grid;
+
+ /** <inheritdoc /> */
+ public int TestProperty { get; set; }
+
+ /** <inheritdoc /> */
+ public bool Initialized { get; private set; }
+
+ /** <inheritdoc /> */
+ public bool Cancelled { get; private set; }
+
+ /** <inheritdoc /> */
+ public bool Executed { get; private set; }
+
+ /** <inheritdoc /> */
+ public Guid NodeId
+ {
+ get { return _grid.Cluster.LocalNode.Id; }
+ }
+
+ /** <inheritdoc /> */
+ public string LastCallContextName { get; private set; }
+
+ /** */
+ public bool ThrowInit { get; set; }
+
+ /** */
+ public bool ThrowExecute { get; set; }
+
+ /** */
+ public bool ThrowCancel { get; set; }
+
+ /** */
+ public object Method(object arg)
+ {
+ return arg;
+ }
+
+ /** */
+ public object ErrMethod(object arg)
+ {
+ throw new ArgumentNullException("arg", "ExpectedException");
+ }
+
+ /** <inheritdoc /> */
+ public void Init(IServiceContext context)
+ {
+ if (ThrowInit)
+ throw new Exception("Expected exception");
+
+ CheckContext(context);
+
+ Assert.IsFalse(context.IsCancelled);
+ Initialized = true;
+ }
+
+ /** <inheritdoc /> */
+ public void Execute(IServiceContext context)
+ {
+ if (ThrowExecute)
+ throw new Exception("Expected exception");
+
+ CheckContext(context);
+
+ Assert.IsFalse(context.IsCancelled);
+ Assert.IsTrue(Initialized);
+ Assert.IsFalse(Cancelled);
+
+ Executed = true;
+ }
+
+ /** <inheritdoc /> */
+ public void Cancel(IServiceContext context)
+ {
+ if (ThrowCancel)
+ throw new Exception("Expected exception");
+
+ CheckContext(context);
+
+ Assert.IsTrue(context.IsCancelled);
+
+ Cancelled = true;
+ }
+
+ /// <summary>
+ /// Checks the service context.
+ /// </summary>
+ private void CheckContext(IServiceContext context)
+ {
+ LastCallContextName = context.Name;
+
+ if (context.AffinityKey != null && !(context.AffinityKey is int))
+ {
+ var portableObject = context.AffinityKey as IPortableObject;
+
+ var key = portableObject != null
+ ? portableObject.Deserialize<PortableObject>()
+ : (PortableObject) context.AffinityKey;
+
+ Assert.AreEqual(AffKey, key.Val);
+ }
+
+ Assert.IsNotNull(_grid);
+
+ Assert.IsTrue(context.Name.StartsWith(SvcName));
+ Assert.AreNotEqual(Guid.Empty, context.ExecutionId);
+ }
+ }
+
+ /// <summary>
+ /// Test portable service.
+ /// </summary>
+ private class TestIgniteServicePortable : TestIgniteServiceSerializable, IPortableMarshalAware
+ {
+ /** <inheritdoc /> */
+ public void WritePortable(IPortableWriter writer)
+ {
+ writer.WriteInt("TestProp", TestProperty);
+ }
+
+ /** <inheritdoc /> */
+ public void ReadPortable(IPortableReader reader)
+ {
+ TestProperty = reader.ReadInt("TestProp");
+ }
+ }
+
+ /// <summary>
+ /// Test portable service with exceptions in marshalling.
+ /// </summary>
+ private class TestIgniteServicePortableErr : TestIgniteServiceSerializable, IPortableMarshalAware
+ {
+ /** */
+ public bool ThrowOnWrite { get; set; }
+
+ /** <inheritdoc /> */
+ public void WritePortable(IPortableWriter writer)
+ {
+ writer.WriteInt("TestProp", TestProperty);
+
+ if (ThrowOnWrite)
+ throw new Exception("Expected exception");
+ }
+
+ /** <inheritdoc /> */
+ public void ReadPortable(IPortableReader reader)
+ {
+ TestProperty = reader.ReadInt("TestProp");
+
+ throw new Exception("Expected exception");
+ }
+ }
+
+ /// <summary>
+ /// Test node filter.
+ /// </summary>
+ [Serializable]
+ private class NodeFilter : IClusterNodeFilter
+ {
+ /// <summary>
+ /// Gets or sets the node identifier.
+ /// </summary>
+ public Guid NodeId { get; set; }
+
+ /** <inheritdoc /> */
+ public bool Invoke(IClusterNode node)
+ {
+ return node.Id == NodeId;
+ }
+ }
+
+ /// <summary>
+ /// Portable object.
+ /// </summary>
+ private class PortableObject
+ {
+ public int Val { get; set; }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTestAsync.cs
----------------------------------------------------------------------
diff --git a/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTestAsync.cs b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTestAsync.cs
new file mode 100644
index 0000000..68ae93e
--- /dev/null
+++ b/modules/platform/src/test/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTestAsync.cs
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+namespace Apache.Ignite.Core.Tests.Services
+{
+ using Apache.Ignite.Core.Services;
+
+ /// <summary>
+ /// Services async tests.
+ /// </summary>
+ public class ServicesTestAsync : ServicesTest
+ {
+ /** <inheritdoc /> */
+ protected override IServices Services
+ {
+ get { return new ServicesAsyncWrapper(Grid1.Services()); }
+ }
+ }
+}
\ No newline at end of file