// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.ClearScript.Util;
using Microsoft.ClearScript.V8;
using Microsoft.ClearScript.Windows;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.ClearScript.Test
{
[TestClass]
[SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "Test classes use TestCleanupAttribute for deterministic teardown.")]
public class CrossEngineTest : ClearScriptTest
{
#region setup / teardown
private static readonly Type[] types =
{
typeof(V8ScriptEngine),
typeof(JScriptEngine),
typeof(VBScriptEngine)
};
private ScriptEngine[] engines;
[TestInitialize]
public void TestInitialize()
{
engines = types.Select(type => (ScriptEngine)type.CreateInstance()).ToArray();
Iterate((engine, type) => engine.AddHostObject(type.Name, type.CreateInstance()));
}
[TestCleanup]
public void TestCleanup()
{
Iterate((engine, type) => engine.Execute(type.Name + ".Dispose()"));
engines.ForEach(engine => engine.Dispose());
BaseTestCleanup();
}
#endregion
#region test methods
// ReSharper disable InconsistentNaming
[TestMethod, TestCategory("CrossEngine")]
public void CrossEngine_Property()
{
var value = new Random();
Iterate(JSOnly, All, (engine, type) =>
{
engine.Script.value = value;
Assert.AreSame(value, engine.Evaluate(type.Name + ".Script.value = value"));
Assert.AreSame(value, engine.Evaluate(type.Name + ".Script.value"));
});
Iterate(VBSOnly, All, (engine, type) =>
{
var innerEngine = (ScriptEngine)engine.Evaluate(type.Name);
innerEngine.Script.value = value;
Assert.AreSame(value, engine.Evaluate(type.Name + ".Script.value"));
});
}
[TestMethod, TestCategory("CrossEngine")]
public void CrossEngine_Property_Scalar()
{
const int value = 123;
Iterate(JSOnly, All, (engine, type) =>
{
engine.Script.value = value;
Assert.AreEqual(value, engine.Evaluate(type.Name + ".Script.value = value"));
Assert.AreEqual(value, engine.Evaluate(type.Name + ".Script.value"));
});
Iterate(VBSOnly, All, (engine, type) =>
{
var innerEngine = (ScriptEngine)engine.Evaluate(type.Name);
innerEngine.Script.value = value;
Assert.AreEqual(value, engine.Evaluate(type.Name + ".Script.value"));
});
}
[TestMethod, TestCategory("CrossEngine")]
public void CrossEngine_Property_Enum()
{
const DayOfWeek value = DayOfWeek.Thursday;
Iterate(JSOnly, All, (engine, type) =>
{
engine.Script.value = value;
Assert.AreEqual(value, engine.Evaluate(type.Name + ".Script.value = value"));
Assert.AreEqual(value, engine.Evaluate(type.Name + ".Script.value"));
});
Iterate(VBSOnly, All, (engine, type) =>
{
var innerEngine = (ScriptEngine)engine.Evaluate(type.Name);
innerEngine.Script.value = value;
Assert.AreEqual(value, engine.Evaluate(type.Name + ".Script.value"));
});
}
[TestMethod, TestCategory("CrossEngine")]
public void CrossEngine_Property_Struct()
{
var value = new DateTime(2007, 5, 22, 6, 15, 43);
Iterate(JSOnly, All, (engine, type) =>
{
engine.Script.value = value;
Assert.AreEqual(value, engine.Evaluate(type.Name + ".Script.value = value"));
Assert.AreEqual(value, engine.Evaluate(type.Name + ".Script.value"));
});
Iterate(VBSOnly, All, (engine, type) =>
{
var innerEngine = (ScriptEngine)engine.Evaluate(type.Name);
innerEngine.Script.value = value;
Assert.AreEqual(value, engine.Evaluate(type.Name + ".Script.value"));
});
}
[TestMethod, TestCategory("CrossEngine")]
public void CrossEngine_Property_Index_ArrayItem()
{
Iterate(JSOnly, JSOnly, (engine, type) =>
{
engine.Execute(type.Name + ".Execute('foo = []')");
Assert.AreEqual(Math.PI, engine.Evaluate(type.Name + ".Script.foo[4] = Math.PI"));
Assert.AreEqual(Math.PI, engine.Evaluate(type.Name + ".Script.foo[4]"));
Assert.AreEqual(5, engine.Evaluate(type.Name + ".Evaluate('foo.length')"));
});
}
[TestMethod, TestCategory("CrossEngine")]
public void CrossEngine_Property_Index_Property()
{
Iterate(JSOnly, JSOnly, (engine, type) =>
{
engine.Execute(type.Name + ".Execute('foo = {}')");
Assert.AreEqual(Math.E, engine.Evaluate(type.Name + ".Script.foo['bar'] = Math.E"));
Assert.AreEqual(Math.E, engine.Evaluate(type.Name + ".Script.foo['bar']"));
Assert.AreEqual(Math.E, engine.Evaluate(type.Name + ".Script.foo.bar"));
});
}
[TestMethod, TestCategory("CrossEngine")]
public void CrossEngine_Property_Method()
{
// WORKAROUND: Windows Script engines apparently refuse to evaluate or execute
// non-enumerable external functions. This affects external script built-ins.
// The workaround is to invoke the built-in from a custom function.
Iterate(All, JSOnly, (engine, type) =>
{
engine.Execute(type.Name + ".Execute(\"function doEval(expr) { return eval(expr); }\")");
Assert.AreEqual(Math.E * Math.PI, engine.Evaluate(type.Name + ".Script.doEval(\"Math.E * Math.PI\")"));
});
Iterate(All, VBSOnly, (engine, type) =>
{
engine.Execute(type.Name + ".Execute(\"function doEval(expr) : doEval = eval(expr): end function\")");
Assert.AreEqual(Math.E * Math.PI, engine.Evaluate(type.Name + ".Script.doEval(\"4 * atn(1) * exp(1)\")"));
});
}
// ReSharper restore InconsistentNaming
#endregion
#region miscellaneous
private void Iterate(Action