|
// 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;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using Microsoft.Build.Utilities;
using Shouldly;
using Xunit;
using Xunit.NetCore.Extensions;
#pragma warning disable 0219
#nullable disable
namespace Microsoft.Build.UnitTests
{
public class TaskItemTests
{
// Make sure a TaskItem can be constructed using an ITaskItem
[Fact]
public void ConstructWithITaskItem()
{
TaskItem from = new TaskItem();
from.ItemSpec = "Monkey.txt";
from.SetMetadata("Dog", "Bingo");
from.SetMetadata("Cat", "Morris");
TaskItem to = new TaskItem((ITaskItem)from);
to.ItemSpec.ShouldBe("Monkey.txt");
((string)to).ShouldBe("Monkey.txt");
to.GetMetadata("Dog").ShouldBe("Bingo");
to.GetMetadata("Cat").ShouldBe("Morris");
// Test that item metadata are case-insensitive.
to.SetMetadata("CaT", "");
to.GetMetadata("Cat").ShouldBe("");
// manipulate the item-spec a bit
to.GetMetadata(FileUtilities.ItemSpecModifiers.Filename).ShouldBe("Monkey");
to.GetMetadata(FileUtilities.ItemSpecModifiers.Extension).ShouldBe(".txt");
to.GetMetadata(FileUtilities.ItemSpecModifiers.RelativeDir).ShouldBe(string.Empty);
}
// Make sure metadata can be cloned from an existing ITaskItem
[Fact]
public void CopyMetadataFromITaskItem()
{
TaskItem from = new TaskItem();
from.ItemSpec = "Monkey.txt";
from.SetMetadata("Dog", "Bingo");
from.SetMetadata("Cat", "Morris");
from.SetMetadata("Bird", "Big");
TaskItem to = new TaskItem();
to.ItemSpec = "Bonobo.txt";
to.SetMetadata("Sponge", "Bob");
to.SetMetadata("Dog", "Harriet");
to.SetMetadata("Cat", "Mike");
from.CopyMetadataTo(to);
to.ItemSpec.ShouldBe("Bonobo.txt"); // ItemSpec is never overwritten
to.GetMetadata("Sponge").ShouldBe("Bob"); // Metadata not in source are preserved.
to.GetMetadata("Dog").ShouldBe("Harriet"); // Metadata present on destination are not overwritten.
to.GetMetadata("Cat").ShouldBe("Mike");
to.GetMetadata("Bird").ShouldBe("Big");
}
[Fact]
public void NullITaskItem()
{
Should.Throw<ArgumentNullException>(() =>
{
ITaskItem item = null;
TaskItem taskItem = new TaskItem(item);
// no NullReferenceException
});
}
[Fact]
public void MetadataNamesAndCount()
{
TaskItem taskItem = new TaskItem("x");
// Without custom metadata, should return the built in metadata
taskItem.MetadataNames.Cast<string>().ShouldBeSetEquivalentTo(FileUtilities.ItemSpecModifiers.All);
taskItem.MetadataCount.ShouldBe(FileUtilities.ItemSpecModifiers.All.Length);
// Now add one
taskItem.SetMetadata("m", "m1");
taskItem.MetadataNames.Cast<string>().ShouldBeSetEquivalentTo(FileUtilities.ItemSpecModifiers.All.Concat(new[] { "m" }));
taskItem.MetadataCount.ShouldBe(FileUtilities.ItemSpecModifiers.All.Length + 1);
}
[Fact]
public void NullITaskItemCast()
{
Should.Throw<ArgumentNullException>(() =>
{
TaskItem item = null;
string result = (string)item;
// no NullReferenceException
});
}
[Fact]
public void ConstructFromDictionary()
{
Hashtable h = new Hashtable();
h[FileUtilities.ItemSpecModifiers.Filename] = "foo";
h[FileUtilities.ItemSpecModifiers.Extension] = "bar";
h["custom"] = "hello";
TaskItem t = new TaskItem("bamboo.baz", h);
// item-spec modifiers were not overridden by dictionary passed to constructor
t.GetMetadata(FileUtilities.ItemSpecModifiers.Filename).ShouldBe("bamboo");
t.GetMetadata(FileUtilities.ItemSpecModifiers.Extension).ShouldBe(".baz");
t.GetMetadata("CUSTOM").ShouldBe("hello");
}
[Fact]
public void CannotChangeModifiers()
{
Should.Throw<ArgumentException>(() =>
{
TaskItem t = new TaskItem("foo");
try
{
t.SetMetadata(FileUtilities.ItemSpecModifiers.FullPath, "bazbaz");
}
catch (Exception e)
{
// so I can see the exception message in NUnit's "Standard Out" window
Console.WriteLine(e.Message);
throw;
}
});
}
[Fact]
public void CannotRemoveModifiers()
{
Should.Throw<ArgumentException>(() =>
{
TaskItem t = new TaskItem("foor");
try
{
t.RemoveMetadata(FileUtilities.ItemSpecModifiers.RootDir);
}
catch (Exception e)
{
// so I can see the exception message in NUnit's "Standard Out" window
Console.WriteLine(e.Message);
throw;
}
});
}
[Fact]
public void CheckMetadataCount()
{
TaskItem t = new TaskItem("foo");
t.MetadataCount.ShouldBe(FileUtilities.ItemSpecModifiers.All.Length);
t.SetMetadata("grog", "RUM");
t.MetadataCount.ShouldBe(FileUtilities.ItemSpecModifiers.All.Length + 1);
}
[Fact]
public void NonexistentRequestFullPath()
{
TaskItem from = new TaskItem();
from.ItemSpec = "Monkey.txt";
from.GetMetadata(FileUtilities.ItemSpecModifiers.FullPath).ShouldBe(
Path.Combine(
Directory.GetCurrentDirectory(),
"Monkey.txt"));
}
[Fact]
public void NonexistentRequestRootDir()
{
TaskItem from = new TaskItem();
from.ItemSpec = "Monkey.txt";
from.GetMetadata(FileUtilities.ItemSpecModifiers.RootDir).ShouldBe(Path.GetPathRoot(from.GetMetadata(FileUtilities.ItemSpecModifiers.FullPath)));
}
[Fact]
public void NonexistentRequestFilename()
{
TaskItem from = new TaskItem();
from.ItemSpec = "Monkey.txt";
from.GetMetadata(FileUtilities.ItemSpecModifiers.Filename).ShouldBe("Monkey");
}
[Fact]
public void NonexistentRequestExtension()
{
TaskItem from = new TaskItem();
from.ItemSpec = "Monkey.txt";
from.GetMetadata(FileUtilities.ItemSpecModifiers.Extension).ShouldBe(".txt");
}
[Fact]
public void NonexistentRequestRelativeDir()
{
TaskItem from = new TaskItem();
from.ItemSpec = "Monkey.txt";
from.GetMetadata(FileUtilities.ItemSpecModifiers.RelativeDir).Length.ShouldBe(0);
}
[Fact]
public void NonexistentRequestDirectory()
{
TaskItem from = new TaskItem();
from.ItemSpec = NativeMethodsShared.IsWindows ? @"c:\subdir\Monkey.txt" : "/subdir/Monkey.txt";
from.GetMetadata(FileUtilities.ItemSpecModifiers.Directory).ShouldBe(NativeMethodsShared.IsWindows ? @"subdir\" : "subdir/");
}
[WindowsOnlyFact("UNC is not implemented except under Windows.")]
public void NonexistentRequestDirectoryUNC()
{
TaskItem from = new TaskItem();
from.ItemSpec = @"\\local\share\subdir\Monkey.txt";
from.GetMetadata(FileUtilities.ItemSpecModifiers.Directory).ShouldBe(@"subdir\");
}
[Fact]
public void NonexistentRequestRecursiveDir()
{
TaskItem from = new TaskItem();
from.ItemSpec = "Monkey.txt";
from.GetMetadata(FileUtilities.ItemSpecModifiers.RecursiveDir).Length.ShouldBe(0);
}
[Fact]
public void NonexistentRequestIdentity()
{
TaskItem from = new TaskItem();
from.ItemSpec = "Monkey.txt";
from.GetMetadata(FileUtilities.ItemSpecModifiers.Identity).ShouldBe("Monkey.txt");
}
[Fact]
public void RequestTimeStamps()
{
TaskItem from = new TaskItem();
from.ItemSpec = FileUtilities.GetTemporaryFile();
from.GetMetadata(FileUtilities.ItemSpecModifiers.ModifiedTime).Length.ShouldBeGreaterThan(0);
from.GetMetadata(FileUtilities.ItemSpecModifiers.CreatedTime).Length.ShouldBeGreaterThan(0);
from.GetMetadata(FileUtilities.ItemSpecModifiers.AccessedTime).Length.ShouldBeGreaterThan(0);
File.Delete(from.ItemSpec);
from.GetMetadata(FileUtilities.ItemSpecModifiers.ModifiedTime).Length.ShouldBe(0);
from.GetMetadata(FileUtilities.ItemSpecModifiers.CreatedTime).Length.ShouldBe(0);
from.GetMetadata(FileUtilities.ItemSpecModifiers.AccessedTime).Length.ShouldBe(0);
}
/// <summary>
/// Verify metadata cannot be created with null name
/// </summary>
[Fact]
public void CreateNullNamedMetadata()
{
Should.Throw<ArgumentNullException>(() =>
{
TaskItem item = new TaskItem("foo");
item.SetMetadata(null, "x");
});
}
/// <summary>
/// Verify metadata cannot be created with empty name
/// </summary>
[Fact]
public void CreateEmptyNamedMetadata()
{
Should.Throw<ArgumentException>(() =>
{
TaskItem item = new TaskItem("foo");
item.SetMetadata("", "x");
});
}
/// <summary>
/// Create a TaskItem with a null metadata value -- this is allowed, but
/// internally converted to the empty string.
/// </summary>
[Fact]
public void CreateTaskItemWithNullMetadata()
{
IDictionary<string, string> metadata = new Dictionary<string, string>();
metadata.Add("m", null);
TaskItem item = new TaskItem("bar", (IDictionary)metadata);
item.GetMetadata("m").ShouldBe(string.Empty);
}
/// <summary>
/// Set metadata value to null value -- this is allowed, but
/// internally converted to the empty string.
/// </summary>
[Fact]
public void SetNullMetadataValue()
{
TaskItem item = new TaskItem("bar");
item.SetMetadata("m", null);
item.GetMetadata("m").ShouldBe(string.Empty);
}
[Fact]
public void ImplementsIMetadataContainer()
{
Dictionary<string, string> metadata = new()
{
{ "a", "a1" },
{ "b", "b1" },
};
TaskItem item = new TaskItem("foo");
IMetadataContainer metadataContainer = (IMetadataContainer)item;
metadataContainer.ImportMetadata(metadata);
var actualMetadata = metadataContainer.EnumerateMetadata().OrderBy(metadata => metadata.Key).ToList();
var expectedMetadata = metadata.OrderBy(metadata => metadata.Value).ToList();
Assert.True(actualMetadata.SequenceEqual(expectedMetadata));
}
#if FEATURE_APPDOMAIN
/// <summary>
/// Test that task items can be successfully constructed based on a task item from another appdomain.
/// </summary>
[Fact]
public void RemoteTaskItem()
{
AppDomain appDomain = null;
try
{
appDomain = AppDomain.CreateDomain(
"generateResourceAppDomain",
null,
AppDomain.CurrentDomain.SetupInformation);
object obj = appDomain.CreateInstanceFromAndUnwrap(
typeof(TaskItemCreator).Module.FullyQualifiedName,
typeof(TaskItemCreator).FullName);
TaskItemCreator creator = (TaskItemCreator)obj;
IDictionary<string, string> metadata = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
metadata.Add("c", "C");
metadata.Add("d", "D");
creator.Run(new[] { "a", "b" }, metadata);
ITaskItem[] itemsInThisAppDomain = new ITaskItem[creator.CreatedTaskItems.Length];
for (int i = 0; i < creator.CreatedTaskItems.Length; i++)
{
itemsInThisAppDomain[i] = new TaskItem(creator.CreatedTaskItems[i]);
itemsInThisAppDomain[i].ItemSpec.ShouldBe(creator.CreatedTaskItems[i].ItemSpec);
itemsInThisAppDomain[i].MetadataCount.ShouldBe(creator.CreatedTaskItems[i].MetadataCount + 1);
Dictionary<string, string> creatorMetadata = new Dictionary<string, string>(creator.CreatedTaskItems[i].MetadataCount);
foreach (string metadatum in creator.CreatedTaskItems[i].MetadataNames)
{
creatorMetadata[metadatum] = creator.CreatedTaskItems[i].GetMetadata(metadatum);
}
Dictionary<string, string> metadataInThisAppDomain = new Dictionary<string, string>(itemsInThisAppDomain[i].MetadataCount);
foreach (string metadatum in itemsInThisAppDomain[i].MetadataNames)
{
if (!string.Equals("OriginalItemSpec", metadatum))
{
metadataInThisAppDomain[metadatum] = itemsInThisAppDomain[i].GetMetadata(metadatum);
}
}
metadataInThisAppDomain.ShouldBe(creatorMetadata, ignoreOrder: true);
}
}
finally
{
if (appDomain != null)
{
AppDomain.Unload(appDomain);
}
}
}
/// <summary>
/// Miniature class to be remoted to another appdomain that just creates some TaskItems and makes them available for returning.
/// </summary>
private sealed class TaskItemCreator
#if FEATURE_APPDOMAIN
: MarshalByRefObject
#endif
{
/// <summary>
/// Task items that will be consumed by the other appdomain
/// </summary>
public ITaskItem[] CreatedTaskItems
{
get;
private set;
}
/// <summary>
/// Creates task items
/// </summary>
public void Run(string[] includes, IDictionary<string, string> metadataToAdd)
{
ErrorUtilities.VerifyThrowArgumentNull(includes, nameof(includes));
CreatedTaskItems = new TaskItem[includes.Length];
for (int i = 0; i < includes.Length; i++)
{
CreatedTaskItems[i] = new TaskItem(includes[i], (IDictionary)metadataToAdd);
}
}
}
#endif
}
}
|