public static VerifierResult VerifyTemplateFolders(bool showWarnings, params 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 allDependencies      = new Dictionary <string, string>(); // filepath, dependency
                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 (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 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))
                                {
                                    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 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));
        }
        public void EnsureVisualBasicTemplatesHaveEquivalentPrimaryOutputsAndFilters()
        {
            var errors       = new List <string>();
            var allTemplates = TemplateJsonValidationTests.GetAllTemplateJsonFiles();

            try
            {
                foreach (var template in allTemplates)
                {
                    if (template[0].ToString().Contains("._VB"))
                    {
                        var vbFileContents = File.ReadAllText(template[0].ToString());
                        var vbTemplate     = JsonConvert.DeserializeObject <ValidationTemplateInfo>(vbFileContents);

                        var csFileContents = File.ReadAllText(template[0].ToString().Replace("._VB", string.Empty));
                        var csTemplate     = JsonConvert.DeserializeObject <ValidationTemplateInfo>(csFileContents);

                        if (vbTemplate.PrimaryOutputs != null)
                        {
                            if (vbTemplate.PrimaryOutputs.Count != csTemplate.PrimaryOutputs.Count)
                            {
                                errors.Add($"{template[0].ToString()} should have {csTemplate.PrimaryOutputs.Count} primary outputs.");
                            }
                        }

                        if (csTemplate.TemplateTags.ContainsKey("wts.compositionFilter"))
                        {
                            var vbFilter = vbTemplate.TemplateTags["wts.compositionFilter"];
                            var csFilter = csTemplate.TemplateTags["wts.compositionFilter"];

                            var vbquery = CompositionQuery.Parse(vbFilter);
                            var csquery = CompositionQuery.Parse(csFilter);
                            if (vbquery.Items.Count != csquery.Items.Count)
                            {
                                errors.Add($"{template[0].ToString()}: {vbFilter} should have contained {csquery.Items.Count} items.");
                            }
                            else
                            {
                                foreach (var vbItem in vbquery.Items)
                                {
                                    var csItem = csquery.Items.First(n => n.Field == vbItem.Field);

                                    if (csItem.Value.Split('|').Count() != vbItem.Value.Split('|').Count())
                                    {
                                        if (!csItem.Value.Contains("Prism") && !csItem.Value.Contains("Caliburn"))
                                        {
                                            errors.Add($"{template[0].ToString()}: check {vbItem.Field} in composition query.");
                                        }
                                    }
                                }
                            }
                        }

                        var csCompOrder = csTemplate.TemplateTags.ContainsKey("wts.compositionOrder") ? csTemplate.TemplateTags["wts.compositionOrder"] : null;
                        var vbCompOrder = vbTemplate.TemplateTags.ContainsKey("wts.compositionOrder") ? vbTemplate.TemplateTags["wts.compositionOrder"] : null;

                        if (csCompOrder != vbCompOrder)
                        {
                            errors.Add($"CompostionOrder issue in {template[0].ToString()}");
                        }
                    }
                }
            }
            catch (Exception exc)
            {
                System.Diagnostics.Debug.WriteLine(exc);
            }

            Assert.True(!errors.Any(), string.Join(Environment.NewLine, errors));
        }