|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using System;
using System.Dynamic;
using System.Linq;
using Microsoft.CodeAnalysis.ExpressionEvaluator;
using Microsoft.VisualStudio.Debugger.Clr;
using Microsoft.VisualStudio.Debugger.Evaluation;
using Microsoft.VisualStudio.Debugger.Evaluation.ClrCompilation;
using Xunit;
using Roslyn.Test.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.UnitTests
{
public class DynamicViewTests : CSharpResultProviderTestBase
{
[Fact]
public void MultipleMembers()
{
var expression = "o";
dynamic o = new ExpandoObject();
o.Philosophers = new object[] { "Pythagoras", "Lucretius", "Zeno" };
o.WhatsForDinner = "Crab Cakes";
o.NumForks = 2;
var type = new DkmClrType((TypeImpl)o.GetType());
var value = CreateDkmClrValue((object)o, type);
var result = FormatResult(expression, value);
Verify(result,
EvalResult(expression, "{System.Dynamic.ExpandoObject}", "System.Dynamic.ExpandoObject", expression, DkmEvaluationResultFlags.Expandable));
var dynamicView = GetChildren(result).Last();
Verify(dynamicView,
EvalResult(Resources.DynamicView, Resources.DynamicViewValueWarning, "", "o, dynamic", DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly));
Verify(GetChildren(dynamicView),
EvalResult("NumForks", "2", "System.Int32", "new Microsoft.CSharp.RuntimeBinder.DynamicMetaObjectProviderDebugView(o).Items[0]", DkmEvaluationResultFlags.ReadOnly),
EvalResult("Philosophers", "{object[3]}", "System.Object[]", "new Microsoft.CSharp.RuntimeBinder.DynamicMetaObjectProviderDebugView(o).Items[1]", DkmEvaluationResultFlags.ReadOnly),
EvalResult("WhatsForDinner", "\"Crab Cakes\"", "System.String", "new Microsoft.CSharp.RuntimeBinder.DynamicMetaObjectProviderDebugView(o).Items[2]", DkmEvaluationResultFlags.ReadOnly));
}
[Fact]
public void MultipleExpansions()
{
var expression = "o";
dynamic o = new ExpandoObject();
o.Answer = 42;
var type = new DkmClrType((TypeImpl)o.GetType());
var value = CreateDkmClrValue((object)o, type);
// Dynamic View should appear after all other expansions.
var result = FormatResult(expression, value);
Verify(result,
EvalResult(expression, "{System.Dynamic.ExpandoObject}", "System.Dynamic.ExpandoObject", expression, DkmEvaluationResultFlags.Expandable));
Verify(GetChildren(result),
EvalResult("Class", "{System.Dynamic.ExpandoClass}", "System.Dynamic.ExpandoClass", "o.Class", DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly | DkmEvaluationResultFlags.CanFavorite, DkmEvaluationResultCategory.Property, DkmEvaluationResultAccessType.Internal),
EvalResult("LockObject", "{object}", "object", "o.LockObject", DkmEvaluationResultFlags.ReadOnly | DkmEvaluationResultFlags.CanFavorite, DkmEvaluationResultCategory.Data, DkmEvaluationResultAccessType.Internal),
EvalResult("System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<string, object>>.Count", "1", "int", "((System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<string, object>>)o).Count", DkmEvaluationResultFlags.ReadOnly, DkmEvaluationResultCategory.Property, DkmEvaluationResultAccessType.Private),
EvalResult("System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<string, object>>.IsReadOnly", "false", "bool", "((System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<string, object>>)o).IsReadOnly", DkmEvaluationResultFlags.Boolean | DkmEvaluationResultFlags.ReadOnly, DkmEvaluationResultCategory.Property, DkmEvaluationResultAccessType.Private),
EvalResult("System.Collections.Generic.IDictionary<string, object>.Keys", "Count = 1", "System.Collections.Generic.ICollection<string> {System.Dynamic.ExpandoObject.KeyCollection}", "((System.Collections.Generic.IDictionary<string, object>)o).Keys", DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly, DkmEvaluationResultCategory.Property, DkmEvaluationResultAccessType.Private),
EvalResult("System.Collections.Generic.IDictionary<string, object>.Values", "Count = 1", "System.Collections.Generic.ICollection<object> {System.Dynamic.ExpandoObject.ValueCollection}", "((System.Collections.Generic.IDictionary<string, object>)o).Values", DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly, DkmEvaluationResultCategory.Property, DkmEvaluationResultAccessType.Private),
EvalResult("_count", "1", "int", "o._count", DkmEvaluationResultFlags.CanFavorite, category: DkmEvaluationResultCategory.Data, access: DkmEvaluationResultAccessType.Private),
EvalResult("_data", "{System.Dynamic.ExpandoObject.ExpandoData}", "System.Dynamic.ExpandoObject.ExpandoData", "o._data", DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.CanFavorite, DkmEvaluationResultCategory.Data, DkmEvaluationResultAccessType.Private),
EvalResult("_propertyChanged", "null", "System.ComponentModel.PropertyChangedEventHandler", "o._propertyChanged", DkmEvaluationResultFlags.CanFavorite, category: DkmEvaluationResultCategory.Data, access: DkmEvaluationResultAccessType.Private),
EvalResult(Resources.StaticMembers, null, "", "System.Dynamic.ExpandoObject", DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly, DkmEvaluationResultCategory.Class),
EvalResult(Resources.DynamicView, Resources.DynamicViewValueWarning, "", "o, dynamic", DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly, DkmEvaluationResultCategory.Method));
}
[Fact]
public void ExceptionTypeMember()
{
var expression = "o";
dynamic o = new ExpandoObject();
var exception = new NotImplementedException();
o.Member = exception;
var type = new DkmClrType((TypeImpl)o.GetType());
var value = CreateDkmClrValue((object)o, type);
var result = FormatResult(expression, value);
Verify(result,
EvalResult(expression, "{System.Dynamic.ExpandoObject}", "System.Dynamic.ExpandoObject", expression, DkmEvaluationResultFlags.Expandable));
var dynamicView = GetChildren(result).Last();
Verify(dynamicView,
EvalResult(Resources.DynamicView, Resources.DynamicViewValueWarning, "", "o, dynamic", DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly));
Verify(GetChildren(dynamicView),
EvalResult("Member", $"{{{exception.ToString()}}}", "System.NotImplementedException", "new Microsoft.CSharp.RuntimeBinder.DynamicMetaObjectProviderDebugView(o).Items[0]", DkmEvaluationResultFlags.ReadOnly));
}
[Fact]
public void DynamicTypeMember()
{
var expression = "o";
dynamic o = new ExpandoObject();
o.Pi = Math.PI;
o.OnAndOn = o;
var type = new DkmClrType((TypeImpl)o.GetType());
var value = CreateDkmClrValue((object)o, type);
var result = FormatResult(expression, value);
Verify(result,
EvalResult(expression, "{System.Dynamic.ExpandoObject}", "System.Dynamic.ExpandoObject", expression, DkmEvaluationResultFlags.Expandable));
var members = GetChildren(result);
var fullNameOnAndOn = "o";
var fullNamePi = "o";
// Expand 3 levels...
for (var i = 0; i < 3; i++)
{
var dynamicView = members.Last();
Verify(dynamicView,
EvalResult(Resources.DynamicView, Resources.DynamicViewValueWarning, "", $"{fullNameOnAndOn}, dynamic", DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly));
members = GetChildren(dynamicView);
fullNamePi = $"new Microsoft.CSharp.RuntimeBinder.DynamicMetaObjectProviderDebugView({fullNameOnAndOn}).Items[1]";
fullNameOnAndOn = $"new Microsoft.CSharp.RuntimeBinder.DynamicMetaObjectProviderDebugView({fullNameOnAndOn}).Items[0]";
Verify(members,
EvalResult("OnAndOn", "{System.Dynamic.ExpandoObject}", "System.Dynamic.ExpandoObject", fullNameOnAndOn, DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly),
EvalResult("Pi", "3.1415926535897931", "System.Double", fullNamePi, DkmEvaluationResultFlags.ReadOnly));
members = GetChildren(members[0]);
}
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/5666")]
public void NoMembers()
{
using (new EnsureEnglishUICulture())
{
var expression = "o";
dynamic o = new ExpandoObject();
var type = new DkmClrType((TypeImpl)o.GetType());
var value = CreateDkmClrValue((object)o, type);
var result = FormatResult(expression, value);
Verify(result,
EvalResult(expression, "{System.Dynamic.ExpandoObject}", "System.Dynamic.ExpandoObject", expression, DkmEvaluationResultFlags.Expandable));
var dynamicView = GetChildren(result).Last();
Verify(dynamicView,
EvalResult(Resources.DynamicView, Resources.DynamicViewValueWarning, "", "o, dynamic", DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly));
Verify(GetChildren(dynamicView),
EvalFailedResult(Resources.ErrorName, GetDynamicDebugViewEmptyMessage()));
}
}
[Fact]
public void NullComObject()
{
var comObjectTypeName = "System.__ComObject";
var expression = $"({comObjectTypeName})null";
var type = new DkmClrType((TypeImpl)typeof(object).Assembly.GetType(comObjectTypeName));
var value = CreateDkmClrValue(null, type);
var result = FormatResult(expression, value);
Verify(result,
EvalResult(expression, "null", comObjectTypeName, expression));
result = FormatResult(expression, expression + ",dynamic", value, inspectionContext: CreateDkmInspectionContext(DkmEvaluationFlags.DynamicView));
Verify(result,
EvalFailedResult(expression, Resources.DynamicViewNotDynamic));
}
[Fact]
public void NullIDynamicMetaObjectProvider()
{
var expression = "o";
var type = new DkmClrType((TypeImpl)typeof(IDynamicMetaObjectProvider));
var value = CreateDkmClrValue(null, type);
var result = FormatResult(expression, value);
Verify(result,
EvalResult(expression, "null", "System.Dynamic.IDynamicMetaObjectProvider", expression));
result = FormatResult(expression, expression + ",dynamic", value, inspectionContext: CreateDkmInspectionContext(DkmEvaluationFlags.DynamicView));
Verify(result,
EvalFailedResult(expression, Resources.DynamicViewNotDynamic));
}
[Fact]
public void NullDynamicObject()
{
var expression = "o";
var type = new DkmClrType((TypeImpl)typeof(ExpandoObject));
var value = CreateDkmClrValue(null, type);
var result = FormatResult(expression, value);
Verify(result,
EvalResult(expression, "null", "System.Dynamic.ExpandoObject", expression, DkmEvaluationResultFlags.Expandable));
Verify(GetChildren(result),
EvalResult(Resources.StaticMembers, null, "", "System.Dynamic.ExpandoObject", DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly, DkmEvaluationResultCategory.Class));
result = FormatResult(expression, expression + ",dynamic", value, inspectionContext: CreateDkmInspectionContext(DkmEvaluationFlags.DynamicView));
Verify(result,
EvalFailedResult(expression, Resources.DynamicViewNotDynamic));
}
[Fact]
public void DynamicTypeError()
{
var expression = "o";
var obj = new ExpandoObject();
// Verify that things *work* in this scenario if there was no error in member access.
var value = CreateDkmClrValue(obj);
var fullName = expression + ", dynamic";
var result = FormatResult(expression, fullName, value, inspectionContext: CreateDkmInspectionContext(DkmEvaluationFlags.DynamicView));
Verify(result,
EvalResult(expression, Resources.DynamicViewValueWarning, "", fullName, DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly));
// Verify no Dynamic View if member access is changed to result in an error.
var runtime = new DkmClrRuntimeInstance(ReflectionUtilities.GetMscorlibAndSystemCore());
value = CreateErrorValue(runtime.GetType(obj.GetType()), "Function evaluation timed out");
result = FormatResult(expression, fullName, value, inspectionContext: CreateDkmInspectionContext(DkmEvaluationFlags.DynamicView));
Verify(result,
EvalFailedResult(expression, Resources.DynamicViewNotDynamic));
}
[Fact]
public void DynamicMetaObjectProviderDebugViewItemsError()
{
var expression = "o";
dynamic o = new ExpandoObject();
o.Answer = 42;
DkmClrRuntimeInstance runtime = null;
runtime = new DkmClrRuntimeInstance(ReflectionUtilities.GetMscorlib(),
getMemberValue: (_, m) => (m == "Items") ? CreateErrorValue(runtime.GetType(typeof(Array)), "Function evaluation timed out") : null);
var type = new DkmClrType(runtime, (TypeImpl)o.GetType());
var value = CreateDkmClrValue((object)o, type);
var fullName = expression + ", dynamic";
var result = FormatResult(expression, fullName, value, inspectionContext: CreateDkmInspectionContext(DkmEvaluationFlags.DynamicView));
Verify(result,
EvalResult(expression, Resources.DynamicViewValueWarning, "", fullName, DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly));
Verify(GetChildren(result),
EvalFailedResult(Resources.ErrorName, "Function evaluation timed out"));
}
[Fact]
public void DynamicMetaObjectProviderDebugViewItemsException()
{
var expression = "o";
var fullName = expression + ", dynamic";
dynamic o = new ExpandoObject();
o.Answer = 42;
DkmClrRuntimeInstance runtime = null;
Func<DkmClrValue> getExceptionValue = () => CreateDkmClrValue(new NotImplementedException(), evalFlags: DkmEvaluationResultFlags.ExceptionThrown);
runtime = new DkmClrRuntimeInstance(ReflectionUtilities.GetMscorlib(), getMemberValue: (_, m) => (m == "Items") ? getExceptionValue() : null);
var type = new DkmClrType(runtime, (TypeImpl)o.GetType());
var value = CreateDkmClrValue((object)o, type);
var result = FormatResult(expression, fullName, value, inspectionContext: CreateDkmInspectionContext(DkmEvaluationFlags.DynamicView));
Verify(result,
EvalResult(expression, Resources.DynamicViewValueWarning, "", fullName, DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly));
var members = GetChildren(result);
Assert.Equal(32, members.Length);
Verify(members[1],
EvalResult("HResult", "-2147467263", "int", null, category: DkmEvaluationResultCategory.Property, access: DkmEvaluationResultAccessType.Public));
getExceptionValue = () => CreateDkmClrValue(new NotImplementedException());
result = FormatResult(expression, fullName, value, inspectionContext: CreateDkmInspectionContext(DkmEvaluationFlags.DynamicView));
Verify(result,
EvalResult(expression, Resources.DynamicViewValueWarning, "", fullName, DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly));
members = GetChildren(result);
Assert.Equal(32, members.Length);
Verify(members[1],
EvalResult("HResult", "-2147467263", "int", "((System.Exception)new Microsoft.CSharp.RuntimeBinder.DynamicMetaObjectProviderDebugView(o).Items).HResult", category: DkmEvaluationResultCategory.Property, access: DkmEvaluationResultAccessType.Public));
}
[Fact]
public void DynamicFormatSpecifier()
{
var expression = "o";
dynamic o = new ExpandoObject();
o.Answer = 42;
var type = new DkmClrType((TypeImpl)o.GetType());
var value = CreateDkmClrValue((object)o, type);
var fullName = expression + ", dynamic";
var result = FormatResult(expression, fullName, value, inspectionContext: CreateDkmInspectionContext(DkmEvaluationFlags.DynamicView));
Verify(result,
EvalResult(expression, Resources.DynamicViewValueWarning, "", fullName, DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.ReadOnly));
Verify(GetChildren(result),
EvalResult("Answer", "42", "System.Int32", "new Microsoft.CSharp.RuntimeBinder.DynamicMetaObjectProviderDebugView(o).Items[0]", DkmEvaluationResultFlags.ReadOnly));
}
[Fact]
public void DynamicFormatSpecifierError()
{
var expression = "o";
var o = new Object();
var type = new DkmClrType((TypeImpl)o.GetType());
var value = CreateDkmClrValue(o, type);
var result = FormatResult(expression, expression + ",dynamic", value, inspectionContext: CreateDkmInspectionContext(DkmEvaluationFlags.DynamicView));
Verify(result,
EvalFailedResult(expression, Resources.DynamicViewNotDynamic));
}
}
}
|