|
// 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.Linq;
using Microsoft.Build.BackEnd;
using Microsoft.Build.Collections;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using Xunit;
#nullable disable
namespace Microsoft.Build.UnitTests.BackEnd
{
public class Lookup_Tests
{
/// <summary>
/// Primary group contains an item for a type and secondary does;
/// primary item should be returned instead of the secondary item.
/// </summary>
[Fact]
public void SecondaryItemShadowedByPrimaryItem()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
table1.Add(new ProjectItemInstance(project, "i1", "a1", project.FullPath));
table1.Add(new ProjectItemInstance(project, "i2", "a%3b1", project.FullPath));
Lookup lookup = LookupHelpers.CreateLookup(table1);
lookup.EnterScope("x");
lookup.PopulateWithItem(new ProjectItemInstance(project, "i1", "a2", project.FullPath));
lookup.PopulateWithItem(new ProjectItemInstance(project, "i2", "a%282", project.FullPath));
// Should return the item from the primary, not the secondary table
Assert.Equal("a2", lookup.GetItems("i1").First().EvaluatedInclude);
Assert.Equal("a(2", lookup.GetItems("i2").First().EvaluatedInclude);
}
/// <summary>
/// Primary group does not contain an item for a type but secondary does;
/// secondary item should be returned.
/// </summary>
[Fact]
public void SecondaryItemNotShadowedByPrimaryItem()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
table1.Add(new ProjectItemInstance(project, "i1", "a1", project.FullPath));
table1.Add(new ProjectItemInstance(project, "i2", "a%3b1", project.FullPath));
Lookup lookup = LookupHelpers.CreateLookup(table1);
lookup.EnterScope("x");
// Should return item from the secondary table.
Assert.Equal("a1", lookup.GetItems("i1").First().EvaluatedInclude);
Assert.Equal("a;1", lookup.GetItems("i2").First().EvaluatedInclude);
}
/// <summary>
/// No items of that type: should return empty group rather than null
/// </summary>
[Fact]
public void UnknownItemType()
{
Lookup lookup = LookupHelpers.CreateEmptyLookup();
lookup.EnterScope("x"); // Doesn't matter really
Assert.Empty(lookup.GetItems("i1"));
}
/// <summary>
/// Adds accumulate as we lookup in the tables
/// </summary>
[Fact]
public void AddsAreCombinedWithPopulates()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
// One item in the project
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
table1.Add(new ProjectItemInstance(project, "i1", "a1", project.FullPath));
Lookup lookup = LookupHelpers.CreateLookup(table1);
// We see the one item
Assert.Equal("a1", lookup.GetItems("i1").First().EvaluatedInclude);
Assert.Single(lookup.GetItems("i1"));
// One item in the project
Assert.Equal("a1", table1["i1"].First().EvaluatedInclude);
Assert.Single(table1["i1"]);
// Start a target
Lookup.Scope enteredScope = lookup.EnterScope("x");
// We see the one item
Assert.Equal("a1", lookup.GetItems("i1").First().EvaluatedInclude);
Assert.Single(lookup.GetItems("i1"));
// One item in the project
Assert.Equal("a1", table1["i1"].First().EvaluatedInclude);
Assert.Single(table1["i1"]);
// Start a task (eg) and add a new item
Lookup.Scope enteredScope2 = lookup.EnterScope("x");
lookup.AddNewItem(new ProjectItemInstance(project, "i1", "a2", project.FullPath));
// Now we see two items
Assert.Equal("a1", lookup.GetItems("i1").First().EvaluatedInclude);
Assert.Equal("a2", lookup.GetItems("i1").ElementAt(1).EvaluatedInclude);
Assert.Equal(2, lookup.GetItems("i1").Count);
// But there's still one item in the project
Assert.Equal("a1", table1["i1"].First().EvaluatedInclude);
Assert.Single(table1["i1"]);
// Finish the task
enteredScope2.LeaveScope();
// We still see two items
Assert.Equal("a1", lookup.GetItems("i1").First().EvaluatedInclude);
Assert.Equal("a2", lookup.GetItems("i1").ElementAt(1).EvaluatedInclude);
Assert.Equal(2, lookup.GetItems("i1").Count);
// But there's still one item in the project
Assert.Equal("a1", table1["i1"].First().EvaluatedInclude);
Assert.Single(table1["i1"]);
// Finish the target
enteredScope.LeaveScope();
// We still see two items
Assert.Equal("a1", lookup.GetItems("i1").First().EvaluatedInclude);
Assert.Equal("a2", lookup.GetItems("i1").ElementAt(1).EvaluatedInclude);
Assert.Equal(2, lookup.GetItems("i1").Count);
// And now the items have gotten put into the global group
Assert.Equal("a1", table1["i1"].First().EvaluatedInclude);
Assert.Equal("a2", table1["i1"].ElementAt(1).EvaluatedInclude);
Assert.Equal(2, table1["i1"].Count);
}
/// <summary>
/// Adds when duplicate removal is enabled removes only duplicates. Tests only item specs, not metadata differences
/// </summary>
[Fact]
public void AddsWithDuplicateRemovalItemSpecsOnly()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
// One item in the project
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
table1.Add(new ProjectItemInstance(project, "i1", "a1", project.FullPath));
// Add an existing duplicate
table1.Add(new ProjectItemInstance(project, "i1", "a1", project.FullPath));
Lookup lookup = LookupHelpers.CreateLookup(table1);
var scope = lookup.EnterScope("test");
// This one should not get added
ProjectItemInstance[] newItems = new ProjectItemInstance[]
{
new ProjectItemInstance(project, "i1", "a1", project.FullPath), // Should not get added
new ProjectItemInstance(project, "i1", "a2", project.FullPath), // Should get added
};
// Perform the addition
lookup.AddNewItemsOfItemType("i1", newItems, doNotAddDuplicates: true);
var group = lookup.GetItems("i1");
// We should have the original two duplicates plus one new addition.
Assert.Equal(3, group.Count);
// Only two of the items should have the 'a1' include.
Assert.Equal(2, group.Where(item => item.EvaluatedInclude == "a1").Count());
// And ensure the other item got added.
Assert.Single(group.Where(item => item.EvaluatedInclude == "a2"));
scope.LeaveScope();
group = lookup.GetItems("i1");
// We should have the original two duplicates plus one new addition.
Assert.Equal(3, group.Count);
// Only two of the items should have the 'a1' include.
Assert.Equal(2, group.Where(item => item.EvaluatedInclude == "a1").Count());
// And ensure the other item got added.
Assert.Single(group.Where(item => item.EvaluatedInclude == "a2"));
}
/// <summary>
/// Adds when duplicate removal is enabled removes only duplicates. Tests only item specs, not metadata differences
/// </summary>
[Fact]
public void AddsWithDuplicateRemovalWithMetadata()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
// Two items, differ only by metadata
table1.Add(new ProjectItemInstance(project, "i1", "a1", new KeyValuePair<string, string>[] { new KeyValuePair<string, string>("m1", "m1") }, project.FullPath));
table1.Add(new ProjectItemInstance(project, "i1", "a1", new KeyValuePair<string, string>[] { new KeyValuePair<string, string>("m1", "m2") }, project.FullPath));
Lookup lookup = LookupHelpers.CreateLookup(table1);
var scope = lookup.EnterScope("test");
// This one should not get added
ProjectItemInstance[] newItems = new ProjectItemInstance[]
{
new ProjectItemInstance(project, "i1", "a1", project.FullPath), // Should get added
new ProjectItemInstance(project, "i1", "a2", new KeyValuePair<string, string>[] { new KeyValuePair<string, string>( "m1", "m1") }, project.FullPath), // Should get added
new ProjectItemInstance(project, "i1", "a1", new KeyValuePair<string, string>[] { new KeyValuePair<string, string>( "m1", "m1") }, project.FullPath), // Should not get added
new ProjectItemInstance(project, "i1", "a1", new KeyValuePair<string, string>[] { new KeyValuePair<string, string>( "m1", "m3") }, project.FullPath), // Should get added
};
// Perform the addition
lookup.AddNewItemsOfItemType("i1", newItems, doNotAddDuplicates: true);
var group = lookup.GetItems("i1");
// We should have the original two duplicates plus one new addition.
Assert.Equal(5, group.Count);
// Four of the items will have the a1 include
Assert.Equal(4, group.Where(item => item.EvaluatedInclude == "a1").Count());
// One item will have the a2 include
Assert.Single(group.Where(item => item.EvaluatedInclude == "a2"));
scope.LeaveScope();
group = lookup.GetItems("i1");
// We should have the original two duplicates plus one new addition.
Assert.Equal(5, group.Count);
// Four of the items will have the a1 include
Assert.Equal(4, group.Where(item => item.EvaluatedInclude == "a1").Count());
// One item will have the a2 include
Assert.Single(group.Where(item => item.EvaluatedInclude == "a2"));
}
[Fact]
public void Removes()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
// One item in the project
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
ProjectItemInstance item1 = new ProjectItemInstance(project, "i1", "a1", project.FullPath);
table1.Add(item1);
Lookup lookup = LookupHelpers.CreateLookup(table1);
// Start a target
Lookup.Scope enteredScope = lookup.EnterScope("x");
// Start a task (eg) and add a new item
Lookup.Scope enteredScope2 = lookup.EnterScope("x");
ProjectItemInstance item2 = new ProjectItemInstance(project, "i1", "a2", project.FullPath);
lookup.AddNewItem(item2);
// Remove one item
lookup.RemoveItem(item1);
// We see one item
Assert.Single(lookup.GetItems("i1"));
Assert.Equal("a2", lookup.GetItems("i1").First().EvaluatedInclude);
// Remove the other item
lookup.RemoveItem(item2);
// We see no items
Assert.Empty(lookup.GetItems("i1"));
// Finish the task
enteredScope2.LeaveScope();
// We still see no items
Assert.Empty(lookup.GetItems("i1"));
// But there's still one item in the project
Assert.Equal("a1", table1["i1"].First().EvaluatedInclude);
Assert.Single(table1["i1"]);
// Finish the target
enteredScope.LeaveScope();
// We still see no items
Assert.Empty(lookup.GetItems("i1"));
// And now there are no items in the project either
Assert.Empty(table1["i1"]);
}
[Fact]
public void RemoveItemPopulatedInLowerScope()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
Lookup lookup = LookupHelpers.CreateLookup(table1);
ProjectItemInstance item1 = new ProjectItemInstance(project, "i1", "a2", project.FullPath);
// Start a target
Lookup.Scope enteredScope = lookup.EnterScope("x");
// There's one item in this batch
lookup.PopulateWithItem(item1);
// We see it
Assert.Single(lookup.GetItems("i1"));
// Make a clone so we can keep an eye on that item
Lookup lookup2 = lookup.Clone();
// We can see the item in the clone
Assert.Single(lookup2.GetItems("i1"));
// Start a task (eg)
Lookup.Scope enteredScope2 = lookup.EnterScope("x");
// We see the item below
Assert.Single(lookup.GetItems("i1"));
// Remove that item
lookup.RemoveItem(item1);
// We see no items
Assert.Empty(lookup.GetItems("i1"));
// The clone is unaffected so far
Assert.Single(lookup2.GetItems("i1"));
// Finish the task
enteredScope2.LeaveScope();
// We still see no items
Assert.Empty(lookup.GetItems("i1"));
// But now the clone doesn't either
Assert.Empty(lookup2.GetItems("i1"));
// Finish the target
enteredScope.LeaveScope();
// We still see no items
Assert.Empty(lookup.GetItems("i1"));
Assert.Empty(lookup2.GetItems("i1"));
}
[Fact]
public void RemoveItemAddedInLowerScope()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
Lookup lookup = LookupHelpers.CreateLookup(table1);
// Start a target
Lookup.Scope enteredScope = lookup.EnterScope("x");
// Add an item
ProjectItemInstance item1 = new ProjectItemInstance(project, "i1", "a2", project.FullPath);
lookup.AddNewItem(item1);
// Start a task (eg)
Lookup.Scope enteredScope2 = lookup.EnterScope("x");
// We see the item below
Assert.Single(lookup.GetItems("i1"));
// Remove that item
lookup.RemoveItem(item1);
// We see no items
Assert.Empty(lookup.GetItems("i1"));
// Finish the task
enteredScope2.LeaveScope();
// We still see no items
Assert.Empty(lookup.GetItems("i1"));
// Finish the target
enteredScope.LeaveScope();
// We still see no items
Assert.Empty(lookup.GetItems("i1"));
}
/// <summary>
/// Ensure that once keepOnlySpecified is set to true, it remains in effect.
/// </summary>
[Fact]
public void KeepMetadataOnlySpecifiedPropagate1()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
Lookup lookup = LookupHelpers.CreateLookup(table1);
Lookup.Scope enteredScope = lookup.EnterScope("x");
// Add an item with m=m1
ProjectItemInstance item1 = new ProjectItemInstance(project, "i1", "a2", project.FullPath);
item1.SetMetadata("m1", "m1");
item1.SetMetadata("m2", "m2");
lookup.AddNewItem(item1);
Lookup.Scope enteredScope2 = lookup.EnterScope("x");
// Get rid of all of the metadata.
Lookup.MetadataModifications newMetadata = new Lookup.MetadataModifications(keepOnlySpecified: true);
ICollection<ProjectItemInstance> group = lookup.GetItems(item1.ItemType);
lookup.ModifyItems(item1.ItemType, group, newMetadata);
group = lookup.GetItems("i1");
Assert.Single(group);
// m1 and m2 are gone.
Assert.Equal(String.Empty, group.First().GetMetadataValue("m1"));
Assert.Equal(String.Empty, group.First().GetMetadataValue("m2"));
enteredScope2.LeaveScope();
// Add metadata m3.
Lookup.MetadataModifications newMetadata2 = new Lookup.MetadataModifications(keepOnlySpecified: false);
newMetadata2.Add("m3", "m3");
group = lookup.GetItems(item1.ItemType);
lookup.ModifyItems(item1.ItemType, group, newMetadata2);
group = lookup.GetItems("i1");
Assert.Single(group);
// m1 and m2 are gone
Assert.Equal(String.Empty, group.First().GetMetadataValue("m1"));
Assert.Equal(String.Empty, group.First().GetMetadataValue("m2"));
// m3 is still there.
Assert.Equal("m3", group.First().GetMetadataValue("m3"));
enteredScope.LeaveScope();
group = lookup.GetItems("i1");
Assert.Single(group);
// m1 and m2 are gone
Assert.Equal(String.Empty, group.First().GetMetadataValue("m1"));
Assert.Equal(String.Empty, group.First().GetMetadataValue("m2"));
// m3 is still there.
Assert.Equal("m3", group.First().GetMetadataValue("m3"));
}
/// <summary>
/// Ensure that if keepOnlySpecified is specified after some metadata have been set in a higher scope that it will
/// eliminate that metadata are the current scope and beyond.
/// </summary>
[Fact]
public void KeepMetadataOnlySpecifiedPropagate2()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
Lookup lookup = LookupHelpers.CreateLookup(table1);
Lookup.Scope enteredScope = lookup.EnterScope("x");
// Add an item with m=m1
ProjectItemInstance item1 = new ProjectItemInstance(project, "i1", "a2", project.FullPath);
item1.SetMetadata("m1", "m1");
item1.SetMetadata("m2", "m2");
lookup.AddNewItem(item1);
Lookup.Scope enteredScope2 = lookup.EnterScope("x");
// Add m3 metadata
Lookup.MetadataModifications newMetadata = new Lookup.MetadataModifications(keepOnlySpecified: false);
newMetadata.Add("m3", "m3");
ICollection<ProjectItemInstance> group = lookup.GetItems(item1.ItemType);
lookup.ModifyItems(item1.ItemType, group, newMetadata);
group = lookup.GetItems("i1");
Assert.Single(group);
// All metadata are present
Assert.Equal("m1", group.First().GetMetadataValue("m1"));
Assert.Equal("m2", group.First().GetMetadataValue("m2"));
Assert.Equal("m3", group.First().GetMetadataValue("m3"));
enteredScope2.LeaveScope();
// Now clear metadata
Lookup.MetadataModifications newMetadata2 = new Lookup.MetadataModifications(keepOnlySpecified: true);
group = lookup.GetItems(item1.ItemType);
lookup.ModifyItems(item1.ItemType, group, newMetadata2);
group = lookup.GetItems("i1");
Assert.Single(group);
// All metadata are gone
Assert.Equal(String.Empty, group.First().GetMetadataValue("m1"));
Assert.Equal(String.Empty, group.First().GetMetadataValue("m2"));
Assert.Equal(String.Empty, group.First().GetMetadataValue("m3"));
enteredScope.LeaveScope();
group = lookup.GetItems("i1");
Assert.Single(group);
// All metadata are gone
Assert.Equal(String.Empty, group.First().GetMetadataValue("m1"));
Assert.Equal(String.Empty, group.First().GetMetadataValue("m2"));
Assert.Equal(String.Empty, group.First().GetMetadataValue("m3"));
}
/// <summary>
/// Ensure that once keepOnlySpecified is set to true, it remains in effect, but that metadata explicitly added at subsequent levels is still retained.
/// </summary>
[Fact]
public void KeepMetadataOnlySpecifiedPropagate3()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
Lookup lookup = LookupHelpers.CreateLookup(table1);
Lookup.Scope enteredScope = lookup.EnterScope("x");
// Add an item with m=m1
ProjectItemInstance item1 = new ProjectItemInstance(project, "i1", "a2", project.FullPath);
item1.SetMetadata("m1", "m1");
item1.SetMetadata("m2", "m2");
lookup.AddNewItem(item1);
Lookup.Scope enteredScope2 = lookup.EnterScope("x");
// Get rid of all of the metadata, then add m3
Lookup.MetadataModifications newMetadata = new Lookup.MetadataModifications(keepOnlySpecified: true);
newMetadata.Add("m3", "m3");
ICollection<ProjectItemInstance> group = lookup.GetItems(item1.ItemType);
lookup.ModifyItems(item1.ItemType, group, newMetadata);
group = lookup.GetItems("i1");
Assert.Single(group);
// m1 and m2 are gone.
Assert.Equal(String.Empty, group.First().GetMetadataValue("m1"));
Assert.Equal(String.Empty, group.First().GetMetadataValue("m2"));
// m3 is still there.
Assert.Equal("m3", group.First().GetMetadataValue("m3"));
enteredScope2.LeaveScope();
// Add metadata m4.
Lookup.MetadataModifications newMetadata2 = new Lookup.MetadataModifications(keepOnlySpecified: true);
newMetadata2.Add("m4", "m4");
group = lookup.GetItems(item1.ItemType);
lookup.ModifyItems(item1.ItemType, group, newMetadata2);
group = lookup.GetItems("i1");
Assert.Single(group);
// m1, m2 and m3 are gone
Assert.Equal(String.Empty, group.First().GetMetadataValue("m1"));
Assert.Equal(String.Empty, group.First().GetMetadataValue("m2"));
Assert.Equal(String.Empty, group.First().GetMetadataValue("m3"));
// m4 is still there.
Assert.Equal("m4", group.First().GetMetadataValue("m4"));
enteredScope.LeaveScope();
group = lookup.GetItems("i1");
Assert.Single(group);
// m1, m2 and m3 are gone
Assert.Equal(String.Empty, group.First().GetMetadataValue("m1"));
Assert.Equal(String.Empty, group.First().GetMetadataValue("m2"));
Assert.Equal(String.Empty, group.First().GetMetadataValue("m3"));
// m4 is still there.
Assert.Equal("m4", group.First().GetMetadataValue("m4"));
}
/// <summary>
/// Ensure that once keepOnlySpecified is set to true, it remains in effect, and that if a metadata modification is declared as 'keep value' that
/// the value as lower scopes is retained.
/// </summary>
[Fact]
public void KeepMetadataOnlySpecifiedPropagate4()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
Lookup lookup = LookupHelpers.CreateLookup(table1);
Lookup.Scope enteredScope = lookup.EnterScope("x");
// Add an item with m=m1
ProjectItemInstance item1 = new ProjectItemInstance(project, "i1", "a2", project.FullPath);
item1.SetMetadata("m1", "m1");
item1.SetMetadata("m2", "m2");
lookup.AddNewItem(item1);
Lookup.Scope enteredScope2 = lookup.EnterScope("x");
// Get rid of all of the metadata, then add m3
Lookup.MetadataModifications newMetadata = new Lookup.MetadataModifications(keepOnlySpecified: true);
newMetadata.Add("m3", "m3");
ICollection<ProjectItemInstance> group = lookup.GetItems(item1.ItemType);
lookup.ModifyItems(item1.ItemType, group, newMetadata);
group = lookup.GetItems("i1");
Assert.Single(group);
// m1 and m2 are gone.
Assert.Equal(String.Empty, group.First().GetMetadataValue("m1"));
Assert.Equal(String.Empty, group.First().GetMetadataValue("m2"));
// m3 is still there.
Assert.Equal("m3", group.First().GetMetadataValue("m3"));
enteredScope2.LeaveScope();
// Keep m3.
Lookup.MetadataModifications newMetadata2 = new Lookup.MetadataModifications(keepOnlySpecified: true);
newMetadata2["m3"] = Lookup.MetadataModification.CreateFromNoChange();
group = lookup.GetItems(item1.ItemType);
lookup.ModifyItems(item1.ItemType, group, newMetadata2);
group = lookup.GetItems("i1");
Assert.Single(group);
// m1 and m2 are gone
Assert.Equal(String.Empty, group.First().GetMetadataValue("m1"));
Assert.Equal(String.Empty, group.First().GetMetadataValue("m2"));
// m3 is still there
Assert.Equal("m3", group.First().GetMetadataValue("m3"));
enteredScope.LeaveScope();
group = lookup.GetItems("i1");
Assert.Single(group);
// m1 and m2 are gone
Assert.Equal(String.Empty, group.First().GetMetadataValue("m1"));
Assert.Equal(String.Empty, group.First().GetMetadataValue("m2"));
// m3 is still there.
Assert.Equal("m3", group.First().GetMetadataValue("m3"));
}
/// <summary>
/// Ensure that when keepOnlySpecified is true, we will clear all metadata unless it is retained using the 'NoChange' modification type.
/// </summary>
[Fact]
public void KeepMetadataOnlySpecified()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
Lookup lookup = LookupHelpers.CreateLookup(table1);
Lookup.Scope enteredScope = lookup.EnterScope("x");
// Add an item with m=m1
ProjectItemInstance item1 = new ProjectItemInstance(project, "i1", "a2", project.FullPath);
item1.SetMetadata("m1", "m1");
item1.SetMetadata("m2", "m2");
lookup.AddNewItem(item1);
Lookup.Scope enteredScope2 = lookup.EnterScope("x");
// Test keeping only specified metadata
Lookup.MetadataModifications newMetadata = new Lookup.MetadataModifications(keepOnlySpecified: true);
newMetadata["m1"] = Lookup.MetadataModification.CreateFromNoChange();
ICollection<ProjectItemInstance> group = lookup.GetItems(item1.ItemType);
lookup.ModifyItems(item1.ItemType, group, newMetadata);
group = lookup.GetItems("i1");
Assert.Single(group);
// m1 is still here.
Assert.Equal("m1", group.First().GetMetadataValue("m1"));
// m2 is gone
Assert.Equal(String.Empty, group.First().GetMetadataValue("m2"));
enteredScope2.LeaveScope();
group = lookup.GetItems("i1");
Assert.Single(group);
// m1 should still be here
Assert.Equal("m1", group.First().GetMetadataValue("m1"));
// m2 is gone.
Assert.Equal(String.Empty, group.First().GetMetadataValue("m2"));
enteredScope.LeaveScope();
group = lookup.GetItems("i1");
Assert.Single(group);
// m1 should still be here
Assert.Equal("m1", group.First().GetMetadataValue("m1"));
// m2 should not persist here either
Assert.Equal(String.Empty, group.First().GetMetadataValue("m2"));
}
[Fact]
public void KeepMetadataOnlySpecifiedNoneSpecified()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
Lookup lookup = LookupHelpers.CreateLookup(table1);
Lookup.Scope enteredScope = lookup.EnterScope("x");
// Add an item with m=m1
ProjectItemInstance item1 = new ProjectItemInstance(project, "i1", "a2", project.FullPath);
item1.SetMetadata("m1", "m1");
item1.SetMetadata("m2", "m2");
lookup.AddNewItem(item1);
Lookup.Scope enteredScope2 = lookup.EnterScope("x");
// Test keeping only specified metadata
Lookup.MetadataModifications newMetadata = new Lookup.MetadataModifications(keepOnlySpecified: true);
ICollection<ProjectItemInstance> group = lookup.GetItems(item1.ItemType);
lookup.ModifyItems(item1.ItemType, group, newMetadata);
group = lookup.GetItems("i1");
Assert.Single(group);
// m1 and m2 are gone.
Assert.Equal(String.Empty, group.First().GetMetadataValue("m1"));
Assert.Equal(String.Empty, group.First().GetMetadataValue("m2"));
enteredScope2.LeaveScope();
group = lookup.GetItems("i1");
Assert.Single(group);
// m1 and m2 are gone.
Assert.Equal(String.Empty, group.First().GetMetadataValue("m1"));
Assert.Equal(String.Empty, group.First().GetMetadataValue("m2"));
enteredScope.LeaveScope();
group = lookup.GetItems("i1");
Assert.Single(group);
// m1 and m2 are gone.
Assert.Equal(String.Empty, group.First().GetMetadataValue("m1"));
Assert.Equal(String.Empty, group.First().GetMetadataValue("m2"));
}
[Fact]
public void ModifyItem()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
Lookup lookup = LookupHelpers.CreateLookup(table1);
Lookup.Scope enteredScope = lookup.EnterScope("x");
// Add an item with m=m1
ProjectItemInstance item1 = new ProjectItemInstance(project, "i1", "a2", project.FullPath);
item1.SetMetadata("m", "m1");
lookup.AddNewItem(item1);
Lookup.Scope enteredScope2 = lookup.EnterScope("x");
// Change the item to be m=m2
Lookup.MetadataModifications newMetadata = new Lookup.MetadataModifications(keepOnlySpecified: false);
newMetadata.Add("m", "m2");
ICollection<ProjectItemInstance> group = new List<ProjectItemInstance>();
group.Add(item1);
lookup.ModifyItems(item1.ItemType, group, newMetadata);
// Now it has m=m2
group = lookup.GetItems("i1");
Assert.Single(group);
Assert.Equal("m2", group.First().GetMetadataValue("m"));
// But the original item hasn't changed yet
Assert.Equal("m1", item1.GetMetadataValue("m"));
enteredScope2.LeaveScope();
// It still has m=m2
group = lookup.GetItems("i1");
Assert.Single(group);
Assert.Equal("m2", group.First().GetMetadataValue("m"));
// The original item still hasn't changed
// even though it was added in this scope
Assert.Equal("m1", item1.GetMetadataValue("m"));
enteredScope.LeaveScope();
// It still has m=m2
group = lookup.GetItems("i1");
Assert.Single(group);
Assert.Equal("m2", group.First().GetMetadataValue("m"));
// But now the original item has changed
Assert.Equal("m2", item1.GetMetadataValue("m"));
}
/// <summary>
/// Modifications should be merged
/// </summary>
[Fact]
public void ModifyItemModifiedInPreviousScope()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
Lookup lookup = LookupHelpers.CreateLookup(table1);
// Add an item with m=m1 and n=n1
ProjectItemInstance item1 = new ProjectItemInstance(project, "i1", "a2", project.FullPath);
item1.SetMetadata("m", "m1");
lookup.PopulateWithItem(item1);
lookup.EnterScope("x");
// Make a modification to the item to be m=m2
Lookup.MetadataModifications newMetadata = new Lookup.MetadataModifications(keepOnlySpecified: false);
newMetadata.Add("m", "m2");
newMetadata.Add("n", "n2");
ICollection<ProjectItemInstance> group = new List<ProjectItemInstance>();
group.Add(item1);
lookup.ModifyItems(item1.ItemType, group, newMetadata);
lookup.EnterScope("x");
// Make another modification to the item
newMetadata = new Lookup.MetadataModifications(keepOnlySpecified: false);
newMetadata.Add("m", "m3");
newMetadata.Add("o", "o3");
lookup.ModifyItems(item1.ItemType, group, newMetadata);
// It's now m=m3, n=n2, o=o3
group = lookup.GetItems("i1");
Assert.Single(group);
Assert.Equal("m3", group.First().GetMetadataValue("m"));
Assert.Equal("n2", group.First().GetMetadataValue("n"));
Assert.Equal("o3", group.First().GetMetadataValue("o"));
}
/// <summary>
/// Modifications should be merged
/// </summary>
[Fact]
public void ModifyItemTwiceInSameScope1()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
Lookup lookup = LookupHelpers.CreateLookup(table1);
// Add an item with m=m1 and n=n1
ProjectItemInstance item1 = new ProjectItemInstance(project, "i1", "a2", project.FullPath);
item1.SetMetadata("m", "m1");
lookup.PopulateWithItem(item1);
lookup.EnterScope("x");
// Make a modification to the item to be m=m2
Lookup.MetadataModifications newMetadata = new Lookup.MetadataModifications(keepOnlySpecified: false);
newMetadata.Add("m", "m2");
ICollection<ProjectItemInstance> group = new List<ProjectItemInstance>();
group.Add(item1);
lookup.ModifyItems(item1.ItemType, group, newMetadata);
// Make an unrelated modification to the item
newMetadata = new Lookup.MetadataModifications(keepOnlySpecified: false);
newMetadata.Add("n", "n1");
lookup.ModifyItems(item1.ItemType, group, newMetadata);
// It's now m=m2
group = lookup.GetItems("i1");
Assert.Single(group);
Assert.Equal("m2", group.First().GetMetadataValue("m"));
}
/// <summary>
/// Modifications should be merged
/// </summary>
[Fact]
public void ModifyItemTwiceInSameScope2()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
Lookup lookup = LookupHelpers.CreateLookup(table1);
// Add an item with m=m1 and n=n1 and o=o1
ProjectItemInstance item1 = new ProjectItemInstance(project, "i1", "a2", project.FullPath);
item1.SetMetadata("m", "m1");
item1.SetMetadata("n", "n1");
item1.SetMetadata("o", "o1");
lookup.PopulateWithItem(item1);
Lookup.Scope enteredScope = lookup.EnterScope("x");
// It's still m=m1, n=n1, o=o1
ICollection<ProjectItemInstance> group = lookup.GetItems("i1");
Assert.Single(group);
Assert.Equal("m1", group.First().GetMetadataValue("m"));
Assert.Equal("n1", group.First().GetMetadataValue("n"));
Assert.Equal("o1", group.First().GetMetadataValue("o"));
// Make a modification to the item to be m=m2 and n=n2
Lookup.MetadataModifications newMetadata = new Lookup.MetadataModifications(keepOnlySpecified: false);
newMetadata.Add("m", "m2");
newMetadata.Add("n", "n2");
group = new List<ProjectItemInstance>();
group.Add(item1);
lookup.ModifyItems("i1", group, newMetadata);
// It's now m=m2, n=n2, o=o1
ICollection<ProjectItemInstance> foundGroup = lookup.GetItems("i1");
Assert.Single(foundGroup);
Assert.Equal("m2", foundGroup.First().GetMetadataValue("m"));
Assert.Equal("n2", foundGroup.First().GetMetadataValue("n"));
Assert.Equal("o1", foundGroup.First().GetMetadataValue("o"));
// Make a modification to the item to be n=n3
newMetadata = new Lookup.MetadataModifications(keepOnlySpecified: false);
newMetadata.Add("n", "n3");
lookup.ModifyItems("i1", group, newMetadata);
// It's now m=m2, n=n3, o=o1
foundGroup = lookup.GetItems("i1");
Assert.Single(foundGroup);
Assert.Equal("m2", foundGroup.First().GetMetadataValue("m"));
Assert.Equal("n3", foundGroup.First().GetMetadataValue("n"));
Assert.Equal("o1", foundGroup.First().GetMetadataValue("o"));
// But the original item hasn't changed yet
Assert.Equal("m1", item1.GetMetadataValue("m"));
Assert.Equal("n1", item1.GetMetadataValue("n"));
Assert.Equal("o1", item1.GetMetadataValue("o"));
enteredScope.LeaveScope();
// It's still m=m2, n=n3, o=o1
foundGroup = lookup.GetItems("i1");
Assert.Single(foundGroup);
Assert.Equal("m2", foundGroup.First().GetMetadataValue("m"));
Assert.Equal("n3", foundGroup.First().GetMetadataValue("n"));
Assert.Equal("o1", foundGroup.First().GetMetadataValue("o"));
// And the original item has changed
Assert.Equal("m2", item1.GetMetadataValue("m"));
Assert.Equal("n3", item1.GetMetadataValue("n"));
Assert.Equal("o1", item1.GetMetadataValue("o"));
}
[Fact]
public void ModifyItemThatWasAddedInSameScope()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
Lookup lookup = LookupHelpers.CreateLookup(table1);
Lookup.Scope enteredScope = lookup.EnterScope("x");
// Add an item with m=m1
ProjectItemInstance item1 = new ProjectItemInstance(project, "i1", "a2", project.FullPath);
item1.SetMetadata("m", "m1");
lookup.AddNewItem(item1);
// Change the item to be m=m2
Lookup.MetadataModifications newMetadata = new Lookup.MetadataModifications(keepOnlySpecified: false);
newMetadata.Add("m", "m2");
ICollection<ProjectItemInstance> group = new List<ProjectItemInstance>();
group.Add(item1);
lookup.ModifyItems(item1.ItemType, group, newMetadata);
// Now it has m=m2
group = lookup.GetItems("i1");
Assert.Single(group);
Assert.Equal("m2", group.First().GetMetadataValue("m"));
// But the original item hasn't changed yet
Assert.Equal("m1", item1.GetMetadataValue("m"));
enteredScope.LeaveScope();
// It still has m=m2
group = lookup.GetItems("i1");
Assert.Single(group);
Assert.Equal("m2", group.First().GetMetadataValue("m"));
// But now the original item has changed as well
Assert.Equal("m2", item1.GetMetadataValue("m"));
}
/// <summary>
/// Modifying an item in the outside scope is prohibited-
/// purely because we don't need to do it in our code
/// </summary>
[Fact]
public void ModifyItemInOutsideScope()
{
Assert.Throws<InternalErrorException>(() =>
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
Lookup lookup = LookupHelpers.CreateLookup(new ItemDictionary<ProjectItemInstance>());
lookup.AddNewItem(new ProjectItemInstance(project, "x", "y", project.FullPath));
});
}
/// <summary>
/// After modification, should be able to GetItem and then modify it again
/// </summary>
[Fact]
public void ModifyItemPreviouslyModifiedAndGottenThroughGetItem()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
Lookup lookup = LookupHelpers.CreateLookup(table1);
// Add an item with m=m1 and n=n1
ProjectItemInstance item1 = new ProjectItemInstance(project, "i1", "a2", project.FullPath);
item1.SetMetadata("m", "m1");
lookup.PopulateWithItem(item1);
Lookup.Scope enteredScope = lookup.EnterScope("x");
// Make a modification to the item to be m=m2
Lookup.MetadataModifications newMetadata = new Lookup.MetadataModifications(keepOnlySpecified: false);
newMetadata.Add("m", "m2");
ICollection<ProjectItemInstance> group = new List<ProjectItemInstance>();
group.Add(item1);
lookup.ModifyItems(item1.ItemType, group, newMetadata);
// Get the item (under the covers, it cloned it in order to apply the modification)
ICollection<ProjectItemInstance> group2 = lookup.GetItems(item1.ItemType);
Assert.Single(group2);
ProjectItemInstance item1b = group2.First();
// Modify to m=m3
Lookup.MetadataModifications newMetadata2 = new Lookup.MetadataModifications(keepOnlySpecified: false);
newMetadata2.Add("m", "m3");
ICollection<ProjectItemInstance> group3 = new List<ProjectItemInstance>();
group3.Add(item1b);
lookup.ModifyItems(item1b.ItemType, group3, newMetadata2);
// Modifications are visible
ICollection<ProjectItemInstance> group4 = lookup.GetItems(item1b.ItemType);
Assert.Single(group4);
Assert.Equal("m3", group4.First().GetMetadataValue("m"));
// Leave scope
enteredScope.LeaveScope();
// Still visible
ICollection<ProjectItemInstance> group5 = lookup.GetItems(item1b.ItemType);
Assert.Single(group5);
Assert.Equal("m3", group5.First().GetMetadataValue("m"));
}
/// <summary>
/// After modification, should be able to GetItem and then modify it again
/// </summary>
[Fact]
public void ModifyItemInProjectPreviouslyModifiedAndGottenThroughGetItem()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
// Create some project state with an item with m=m1 and n=n1
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
ProjectItemInstance item1 = new ProjectItemInstance(project, "i1", "a2", project.FullPath);
item1.SetMetadata("m", "m1");
table1.Add(item1);
Lookup lookup = LookupHelpers.CreateLookup(table1);
Lookup.Scope enteredScope = lookup.EnterScope("x");
// Make a modification to the item to be m=m2
Lookup.MetadataModifications newMetadata = new Lookup.MetadataModifications(keepOnlySpecified: false);
newMetadata.Add("m", "m2");
List<ProjectItemInstance> group = new List<ProjectItemInstance>();
group.Add(item1);
lookup.ModifyItems(item1.ItemType, group, newMetadata);
// Get the item (under the covers, it cloned it in order to apply the modification)
ICollection<ProjectItemInstance> group2 = lookup.GetItems(item1.ItemType);
Assert.Single(group2);
ProjectItemInstance item1b = group2.First();
// Modify to m=m3
Lookup.MetadataModifications newMetadata2 = new Lookup.MetadataModifications(keepOnlySpecified: false);
newMetadata2.Add("m", "m3");
List<ProjectItemInstance> group3 = new List<ProjectItemInstance>();
group3.Add(item1b);
lookup.ModifyItems(item1b.ItemType, group3, newMetadata2);
// Modifications are visible
ICollection<ProjectItemInstance> group4 = lookup.GetItems(item1b.ItemType);
Assert.Single(group4);
Assert.Equal("m3", group4.First().GetMetadataValue("m"));
// Leave scope
enteredScope.LeaveScope();
// Still visible
ICollection<ProjectItemInstance> group5 = lookup.GetItems(item1b.ItemType);
Assert.Single(group5);
Assert.Equal("m3", group5.First().GetMetadataValue("m"));
// And the one in the project is changed
Assert.Equal("m3", item1.GetMetadataValue("m"));
}
/// <summary>
/// After modification, should be able to GetItem and then remove it
/// </summary>
[Fact]
public void RemoveItemPreviouslyModifiedAndGottenThroughGetItem()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
Lookup lookup = LookupHelpers.CreateLookup(table1);
// Add an item with m=m1 and n=n1
ProjectItemInstance item1 = new ProjectItemInstance(project, "i1", "a2", project.FullPath);
item1.SetMetadata("m", "m1");
lookup.PopulateWithItem(item1);
lookup.EnterScope("x");
// Make a modification to the item to be m=m2
Lookup.MetadataModifications newMetadata = new Lookup.MetadataModifications(keepOnlySpecified: false);
newMetadata.Add("m", "m2");
List<ProjectItemInstance> group = new List<ProjectItemInstance>();
group.Add(item1);
lookup.ModifyItems(item1.ItemType, group, newMetadata);
// Get the item (under the covers, it cloned it in order to apply the modification)
ICollection<ProjectItemInstance> group2 = lookup.GetItems(item1.ItemType);
Assert.Single(group2);
ProjectItemInstance item1b = group2.First();
// Remove the item
lookup.RemoveItem(item1b);
// There's now no items at all
ICollection<ProjectItemInstance> group3 = lookup.GetItems(item1.ItemType);
Assert.Empty(group3);
}
/// <summary>
/// After modification, should be able to GetItem and then remove it
/// </summary>
[Fact]
public void RemoveItemFromProjectPreviouslyModifiedAndGottenThroughGetItem()
{
ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance();
// Create some project state with an item with m=m1 and n=n1
ItemDictionary<ProjectItemInstance> table1 = new ItemDictionary<ProjectItemInstance>();
ProjectItemInstance item1 = new ProjectItemInstance(project, "i1", "a2", project.FullPath);
item1.SetMetadata("m", "m1");
table1.Add(item1);
Lookup lookup = LookupHelpers.CreateLookup(table1);
Lookup.Scope enteredScope = lookup.EnterScope("x");
// Make a modification to the item to be m=m2
Lookup.MetadataModifications newMetadata = new Lookup.MetadataModifications(keepOnlySpecified: false);
newMetadata.Add("m", "m2");
List<ProjectItemInstance> group = new List<ProjectItemInstance>();
group.Add(item1);
lookup.ModifyItems(item1.ItemType, group, newMetadata);
// Get the item (under the covers, it cloned it in order to apply the modification)
ICollection<ProjectItemInstance> group2 = lookup.GetItems(item1.ItemType);
Assert.Single(group2);
ProjectItemInstance item1b = group2.First();
// Remove the item
lookup.RemoveItem(item1b);
// There's now no items at all
ICollection<ProjectItemInstance> group3 = lookup.GetItems(item1.ItemType);
Assert.Empty(group3);
// Leave scope
enteredScope.LeaveScope();
// And now none left in the project either
Assert.Empty(table1["i1"]);
}
/// <summary>
/// If the property isn't modified, the initial property
/// should be returned
/// </summary>
[Fact]
public void UnmodifiedProperty()
{
PropertyDictionary<ProjectPropertyInstance> group = new PropertyDictionary<ProjectPropertyInstance>();
ProjectPropertyInstance property = ProjectPropertyInstance.Create("p1", "v1");
group.Set(property);
Lookup lookup = LookupHelpers.CreateLookup(group);
Assert.Equal(property, lookup.GetProperty("p1"));
lookup.EnterScope("x");
Assert.Equal(property, lookup.GetProperty("p1"));
}
/// <summary>
/// If the property isn't found, should return null
/// </summary>
[Fact]
public void NonexistentProperty()
{
PropertyDictionary<ProjectPropertyInstance> group = new PropertyDictionary<ProjectPropertyInstance>();
Lookup lookup = LookupHelpers.CreateLookup(group);
Assert.Null(lookup.GetProperty("p1"));
lookup.EnterScope("x");
Assert.Null(lookup.GetProperty("p1"));
}
/// <summary>
/// If the property is modified, the updated value should be returned,
/// both before and after leaving scope.
/// </summary>
[Fact]
public void ModifiedProperty()
{
PropertyDictionary<ProjectPropertyInstance> group = new PropertyDictionary<ProjectPropertyInstance>();
group.Set(ProjectPropertyInstance.Create("p1", "v1"));
Lookup lookup = LookupHelpers.CreateLookup(group);
// Enter scope so that property sets are allowed on it
Lookup.Scope enteredScope = lookup.EnterScope("x");
// Change the property value
lookup.SetProperty(ProjectPropertyInstance.Create("p1", "v2"));
// Lookup is updated, but not original item group
Assert.Equal("v2", lookup.GetProperty("p1").EvaluatedValue);
Assert.Equal("v1", group["p1"].EvaluatedValue);
Lookup.Scope enteredScope2 = lookup.EnterScope("x");
// Change the value again in the new scope
lookup.SetProperty(ProjectPropertyInstance.Create("p1", "v3"));
// Lookup is updated, but not the original item group
Assert.Equal("v3", lookup.GetProperty("p1").EvaluatedValue);
Assert.Equal("v1", group["p1"].EvaluatedValue);
Lookup.Scope enteredScope3 = lookup.EnterScope("x");
// Change the value again in the new scope
lookup.SetProperty(ProjectPropertyInstance.Create("p1", "v4"));
Assert.Equal("v4", lookup.GetProperty("p1").EvaluatedValue);
enteredScope3.LeaveScope();
Assert.Equal("v4", lookup.GetProperty("p1").EvaluatedValue);
// Leave to the outer scope
enteredScope2.LeaveScope();
enteredScope.LeaveScope();
// Now the lookup and original group are updated
Assert.Equal("v4", lookup.GetProperty("p1").EvaluatedValue);
Assert.Equal("v4", group["p1"].EvaluatedValue);
}
}
internal sealed class LookupHelpers
{
internal static Lookup CreateEmptyLookup()
{
Lookup lookup = new Lookup(new ItemDictionary<ProjectItemInstance>(), new PropertyDictionary<ProjectPropertyInstance>());
return lookup;
}
internal static Lookup CreateLookup(ItemDictionary<ProjectItemInstance> items)
{
Lookup lookup = new Lookup(items, new PropertyDictionary<ProjectPropertyInstance>());
return lookup;
}
internal static Lookup CreateLookup(PropertyDictionary<ProjectPropertyInstance> properties)
{
Lookup lookup = new Lookup(new ItemDictionary<ProjectItemInstance>(), properties);
return lookup;
}
internal static Lookup CreateLookup(PropertyDictionary<ProjectPropertyInstance> properties, ItemDictionary<ProjectItemInstance> items)
{
Lookup lookup = new Lookup(items, properties);
return lookup;
}
}
}
|