Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 51 additions & 5 deletions src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,24 @@ public void TestTaskResolutionFailureWithNoUsingTask()
_logger.AssertLogContains("MSB4036");
}

/// <summary>
/// https://github.com/dotnet/msbuild/issues/8864
/// </summary>
[Fact]
public void TestTaskDictionaryOutputItems()
{
string customTaskPath = Assembly.GetExecutingAssembly().Location;
MockLogger ml = ObjectModelHelpers.BuildProjectExpectSuccess($"""
<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
<UsingTask TaskName=`TaskThatReturnsDictionaryTaskItem` AssemblyFile=`{customTaskPath}`/>
<Target Name=`Build`>
<TaskThatReturnsDictionaryTaskItem>
<Output TaskParameter="DictionaryTaskItemOutput" ItemName="Outputs"/>
</TaskThatReturnsDictionaryTaskItem>
</Target>
</Project>
""");
}
#endregion

#region ITestTaskHost Members
Expand Down Expand Up @@ -1423,11 +1441,11 @@ private ProjectInstance CreateTestProject()
<Target Name='Skip' Inputs='testProject.proj' Outputs='testProject.proj' />

<Target Name='Error' >
<ErrorTask1 ContinueOnError='True'/>
<ErrorTask2 ContinueOnError='False'/>
<ErrorTask3 />
<OnError ExecuteTargets='Foo'/>
<OnError ExecuteTargets='Bar'/>
<ErrorTask1 ContinueOnError='True'/>
<ErrorTask2 ContinueOnError='False'/>
<ErrorTask3 />
<OnError ExecuteTargets='Foo'/>
<OnError ExecuteTargets='Bar'/>
</Target>

<Target Name='Foo' Inputs='foo.cpp' Outputs='foo.o'>
Expand Down Expand Up @@ -1468,4 +1486,32 @@ private ProjectInstance CreateTestProject()
return project.CreateProjectInstance();
}
}

/// <summary>
/// Task that returns a custom ITaskItem implementation that has a custom IDictionary type returned from CloneCustomMetadata()
/// </summary>
public sealed class TaskThatReturnsDictionaryTaskItem : Utilities.Task
{
public override bool Execute() => true;

[Output]
public ITaskItem DictionaryTaskItemOutput { get => new DictionaryTaskItem(); }
}

internal sealed class DictionaryTaskItem : ITaskItem
{
public string ItemSpec { get => $"{nameof(DictionaryTaskItem)}spec"; set => throw new NotImplementedException(); }

public ICollection MetadataNames => throw new NotImplementedException();

public int MetadataCount => throw new NotImplementedException();

private Dictionary<string, string> metaData = new() { ["a"] = "b" };

public IDictionary CloneCustomMetadata() => new Dictionary<string, string>(metaData);
public string GetMetadata(string metadataName) => throw new NotImplementedException();
public void SetMetadata(string metadataName, string metadataValue) => throw new NotImplementedException();
public void RemoveMetadata(string metadataName) => throw new NotImplementedException();
public void CopyMetadataTo(ITaskItem destinationItem) => throw new NotImplementedException();
}
}