From e1809a0462265899ac907ecb3bb5792175427f51 Mon Sep 17 00:00:00 2001 From: Andrey Taritsyn Date: Sun, 2 Jul 2017 08:15:08 +0300 Subject: [PATCH] Added a ability to select the default JavaScript engine (#413) * Added a ability to select the default JavaScript engine * Added a unit tests --- .../ReactEngineNotFoundException.cs | 10 +- src/React.Core/JavaScriptEngineFactory.cs | 17 +++ .../Core/JavaScriptEngineFactoryTest.cs | 130 +++++++++++++++++- 3 files changed, 150 insertions(+), 7 deletions(-) diff --git a/src/React.Core/Exceptions/ReactEngineNotFoundException.cs b/src/React.Core/Exceptions/ReactEngineNotFoundException.cs index e2ca4cf8f..2e218884f 100644 --- a/src/React.Core/Exceptions/ReactEngineNotFoundException.cs +++ b/src/React.Core/Exceptions/ReactEngineNotFoundException.cs @@ -25,6 +25,12 @@ public class ReactEngineNotFoundException : ReactException /// public ReactEngineNotFoundException() : base(GetMessage()) { } + /// + /// Initializes a new instance of the class. + /// + /// The message that describes the error. + public ReactEngineNotFoundException(string message) : base(message) { } + #if NET40 /// /// Used by deserialization @@ -40,8 +46,8 @@ private static string GetMessage() { return "No usable JavaScript engine was found. Please install a JavaScript engine such " + - "as React.JavaScriptEngine.ClearScriptV8 (on Windows) or " + - "React.JavaScriptEngine.VroomJs (on Linux and Mac OS X). Refer to the ReactJS.NET " + + "as JavaScriptEngineSwitcher.V8.V8JsEngine (on Windows) or " + + "React.VroomJsEngine (on Linux and Mac OS X). Refer to the ReactJS.NET " + "documentation for more details."; } } diff --git a/src/React.Core/JavaScriptEngineFactory.cs b/src/React.Core/JavaScriptEngineFactory.cs index 073f7b791..45d038674 100644 --- a/src/React.Core/JavaScriptEngineFactory.cs +++ b/src/React.Core/JavaScriptEngineFactory.cs @@ -251,6 +251,23 @@ public virtual void ReturnEngineToPool(IJsEngine engine) private static Func GetFactory(JsEngineSwitcher jsEngineSwitcher, bool allowMsie) { EnsureJsEnginesRegistered(jsEngineSwitcher, allowMsie); + + string defaultEngineName = jsEngineSwitcher.DefaultEngineName; + if (!string.IsNullOrWhiteSpace(defaultEngineName)) + { + var engineFactory = jsEngineSwitcher.EngineFactories.Get(defaultEngineName); + if (engineFactory != null) + { + return engineFactory.CreateEngine; + } + else + { + throw new ReactEngineNotFoundException( + "Could not find a factory, that creates an instance of the JavaScript " + + "engine with name `" + defaultEngineName + "`."); + } + } + foreach (var engineFactory in jsEngineSwitcher.EngineFactories) { IJsEngine engine = null; diff --git a/tests/React.Tests/Core/JavaScriptEngineFactoryTest.cs b/tests/React.Tests/Core/JavaScriptEngineFactoryTest.cs index 0363ae8fa..925243071 100644 --- a/tests/React.Tests/Core/JavaScriptEngineFactoryTest.cs +++ b/tests/React.Tests/Core/JavaScriptEngineFactoryTest.cs @@ -19,6 +19,9 @@ namespace React.Tests.Core { public class JavaScriptEngineFactoryTest { + private static object _engineSwitcherSynchronizer = new object(); + + private JavaScriptEngineFactory CreateBasicFactory() { var config = new Mock(); @@ -44,11 +47,17 @@ Func innerEngineFactory engineFactory.Setup(x => x.CreateEngine()).Returns(innerEngineFactory); // JsEngineSwitcher is a singleton :( - var engineFactories = JsEngineSwitcher.Instance.EngineFactories; - engineFactories.Clear(); - engineFactories.Add(engineFactory.Object); - - return new JavaScriptEngineFactory(JsEngineSwitcher.Instance, config.Object, fileSystem.Object); + lock (_engineSwitcherSynchronizer) + { + var engineSwitcher = JsEngineSwitcher.Instance; + engineSwitcher.DefaultEngineName = string.Empty; + + var engineFactories = engineSwitcher.EngineFactories; + engineFactories.Clear(); + engineFactories.Add(engineFactory.Object); + + return new JavaScriptEngineFactory(engineSwitcher, config.Object, fileSystem.Object); + } } [Fact] @@ -176,5 +185,116 @@ public void ShouldCatchErrorsWhileLoadingScripts() var ex = Assert.Throws(() => factory.GetEngineForCurrentThread()); Assert.Equal("Error while loading \"foo.js\": Fail\r\nLine: 42\r\nColumn: 911", ex.Message); } + + [Fact] + public void ShouldReturnDefaultEngine() + { + const string someEngineName = "SomeEngine"; + const string defaultEngineName = "DefaultEngine"; + const string someOtherEngineName = "SomeOtherEngine"; + + var someEngineFactory = new Mock(); + someEngineFactory.Setup(x => x.EngineName).Returns(someEngineName); + someEngineFactory.Setup(x => x.CreateEngine()).Returns(() => + { + var someEngine = new Mock(); + someEngine.Setup(x => x.Name).Returns(someEngineName); + return someEngine.Object; + }); + + var defaultEngineFactory = new Mock(); + defaultEngineFactory.Setup(x => x.EngineName).Returns(defaultEngineName); + defaultEngineFactory.Setup(x => x.CreateEngine()).Returns(() => + { + var defaultEngine = new Mock(); + defaultEngine.Setup(x => x.Name).Returns(defaultEngineName); + return defaultEngine.Object; + }); + + var someOtherEngineFactory = new Mock(); + someOtherEngineFactory.Setup(x => x.EngineName).Returns(someOtherEngineName); + someOtherEngineFactory.Setup(x => x.CreateEngine()).Returns(() => + { + var someOtherEngine = new Mock(); + someOtherEngine.Setup(x => x.Name).Returns(someOtherEngineName); + return someOtherEngine.Object; + }); + + var config = new Mock(); + config.Setup(x => x.ScriptsWithoutTransform).Returns(new List()); + config.Setup(x => x.LoadReact).Returns(true); + + var fileSystem = new Mock(); + + JavaScriptEngineFactory factory; + + // JsEngineSwitcher is a singleton :( + lock (_engineSwitcherSynchronizer) + { + var engineSwitcher = JsEngineSwitcher.Instance; + engineSwitcher.DefaultEngineName = defaultEngineName; + + var engineFactories = engineSwitcher.EngineFactories; + engineFactories.Clear(); + engineFactories.Add(someEngineFactory.Object); + engineFactories.Add(defaultEngineFactory.Object); + engineFactories.Add(someOtherEngineFactory.Object); + + factory = new JavaScriptEngineFactory(engineSwitcher, config.Object, fileSystem.Object); + } + + var engine = factory.GetEngineForCurrentThread(); + + Assert.Equal(defaultEngineName, engine.Name); + } + + [Fact] + public void ShouldThrowIfDefaultEngineFactoryNotFound() + { + const string someEngineName = "SomeEngine"; + const string defaultEngineName = "DefaultEngine"; + const string someOtherEngineName = "SomeOtherEngine"; + + var someEngineFactory = new Mock(); + someEngineFactory.Setup(x => x.EngineName).Returns(someEngineName); + someEngineFactory.Setup(x => x.CreateEngine()).Returns(() => + { + var someEngine = new Mock(); + someEngine.Setup(x => x.Name).Returns(someEngineName); + return someEngine.Object; + }); + + var someOtherEngineFactory = new Mock(); + someOtherEngineFactory.Setup(x => x.EngineName).Returns(someOtherEngineName); + someOtherEngineFactory.Setup(x => x.CreateEngine()).Returns(() => + { + var someOtherEngine = new Mock(); + someOtherEngine.Setup(x => x.Name).Returns(someOtherEngineName); + return someOtherEngine.Object; + }); + + var config = new Mock(); + config.Setup(x => x.ScriptsWithoutTransform).Returns(new List()); + config.Setup(x => x.LoadReact).Returns(true); + + var fileSystem = new Mock(); + + // JsEngineSwitcher is a singleton :( + lock (_engineSwitcherSynchronizer) + { + var engineSwitcher = JsEngineSwitcher.Instance; + engineSwitcher.DefaultEngineName = defaultEngineName; + + var engineFactories = engineSwitcher.EngineFactories; + engineFactories.Clear(); + engineFactories.Add(someEngineFactory.Object); + engineFactories.Add(someOtherEngineFactory.Object); + + Assert.Throws(() => + { + var factory = new JavaScriptEngineFactory(engineSwitcher, config.Object, fileSystem.Object); + }); + } + } } }