File: GetAssemblyIdentity_Tests.cs
Web Access
Project: ..\..\..\src\Tasks.UnitTests\Microsoft.Build.Tasks.UnitTests.csproj (Microsoft.Build.Tasks.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.IO;
using System.Reflection;
using Microsoft.Build.Framework;
using Microsoft.Build.Tasks;
using Microsoft.Build.Utilities;
using Shouldly;
using Xunit;
 
#nullable disable
 
namespace Microsoft.Build.UnitTests
{
    public sealed class GetAssemblyIdentity_Tests
    {
        private readonly ITestOutputHelper _output;
 
        public GetAssemblyIdentity_Tests(ITestOutputHelper output)
        {
            _output = output;
        }
 
        private GetAssemblyIdentity CreateTaskUnderTest(MockEngine engine = null)
        {
            return new GetAssemblyIdentity
            {
                BuildEngine = engine ?? new MockEngine(_output),
            };
        }
 
        [Fact]
        public void AbsolutePathToExistingAssembly_ProducesCorrectIdentity()
        {
            // Use this test assembly as the input — it's guaranteed to exist.
            // Use the compile-time assembly name as the baseline rather than
            // AssemblyName.GetAssemblyName(path), to avoid asserting against the same
            // reflection API the production code uses internally.
            string assemblyPath = typeof(GetAssemblyIdentity_Tests).Assembly.Location;
            AssemblyName expectedName = typeof(GetAssemblyIdentity_Tests).Assembly.GetName();
 
            GetAssemblyIdentity task = CreateTaskUnderTest();
            task.AssemblyFiles = [new TaskItem(assemblyPath)];
 
            task.Execute().ShouldBeTrue();
 
            task.Assemblies.Length.ShouldBe(1);
            task.Assemblies[0].ItemSpec.ShouldBe(expectedName.FullName);
            task.Assemblies[0].GetMetadata("Name").ShouldBe(expectedName.Name);
            task.Assemblies[0].GetMetadata("Version").ShouldBe(expectedName.Version.ToString());
        }
 
        [Fact]
        public void NonExistentFile_LogsErrorAndReturnsFalse()
        {
            var engine = new MockEngine(_output);
            GetAssemblyIdentity task = CreateTaskUnderTest(engine);
            task.AssemblyFiles = [new TaskItem("does-not-exist.dll")];
 
            task.Execute().ShouldBeFalse();
 
            AssertCouldNotGetAssemblyName(engine);
            engine.Log.ShouldContain("does-not-exist.dll");
        }
 
        [Fact]
        public void NonAssemblyFile_LogsErrorAndReturnsFalse()
        {
            using TestEnvironment env = TestEnvironment.Create(_output);
            TransientTestFile textFile = env.CreateFile("notanassembly.txt", "This is not an assembly.");
 
            var engine = new MockEngine(_output);
            GetAssemblyIdentity task = CreateTaskUnderTest(engine);
            task.AssemblyFiles = [new TaskItem(textFile.Path)];
 
            task.Execute().ShouldBeFalse();
 
            AssertCouldNotGetAssemblyName(engine);
        }
 
        [Fact]
        public void MixedBatch_GoodAndBadItems()
        {
            using TestEnvironment env = TestEnvironment.Create(_output);
            TransientTestFile textFile = env.CreateFile("bad.txt", "not an assembly");
 
            string goodAssemblyPath = typeof(GetAssemblyIdentity_Tests).Assembly.Location;
            AssemblyName expectedName = typeof(GetAssemblyIdentity_Tests).Assembly.GetName();
 
            var engine = new MockEngine(_output);
            GetAssemblyIdentity task = CreateTaskUnderTest(engine);
            task.AssemblyFiles =
            [
                new TaskItem(goodAssemblyPath),
                new TaskItem(textFile.Path),
            ];
 
            // Execute returns false because one item failed.
            task.Execute().ShouldBeFalse();
 
            // The good item should still appear in the output.
            task.Assemblies.Length.ShouldBe(1);
            task.Assemblies[0].ItemSpec.ShouldBe(expectedName.FullName);
 
            // The bad item should have produced an error.
            engine.Errors.ShouldBe(1);
        }
 
        [Fact]
        public void EmptyItemSpec_LogsErrorAndReturnsFalse()
        {
            var engine = new MockEngine(_output);
            GetAssemblyIdentity task = CreateTaskUnderTest(engine);
            task.AssemblyFiles = [new TaskItem("")];
 
            task.Execute().ShouldBeFalse();
 
            AssertCouldNotGetAssemblyName(engine);
        }
 
        [Fact]
        public void NullItemSpec_LogsErrorAndReturnsFalse()
        {
            var engine = new MockEngine(_output);
            GetAssemblyIdentity task = CreateTaskUnderTest(engine);
            Assert.Throws<ArgumentNullException>(() => task.AssemblyFiles = [new TaskItem((ITaskItem)null)]);
        }
 
        [Fact]
        public void RelativePath_ResolvesAgainstProjectDirectory()
        {
            using TestEnvironment env = TestEnvironment.Create(_output);
            TransientTestFolder projectDir = env.CreateFolder();
 
            string sourceAssembly = typeof(GetAssemblyIdentity_Tests).Assembly.Location;
            AssemblyName expectedName = typeof(GetAssemblyIdentity_Tests).Assembly.GetName();
 
            string relativeFileName = "TestAssembly.dll";
            File.Copy(sourceAssembly, Path.Combine(projectDir.Path, relativeFileName));
 
            var engine = new MockEngine(_output);
            GetAssemblyIdentity task = CreateTaskUnderTest(engine);
            task.TaskEnvironment = TaskEnvironment.CreateWithProjectDirectoryAndEnvironment(projectDir.Path);
            task.AssemblyFiles = [new TaskItem(relativeFileName)];
 
            task.Execute().ShouldBeTrue();
 
            task.Assemblies.Length.ShouldBe(1);
            task.Assemblies[0].ItemSpec.ShouldBe(expectedName.FullName);
        }
 
        [Fact]
        public void OutputItem_DoesNotContainAbsolutizedPaths()
        {
            string assemblyPath = typeof(GetAssemblyIdentity_Tests).Assembly.Location;
            AssemblyName expectedName = typeof(GetAssemblyIdentity_Tests).Assembly.GetName();
 
            GetAssemblyIdentity task = CreateTaskUnderTest();
            task.AssemblyFiles = [new TaskItem(assemblyPath)];
 
            task.Execute().ShouldBeTrue();
 
            task.Assemblies.Length.ShouldBe(1);
 
            // ItemSpec should be the assembly identity string, not a path.
            task.Assemblies[0].ItemSpec.ShouldBe(expectedName.FullName);
        }
 
        [Fact]
        public void OutputItem_CopiesInputMetadataVerbatim()
        {
            string assemblyPath = typeof(GetAssemblyIdentity_Tests).Assembly.Location;
 
            var inputItem = new TaskItem(assemblyPath);
            inputItem.SetMetadata("CustomMeta", "CustomValue");
 
            GetAssemblyIdentity task = CreateTaskUnderTest();
            task.AssemblyFiles = [inputItem];
 
            task.Execute().ShouldBeTrue();
 
            task.Assemblies.ShouldHaveSingleItem()
                .GetMetadata("CustomMeta").ShouldBe("CustomValue");
        }
 
        private void AssertCouldNotGetAssemblyName(MockEngine engine)
        {
            engine.Errors.ShouldBe(1);
            engine.Log.ShouldContain("MSB3441");
        }
    }
}