File: ObjectModelRemoting\Helpers\ViewValidation.evaluation.cs
Web Access
Project: ..\..\..\src\Build.OM.UnitTests\Microsoft.Build.Engine.OM.UnitTests.csproj (Microsoft.Build.Engine.OM.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#nullable disable
 
namespace Microsoft.Build.UnitTests.OM.ObjectModelRemoting
{
    using System.Collections.Generic;
    using System.Linq;
    using Microsoft.Build.Evaluation;
    using Microsoft.Build.Framework;
    using Xunit;
 
    internal sealed class ProjectPair : LinkPair<Project>
    {
        public ProjectPair(Project view, Project real)
            : base(view, real)
        {
        }
 
        public void ValidatePropertyValue(string name, string value)
        {
            Assert.Equal(value, this.View.GetPropertyValue(name));
            Assert.Equal(value, this.Real.GetPropertyValue(name));
        }
 
        private ProjectItem VerifyAfterAddSingleItem(ObjectType where, ICollection<ProjectItem> added, IEnumerable<KeyValuePair<string, string>> metadata)
        {
            Assert.NotNull(added);
            Assert.Equal(1, added.Count);
            var result = added.First();
            Assert.NotNull(result);
 
            // validate there is exactly 1 item with this include in both view and real and it is the exact same object.
            Assert.Same(result, this.GetSingleItemWithVerify(where, result.EvaluatedInclude));
 
 
            if (metadata != null)
            {
                foreach (var m in metadata)
                {
                    Assert.True(result.HasMetadata(m.Key));
                    var md = result.GetMetadata(m.Key);
                    Assert.NotNull(md);
                    Assert.Equal(m.Value, md.UnevaluatedValue);
                }
            }
 
            return result;
        }
 
        public ProjectItem AddSingleItemWithVerify(ObjectType where, string itemType, string unevaluatedInclude, IEnumerable<KeyValuePair<string, string>> metadata = null)
        {
            var toAdd = this.Get(where);
            var added = (metadata == null) ? toAdd.AddItem(itemType, unevaluatedInclude) : toAdd.AddItem(itemType, unevaluatedInclude, metadata);
            return VerifyAfterAddSingleItem(where, added, metadata);
        }
 
        public ProjectItem AddSingleItemFastWithVerify(ObjectType where, string itemType, string unevaluatedInclude, IEnumerable<KeyValuePair<string, string>> metadata = null)
        {
            var toAdd = this.Get(where);
            var added = (metadata == null) ? toAdd.AddItemFast(itemType, unevaluatedInclude) : toAdd.AddItemFast(itemType, unevaluatedInclude, metadata);
            return VerifyAfterAddSingleItem(where, added, metadata);
        }
 
        public ProjectItem GetSingleItemWithVerify(ObjectType which, string evaluatedInclude)
        {
            var realItems = this.Real.GetItemsByEvaluatedInclude(evaluatedInclude);
            var viewItems = this.View.GetItemsByEvaluatedInclude(evaluatedInclude);
 
            ViewValidation.Verify(viewItems, realItems, ViewValidation.Verify, new ValidationContext(this));
            if (viewItems == null || viewItems.Count == 0)
            {
                return null;
            }
 
            Assert.Equal(1, viewItems.Count);
            return which == ObjectType.View ? viewItems.First() : realItems.First();
        }
 
        public ProjectProperty SetPropertyWithVerify(ObjectType where, string name, string unevaluatedValue)
        {
            var toAdd = this.Get(where);
            var added = toAdd.SetProperty(name, unevaluatedValue);
            Assert.NotNull(added);
            Assert.Same(added, toAdd.GetProperty(name));
            Assert.Equal(unevaluatedValue, added.UnevaluatedValue);
 
            var view = this.View.GetProperty(name);
            var real = this.Real.GetProperty(name);
            ViewValidation.Verify(view, real, new ValidationContext(this));
 
            return added;
        }
    }
 
    internal static partial class ViewValidation
    {
        public static void Verify(ProjectProperty view, ProjectProperty real, ValidationContext context)
        {
            if (view == null && real == null)
            {
                return;
            }
 
            VerifyLinkedNotNull(view);
            VerifyNotLinkedNotNull(real);
 
            Assert.Equal(real.Name, view.Name);
            Assert.Equal(real.EvaluatedValue, view.EvaluatedValue);
            Assert.Equal(real.UnevaluatedValue, view.UnevaluatedValue);
            Assert.Equal(real.IsEnvironmentProperty, view.IsEnvironmentProperty);
            Assert.Equal(real.IsGlobalProperty, view.IsGlobalProperty);
            Assert.Equal(real.IsReservedProperty, view.IsReservedProperty);
            Assert.Equal(real.IsImported, view.IsImported);
 
            Verify(view.Xml, real.Xml);
 
            VerifyLinkedNotNull(view.Project);
            VerifyNotLinkedNotNull(real.Project);
            if (context?.Pair != null)
            {
                Assert.Same(context.Pair.View, view.Project);
                Assert.Same(context.Pair.Real, real.Project);
            }
 
            Verify(view.Predecessor, real.Predecessor, context);
        }
 
        // public static void Verify(ProjectMetadata view, ProjectMetadata real) => Verify(view, real, null);
        public static void Verify(ProjectMetadata view, ProjectMetadata real, ValidationContext context)
        {
            if (view == null && real == null)
            {
                return;
            }
 
            VerifyLinkedNotNull(view);
            VerifyNotLinkedNotNull(real);
 
            Assert.Equal(real.Name, view.Name);
            Assert.Equal(real.EvaluatedValue, view.EvaluatedValue);
            Assert.Equal(real.UnevaluatedValue, view.UnevaluatedValue);
            Assert.Equal(real.ItemType, view.ItemType);
            Assert.Equal(real.IsImported, view.IsImported);
 
            VerifySameLocation(real.Location, view.Location);
            VerifySameLocation(real.ConditionLocation, view.ConditionLocation);
 
            Verify(view.Xml, real.Xml);
 
            VerifyLinkedNotNull(view.Project);
            VerifyNotLinkedNotNull(real.Project);
            if (context?.Pair != null)
            {
                Assert.Same(context?.Pair.View, view.Project);
                Assert.Same(context?.Pair.Real, real.Project);
            }
 
            Verify(view.Predecessor, real.Predecessor, context);
        }
 
        // public static void Verify(ProjectItemDefinition view, ProjectItemDefinition real) => Verify(view, real, null);
        public static void Verify(ProjectItemDefinition view, ProjectItemDefinition real, ValidationContext context)
        {
            if (view == null && real == null)
            {
                return;
            }
 
            VerifyLinkedNotNull(view);
            VerifyNotLinkedNotNull(real);
 
            // note ItemDefinition does not have a XML element
            // this is since it is [or can be] a aggregation of multiple ProjectItemDefinitionElement's.
            // This is somewhat of deficiency of MSBuild API.
            // (for example SetMetadata will always create a new ItemDefinitionElement because of that, for new metadata).
 
            Assert.Equal(real.ItemType, view.ItemType);
            Assert.Equal(real.MetadataCount, view.MetadataCount);
 
            Verify(view.Metadata, real.Metadata, Verify, context);
 
            foreach (var rm in real.Metadata)
            {
                var rv = real.GetMetadataValue(rm.Name);
                var vv = view.GetMetadataValue(rm.Name);
                Assert.Equal(rv, vv);
 
                var grm = real.GetMetadata(rm.Name);
                var gvm = view.GetMetadata(rm.Name);
 
                Verify(gvm, grm, context);
            }
 
            VerifyLinkedNotNull(view.Project);
            VerifyNotLinkedNotNull(real.Project);
            if (context?.Pair != null)
            {
                Assert.Same(context?.Pair.View, view.Project);
                Assert.Same(context?.Pair.Real, real.Project);
            }
        }
 
        // public static void Verify(ProjectItem view, ProjectItem real) => Verify(view, real, null);
        public static void Verify(ProjectItem view, ProjectItem real, ValidationContext context = null)
        {
            if (view == null && real == null)
            {
                return;
            }
 
            VerifyLinkedNotNull(view);
            VerifyNotLinkedNotNull(real);
 
            Verify(view.Xml, real.Xml);
 
            Assert.Equal(real.ItemType, view.ItemType);
            Assert.Equal(real.UnevaluatedInclude, view.UnevaluatedInclude);
            Assert.Equal(real.EvaluatedInclude, view.EvaluatedInclude);
            Assert.Equal(real.IsImported, view.IsImported);
 
            Assert.Equal(real.DirectMetadataCount, view.DirectMetadataCount);
            Verify(view.DirectMetadata, real.DirectMetadata, Verify, context);
 
            Assert.Equal(real.MetadataCount, view.MetadataCount);
            Verify(view.Metadata, real.Metadata, Verify, context);
 
            foreach (var rm in real.Metadata)
            {
                var rv = real.GetMetadataValue(rm.Name);
                var vv = view.GetMetadataValue(rm.Name);
                Assert.Equal(rv, vv);
 
                var grm = real.GetMetadata(rm.Name);
                var gvm = view.GetMetadata(rm.Name);
 
                Verify(gvm, grm, context);
 
                Assert.Equal(real.HasMetadata(rm.Name), view.HasMetadata(rm.Name));
            }
 
            Assert.Equal(real.HasMetadata("random non existent"), view.HasMetadata("random non existent"));
            Assert.Equal(real.GetMetadataValue("random non existent"), view.GetMetadataValue("random non existent"));
 
            VerifyLinkedNotNull(view.Project);
            VerifyNotLinkedNotNull(real.Project);
            if (context?.Pair != null)
            {
                Assert.Same(context.Pair.View, view.Project);
                Assert.Same(context.Pair.Real, real.Project);
            }
        }
 
 
        private static void Verify(SdkReference view, SdkReference real, ValidationContext context = null)
        {
            if (view == null && real == null)
            {
                return;
            }
 
            Assert.NotNull(view);
            Assert.NotNull(real);
 
            Assert.Equal(real.Name, view.Name);
            Assert.Equal(real.Version, view.Version);
            Assert.Equal(real.MinimumVersion, view.MinimumVersion);
        }
 
        private static void Verify(SdkResult view, SdkResult real, ValidationContext context = null)
        {
            if (view == null && real == null)
            {
                return;
            }
 
            Assert.NotNull(view);
            Assert.NotNull(real);
            Assert.Equal(real.Success, view.Success);
            Assert.Equal(real.Path, view.Path);
            Verify(view.SdkReference, real.SdkReference, context);
        }
 
        private static void Verify(ResolvedImport view, ResolvedImport real, ValidationContext context = null)
        {
            Assert.Equal(real.IsImported, view.IsImported);
            Verify(view.ImportingElement, real.ImportingElement);
            Verify(view.ImportedProject, real.ImportedProject);
            Verify(view.SdkResult, real.SdkResult, context);
        }
 
        private static void Verify(List<string> viewProps, List<string> realProps, ValidationContext context = null)
        {
            if (viewProps == null && realProps == null)
            {
                return;
            }
 
            Assert.NotNull(viewProps);
            Assert.NotNull(realProps);
            Assert.Equal(realProps.Count, viewProps.Count);
 
            for (int i = 0; i < realProps.Count; i++)
            {
                Assert.Equal(realProps[i], viewProps[i]);
            }
        }
 
        public static void Verify(Project view, Project real, ValidationContext context = null)
        {
            if (view == null && real == null)
            {
                return;
            }
 
            var pair = new ProjectPair(view, real);
            Verify(pair, context);
        }
 
        public static void Verify(ProjectPair pair, ValidationContext context = null)
        {
            if (pair == null)
            {
                return;
            }
 
            var real = pair.Real;
            var view = pair.View;
            context ??= new ValidationContext();
            context.Pair = pair;
 
 
            Verify(view.Xml, real.Xml);
 
            Verify(view.ItemsIgnoringCondition, real.ItemsIgnoringCondition, Verify, context);
            Verify(view.Items, real.Items, Verify, context);
            Verify(view.ItemDefinitions, real.ItemDefinitions, Verify, context);
            Verify(view.ConditionedProperties, real.ConditionedProperties, Verify, context);
            Verify(view.Properties, real.Properties, Verify, context);
            Verify(view.GlobalProperties, real.GlobalProperties, (a, b, p) => Assert.Equal(b, a), context);
            Verify(view.Imports, real.Imports, Verify, context);
            Verify(view.ItemTypes, real.ItemTypes, (a, b, p) => Assert.Equal(b, a), context);
 
            // this can only be used if project is loaded with ProjectLoadSettings.RecordDuplicateButNotCircularImports
            // or it throws otherwise. Slightly odd and inconvenient API design, but thats how it is.
            bool isImportsIncludingDuplicatesAvailable = false;
            try
            {
                var testLoadSettings = real.ImportsIncludingDuplicates;
                isImportsIncludingDuplicatesAvailable = true;
            }
            catch { }
 
            if (isImportsIncludingDuplicatesAvailable)
            {
                Verify(view.ImportsIncludingDuplicates, real.ImportsIncludingDuplicates, Verify, context);
            }
 
            Verify(view.AllEvaluatedProperties, real.AllEvaluatedProperties, Verify, context);
            Verify(view.AllEvaluatedItemDefinitionMetadata, real.AllEvaluatedItemDefinitionMetadata, Verify, context);
            Verify(view.AllEvaluatedItems, real.AllEvaluatedItems, Verify, context);
 
            Assert.NotSame(view.ProjectCollection, real.ProjectCollection);
            Assert.Equal(real.ToolsVersion, view.ToolsVersion);
            Assert.Equal(real.SubToolsetVersion, view.SubToolsetVersion);
            Assert.Equal(real.DirectoryPath, view.DirectoryPath);
            Assert.Equal(real.FullPath, view.FullPath);
            Assert.Equal(real.SkipEvaluation, view.SkipEvaluation);
            Assert.Equal(real.ThrowInsteadOfSplittingItemElement, view.ThrowInsteadOfSplittingItemElement);
            Assert.Equal(real.IsDirty, view.IsDirty);
            Assert.Equal(real.DisableMarkDirty, view.DisableMarkDirty);
            Assert.Equal(real.IsBuildEnabled, view.IsBuildEnabled);
 
            VerifySameLocation(real.ProjectFileLocation, view.ProjectFileLocation, context);
 
            // we currently dont support "Execution" remoting.
            // Verify(view.Targets, real.Targets, Verify, view, real);
            Assert.Equal(real.EvaluationCounter, view.EvaluationCounter);
            Assert.Equal(real.LastEvaluationId, view.LastEvaluationId);
        }
    }
}