in code/tools/TemplateValidator/TemplateFolderVerifier.cs [24:308]
public static VerifierResult VerifyTemplateFolders(bool showWarnings, IEnumerable<string> templateFolders)
{
var results = new List<string>();
if (templateFolders == null)
{
results.Add("Paths to template folders not provided.");
}
else
{
results.AddRange(from templateFolder
in templateFolders
where !Directory.Exists(templateFolder)
select $"Folder '{templateFolder}' does not exist.");
}
// Don't bother with actual tests if not all folders are valid
if (!results.Any())
{
var allTemplateFilePaths = new List<string>();
foreach (var rootFolder in templateFolders)
{
allTemplateFilePaths.AddRange(new DirectoryInfo(rootFolder).GetFiles("template.json", SearchOption.AllDirectories)
.Select(file => file.FullName));
}
var allIdentities = new Dictionary<string, string>(); // identity, filepath
var allGroupIdentities = new List<string>(); // identity, identity
var allDependencies = new Dictionary<string, string>(); // filepath, dependency
var allRequirements = new Dictionary<string, string>(); // filepath, requirement
var allExclusions = new Dictionary<string, string>(); // filepath, exclusion
var allFileHashes = new Dictionary<string, string>(); // filehash, filepath
var allCompFilters = new Dictionary<string, string>(); // filepath, filter
var allPageIdentities = new List<string>();
var allFeatureIdentities = new List<string>();
var allServiceIdentities = new List<string>();
var allTestingIdentities = new List<string>();
foreach (var templateFilePath in allTemplateFilePaths)
{
var fileContents = File.ReadAllText(templateFilePath);
var template = JsonConvert.DeserializeObject<ValidationTemplateInfo>(fileContents);
if (template.Identity != null)
{
if (allIdentities.ContainsKey(template.Identity))
{
results.Add($"Duplicate Identity detected in: '{templateFilePath}' & '{allIdentities[template.Identity]}'");
}
else
{
allIdentities.Add(template.Identity, templateFilePath);
if (!allGroupIdentities.Contains(template.GroupIdentity))
{
allGroupIdentities.Add(template.GroupIdentity);
}
if (template.GetTemplateType() == TemplateType.Page)
{
allPageIdentities.Add(template.Identity);
}
else if (template.GetTemplateType() == TemplateType.Feature)
{
allFeatureIdentities.Add(template.Identity);
}
else if (template.GetTemplateType() == TemplateType.Service)
{
allServiceIdentities.Add(template.Identity);
}
else if (template.GetTemplateType() == TemplateType.Testing)
{
allTestingIdentities.Add(template.Identity);
}
}
// Check that localized files have the same identity
foreach (var localizedFile in new DirectoryInfo(Path.GetDirectoryName(templateFilePath)).EnumerateFiles("*.template.json"))
{
var localizedContents = File.ReadAllText(localizedFile.FullName);
var localizedTemplate = JsonConvert.DeserializeObject<ValidationTemplateInfo>(localizedContents);
if (template.Identity != localizedTemplate.Identity)
{
results.Add($"'{localizedFile.FullName}' does not have the correct identity.");
}
if (template.Name != localizedTemplate.Name)
{
results.Add($"'{localizedFile.FullName}' does not have the correct name.");
}
}
}
else
{
if (allIdentities.ContainsKey(template.Name))
{
results.Add($"Duplicate Identity detected in: '{templateFilePath}' & '{allIdentities[template.Name]}'");
}
else
{
allIdentities.Add(template.Name, templateFilePath);
}
}
// Get list of dependencies while the file is open. These are all checked later
if (template.TemplateTags.ContainsKey("wts.dependencies"))
{
allDependencies.Add(templateFilePath, template.TemplateTags["wts.dependencies"]);
}
// Get list of requirements while the file is open. These are all checked later
if (template.TemplateTags.ContainsKey("wts.requirements"))
{
allRequirements.Add(templateFilePath, template.TemplateTags["wts.requirements"]);
}
// Get list of exclusions while the file is open. These are all checked later
if (template.TemplateTags.ContainsKey("wts.exclusions"))
{
allExclusions.Add(templateFilePath, template.TemplateTags["wts.exclusions"]);
}
// Get list of filters while the file is open. These are all checked later
if (template.TemplateTags.ContainsKey("wts.compositionFilter"))
{
allCompFilters.Add(templateFilePath, template.TemplateTags["wts.compositionFilter"]);
}
var templateRoot = templateFilePath.Replace("\\.template.config\\template.json", string.Empty);
foreach (var file in new DirectoryInfo(templateRoot).GetFiles("*.*", SearchOption.AllDirectories))
{
// Filter out files the following tests cannot handle
if (!file.Name.Contains("_postaction")
&& !file.Name.Contains("_gpostaction")
&& !file.Name.Contains("_searchreplace")
&& !file.FullName.Contains("\\Projects\\Default")
&& !file.FullName.Contains("\\Test\\")
&& !file.FullName.Contains(".template.config"))
{
// Projects will include files that aren't directly referenced
if (template.GetTemplateOutputType() == TemplateOutputType.Item)
{
// Use of FileInfo and Path to handle comparison of relative and exact paths
if (template.PrimaryOutputs.All(p => file.FullName != new FileInfo(Path.Combine(templateRoot, p.Path)).FullName && !excludedPrimaryOutputFiles.Any(f => file.FullName.EndsWith(f, StringComparison.Ordinal))))
{
results.Add($"'{file.FullName}' is not used in the template.");
}
}
// Duplicate file checking can be avoided as some duplicate files exist in the official templates at the time of writing.
// It is done by default to encourage anyone creating new templates to follow this guidance.
if (showWarnings)
{
// Ignore xaml files as we know these are duplicated across VB & C# versions of the same template
if (file.Extension != ".xaml")
{
var hash = GetFileHash(file.FullName);
// if hash is already in the dictionary then write to results as a duplicate file
// if not add to the dictionary
if (allFileHashes.ContainsKey(hash))
{
results.Add($"WARNING: '{file.FullName}' and '{allFileHashes[hash]}' have identical contents and could be combined into a single template.");
}
else
{
allFileHashes.Add(hash, file.FullName);
}
}
}
}
}
}
foreach (var dependencies in allDependencies)
{
foreach (var dependency in dependencies.Value.Split('|'))
{
if (!allIdentities.ContainsKey(dependency))
{
results.Add($"'{dependencies.Key}' contains dependency '{dependency}' that does not exist.");
}
}
}
foreach (var requirements in allRequirements)
{
foreach (var requirement in requirements.Value.Split('|'))
{
if (!allIdentities.ContainsKey(requirement))
{
results.Add($"'{requirements.Key}' contains requirement '{requirements}' that does not exist.");
}
}
}
foreach (var exclusions in allExclusions)
{
foreach (var exclusion in exclusions.Value.Split('|'))
{
if (!allGroupIdentities.Contains(exclusion))
{
results.Add($"'{exclusions.Key}' contains exclusion '{exclusion}' that does not exist.");
}
}
}
foreach (var compFilter in allCompFilters)
{
var query = CompositionQuery.Parse(compFilter.Value);
foreach (var queryItem in query.Items)
{
if (queryItem.Field == "identity")
{
foreach (var templateIdentity in queryItem.Value.Split('|'))
{
if (!allIdentities.Keys.Contains(templateIdentity))
{
results.Add($"'{compFilter.Key}' contains composition filter identity '{templateIdentity}' that does not exist.");
}
}
}
else if (queryItem.Field == "page")
{
foreach (var templateIdentity in queryItem.Value.Split('|'))
{
if (!allPageIdentities.Contains(templateIdentity))
{
results.Add($"'{compFilter.Key}' contains composition filter page identity '{templateIdentity}' that does not exist.");
}
}
}
else if (queryItem.Field == "feature")
{
foreach (var templateIdentity in queryItem.Value.Split('|'))
{
if (!allFeatureIdentities.Contains(templateIdentity))
{
results.Add($"'{compFilter.Key}' contains composition filter feature identity '{templateIdentity}' that does not exist.");
}
}
}
else if (queryItem.Field == "service")
{
foreach (var templateIdentity in queryItem.Value.Split('|'))
{
if (!allServiceIdentities.Contains(templateIdentity))
{
results.Add($"'{compFilter.Key}' contains composition filter service identity '{templateIdentity}' that does not exist.");
}
}
}
else if (queryItem.Field == "testing")
{
foreach (var templateIdentity in queryItem.Value.Split('|'))
{
if (!allTestingIdentities.Contains(templateIdentity))
{
results.Add($"'{compFilter.Key}' contains composition filter testing identity '{templateIdentity}' that does not exist.");
}
}
}
}
}
if (!results.Any())
{
CheckIconXamlFiles(templateFolders, results);
}
}
var success = results.Count == 0;
if (success)
{
results.Add("All looks good.");
}
return new VerifierResult(success, results);
}