diff --git a/README.md b/README.md
index d4ba7c9ef..967654c56 100644
--- a/README.md
+++ b/README.md
@@ -9,16 +9,26 @@ I need an app that uses MVVM Light, uses master detail, can suspend and resume,
## Build Status
-|Branch |CI |Gen Tests |Full Tests |Test Version|Version|
-|:--------|:----------------:|:---------------:|:---------------:|:---------------:|:---------------:|
-|master|[![Build status](https://ci.appveyor.com/api/projects/status/nf8r35r45o4yqbqs/branch/master?svg=true)](https://ci.appveyor.com/project/ralarcon/windowstemplatestudio/branch/master)|[![Generation Tests](https://winappstudio.visualstudio.com/_apis/public/build/definitions/5c80cfe7-3bfb-4799-9d04-803c84df7a60/141/badge)](https://winappstudio.visualstudio.com/DefaultCollection/Vegas/_build/index?definitionId=141)|[![Full Integration Tests](https://winappstudio.visualstudio.com/_apis/public/build/definitions/5c80cfe7-3bfb-4799-9d04-803c84df7a60/129/badge)](https://winappstudio.visualstudio.com/DefaultCollection/Vegas/_build/index?definitionId=129)|[![Prerelease Version](https://wtsrepository.blob.core.windows.net/badges/img.prerelease.version.svg)](https://github.com/Microsoft/WindowsTemplateStudio/blob/master/docs/getting-started-extension.md#nightly--pre-release-feeds-for-windows-template-studio) |[![Production Version](https://wtsrepository.blob.core.windows.net/badges/img.release.version.svg)](https://marketplace.visualstudio.com/items?itemName=WASTeamAccount.WindowsTemplateStudio)|
-|dev|[![Build status](https://ci.appveyor.com/api/projects/status/nf8r35r45o4yqbqs/branch/dev?svg=true)](https://ci.appveyor.com/project/ralarcon/windowstemplatestudio/branch/dev)|[![Generation Tests](https://winappstudio.visualstudio.com/_apis/public/build/definitions/5c80cfe7-3bfb-4799-9d04-803c84df7a60/135/badge)](https://winappstudio.visualstudio.com/DefaultCollection/Vegas/_build/index?definitionId=135)|[![Full Integration Tests](https://winappstudio.visualstudio.com/_apis/public/build/definitions/5c80cfe7-3bfb-4799-9d04-803c84df7a60/128/badge)](https://winappstudio.visualstudio.com/DefaultCollection/Vegas/_build/index?definitionId=128)|[![Nightly Version](https://wtsrepository.blob.core.windows.net/badges/img.nightly.version.svg)](https://github.com/Microsoft/WindowsTemplateStudio/blob/master/docs/getting-started-extension.md#nightly--pre-release-feeds-for-windows-template-studio)||
+
+|Branch |CI |Test Version|Version|
+|:--------|:----------------:|:---------------:|:---------------:|
+|master|[![Build status](https://ci.appveyor.com/api/projects/status/nf8r35r45o4yqbqs/branch/master?svg=true)](https://ci.appveyor.com/project/ralarcon/windowstemplatestudio/branch/master)|[![Prerelease Version](https://wtsrepository.blob.core.windows.net/badges/img.prerelease.version.svg)](https://github.com/Microsoft/WindowsTemplateStudio/blob/master/docs/getting-started-extension.md#nightly--pre-release-feeds-for-windows-template-studio) |[![Production Version](https://wtsrepository.blob.core.windows.net/badges/img.release.version.svg)](https://marketplace.visualstudio.com/items?itemName=WASTeamAccount.WindowsTemplateStudio)|
+|dev|[![Build status](https://ci.appveyor.com/api/projects/status/nf8r35r45o4yqbqs/branch/dev?svg=true)](https://ci.appveyor.com/project/ralarcon/windowstemplatestudio/branch/dev)|[![Nightly Version](https://wtsrepository.blob.core.windows.net/badges/img.nightly.version.svg)](https://github.com/Microsoft/WindowsTemplateStudio/blob/master/docs/getting-started-extension.md#nightly--pre-release-feeds-for-windows-template-studio)||
+
+
+
+|Branch |Gen Tests |Full Tests |WACK Tests |
+|:--------|:---------------:|:---------------:|:---------------:|
+|master|[![Generation Tests](https://winappstudio.visualstudio.com/_apis/public/build/definitions/5c80cfe7-3bfb-4799-9d04-803c84df7a60/141/badge)](https://winappstudio.visualstudio.com/DefaultCollection/Vegas/_build/index?definitionId=141)|[![Full Integration Tests](https://winappstudio.visualstudio.com/_apis/public/build/definitions/5c80cfe7-3bfb-4799-9d04-803c84df7a60/129/badge)](https://winappstudio.visualstudio.com/DefaultCollection/Vegas/_build/index?definitionId=129)|
+|dev|[![Generation Tests](https://winappstudio.visualstudio.com/_apis/public/build/definitions/5c80cfe7-3bfb-4799-9d04-803c84df7a60/135/badge)](https://winappstudio.visualstudio.com/DefaultCollection/Vegas/_build/index?definitionId=135)|[![Full Integration Tests](https://winappstudio.visualstudio.com/_apis/public/build/definitions/5c80cfe7-3bfb-4799-9d04-803c84df7a60/128/badge)](https://winappstudio.visualstudio.com/DefaultCollection/Vegas/_build/index?definitionId=128)|[![Wack Tests](https://winappstudio.visualstudio.com/DefaultCollection/_apis/public/build/definitions/5c80cfe7-3bfb-4799-9d04-803c84df7a60/142/badge)](https://winappstudio.visualstudio.com/DefaultCollection/Vegas/_build/index?definitionId=142)
+
+
> The builds include test verifications to validate the contributions:
> * *CI Build*: Includes all unit test + minimum integration verifications (minumum generation + build + code style rules). Runs every PR requested / PR accepted.
> * *Gen Tests*: Includes tests to verify combinations and variations of templates from a project generation point of view. Runs every PR accepted and takes a bit to complete.
> * *Full Tests*: Includes `Gen Tests` and actually builds the solutions generated to ensure no build time issues found. Runs every PR accepted and takes longer to be completed.
->
+> * *Wack Tests*: Includes tests that run the App Certification Kit against the generated projects to ensure there are no issues blocking a submission to the store. Runs once nightly and takes quite a while to complete.
## Features
diff --git a/code/src/Core/Core.csproj b/code/src/Core/Core.csproj
index 4b3066ed3..ac9a7d189 100644
--- a/code/src/Core/Core.csproj
+++ b/code/src/Core/Core.csproj
@@ -182,6 +182,8 @@
+
+
diff --git a/code/src/Core/PostActions/Catalog/Merge/GetMergeFilesFromProjectPostAction.cs b/code/src/Core/PostActions/Catalog/Merge/GetMergeFilesFromProjectPostAction.cs
index cf64be85c..4d85f196f 100644
--- a/code/src/Core/PostActions/Catalog/Merge/GetMergeFilesFromProjectPostAction.cs
+++ b/code/src/Core/PostActions/Catalog/Merge/GetMergeFilesFromProjectPostAction.cs
@@ -18,7 +18,7 @@ namespace Microsoft.Templates.Core.PostActions.Catalog.Merge
public override void Execute()
{
- if (Regex.IsMatch(_config, MergePostAction.GlobalExtension))
+ if (Regex.IsMatch(_config, MergeConfiguration.GlobalExtension))
{
GetFileFromProject();
}
@@ -55,16 +55,16 @@ namespace Microsoft.Templates.Core.PostActions.Catalog.Merge
private string GetMergeFileFromDirectory(string directory)
{
- if (Path.GetFileName(_config).StartsWith(MergePostAction.Extension))
+ if (Path.GetFileName(_config).StartsWith(MergeConfiguration.Extension))
{
var extension = Path.GetExtension(_config);
- return Directory.EnumerateFiles(directory, $"*{extension}").FirstOrDefault(f => !Regex.IsMatch(f, MergePostAction.PostactionRegex));
+ return Directory.EnumerateFiles(directory, $"*{extension}").FirstOrDefault(f => !Regex.IsMatch(f, MergeConfiguration.PostactionRegex));
}
else
{
var filePath = Path.Combine(directory, Path.GetFileName(_config));
- var path = Regex.Replace(filePath, MergePostAction.PostactionRegex, ".");
+ var path = Regex.Replace(filePath, MergeConfiguration.PostactionRegex, ".");
return path;
}
diff --git a/code/src/Core/PostActions/Catalog/Merge/MergeConfiguration.cs b/code/src/Core/PostActions/Catalog/Merge/MergeConfiguration.cs
index 9ae891897..40c808e69 100644
--- a/code/src/Core/PostActions/Catalog/Merge/MergeConfiguration.cs
+++ b/code/src/Core/PostActions/Catalog/Merge/MergeConfiguration.cs
@@ -12,6 +12,19 @@ namespace Microsoft.Templates.Core.PostActions.Catalog.Merge
{
public class MergeConfiguration
{
+ public const string Suffix = "postaction";
+ public const string NewSuffix = "failedpostaction";
+
+ public const string PostactionRegex = @"(\$\S*)?(_" + Suffix + "|_g" + Suffix + @")\.";
+ public const string FailedPostactionRegex = @"(\$\S*)?(_" + NewSuffix + "|_g" + NewSuffix + @")(\d)?\.";
+
+ public const string Extension = "_" + Suffix + ".";
+ public const string GlobalExtension = "$*_g" + Suffix + ".";
+
+ public const string ResourceDictionaryMatch = @"";
+
public string FilePath { get; private set; }
public bool FailOnError { get; private set; }
diff --git a/code/src/Core/PostActions/Catalog/Merge/MergeFailureType.cs b/code/src/Core/PostActions/Catalog/Merge/MergeFailureType.cs
index 958e49e1a..29ee92ed4 100644
--- a/code/src/Core/PostActions/Catalog/Merge/MergeFailureType.cs
+++ b/code/src/Core/PostActions/Catalog/Merge/MergeFailureType.cs
@@ -7,6 +7,7 @@ namespace Microsoft.Templates.Core.PostActions.Catalog.Merge
public enum MergeFailureType
{
FileNotFound,
- LineNotFound
+ LineNotFound,
+ KeyAlreadyDefined
}
}
diff --git a/code/src/Core/PostActions/Catalog/Merge/MergePostAction.cs b/code/src/Core/PostActions/Catalog/Merge/MergePostAction.cs
index 2c65613b9..55b6f9bdd 100644
--- a/code/src/Core/PostActions/Catalog/Merge/MergePostAction.cs
+++ b/code/src/Core/PostActions/Catalog/Merge/MergePostAction.cs
@@ -16,15 +16,6 @@ namespace Microsoft.Templates.Core.PostActions.Catalog.Merge
{
public class MergePostAction : PostAction
{
- private const string Suffix = "postaction";
- private const string NewSuffix = "failedpostaction";
-
- public const string Extension = "_" + Suffix + ".";
- public const string GlobalExtension = "$*_g" + Suffix + ".";
-
- public const string PostactionRegex = @"(\$\S*)?(_" + Suffix + "|_g" + Suffix + @")\.";
- public const string FailedPostactionRegex = @"(\$\S*)?(_" + NewSuffix + "|_g" + NewSuffix + @")(\d)?\.";
-
public MergePostAction(MergeConfiguration config) : base(config)
{
}
@@ -80,32 +71,36 @@ namespace Microsoft.Templates.Core.PostActions.Catalog.Merge
File.Delete(_config.FilePath);
}
- private void AddFailedMergePostActionsFileNotFound(string originalFilePath)
+ protected void AddFailedMergePostActions(string originalFilePath, MergeFailureType mergeFailureType, string description)
{
- var sourceFileName = originalFilePath.Replace(GenContext.Current.OutputPath + Path.DirectorySeparatorChar, string.Empty);
- var postactionFileName = _config.FilePath.Replace(GenContext.Current.OutputPath + Path.DirectorySeparatorChar, string.Empty);
-
- var description = string.Format(StringRes.FailedMergePostActionFileNotFound, sourceFileName);
+ var sourceFileName = GetRelativePath(originalFilePath);
+ var postactionFileName = GetRelativePath(_config.FilePath);
var failedFileName = GetFailedPostActionFileName();
- GenContext.Current.FailedMergePostActions.Add(new FailedMergePostAction(sourceFileName, _config.FilePath, failedFileName, description, MergeFailureType.FileNotFound));
+ GenContext.Current.FailedMergePostActions.Add(new FailedMergePostAction(sourceFileName, _config.FilePath, failedFileName, description, mergeFailureType));
File.Copy(_config.FilePath, failedFileName, true);
}
+ protected string GetRelativePath(string path)
+ {
+ return path.Replace(GenContext.Current.OutputPath + Path.DirectorySeparatorChar, string.Empty);
+ }
+
+ private void AddFailedMergePostActionsFileNotFound(string originalFilePath)
+ {
+ var description = string.Format(StringRes.FailedMergePostActionFileNotFound, GetRelativePath(originalFilePath));
+ AddFailedMergePostActions(originalFilePath, MergeFailureType.FileNotFound, description);
+ }
+
private void AddFailedMergePostActionsAddLineNotFound(string originalFilePath, string errorLine)
{
- var sourceFileName = originalFilePath.Replace(GenContext.Current.OutputPath + Path.DirectorySeparatorChar, string.Empty);
-
- var postactionFileName = _config.FilePath.Replace(GenContext.Current.OutputPath + Path.DirectorySeparatorChar, string.Empty);
- var description = string.Format(StringRes.FailedMergePostActionLineNotFound, errorLine.Trim(), sourceFileName);
- var failedFileName = GetFailedPostActionFileName();
- GenContext.Current.FailedMergePostActions.Add(new FailedMergePostAction(sourceFileName, _config.FilePath, failedFileName, description, MergeFailureType.LineNotFound));
- File.Copy(_config.FilePath, failedFileName, true);
+ var description = string.Format(StringRes.FailedMergePostActionLineNotFound, errorLine.Trim(), GetRelativePath(originalFilePath));
+ AddFailedMergePostActions(originalFilePath, MergeFailureType.LineNotFound, description);
}
private string GetFailedPostActionFileName()
{
- var newFileName = Path.GetFileNameWithoutExtension(_config.FilePath).Replace(Suffix, NewSuffix);
+ var newFileName = Path.GetFileNameWithoutExtension(_config.FilePath).Replace(MergeConfiguration.Suffix, MergeConfiguration.NewSuffix);
var folder = Path.GetDirectoryName(_config.FilePath);
var extension = Path.GetExtension(_config.FilePath);
@@ -120,16 +115,16 @@ namespace Microsoft.Templates.Core.PostActions.Catalog.Merge
private string GetFilePath()
{
- if (Path.GetFileName(_config.FilePath).StartsWith(Extension, StringComparison.InvariantCultureIgnoreCase))
+ if (Path.GetFileName(_config.FilePath).StartsWith(MergeConfiguration.Extension, StringComparison.InvariantCultureIgnoreCase))
{
var extension = Path.GetExtension(_config.FilePath);
var directory = Path.GetDirectoryName(_config.FilePath);
- return Directory.EnumerateFiles(directory, $"*{extension}").FirstOrDefault(f => !f.Contains(Suffix));
+ return Directory.EnumerateFiles(directory, $"*{extension}").FirstOrDefault(f => !f.Contains(MergeConfiguration.Suffix));
}
else
{
- var path = Regex.Replace(_config.FilePath, PostactionRegex, ".");
+ var path = Regex.Replace(_config.FilePath, MergeConfiguration.PostactionRegex, ".");
return path;
}
diff --git a/code/src/Core/PostActions/Catalog/Merge/MergeResourceDictionaryPostAction.cs b/code/src/Core/PostActions/Catalog/Merge/MergeResourceDictionaryPostAction.cs
new file mode 100644
index 000000000..2d7f7b234
--- /dev/null
+++ b/code/src/Core/PostActions/Catalog/Merge/MergeResourceDictionaryPostAction.cs
@@ -0,0 +1,117 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Xml;
+using System.Xml.Linq;
+using Microsoft.Templates.Core.Gen;
+using Microsoft.Templates.Core.Resources;
+
+namespace Microsoft.Templates.Core.PostActions.Catalog.Merge
+{
+ public class MergeResourceDictionaryPostAction : MergePostAction
+ {
+ private const string mergeDictionaryPattern = @"
+
+
+
+
+";
+
+ public MergeResourceDictionaryPostAction(MergeConfiguration config) : base(config)
+ {
+ }
+
+ public override void Execute()
+ {
+ string originalFilePath = GetFilePath();
+ if (!File.Exists(originalFilePath))
+ {
+ File.Copy(_config.FilePath, originalFilePath);
+ GenContext.Current.ProjectItems.Add(originalFilePath);
+ AddToMergeDictionary(originalFilePath);
+ }
+ else
+ {
+ var mergeRoot = XElement.Load(_config.FilePath);
+ var sourceRoot = XElement.Load(originalFilePath);
+
+ foreach (var node in GetNodesToMerge(mergeRoot))
+ {
+ var sourceNode = sourceRoot.Elements().FirstOrDefault(e => GetKey(e) == GetKey(node));
+ if (sourceNode == null)
+ {
+ AddNodeToSource(sourceRoot, node);
+ }
+ else
+ {
+ if (!XNode.DeepEquals(node, sourceNode))
+ {
+ var errorMessage = string.Format(StringRes.FailedMergePostActionKeyAlreadyDefined, GetKey(node), GetRelativePath(originalFilePath));
+ if (_config.FailOnError)
+ {
+ throw new InvalidDataException(errorMessage);
+ }
+ else
+ {
+ AddFailedMergePostActions(originalFilePath, MergeFailureType.KeyAlreadyDefined, errorMessage);
+ File.Delete(_config.FilePath);
+ return;
+ }
+ }
+ }
+ }
+
+ using (TextWriter writeFile = new StreamWriter(originalFilePath))
+ {
+ var writer = new ResourceDictionaryWriter(writeFile);
+ writer.WriteResourceDictionary(sourceRoot);
+ writer.Flush();
+ writer.Close();
+ }
+ }
+
+ File.Delete(_config.FilePath);
+ }
+
+ private static void AddToMergeDictionary(string originalFilePath)
+ {
+ var relPath = originalFilePath.Replace(GenContext.Current.OutputPath, "").Replace(@"\", @"/");
+ var postactionContent = mergeDictionaryPattern.Replace("{filePath}", relPath);
+ var mergeDictionaryName = Path.GetFileNameWithoutExtension(originalFilePath);
+ File.WriteAllText(GenContext.Current.OutputPath + $"/App${mergeDictionaryName}_gpostaction.xaml", postactionContent);
+ }
+
+ private static void AddNodeToSource(XElement sourceRoot, XElement node)
+ {
+ if (node.PreviousNode != null && node.PreviousNode.NodeType == XmlNodeType.Comment)
+ {
+ sourceRoot.Add(node.PreviousNode);
+ }
+
+ sourceRoot.Add(node);
+ }
+
+ private string GetKey(XElement node)
+ {
+ XNamespace ns = node.GetNamespaceOfPrefix("x");
+ return node.Attribute(ns + "Key").Value;
+ }
+
+ private IEnumerable GetNodesToMerge(XElement rootNode)
+ {
+ return rootNode.Descendants().Where(e => e.Attributes().Any(a => a.Name.LocalName == "Key"));
+ }
+
+ private string GetFilePath()
+ {
+ return Regex.Replace(_config.FilePath, MergeConfiguration.PostactionRegex, ".");
+ }
+ }
+}
diff --git a/code/src/Core/PostActions/Catalog/Merge/ResourceDictionaryWriter.cs b/code/src/Core/PostActions/Catalog/Merge/ResourceDictionaryWriter.cs
new file mode 100644
index 000000000..fc4e10788
--- /dev/null
+++ b/code/src/Core/PostActions/Catalog/Merge/ResourceDictionaryWriter.cs
@@ -0,0 +1,133 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Xml;
+using System.Xml.Linq;
+
+namespace Microsoft.Templates.Core.PostActions.Catalog.Merge
+{
+ public class ResourceDictionaryWriter : XmlTextWriter
+ {
+ private TextWriter writer;
+ private const string intend = " ";
+
+ public ResourceDictionaryWriter(TextWriter w) : base(w)
+ {
+ writer = w;
+ }
+
+ public ResourceDictionaryWriter(Stream w, Encoding encoding) : base(w, encoding)
+ {
+ }
+
+ public ResourceDictionaryWriter(string filename, Encoding encoding) : base(filename, encoding)
+ {
+ }
+
+ public override void WriteStartElement(string prefix, string localName, string ns)
+ {
+ if (!string.IsNullOrEmpty(prefix))
+ {
+ localName = prefix + ":" + localName;
+ prefix = "";
+ }
+
+ base.WriteStartElement(prefix, localName, ns);
+ }
+
+ public new void WriteAttributeString(string prefix, string localName, string value)
+ {
+ if (!string.IsNullOrEmpty(prefix))
+ {
+ localName = prefix + ":" + localName;
+ }
+
+ WriteAttributeString(localName, value);
+ }
+
+ public void WriteResourceDictionary(XElement e)
+ {
+ WriteStartElement(e.Name.LocalName);
+ WriteNamespaceDeclaration("xmlns", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
+ WriteNamespaceDeclaration("xmlns:x", "http://schemas.microsoft.com/winfx/2006/xaml");
+ WriteNewLine();
+ WriteNewLine();
+ foreach (var node in e.Elements())
+ {
+ WriteElement(node);
+ }
+ WriteFullEndElement();
+ WriteNewLine();
+ }
+
+ private void WriteElement(XElement e)
+ {
+ WriteComments(e);
+ WriteRaw(intend);
+ WriteStartElement(e.GetPrefixOfNamespace(e.Name.Namespace), e.Name.LocalName, "");
+ WriteAttributes(e);
+ if (e.Descendants().Count() > 0)
+ {
+ WriteChildElements(e);
+ }
+ else
+ {
+ WriteRaw(e.Value);
+ }
+ WriteEndElement();
+ WriteNewLine();
+ if (e.Name.LocalName == "Style")
+ {
+ WriteNewLine();
+ }
+ }
+
+ private void WriteNewLine()
+ {
+ WriteRaw("\r\n");
+ }
+
+ private void WriteNamespaceDeclaration(string key, string value)
+ {
+ writer.WriteLine();
+ WriteAttributeString(" " + key, value);
+ }
+
+ private void WriteComments(XElement e)
+ {
+ if (e.PreviousNode != null && e.PreviousNode.NodeType == XmlNodeType.Comment)
+ {
+ WriteRaw(intend);
+ WriteComment((e.PreviousNode as XComment).Value);
+ WriteNewLine();
+ }
+ }
+
+ private void WriteChildElements(XElement e)
+ {
+ WriteNewLine();
+ foreach (var n in e.Descendants())
+ {
+ WriteRaw(intend);
+ WriteRaw(intend);
+ WriteStartElement(e.GetPrefixOfNamespace(n.Name.Namespace), n.Name.LocalName, "");
+ WriteAttributes(n);
+ WriteEndElement();
+ WriteNewLine();
+ }
+ WriteRaw(intend);
+ }
+
+ private void WriteAttributes(XElement e)
+ {
+ foreach (var a in e.Attributes())
+ {
+ WriteAttributeString(e.GetPrefixOfNamespace(a.Name.Namespace), a.Name.LocalName, a.Value);
+ }
+ }
+ }
+}
diff --git a/code/src/Core/PostActions/NewItemPostActionFactory.cs b/code/src/Core/PostActions/NewItemPostActionFactory.cs
index 770ecc3ec..1eea29a34 100644
--- a/code/src/Core/PostActions/NewItemPostActionFactory.cs
+++ b/code/src/Core/PostActions/NewItemPostActionFactory.cs
@@ -21,7 +21,7 @@ namespace Microsoft.Templates.Core.PostActions
AddFileNameSearchActions(genInfo, postActions);
AddGetMergeFilesFromProjectPostAction(postActions);
AddGenerateMergeInfoPostAction(postActions);
- AddMergeActions(postActions, $"*{MergePostAction.Extension}*", false);
+ AddMergeActions(postActions, $"*{MergeConfiguration.Extension}*", false);
return postActions;
}
@@ -30,7 +30,7 @@ namespace Microsoft.Templates.Core.PostActions
{
var postActions = new List();
- AddGlobalMergeActions(postActions, $"*{MergePostAction.GlobalExtension}*", false);
+ AddGlobalMergeActions(postActions, $"*{MergeConfiguration.GlobalExtension}*", false);
postActions.Add(new SortUsingsPostAction());
postActions.Add(new SortImportsPostAction());
diff --git a/code/src/Core/PostActions/NewProjectPostActionFactory.cs b/code/src/Core/PostActions/NewProjectPostActionFactory.cs
index f9f3a52b3..cee5fb5f4 100644
--- a/code/src/Core/PostActions/NewProjectPostActionFactory.cs
+++ b/code/src/Core/PostActions/NewProjectPostActionFactory.cs
@@ -20,7 +20,7 @@ namespace Microsoft.Templates.Core.PostActions
AddFileNameSearchActions(genInfo, postActions);
AddPredefinedActions(genInfo, genResult, postActions);
- AddMergeActions(postActions, $"*{MergePostAction.Extension}*", true);
+ AddMergeActions(postActions, $"*{MergeConfiguration.Extension}*", true);
AddSearchAndReplaceActions(postActions, $"*{SearchAndReplacePostAction.Extension}*");
return postActions;
@@ -30,7 +30,7 @@ namespace Microsoft.Templates.Core.PostActions
{
var postActions = new List();
- AddGlobalMergeActions(postActions, $"*{MergePostAction.GlobalExtension}*", true);
+ AddGlobalMergeActions(postActions, $"*{MergeConfiguration.GlobalExtension}*", true);
postActions.Add(new SortUsingsPostAction());
postActions.Add(new SortImportsPostAction());
postActions.Add(new AddContextItemsToProjectPostAction());
diff --git a/code/src/Core/PostActions/PostActionFactory.cs b/code/src/Core/PostActions/PostActionFactory.cs
index b803f627b..7d1fa9349 100644
--- a/code/src/Core/PostActions/PostActionFactory.cs
+++ b/code/src/Core/PostActions/PostActionFactory.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -34,7 +35,7 @@ namespace Microsoft.Templates.Core.PostActions
{
Directory
.EnumerateFiles(GenContext.Current.OutputPath, "*.*", SearchOption.AllDirectories)
- .Where(f => Regex.IsMatch(f, MergePostAction.PostactionRegex))
+ .Where(f => Regex.IsMatch(f, MergeConfiguration.PostactionRegex))
.ToList()
.ForEach(f => postActions.Add(new GetMergeFilesFromProjectPostAction(f)));
}
@@ -43,7 +44,7 @@ namespace Microsoft.Templates.Core.PostActions
{
Directory
.EnumerateFiles(GenContext.Current.OutputPath, "*.*", SearchOption.AllDirectories)
- .Where(f => Regex.IsMatch(f, MergePostAction.PostactionRegex))
+ .Where(f => Regex.IsMatch(f, MergeConfiguration.PostactionRegex))
.ToList()
.ForEach(f => postActions.Add(new GenerateMergeInfoPostAction(f)));
}
@@ -71,7 +72,7 @@ namespace Microsoft.Templates.Core.PostActions
Directory
.EnumerateFiles(GenContext.Current.OutputPath, searchPattern, SearchOption.AllDirectories)
.ToList()
- .ForEach(f => postActions.Add(new MergePostAction(new MergeConfiguration(f, failOnError))));
+ .ForEach(f => AddMergePostAction(postActions, failOnError, f));
}
internal void AddFileNameSearchActions(GenInfo genInfo, List postActions)
@@ -100,5 +101,22 @@ namespace Microsoft.Templates.Core.PostActions
.ToList()
.ForEach(f => postActions.Add(new SearchAndReplacePostAction(f)));
}
+
+ private static void AddMergePostAction(List postActions, bool failOnError, string f)
+ {
+ if (IsResourceDictionaryPostaction(f))
+ {
+ postActions.Add(new MergeResourceDictionaryPostAction(new MergeConfiguration(f, failOnError)));
+ }
+ else
+ {
+ postActions.Add(new MergePostAction(new MergeConfiguration(f, failOnError)));
+ }
+ }
+
+ private static bool IsResourceDictionaryPostaction(string f)
+ {
+ return Path.GetExtension(f).ToLower() == ".xaml" & File.ReadAllText(f).StartsWith(MergeConfiguration.ResourceDictionaryMatch);
+ }
}
}
diff --git a/code/src/Core/Resources/StringRes.Designer.cs b/code/src/Core/Resources/StringRes.Designer.cs
index 6bf0e6a0b..0da2de926 100644
--- a/code/src/Core/Resources/StringRes.Designer.cs
+++ b/code/src/Core/Resources/StringRes.Designer.cs
@@ -150,6 +150,15 @@ namespace Microsoft.Templates.Core.Resources {
}
}
+ ///
+ /// Looks up a localized string similar to Key {0} already defined with different value or elements in file '{1}'.
+ ///
+ internal static string FailedMergePostActionKeyAlreadyDefined {
+ get {
+ return ResourceManager.GetString("FailedMergePostActionKeyAlreadyDefined", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Could not find the expected line `{0}` in file '{1}'. Please merge the content from the postaction file manually..
///
diff --git a/code/src/Core/Resources/StringRes.resx b/code/src/Core/Resources/StringRes.resx
index 76bb60018..65e9c1943 100644
--- a/code/src/Core/Resources/StringRes.resx
+++ b/code/src/Core/Resources/StringRes.resx
@@ -384,4 +384,7 @@ The following changes could not be integrated: {2}
Error reading the instance locking file.
+
+ Key {0} already defined with different value or elements in file '{1}'
+
\ No newline at end of file
diff --git a/code/src/UI/Generation/NewItemGenController.cs b/code/src/UI/Generation/NewItemGenController.cs
index 5e4efcaae..704d8cc28 100644
--- a/code/src/UI/Generation/NewItemGenController.cs
+++ b/code/src/UI/Generation/NewItemGenController.cs
@@ -132,7 +132,7 @@ namespace Microsoft.Templates.UI
var result = new TempGenerationResult();
var files = Directory
.EnumerateFiles(GenContext.Current.OutputPath, "*", SearchOption.AllDirectories)
- .Where(f => !Regex.IsMatch(f, MergePostAction.PostactionRegex) && !Regex.IsMatch(f, MergePostAction.FailedPostactionRegex))
+ .Where(f => !Regex.IsMatch(f, MergeConfiguration.PostactionRegex) && !Regex.IsMatch(f, MergeConfiguration.FailedPostactionRegex))
.ToList();
foreach (var file in files)
diff --git a/code/test/Core.Test/Core.Test.csproj b/code/test/Core.Test/Core.Test.csproj
index 4e5c79f95..5629bd49b 100644
--- a/code/test/Core.Test/Core.Test.csproj
+++ b/code/test/Core.Test/Core.Test.csproj
@@ -146,6 +146,7 @@
+
@@ -272,6 +273,40 @@
+
+
+ MSBuild:UpdateDesignTimeXaml
+ Always
+
+
+
+
+ MSBuild:UpdateDesignTimeXaml
+ Designer
+ PreserveNewest
+
+
+
+
+ MSBuild:UpdateDesignTimeXaml
+ Designer
+ PreserveNewest
+
+
+
+
+ MSBuild:UpdateDesignTimeXaml
+ Designer
+ Always
+
+
+
+
+ MSBuild:UpdateDesignTimeXaml
+ Always
+ Designer
+
+
IF EXIST "$(ProjectDir)test.config.json.with.secrets" COPY /Y "$(ProjectDir)test.config.json.with.secrets" "$(TargetDir)test.config.json"
diff --git a/code/test/Core.Test/PostActions/Catalog/MergeResourceDictionaryPostactionTest.cs b/code/test/Core.Test/PostActions/Catalog/MergeResourceDictionaryPostactionTest.cs
new file mode 100644
index 000000000..9420cc09c
--- /dev/null
+++ b/code/test/Core.Test/PostActions/Catalog/MergeResourceDictionaryPostactionTest.cs
@@ -0,0 +1,71 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Templates.Core.Diagnostics;
+using Microsoft.Templates.Core.Gen;
+using Microsoft.Templates.Core.PostActions.Catalog.Merge;
+using Xunit;
+
+namespace Microsoft.Templates.Core.Test.PostActions.Catalog
+{
+ [Trait("ExecutionSet", "Minimum")]
+ public class MergeResourceDictionaryPostactionTest : IContextProvider
+ {
+ public string ProjectName => throw new NotImplementedException();
+
+ public string OutputPath => Directory.GetCurrentDirectory();
+
+ public string ProjectPath => throw new NotImplementedException();
+
+ public List ProjectItems => throw new NotImplementedException();
+
+ public List FilesToOpen => throw new NotImplementedException();
+
+ public List FailedMergePostActions => throw new NotImplementedException();
+
+ public Dictionary> MergeFilesFromProject => throw new NotImplementedException();
+
+ public Dictionary ProjectMetrics => throw new NotImplementedException();
+
+ [Fact]
+ public void MergeResourceDictionaryPostaction()
+ {
+ var source = Path.GetFullPath(@".\TestData\Merge\Style.xaml");
+ var postaction = Path.GetFullPath(@".\TestData\Merge\Style_postaction.xaml");
+ var expected = File.ReadAllText(@".\TestData\Merge\Style_expected.xaml").Replace("\r\n", "");
+
+ var config = new MergeConfiguration(postaction, true);
+
+ var mergeResourceDictionaryPostAction = new MergeResourceDictionaryPostAction(config);
+ mergeResourceDictionaryPostAction.Execute();
+
+ var result = File.ReadAllText(source).Replace("\r\n", "");
+
+ Assert.Equal(result, expected);
+ }
+
+ [Fact]
+ public void MergeResourceDictionaryPostaction_Failing()
+ {
+ var source = Path.GetFullPath(@".\TestData\Merge\Style_fail.xaml");
+ var postaction = Path.GetFullPath(@".\TestData\Merge\Style_fail_postaction.xaml");
+ var expected = File.ReadAllText(@".\TestData\Merge\Style_expected.xaml");
+
+ GenContext.Current = this;
+ var config = new MergeConfiguration(postaction, true);
+
+ var mergeResourceDictionaryPostAction = new MergeResourceDictionaryPostAction(config);
+
+ Exception ex = Assert.Throws(() => mergeResourceDictionaryPostAction.Execute());
+
+ Assert.Equal($"Key PageTitleStyle already defined with different value or elements in file '{source.Replace(GenContext.Current.OutputPath + Path.DirectorySeparatorChar, "")}'", ex.Message);
+ }
+ }
+}
diff --git a/code/test/Core.Test/TestData/Merge/Style.xaml b/code/test/Core.Test/TestData/Merge/Style.xaml
new file mode 100644
index 000000000..3e11bd8ac
--- /dev/null
+++ b/code/test/Core.Test/TestData/Merge/Style.xaml
@@ -0,0 +1,7 @@
+
+
+ 28
+ 16
+
diff --git a/code/test/Core.Test/TestData/Merge/Style_expected.xaml b/code/test/Core.Test/TestData/Merge/Style_expected.xaml
new file mode 100644
index 000000000..dbbe6dbde
--- /dev/null
+++ b/code/test/Core.Test/TestData/Merge/Style_expected.xaml
@@ -0,0 +1,19 @@
+
+
+ 28
+ 16
+ 5
+
+
+
+
+
diff --git a/code/test/Core.Test/TestData/Merge/Style_fail.xaml b/code/test/Core.Test/TestData/Merge/Style_fail.xaml
new file mode 100644
index 000000000..70b04b283
--- /dev/null
+++ b/code/test/Core.Test/TestData/Merge/Style_fail.xaml
@@ -0,0 +1,15 @@
+
+
+ 28
+ 16
+
+
diff --git a/code/test/Core.Test/TestData/Merge/Style_fail_postaction.xaml b/code/test/Core.Test/TestData/Merge/Style_fail_postaction.xaml
new file mode 100644
index 000000000..6274549d2
--- /dev/null
+++ b/code/test/Core.Test/TestData/Merge/Style_fail_postaction.xaml
@@ -0,0 +1,16 @@
+
+
+ 16
+
+ 5
+
+
diff --git a/code/test/Core.Test/TestData/Merge/Style_postaction.xaml b/code/test/Core.Test/TestData/Merge/Style_postaction.xaml
new file mode 100644
index 000000000..3004bdf5c
--- /dev/null
+++ b/code/test/Core.Test/TestData/Merge/Style_postaction.xaml
@@ -0,0 +1,16 @@
+
+
+ 16
+ 5
+
+
+
diff --git a/docs/getting-started-developers.md b/docs/getting-started-developers.md
index 835dcb422..b0effe0ec 100644
--- a/docs/getting-started-developers.md
+++ b/docs/getting-started-developers.md
@@ -99,6 +99,10 @@ The following list shows which tests are executed in which build. Within the Tem
* ExecutionSet=BuildStyleCop
* ExecutionSet=TemplateValidation
+* VSO 'Templates.Test.Wack' Build (Wack Tests):
+ * Templates.Test
+ * ExecutionSet=ManualOnly
+
To shorten test execution time traits in Templates.Test are run parallel using this [script](../_build/ParallelTestExecution.ps1).
To execute this script locally use the following powershell command: