// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.CSharp.RuntimeBinder;
using Microsoft.ClearScript.V8;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.ClearScript.Test
{
[TestClass]
[DeploymentItem("ClearScriptV8-64.dll")]
[DeploymentItem("ClearScriptV8-32.dll")]
[DeploymentItem("v8-x64.dll")]
[DeploymentItem("v8-ia32.dll")]
[SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "Test classes use TestCleanupAttribute for deterministic teardown.")]
public class MemberAccessTest : ClearScriptTest
{
#region setup / teardown
private ScriptEngine engine;
private TestObject testObject;
[TestInitialize]
public void TestInitialize()
{
engine = new V8ScriptEngine(V8ScriptEngineFlags.EnableDebugging);
engine.AddHostObject("host", new ExtendedHostFunctions());
engine.AddHostObject("mscorlib", HostItemFlags.GlobalMembers, new HostTypeCollection("mscorlib"));
engine.AddHostObject("ClearScriptTest", HostItemFlags.GlobalMembers, new HostTypeCollection("ClearScriptTest").GetNamespaceNode("Microsoft.ClearScript.Test"));
engine.AddHostObject("testObject", testObject = new TestObject());
}
[TestCleanup]
public void TestCleanup()
{
testObject = null;
engine.Dispose();
BaseTestCleanup();
}
#endregion
#region test methods
// ReSharper disable InconsistentNaming
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Field()
{
testObject.Field = Enumerable.Range(0, 10).ToArray();
Assert.AreEqual(10, engine.Evaluate("testObject.Field.Length"));
engine.Execute("testObject.Field = host.newArr(System.Int32, 5)");
Assert.AreEqual(5, testObject.Field.Length);
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Field_Null()
{
engine.Execute("testObject.Field = null");
Assert.IsNull(testObject.Field);
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Field_BadAssignment()
{
TestUtil.AssertException(() => engine.Execute("testObject.Field = host.newArr(System.Double, 5)"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Field_Scalar()
{
testObject.ScalarField = 12345;
Assert.AreEqual(12345, engine.Evaluate("testObject.ScalarField"));
engine.Execute("testObject.ScalarField = 4321");
Assert.AreEqual(4321, testObject.ScalarField);
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Field_Scalar_Overflow()
{
TestUtil.AssertException(() => engine.Execute("testObject.ScalarField = 54321"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Field_Scalar_BadAssignment()
{
TestUtil.AssertException(() => engine.Execute("testObject.ScalarField = TestEnum.Second"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Field_Enum()
{
testObject.EnumField = TestEnum.Second;
Assert.AreEqual(TestEnum.Second, engine.Evaluate("testObject.EnumField"));
engine.Execute("testObject.EnumField = TestEnum.Third");
Assert.AreEqual(TestEnum.Third, testObject.EnumField);
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Field_Enum_Zero()
{
engine.Execute("testObject.EnumField = 0");
Assert.AreEqual((TestEnum)0, testObject.EnumField);
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Field_Enum_BadAssignment()
{
TestUtil.AssertException(() => engine.Execute("testObject.EnumField = 1"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Field_Struct()
{
testObject.StructField = TimeSpan.FromDays(5);
Assert.AreEqual(TimeSpan.FromDays(5), engine.Evaluate("testObject.StructField"));
engine.Execute("testObject.StructField = System.TimeSpan.FromSeconds(25)");
Assert.AreEqual(TimeSpan.FromSeconds(25), testObject.StructField);
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Field_Struct_BadAssignment()
{
TestUtil.AssertException(() => engine.Execute("testObject.StructField = System.DateTime.Now"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Property()
{
testObject.Property = Enumerable.Range(0, 10).ToArray();
Assert.AreEqual(10, engine.Evaluate("testObject.Property.Length"));
engine.Execute("testObject.Property = host.newArr(System.Int32, 5)");
Assert.AreEqual(5, testObject.Property.Length);
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Property_Null()
{
engine.Execute("testObject.Property = null");
Assert.IsNull(testObject.Property);
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Property_BadAssignment()
{
TestUtil.AssertException(() => engine.Execute("testObject.Property = host.newArr(System.Double, 5)"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Property_Scalar()
{
testObject.ScalarProperty = 12345;
Assert.AreEqual(12345, engine.Evaluate("testObject.ScalarProperty"));
engine.Execute("testObject.ScalarProperty = 4321");
Assert.AreEqual(4321, testObject.ScalarProperty);
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Property_Scalar_Overflow()
{
TestUtil.AssertException(() => engine.Execute("testObject.ScalarProperty = 54321"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Property_Scalar_BadAssignment()
{
TestUtil.AssertException(() => engine.Execute("testObject.ScalarProperty = TestEnum.Second"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Property_Enum()
{
testObject.EnumProperty = TestEnum.Second;
Assert.AreEqual(TestEnum.Second, engine.Evaluate("testObject.EnumProperty"));
engine.Execute("testObject.EnumProperty = TestEnum.Third");
Assert.AreEqual(TestEnum.Third, testObject.EnumProperty);
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Property_Enum_Zero()
{
engine.Execute("testObject.EnumProperty = 0");
Assert.AreEqual((TestEnum)0, testObject.EnumProperty);
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Property_Enum_BadAssignment()
{
TestUtil.AssertException(() => engine.Execute("testObject.EnumProperty = 1"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Property_Struct()
{
testObject.StructProperty = TimeSpan.FromDays(5);
Assert.AreEqual(TimeSpan.FromDays(5), engine.Evaluate("testObject.StructProperty"));
engine.Execute("testObject.StructProperty = System.TimeSpan.FromSeconds(25)");
Assert.AreEqual(TimeSpan.FromSeconds(25), testObject.StructProperty);
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Property_Struct_BadAssignment()
{
TestUtil.AssertException(() => engine.Execute("testObject.StructProperty = System.DateTime.Now"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Property_Blocked()
{
TestUtil.AssertException(() => engine.Execute("host.proc(0, function () {}).Method"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_ReadOnlyProperty()
{
Assert.AreEqual(testObject.ReadOnlyProperty, (int)engine.Evaluate("testObject.ReadOnlyProperty"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_ReadOnlyProperty_Write()
{
TestUtil.AssertException(() => engine.Execute("testObject.ReadOnlyProperty = 2"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Event()
{
engine.Execute("var connection = testObject.Event.connect(function (sender, args) { sender.ScalarProperty = args.Arg; })");
testObject.FireEvent(5432);
Assert.AreEqual(5432, testObject.ScalarProperty);
engine.Execute("connection.disconnect()");
testObject.FireEvent(2345);
Assert.AreEqual(5432, testObject.ScalarProperty);
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Method()
{
Assert.AreEqual(testObject.Method("foo", 4), engine.Evaluate("testObject.Method('foo', 4)"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Method_NoMatchingOverload()
{
TestUtil.AssertException(() => engine.Execute("testObject.Method('foo', TestEnum.Second)"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Method_Generic()
{
Assert.AreEqual(testObject.Method("foo", 4, TestEnum.Second), engine.Evaluate("testObject.Method('foo', 4, TestEnum.Second)"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Method_Generic_TypeArgConstraintFailure()
{
TestUtil.AssertException(() => engine.Execute("testObject.Method('foo', 4, testObject)"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Method_GenericRedundant()
{
Assert.AreEqual(testObject.Method("foo", 4, TestEnum.Second), engine.Evaluate("testObject.Method(TestEnum, 'foo', 4, TestEnum.Second)"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Method_GenericRedundant_MismatchedTypeArg()
{
TestUtil.AssertException(() => engine.Execute("testObject.Method(System.Int32, 'foo', 4, TestEnum.Second)"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Method_GenericExplicit()
{
Assert.AreEqual(testObject.Method(4), engine.Evaluate("testObject.Method(TestEnum, 4)"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Method_GenericExplicit_MissingTypeArg()
{
TestUtil.AssertException(() => engine.Execute("testObject.Method(4)"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Method_GetType()
{
engine.AllowReflection = true;
Assert.AreEqual(testObject.GetType(), engine.Evaluate("testObject.GetType()"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Method_GetType_Blocked()
{
TestUtil.AssertException(() => engine.Execute("testObject.GetType()"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Method_GetType_Blocked_Exception()
{
TestUtil.AssertException(() => engine.Execute(@"
try {
testObject.GetType();
}
catch (exception)
{
exception.hostException.GetType();
}
"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_Method_GetType_Blocked_Exception_Interface()
{
engine.AddHostType(typeof(_Exception));
TestUtil.AssertException(() => engine.Execute(@"
try {
testObject.GetType();
}
catch (exception)
{
host.cast(_Exception, exception.hostException).GetType();
}
"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_BindTestMethod_BaseClass()
{
var arg = new TestArg() as BaseTestArg;
engine.AddRestrictedHostObject("arg", arg);
Assert.AreEqual(testObject.BindTestMethod(arg), engine.Evaluate("testObject.BindTestMethod(arg)"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_BindTestMethod_Interface()
{
var arg = new TestArg() as ITestArg;
engine.AddRestrictedHostObject("arg", arg);
Assert.AreEqual(testObject.BindTestMethod(arg), engine.Evaluate("testObject.BindTestMethod(arg)"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_ExtensionMethod()
{
Assert.AreEqual(testObject.ExtensionMethod("foo", 4), engine.Evaluate("testObject.ExtensionMethod('foo', 4)"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_ExtensionMethod_NoMatchingOverload()
{
TestUtil.AssertException(() => engine.Execute("testObject.ExtensionMethod('foo', TestEnum.Second)"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_ExtensionMethod_Generic()
{
Assert.AreEqual(testObject.ExtensionMethod("foo", 4, TestEnum.Second), engine.Evaluate("testObject.ExtensionMethod('foo', 4, TestEnum.Second)"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_ExtensionMethod_Generic_TypeArgConstraintFailure()
{
TestUtil.AssertException(() => engine.Execute("testObject.ExtensionMethod('foo', 4, testObject)"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_ExtensionMethod_GenericRedundant()
{
Assert.AreEqual(testObject.ExtensionMethod("foo", 4, TestEnum.Second), engine.Evaluate("testObject.ExtensionMethod(TestEnum, 'foo', 4, TestEnum.Second)"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_ExtensionMethod_GenericRedundant_MismatchedTypeArg()
{
TestUtil.AssertException(() => engine.Execute("testObject.ExtensionMethod(System.Int32, 'foo', 4, TestEnum.Second)"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_ExtensionMethod_GenericExplicit()
{
Assert.AreEqual(testObject.ExtensionMethod(4), engine.Evaluate("testObject.ExtensionMethod(TestEnum, 4)"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_ExtensionMethod_GenericExplicit_MissingTypeArg()
{
TestUtil.AssertException(() => engine.Execute("testObject.ExtensionMethod(4)"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_ExtensionBindTestMethod_BaseClass()
{
var arg = new TestArg() as BaseTestArg;
engine.AddRestrictedHostObject("arg", arg);
Assert.AreEqual(testObject.ExtensionBindTestMethod(arg), engine.Evaluate("testObject.ExtensionBindTestMethod(arg)"));
}
[TestMethod, TestCategory("MemberAccess")]
public void MemberAccess_ExtensionBindTestMethod_Interface()
{
var arg = new TestArg() as ITestArg;
engine.AddRestrictedHostObject("arg", arg);
Assert.AreEqual(testObject.ExtensionBindTestMethod(arg), engine.Evaluate("testObject.ExtensionBindTestMethod(arg)"));
}
// ReSharper restore InconsistentNaming
#endregion
}
}