File: Construction\ElementLocationPublic_Tests.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.
 
using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.Build.Construction;
using Shouldly;
using Xunit;
 
#nullable disable
 
namespace Microsoft.Build.UnitTests.Construction
{
    /// <summary>
    /// Tests for the ElementLocation class
    /// </summary>
    public class ElementLocationPublic_Tests
    {
        /// <summary>
        /// Check that we can get the file name off an element and attribute, even if
        /// it wouldn't normally have got one because the project wasn't
        /// loaded from disk, or has been edited since.
        /// This is really a test of our XmlDocumentWithLocation.
        /// </summary>
        [Fact]
        public void ShouldHaveFilePathLocationEvenIfNotLoadedNorSavedYet()
        {
            ProjectRootElement project = ProjectRootElement.Create();
            project.FullPath = "c:\\x";
            ProjectTargetElement target = project.CreateTargetElement("t");
            target.Outputs = "o";
            project.AppendChild(target);
 
            Assert.Equal(project.FullPath, target.Location.File);
            Assert.Equal(project.FullPath, target.OutputsLocation.File);
        }
 
        /// <summary>
        /// Element location should reflect rename.
        /// This is really a test of our XmlXXXXWithLocation.
        /// </summary>
        [Fact]
        public void XmlLocationReflectsRename()
        {
            ProjectRootElement project = ProjectRootElement.Create();
            project.FullPath = "c:\\x";
            ProjectTargetElement target = project.CreateTargetElement("t");
            target.Outputs = "o";
            project.AppendChild(target);
 
            Assert.Equal(project.FullPath, target.Location.File);
            Assert.Equal(project.FullPath, target.OutputsLocation.File);
 
            project.FullPath = "c:\\y";
 
            Assert.Equal(project.FullPath, target.Location.File);
            Assert.Equal(project.FullPath, target.OutputsLocation.File);
        }
 
        /// <summary>
        /// We should cache ElementLocation objects for perf.
        /// </summary>
        [Fact]
        public void XmlLocationsAreCached()
        {
            ProjectRootElement project = ProjectRootElement.Create();
            project.FullPath = "c:\\x";
            ProjectTargetElement target = project.CreateTargetElement("t");
            target.Outputs = "o";
            project.AppendChild(target);
 
            ElementLocation e1 = target.Location;
            ElementLocation e2 = target.OutputsLocation;
 
            Assert.True(Object.ReferenceEquals(e1, target.Location));
            Assert.True(Object.ReferenceEquals(e2, target.OutputsLocation));
        }
 
        /// <summary>
        /// Test many of the getters
        /// </summary>
        [Fact]
        public void LocationStringsMedley()
        {
            string content = @"
            <Project ToolsVersion=`msbuilddefaulttoolsversion`>
                    <UsingTask TaskName='t' AssemblyName='a' Condition='true'/>
                    <UsingTask TaskName='t' AssemblyFile='a' Condition='true'/>
                    <ItemDefinitionGroup Condition='true' Label='l'>
                        <m Condition='true'/>
                    </ItemDefinitionGroup>
                    <ItemGroup>
                        <i Include='i' Condition='true' Exclude='r'>
                            <m Condition='true'/>
                        </i>
                    </ItemGroup>
                    <PropertyGroup>
                        <p Condition='true'/>
                    </PropertyGroup>
                    <Target Name='Build' Condition='true' Inputs='i' Outputs='o'>
                        <ItemGroup>
                            <i Include='i' Condition='true' Exclude='r'>
                                <m Condition='true'/>
                            </i>
                            <i Remove='r'/>
                        </ItemGroup>
                        <PropertyGroup>
                            <p Condition='true'/>
                        </PropertyGroup>
                        <Error Text='xyz' ContinueOnError='true' Importance='high'/>
                    </Target>
                    <Import Project='p' Condition='false'/>
                </Project>
                ";
 
            var project = ObjectModelHelpers.CreateInMemoryProject(content);
 
            string locations = project.Xml.Location.LocationString + "\r\n";
 
            List<string> attributeLocations = new List<string>(2);
 
            foreach (var element in project.Xml.AllChildren)
            {
                foreach (var property in element.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
                {
                    if (!property.Name.Equals("ImplicitImportLocation") && property.Name.Contains("Location"))
                    {
                        if (property.Name == "ParameterLocations")
                        {
                            var values = new List<KeyValuePair<string, ElementLocation>>((ICollection<KeyValuePair<string, ElementLocation>>)property.GetValue(element, null));
 
                            values.ForEach(value => attributeLocations.Add(value.Key + ":" + value.Value.LocationString));
                        }
                        else
                        {
                            var value = ((ElementLocation)property.GetValue(element, null));
 
                            if (value != null) // null means attribute is not present
                            {
                                locations += value.LocationString + "\r\n";
                            }
                        }
                    }
                }
            }
 
            locations = locations.Replace(project.FullPath, "c:\\foo\\bar.csproj");
 
            string expected = @"c:\foo\bar.csproj (2,13)
c:\foo\bar.csproj (3,32)
c:\foo\bar.csproj (3,45)
c:\foo\bar.csproj (3,62)
c:\foo\bar.csproj (3,21)
c:\foo\bar.csproj (4,32)
c:\foo\bar.csproj (4,45)
c:\foo\bar.csproj (4,62)
c:\foo\bar.csproj (4,21)
c:\foo\bar.csproj (5,42)
c:\foo\bar.csproj (5,59)
c:\foo\bar.csproj (5,21)
c:\foo\bar.csproj (6,28)
c:\foo\bar.csproj (6,25)
c:\foo\bar.csproj (8,21)
c:\foo\bar.csproj (9,28)
c:\foo\bar.csproj (9,57)
c:\foo\bar.csproj (9,40)
c:\foo\bar.csproj (9,25)
c:\foo\bar.csproj (10,32)
c:\foo\bar.csproj (10,29)
c:\foo\bar.csproj (13,21)
c:\foo\bar.csproj (14,28)
c:\foo\bar.csproj (14,25)
c:\foo\bar.csproj (16,29)
c:\foo\bar.csproj (16,59)
c:\foo\bar.csproj (16,70)
c:\foo\bar.csproj (16,29)
c:\foo\bar.csproj (16,42)
c:\foo\bar.csproj (16,21)
c:\foo\bar.csproj (17,25)
c:\foo\bar.csproj (18,32)
c:\foo\bar.csproj (18,61)
c:\foo\bar.csproj (18,44)
c:\foo\bar.csproj (18,29)
c:\foo\bar.csproj (19,36)
c:\foo\bar.csproj (19,33)
c:\foo\bar.csproj (21,32)
c:\foo\bar.csproj (21,29)
c:\foo\bar.csproj (23,25)
c:\foo\bar.csproj (24,32)
c:\foo\bar.csproj (24,29)
c:\foo\bar.csproj (26,43)
c:\foo\bar.csproj (26,25)
c:\foo\bar.csproj (28,29)
c:\foo\bar.csproj (28,41)
c:\foo\bar.csproj (28,21)
";
 
            Helpers.VerifyAssertLineByLine(expected, locations);
 
            // attribute order depends on dictionary internals
            attributeLocations.ShouldBe(new[] { "Text: (26,32)", "Importance: (26,66)" }, ignoreOrder: true);
        }
    }
}