| /* |
| * 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.Plugin |
| { |
| using System; |
| using System.Collections.Generic; |
| using System.IO; |
| using System.Linq; |
| using System.Threading; |
| using System.Threading.Tasks; |
| using Apache.Ignite.Core.Binary; |
| using Apache.Ignite.Core.Common; |
| using Apache.Ignite.Core.Interop; |
| using Apache.Ignite.Core.Plugin; |
| using Apache.Ignite.Core.Resource; |
| using NUnit.Framework; |
| |
| /// <summary> |
| /// Ignite plugin test. |
| /// </summary> |
| public class PluginTest |
| { |
| /** Plugin log. */ |
| private static readonly List<string> PluginLog = new List<string>(); |
| |
| /// <summary> |
| /// Tests the plugin life cycle. |
| /// </summary> |
| [Test] |
| public void IgnitionStart_ValidPluginConfiguration_StartsAndReturnsWorkingPlugin() |
| { |
| var cfg = new IgniteConfiguration(TestUtils.GetTestConfiguration()) |
| { |
| PluginConfigurations = new[] {new TestIgnitePluginConfiguration {PluginProperty = "barbaz"}} |
| }; |
| |
| TestIgnitePlugin plugin; |
| |
| using (var ignite = Ignition.Start(cfg)) |
| { |
| Assert.Throws<PluginNotFoundException>(() => ignite.GetPlugin<object>("foobar")); |
| Assert.Throws<Exception>(() => ignite.GetPlugin<string>(TestIgnitePluginProvider.PluginName)); |
| |
| plugin = ignite.GetPlugin<TestIgnitePlugin>(TestIgnitePluginProvider.PluginName); |
| Assert.IsNotNull(plugin); |
| |
| var prov = plugin.Provider; |
| Assert.IsTrue(prov.Started); |
| Assert.AreEqual(null, prov.Stopped); |
| Assert.AreEqual(TestIgnitePluginProvider.PluginName, prov.Name); |
| Assert.IsNotNull(prov.Copyright); |
| Assert.IsNotNull(prov.Context); |
| |
| var ctx = prov.Context; |
| Assert.IsNotNull(ctx.Ignite); |
| Assert.AreEqual("barbaz", ctx.PluginConfiguration.PluginProperty); |
| CheckResourceInjection(ctx); |
| |
| var plugin2 = ignite.GetPlugin<TestIgnitePlugin>(TestIgnitePluginProvider.PluginName); |
| Assert.AreEqual(plugin, plugin2); |
| |
| var extension = plugin.Provider.Context.GetExtension(0); |
| Assert.IsNotNull(extension); |
| |
| CheckPluginTarget(extension, "barbaz", plugin.Provider); |
| } |
| |
| Assert.AreEqual(true, plugin.Provider.Stopped); |
| Assert.AreEqual(true, plugin.Provider.IgniteStopped); |
| } |
| |
| /// <summary> |
| /// Checks the resource injection. |
| /// </summary> |
| private static void CheckResourceInjection(IPluginContext<TestIgnitePluginConfiguration> ctx) |
| { |
| var obj = new Injectee(); |
| |
| Assert.IsNull(obj.Ignite); |
| |
| ctx.InjectResources(obj); |
| |
| Assert.IsNotNull(obj.Ignite); |
| Assert.AreEqual(ctx.Ignite.Name, obj.Ignite.Name); |
| } |
| |
| /// <summary> |
| /// Checks the plugin target operations. |
| /// </summary> |
| private static void CheckPluginTarget(IPlatformTarget target, string expectedName, |
| TestIgnitePluginProvider provider) |
| { |
| // Returns name. |
| Assert.AreEqual(expectedName, target.OutStream(1, r => r.ReadString())); |
| |
| // Increments arg by one. |
| Assert.AreEqual(3, target.InLongOutLong(1, 2)); |
| Assert.AreEqual(5, target.InLongOutLong(1, 4)); |
| |
| // Returns string length. |
| Assert.AreEqual(3, target.InStreamOutLong(1, w => w.WriteString("foo"))); |
| Assert.AreEqual(6, target.InStreamOutLong(1, w => w.WriteString("foobar"))); |
| |
| // Returns uppercase string. |
| Assert.AreEqual("FOO", target.InStreamOutStream(1, w => w.WriteString("foo"), r => r.ReadString())); |
| Assert.AreEqual("BAR", target.InStreamOutStream(1, w => w.WriteString("bar"), r => r.ReadString())); |
| |
| // Returns target with specified name. |
| var newTarget = target.InStreamOutObject(1, w => w.WriteString("name1")); |
| Assert.AreEqual("name1", newTarget.OutStream(1, r => r.ReadString())); |
| |
| // Invokes callback to modify name, returns target with specified name appended. |
| var res = target.InObjectStreamOutObjectStream(1, newTarget, w => w.WriteString("_abc"), |
| (reader, t) => Tuple.Create(reader.ReadString(), t)); |
| |
| Assert.AreEqual("NAME1", res.Item1); // Old name converted by callback. |
| Assert.AreEqual("name1_abc", res.Item2.OutStream(1, r => r.ReadString())); |
| Assert.AreEqual("name1", provider.CallbackResult); // Old name. |
| |
| // Returns a copy with same name. |
| var resCopy = res.Item2.OutObject(1); |
| Assert.AreEqual("name1_abc", resCopy.OutStream(1, r => r.ReadString())); |
| |
| // Async operation. |
| var task = target.DoOutOpAsync(1, w => w.WriteString("foo"), r => r.ReadString()); |
| Assert.IsFalse(task.IsCompleted); |
| var asyncRes = task.Result; |
| Assert.IsTrue(task.IsCompleted); |
| Assert.AreEqual("FOO", asyncRes); |
| |
| // Async operation with cancellation. |
| var cts = new CancellationTokenSource(); |
| task = target.DoOutOpAsync(1, w => w.WriteString("foo"), r => r.ReadString(), cts.Token); |
| Assert.IsFalse(task.IsCompleted); |
| cts.Cancel(); |
| Assert.IsTrue(task.IsCanceled); |
| var aex = Assert.Throws<AggregateException>(() => { asyncRes = task.Result; }); |
| Assert.IsInstanceOf<TaskCanceledException>(aex.GetBaseException()); |
| |
| // Async operation with exception in entry point. |
| Assert.Throws<TestIgnitePluginException>(() => target.DoOutOpAsync<object>(2, null, null)); |
| |
| // Async operation with exception in future. |
| var errTask = target.DoOutOpAsync<object>(3, null, null); |
| Assert.IsFalse(errTask.IsCompleted); |
| aex = Assert.Throws<AggregateException>(() => errTask.Wait()); |
| Assert.IsInstanceOf<IgniteException>(aex.InnerExceptions.Single()); |
| |
| // Throws custom mapped exception. |
| var ex = Assert.Throws<TestIgnitePluginException>(() => target.InLongOutLong(-1, 0)); |
| Assert.AreEqual("Baz", ex.Message); |
| Assert.AreEqual(Ignition.GetIgnite(null), ex.Ignite); |
| Assert.AreEqual("org.apache.ignite.platform.plugin.PlatformTestPluginException", ex.ClassName); |
| |
| var javaEx = ex.InnerException as JavaException; |
| Assert.IsNotNull(javaEx); |
| Assert.AreEqual("Baz", javaEx.JavaMessage); |
| Assert.AreEqual("org.apache.ignite.platform.plugin.PlatformTestPluginException", javaEx.JavaClassName); |
| Assert.IsTrue(javaEx.Message.Contains( |
| "at org.apache.ignite.platform.plugin.PlatformTestPluginTarget.processInLongOutLong")); |
| } |
| |
| [Test] |
| public void IgnitionStart_MissingPluginConfigurationAttribute_ThrowsException() |
| { |
| var ex = Assert.Throws<IgniteException>(() => TryStart(new[] { new NoAttributeConfig() })); |
| Assert.IsNotNull(ex.InnerException); |
| Assert.AreEqual(string.Format("{0} of type {1} has no {2}", typeof(IPluginConfiguration), |
| typeof(NoAttributeConfig), typeof(PluginProviderTypeAttribute)), ex.InnerException.Message); |
| } |
| |
| [Test] |
| public void IgnitionStart_EmptyPluginName_ThrowsException() |
| { |
| var ex = Assert.Throws<IgniteException>(() => TryStart(new[] {new EmptyNameConfig()})); |
| Assert.IsNotNull(ex.InnerException); |
| Assert.AreEqual(string.Format("{0}.Name should not be null or empty: {1}", typeof(IPluginProvider<>), |
| typeof(EmptyNamePluginProvider)), ex.InnerException.Message); |
| } |
| |
| [Test] |
| public void IgnitionStart_DuplicatePluginName_ThrowsException() |
| { |
| var ex = Assert.Throws<IgniteException>(() => TryStart(new[] |
| { |
| new TestIgnitePluginConfiguration(), |
| new TestIgnitePluginConfiguration() |
| })); |
| Assert.IsNotNull(ex.InnerException); |
| Assert.AreEqual(string.Format("Duplicate plugin name 'TestPlugin1' is used by plugin providers " + |
| "'{0}' and '{0}'", typeof(TestIgnitePluginProvider)), |
| ex.InnerException.Message); |
| } |
| |
| /// <summary> |
| /// Tests invalid plugins. |
| /// </summary> |
| [Test] |
| public void IgnitionStart_PluginProviderThrowsException_StopsAndRethrowsException() |
| { |
| PluginLog.Clear(); |
| |
| var ex = Assert.Throws<IgniteException>(() => TryStart(new IPluginConfiguration[] |
| { |
| new NormalConfig(), new ExceptionConfig() |
| })); |
| Assert.IsNotNull(ex.InnerException); |
| Assert.IsInstanceOf<IOException>(ex.InnerException); |
| Assert.AreEqual("Failure in plugin provider", ex.InnerException.Message); |
| |
| // Verify that plugins are started and stopped in correct order: |
| Assert.AreEqual( |
| new[] |
| { |
| "normalPlugin.Start", "errPlugin.Start", |
| "errPlugin.Stop", "normalPlugin.Stop" |
| }, PluginLog); |
| } |
| |
| private static void TryStart(ICollection<IPluginConfiguration> x) |
| { |
| try |
| { |
| var igniteConfiguration = new IgniteConfiguration(TestUtils.GetTestConfiguration()) |
| { |
| PluginConfigurations = x |
| }; |
| |
| Ignition.Start(igniteConfiguration); |
| } |
| finally |
| { |
| Ignition.StopAll(true); |
| } |
| } |
| |
| private class NoAttributeConfig : IPluginConfiguration |
| { |
| public int? PluginConfigurationClosureFactoryId |
| { |
| get { return null; } |
| } |
| |
| public void WriteBinary(IBinaryRawWriter writer) |
| { |
| throw new NotImplementedException(); |
| } |
| } |
| |
| [PluginProviderType(typeof(EmptyNamePluginProvider))] |
| private class EmptyNameConfig : IPluginConfiguration |
| { |
| public int? PluginConfigurationClosureFactoryId |
| { |
| get { return null; } |
| } |
| |
| public void WriteBinary(IBinaryRawWriter writer) |
| { |
| throw new NotImplementedException(); |
| } |
| } |
| |
| private class EmptyNamePluginProvider : IPluginProvider<EmptyNameConfig> |
| { |
| public string Name { get { return null; } } |
| public string Copyright { get { return null; } } |
| public T GetPlugin<T>() where T : class { return default(T); } |
| public void Start(IPluginContext<EmptyNameConfig> context) { /* No-op. */ } |
| public void Stop(bool cancel) { /* No-op. */ } |
| public void OnIgniteStart() { /* No-op. */ } |
| public void OnIgniteStop(bool cancel) { /* No-op. */ } |
| } |
| |
| [PluginProviderType(typeof(ExceptionPluginProvider))] |
| private class ExceptionConfig : IPluginConfiguration |
| { |
| public int? PluginConfigurationClosureFactoryId |
| { |
| get { return null; } |
| } |
| |
| public void WriteBinary(IBinaryRawWriter writer) |
| { |
| throw new NotImplementedException(); |
| } |
| } |
| |
| private class ExceptionPluginProvider : IPluginProvider<ExceptionConfig> |
| { |
| public string Name { get { return "errPlugin"; } } |
| public string Copyright { get { return null; } } |
| public T GetPlugin<T>() where T : class { return default(T); } |
| |
| public void Stop(bool cancel) |
| { |
| PluginLog.Add(Name + ".Stop"); |
| } |
| |
| public void OnIgniteStart() |
| { |
| PluginLog.Add(Name + ".OnIgniteStart"); |
| } |
| |
| public void OnIgniteStop(bool cancel) |
| { |
| PluginLog.Add(Name + ".OnIgniteStop"); |
| } |
| |
| public void Start(IPluginContext<ExceptionConfig> context) |
| { |
| PluginLog.Add(Name + ".Start"); |
| throw new IOException("Failure in plugin provider"); |
| } |
| } |
| |
| [PluginProviderType(typeof(NormalPluginProvider))] |
| private class NormalConfig : IPluginConfiguration |
| { |
| public int? PluginConfigurationClosureFactoryId |
| { |
| get { return null; } |
| } |
| |
| public void WriteBinary(IBinaryRawWriter writer) |
| { |
| throw new NotImplementedException(); |
| } |
| } |
| |
| private class NormalPluginProvider : IPluginProvider<NormalConfig> |
| { |
| public string Name { get { return "normalPlugin"; } } |
| public string Copyright { get { return null; } } |
| public T GetPlugin<T>() where T : class { return default(T); } |
| |
| public void Stop(bool cancel) |
| { |
| PluginLog.Add(Name + ".Stop"); |
| } |
| |
| public void OnIgniteStart() |
| { |
| PluginLog.Add(Name + ".OnIgniteStart"); |
| } |
| |
| public void OnIgniteStop(bool cancel) |
| { |
| PluginLog.Add(Name + ".OnIgniteStop"); |
| } |
| |
| public void Start(IPluginContext<NormalConfig> context) |
| { |
| PluginLog.Add(Name + ".Start"); |
| } |
| } |
| |
| private class Injectee |
| { |
| [InstanceResource] |
| // ReSharper disable once UnusedAutoPropertyAccessor.Local |
| public IIgnite Ignite { get; set; } |
| } |
| } |
| } |