public void FindExtraLocStrings() { // Load the strings. string stringsFile = Path.Combine(TestUtil.GetSrcPath(), "Misc", "layoutbin", "en-US", "strings.json"); Assert.True(File.Exists(stringsFile), $"File does not exist: {stringsFile}"); var resourceDictionary = IOUtil.LoadObject <Dictionary <string, object> >(stringsFile); // Find all loc string key in source file. // // Note, narrow the search to each project folder only. Otherwise intermittent errors occur // when recursively searching due to parallel tests are deleting temp folders (DirectoryNotFoundException). var keys = new List <string>(); string[] sourceFiles = Directory.GetFiles(TestUtil.GetProjectPath("Microsoft.VisualStudio.Services.Agent"), "*.cs", SearchOption.AllDirectories) .Concat(Directory.GetFiles(TestUtil.GetProjectPath("Agent.Listener"), "*.cs", SearchOption.AllDirectories)) .Concat(Directory.GetFiles(TestUtil.GetProjectPath("Agent.Worker"), "*.cs", SearchOption.AllDirectories)) .Concat(Directory.GetFiles(TestUtil.GetProjectPath("Agent.Plugins"), "*.cs", SearchOption.AllDirectories)) .Concat(Directory.GetFiles(TestUtil.GetProjectPath("Agent.Sdk"), "*.cs", SearchOption.AllDirectories)) .ToArray(); foreach (string sourceFile in sourceFiles) { // Skip files in the obj directory. if (sourceFile.Contains(StringUtil.Format("{0}obj{0}", Path.DirectorySeparatorChar))) { continue; } foreach (string line in File.ReadAllLines(sourceFile)) { // Search for calls to the StringUtil.Loc method within the line. const string Pattern = "StringUtil.Loc("; int searchIndex = 0; int patternIndex; while (searchIndex < line.Length && (patternIndex = line.IndexOf(Pattern, searchIndex)) >= 0) { // Bump the search index in preparation for the for the next iteration within the same line. searchIndex = patternIndex + Pattern.Length; // Extract the resource key. int keyStartIndex = patternIndex + Pattern.Length; int keyEndIndex; if (keyStartIndex + 2 < line.Length && // Key should start with a ", be followed by at least line[keyStartIndex] == '"' && // one character, and end with a ". (keyEndIndex = line.IndexOf('"', keyStartIndex + 1)) > 0) { // Remove the first and last double quotes. keyStartIndex++; keyEndIndex--; string key = line.Substring( startIndex: keyStartIndex, length: keyEndIndex - keyStartIndex + 1); if (ValidKeyRegex.IsMatch(key)) { // A valid key was extracted. keys.Add(key); continue; } } } } } // find extra loc strings. var extraKeys = resourceDictionary.Keys.Where(x => !keys.Contains(x))?.ToList(); if (extraKeys != null) { Assert.True(extraKeys.Count == 0, $"Please save company's money by removing extra loc strings:{Environment.NewLine}{string.Join(Environment.NewLine, extraKeys)}"); } }
private void ValidateLocStrings(TestHostContext hc, string project) { using (hc) { Tracing trace = hc.GetTrace(); var keys = new List <string>(); var badLines = new List <BadLineInfo>(); // Search for source files within the project. trace.Verbose("Searching source files:"); string[] sourceFiles = Directory.GetFiles( TestUtil.GetProjectPath(project), "*.cs", SearchOption.AllDirectories); foreach (string sourceFile in sourceFiles) { // Skip files in the obj directory. if (sourceFile.Contains(StringUtil.Format("{0}obj{0}", Path.DirectorySeparatorChar))) { continue; } trace.Verbose($" {sourceFile}"); foreach (string line in File.ReadAllLines(sourceFile)) { // Search for calls to the StringUtil.Loc method within the line. const string Pattern = "StringUtil.Loc("; int searchIndex = 0; int patternIndex; while (searchIndex < line.Length && (patternIndex = line.IndexOf(Pattern, searchIndex)) >= 0) { // Bump the search index in preparation for the for the next iteration within the same line. searchIndex = patternIndex + Pattern.Length; // Extract the resource key. int keyStartIndex = patternIndex + Pattern.Length; int keyEndIndex; if (keyStartIndex + 2 < line.Length && // Key should start with a ", be followed by at least line[keyStartIndex] == '"' && // one character, and end with a ". (keyEndIndex = line.IndexOf('"', keyStartIndex + 1)) > 0) { // Remove the first and last double quotes. keyStartIndex++; keyEndIndex--; string key = line.Substring( startIndex: keyStartIndex, length: keyEndIndex - keyStartIndex + 1); if (ValidKeyRegex.IsMatch(key)) { // A valid key was extracted. keys.Add(key); continue; } } // Something went wrong. The pattern was found, but the resource key could not be determined. badLines.Add(new BadLineInfo { File = sourceFile, Line = line }); } } } // Load the strings. string stringsFile = Path.Combine(TestUtil.GetSrcPath(), "Misc", "layoutbin", "en-US", "strings.json"); Assert.True(File.Exists(stringsFile), $"File does not exist: {stringsFile}"); var resourceDictionary = IOUtil.LoadObject <Dictionary <string, object> >(stringsFile); // Find missing keys. string[] missingKeys = keys .Where(x => !resourceDictionary.ContainsKey(x)) .OrderBy(x => x) .ToArray(); if (missingKeys.Length > 0) { trace.Error("One or more resource keys missing from resources file:"); foreach (string missingKey in missingKeys) { trace.Error($" {missingKey}"); } } // Validate whether resource keys couldn't be interpreted. if (badLines.Count > 0) { trace.Error("Bad lines detected. Unable to interpret resource key(s)."); IEnumerable <IGrouping <string, BadLineInfo> > badLineGroupings = badLines .GroupBy(x => x.File) .OrderBy(x => x.Key) .ToArray(); foreach (IGrouping <string, BadLineInfo> badLineGrouping in badLineGroupings) { trace.Error($"File: {badLineGrouping.First().File}"); foreach (BadLineInfo badLine in badLineGrouping) { trace.Error($" Line: {badLine.Line}"); } } } Assert.True(missingKeys.Length == 0, $"One or more resource keys missing from resources files. Consult the trace log: {hc.TraceFileName}"); Assert.True(badLines.Count == 0, $"Unable to determine one or more resource keys. Consult the trace log: {hc.TraceFileName}"); } }