|
// 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.IO;
using System.Linq;
using System.Xml;
using Microsoft.Build.Evaluation;
using Xunit;
#nullable disable
namespace Microsoft.Build.UnitTests.OM.Definition
{
/// <summary>
/// Tests for protecting imported files while editing
/// </summary>
public class ProtectImports_Tests : IDisposable
{
#region Constants
/// <summary>
/// Imported metadata name
/// </summary>
private const string ImportedMetadataName = "ImportedMetadataName";
/// <summary>
/// Imported metadata value
/// </summary>
private const string ImportedMetadataValue = "ImportedMetadataValue";
/// <summary>
/// Item name to use
/// </summary>
private const string ItemName = "ItemName";
/// <summary>
/// Item type to use
/// </summary>
private const string ItemType = "ItemType";
/// <summary>
/// New value
/// </summary>
private const string NewValue = "NewValue";
/// <summary>
/// It's non-overridable just in the sense that the tests aren't providing new values to it; nothing else implied
/// </summary>
private const string NonOverridableMetadataName = "NonOverridableMetadataName";
/// <summary>
/// Overridable metadata name
/// </summary>
private const string OverridableMetadataName = "OverridableMetadataName";
/// <summary>
/// Project metadata name
/// </summary>
private const string ProjectMetadataName = "ProjectMetadataName";
/// <summary>
/// Project metadata value
/// </summary>
private const string ProjectMetadataValue = "ProjectMetadataValue";
/// <summary>
/// Same property name
/// </summary>
private const string PropertyName = "ImportedProperty";
#endregion
/// <summary>
/// Import filename
/// </summary>
private string _importFilename;
#region Test lifetime
/// <summary>
/// Configures the overall test.
/// </summary>
public ProtectImports_Tests()
{
string importContents =
@"<Project>
<PropertyGroup>
<$propertyName>OldPropertyValue</$propertyName>
</PropertyGroup>
<ItemDefinitionGroup>
<$itemType>
<$overridableMetadataName>ImportValue</$overridableMetadataName>
<$nonOverridableMetadataName>ImportValue</$nonOverridableMetadataName>
</$itemType>
</ItemDefinitionGroup>
<ItemGroup>
<$itemType Include=""$itemName"">
<$importedMetadataName>$importedMetadataValue</$importedMetadataName>
</$itemType>
</ItemGroup>
</Project>";
importContents = Expand(importContents);
_importFilename = Microsoft.Build.Shared.FileUtilities.GetTemporaryFileName() + ".targets";
File.WriteAllText(_importFilename, importContents);
}
/// <summary>
/// Undoes the test configuration.
/// </summary>
public void Dispose()
{
if (File.Exists(_importFilename))
{
File.Delete(_importFilename);
}
}
#endregion
#region Property Tests
/// <summary>
/// Tests against edits into imported properties through the property itself.
/// </summary>
[Fact]
public void PropertySetViaProperty()
{
Assert.Throws<InvalidOperationException>(() =>
{
Project project = GetProject();
ProjectProperty property = GetProperty(project);
// This should throw
property.UnevaluatedValue = NewValue;
});
}
/// <summary>
/// Tests against edits into imported properties through the project.
/// Instead of editing the existing property, because it originated
/// in an imported file, it should create a new one in the main project.
/// </summary>
[Fact]
public void PropertySetViaProject()
{
Project project = GetProject();
GetProperty(project);
project.SetProperty(PropertyName, NewValue);
Assert.Equal(NewValue, project.GetPropertyValue(PropertyName));
}
/// <summary>
/// Tests against edits into imported properties through the property itself.
/// </summary>
[Fact]
public void PropertyRemove()
{
Assert.Throws<InvalidOperationException>(() =>
{
Project project = GetProject();
ProjectProperty property = GetProperty(project);
// This should throw
project.RemoveProperty(property);
});
}
#endregion
#region Item Tests
/// <summary>
/// Tests imported item type change.
/// </summary>
[Fact]
public void ItemImportedChangeType()
{
Assert.Throws<InvalidOperationException>(() =>
{
Project project = GetProject();
ProjectItem item = GetImportedItem(project);
// This should throw
item.ItemType = "NewItemType";
});
}
/// <summary>
/// Tests imported item renaming.
/// </summary>
[Fact]
public void ItemImportedRename()
{
Assert.Throws<InvalidOperationException>(() =>
{
Project project = GetProject();
ProjectItem item = GetImportedItem(project);
// This should throw
item.Rename("NewItemName");
});
}
/// <summary>
/// Tests imported item SetUnevaluatedValue.
/// </summary>
[Fact]
public void ItemImportedSetUnevaluatedValue()
{
Assert.Throws<InvalidOperationException>(() =>
{
Project project = GetProject();
ProjectItem item = GetImportedItem(project);
// This should throw
item.UnevaluatedInclude = "NewItemName";
});
}
/// <summary>
/// Tests imported item removal.
/// </summary>
[Fact]
public void ItemImportedRemove()
{
Assert.Throws<InvalidOperationException>(() =>
{
Project project = GetProject();
ProjectItem item = GetImportedItem(project);
// This should throw
project.RemoveItem(item);
});
}
/// <summary>
/// Tests project item type change.
/// </summary>
[Fact]
public void ItemProjectChangeType()
{
Project project = GetProject();
ProjectItem item = GetProjectItem(project);
item.ItemType = NewValue;
Assert.Single(project.GetItems(NewValue)); // "Item in project didn't change name"
Assert.True(project.IsDirty); // "Project was not marked dirty."
}
/// <summary>
/// Tests project item renaming.
/// </summary>
[Fact]
public void ItemProjectRename()
{
Project project = GetProject();
ProjectItem item = GetProjectItem(project);
item.Rename(NewValue);
Assert.Equal(NewValue, item.EvaluatedInclude); // "Item in project didn't change name."
Assert.True(project.IsDirty); // "Project was not marked dirty."
}
/// <summary>
/// Tests project item SetUnevaluatedValue.
/// </summary>
[Fact]
public void ItemProjectSetUnevaluatedValue()
{
Project project = GetProject();
ProjectItem item = GetProjectItem(project);
item.UnevaluatedInclude = NewValue;
Assert.Equal(NewValue, item.EvaluatedInclude); // "Item in project didn't change name."
Assert.True(project.IsDirty); // "Project was not marked dirty."
}
/// <summary>
/// Tests project item removal.
/// </summary>
[Fact]
public void ItemProjectRemove()
{
Project project = GetProject();
ProjectItem item = GetProjectItem(project);
project.RemoveItem(item);
Assert.Single(project.GetItems(ItemType)); // "Item in project wasn't removed."
Assert.True(project.IsDirty); // "Project was not marked dirty."
}
#endregion
#region Metadata Tests
/// <summary>
/// Tests setting existing metadata in import.
/// </summary>
[Fact]
public void MetadataImportSetViaProject()
{
Assert.Throws<InvalidOperationException>(() =>
{
Project project = GetProject();
ProjectItem item = GetImportedItem(project);
// This should throw
item.SetMetadataValue(ImportedMetadataName, "NewImportedMetadataValue");
});
}
/// <summary>
/// Tests setting new metadata in import.
/// </summary>
[Fact]
public void MetadataImportAdd()
{
Assert.Throws<InvalidOperationException>(() =>
{
Project project = GetProject();
ProjectItem item = GetImportedItem(project);
// This should throw
item.SetMetadataValue("NewMetadata", "NewImportedMetadataValue");
});
}
/// <summary>
/// Tests setting new metadata in import.
/// </summary>
[Fact]
public void MetadataImportSetViaMetadata()
{
Assert.Throws<InvalidOperationException>(() =>
{
Project project = GetProject();
ProjectMetadata metadata = GetImportedMetadata(project);
// This should throw
metadata.UnevaluatedValue = NewValue;
});
}
/// <summary>
/// Tests removing metadata in import.
/// </summary>
[Fact]
public void MetadataImportRemove()
{
Assert.Throws<InvalidOperationException>(() =>
{
Project project = GetProject();
ProjectItem item = GetImportedItem(project);
// This should throw
item.RemoveMetadata(ImportedMetadataName);
});
}
/// <summary>
/// Tests setting existing metadata in import.
/// </summary>
[Fact]
public void MetadataProjectSetViaItem()
{
Project project = GetProject();
ProjectItem item = GetProjectItem(project);
item.SetMetadataValue(ProjectMetadataName, NewValue);
Assert.Equal(NewValue, item.GetMetadataValue(ProjectMetadataName)); // "Metadata not saved correctly in project."
Assert.True(project.IsDirty); // "Project was not marked dirty."
}
/// <summary>
/// Tests setting new metadata in import.
/// </summary>
[Fact]
public void MetadataProjectAdd()
{
Project project = GetProject();
ProjectItem item = GetProjectItem(project);
string newName = "NewMetadata";
item.SetMetadataValue(newName, NewValue);
Assert.Equal(NewValue, item.GetMetadataValue(newName)); // "Metadata not saved correctly in project."
Assert.True(project.IsDirty); // "Project was not marked dirty."
}
/// <summary>
/// Tests setting new metadata in import.
/// </summary>
[Fact]
public void MetadataProjectSetViaMetadata()
{
Project project = GetProject();
ProjectMetadata metadata = GetProjectMetadata(project);
string newValue = "NewProjectMetadataValue";
metadata.UnevaluatedValue = newValue;
Assert.Equal(newValue, metadata.EvaluatedValue);
Assert.True(project.IsDirty); // "Project was not marked dirty."
}
/// <summary>
/// Tests removing metadata in import.
/// </summary>
[Fact]
public void MetadataProjectRemove()
{
Project project = GetProject();
ProjectItem item = GetProjectItem(project);
item.RemoveMetadata(ProjectMetadataName);
Assert.False(item.HasMetadata(ProjectMetadataName)); // "Metadata was not removed from project."
Assert.True(project.IsDirty); // "Project was not marked dirty."
}
#endregion
#region Metadata in Item Definition Tests
/// <summary>
/// Tests setting new metadata in import.
/// </summary>
[Fact]
public void DefinitionMetadataImportSetViaMetadata()
{
Assert.Throws<InvalidOperationException>(() =>
{
Project project = GetProject();
ProjectMetadata metadata = GetNonOverridableMetadata(project);
// This should throw
metadata.UnevaluatedValue = NewValue;
});
}
/// <summary>
/// Tests removing metadata in imported item definition.
/// </summary>
[Fact]
public void DefinitionMetadataImportRemove()
{
Assert.Throws<InvalidOperationException>(() =>
{
Project project = GetProject();
ProjectItem item = GetProjectItem(project);
// This should throw
item.RemoveMetadata(NonOverridableMetadataName);
});
}
/// <summary>
/// Tests setting existing metadata in import.
/// </summary>
[Fact]
public void DefinitionMetadataProjectSetViaProject()
{
Project project = GetProject();
ProjectItem item = GetProjectItem(project);
item.SetMetadataValue(OverridableMetadataName, NewValue);
Assert.Equal(NewValue, item.GetMetadataValue(OverridableMetadataName)); // "Metadata not set correctly in project."
Assert.True(project.IsDirty); // "Project was not marked dirty."
}
/// <summary>
/// Tests setting new metadata in import.
/// </summary>
[Fact]
public void DefinitionMetadataProjectSetViaMetadata()
{
Project project = GetProject();
ProjectMetadata metadata = GetOverridableMetadata(project);
metadata.UnevaluatedValue = NewValue;
Assert.Equal(NewValue, metadata.EvaluatedValue);
Assert.True(project.IsDirty); // "Project was not marked dirty."
}
/// <summary>
/// Tests removing metadata in import.
/// </summary>
[Fact]
public void DefinitionMetadataProjectRemove()
{
Project project = GetProject();
ProjectItem item = GetProjectItem(project);
item.RemoveMetadata(OverridableMetadataName);
ProjectMetadata metadata = item.GetMetadata(OverridableMetadataName);
Assert.NotNull(metadata); // "Imported metadata not found after the project's one was removed."
Assert.True(metadata.IsImported); // "IsImported property is not set."
}
#endregion
#region Test helpers
/// <summary>
/// Expands variables in the string contents.
/// </summary>
/// <param name="original">String to be expanded.</param>
/// <returns>Expanded string.</returns>
private string Expand(string original)
{
string expanded = original.Replace("$importFilename", _importFilename);
expanded = expanded.Replace("$importedMetadataName", ImportedMetadataName);
expanded = expanded.Replace("$importedMetadataValue", ImportedMetadataValue);
expanded = expanded.Replace("$itemName", ItemName);
expanded = expanded.Replace("$itemType", ItemType);
expanded = expanded.Replace("$projectMetadataName", ProjectMetadataName);
expanded = expanded.Replace("$overridableMetadataName", OverridableMetadataName);
expanded = expanded.Replace("$nonOverridableMetadataName", NonOverridableMetadataName);
expanded = expanded.Replace("$projectMetadataValue", ProjectMetadataValue);
expanded = expanded.Replace("$propertyName", PropertyName);
return expanded;
}
/// <summary>
/// Gets the test item from the import.
/// </summary>
/// <param name="project">The project.</param>
/// <returns>The item.</returns>
private ProjectItem GetImportedItem(Project project)
{
IEnumerable<ProjectItem> items = project.GetItems(ItemType).Where(pi => pi.IsImported);
Assert.Single(items); // "Wrong number of items in the import."
ProjectItem item = items.First();
Assert.Equal(_importFilename, item.Xml.ContainingProject.FullPath); // "Item was not found in the imported project."
return item;
}
/// <summary>
/// Gets the test metadata from the import.
/// </summary>
/// <param name="project">The project.</param>
/// <returns>The metadata.</returns>
private ProjectMetadata GetImportedMetadata(Project project)
{
ProjectItem item = GetImportedItem(project);
IEnumerable<ProjectMetadata> metadatum = item.Metadata.Where(m => m.Name == ImportedMetadataName);
Assert.Single(metadatum); // "Incorrect number of imported metadata found."
ProjectMetadata metadata = metadatum.First();
Assert.True(metadata.IsImported); // "IsImport property is not set."
return metadata;
}
/// <summary>
/// Gets the test templetized metadata from the import.
/// </summary>
/// <param name="project">The project.</param>
/// <returns>The metadata.</returns>
private ProjectMetadata GetNonOverridableMetadata(Project project)
{
ProjectItem item = GetProjectItem(project);
IEnumerable<ProjectMetadata> metadatum = item.Metadata.Where(m => m.Name == NonOverridableMetadataName);
Assert.Single(metadatum); // "Incorrect number of imported metadata found."
ProjectMetadata metadata = metadatum.First();
Assert.True(metadata.IsImported); // "IsImport property is not set."
return metadata;
}
/// <summary>
/// Gets the test templetized metadata from the project.
/// </summary>
/// <param name="project">The project.</param>
/// <returns>The metadata.</returns>
private ProjectMetadata GetOverridableMetadata(Project project)
{
ProjectItem item = GetProjectItem(project);
IEnumerable<ProjectMetadata> metadatum = item.Metadata.Where(m => m.Name == OverridableMetadataName);
Assert.Single(metadatum); // "Incorrect number of imported metadata found."
ProjectMetadata metadata = metadatum.First();
Assert.False(metadata.IsImported); // "IsImport property is set."
return metadata;
}
/// <summary>
/// Creates a new project from expanding the template contents.
/// </summary>
/// <returns>The project instance.</returns>
private Project GetProject()
{
string projectContents =
@"<Project>
<PropertyGroup>
<$propertyName>OldPropertyValueInProject</$propertyName>
</PropertyGroup>
<ItemGroup>
<$itemType Include=""$itemName"">
<$projectMetadataName>projectValue</$projectMetadataName>
<$overridableMetadataName>ProjectValue</$overridableMetadataName>
</$itemType>
</ItemGroup>
<Import Project=""$importFilename""/>
</Project>";
projectContents = Expand(projectContents);
using ProjectFromString projectFromString = new(projectContents);
Project project = projectFromString.Project;
return project;
}
/// <summary>
/// Gets the test property.
/// </summary>
/// <param name="project">The test project.</param>
/// <returns>The test property.</returns>
private ProjectProperty GetProperty(Project project)
{
ProjectProperty property = project.GetProperty(PropertyName);
Assert.Equal(_importFilename, property.Xml.ContainingProject.FullPath); // "Property was not found in the imported project."
Assert.True(property.IsImported); // "IsImported property was not set."
return property;
}
/// <summary>
/// Gets the test item from the project.
/// </summary>
/// <param name="project">The project.</param>
/// <returns>The item.</returns>
private ProjectItem GetProjectItem(Project project)
{
IEnumerable<ProjectItem> items = project.GetItems(ItemType).Where(pi => !pi.IsImported);
Assert.Single(items); // "Wrong number of items in the project."
ProjectItem item = items.First();
Assert.Null(item.Xml.ContainingProject.FullPath); // "Item was not found in the project." // null because XML is in-memory
return item;
}
/// <summary>
/// Gets the test metadata from the project.
/// </summary>
/// <param name="project">The project.</param>
/// <returns>The metadata.</returns>
private ProjectMetadata GetProjectMetadata(Project project)
{
ProjectItem item = GetProjectItem(project);
IEnumerable<ProjectMetadata> metadatum = item.Metadata.Where(m => m.Name == ProjectMetadataName);
Assert.Single(metadatum); // "Incorrect number of imported metadata found."
ProjectMetadata metadata = metadatum.First();
Assert.False(metadata.IsImported); // "IsImport property is set."
return metadata;
}
#endregion
}
}
|