aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Tests/ConsistencyTests/StringUsageReporter.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Tests/ConsistencyTests/StringUsageReporter.cs')
-rw-r--r--MediaBrowser.Tests/ConsistencyTests/StringUsageReporter.cs260
1 files changed, 260 insertions, 0 deletions
diff --git a/MediaBrowser.Tests/ConsistencyTests/StringUsageReporter.cs b/MediaBrowser.Tests/ConsistencyTests/StringUsageReporter.cs
new file mode 100644
index 000000000..d036a6c6d
--- /dev/null
+++ b/MediaBrowser.Tests/ConsistencyTests/StringUsageReporter.cs
@@ -0,0 +1,260 @@
+using MediaBrowser.Tests.ConsistencyTests.TextIndexing;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml;
+
+namespace MediaBrowser.Tests.ConsistencyTests
+{
+ /// <summary>
+ /// This class contains tests for reporting the usage of localization string tokens
+ /// in the dashboard-ui or similar.
+ /// </summary>
+ /// <remarks>
+ /// <para>Run one of the two tests using Visual Studio's "Test Explorer":</para>
+ /// <para>
+ /// <list type="bullet">
+ /// <item><see cref="ReportStringUsage"/></item>
+ /// <item><see cref="ReportUnusedStrings"/></item>
+ /// </list>
+ /// </para>
+ /// <para>
+ /// On successful run, the bottom section of the test explorer will contain a link "Output".
+ /// This link will open the test results, displaying the trace and two attachment links.
+ /// One link will open the output folder, the other link will open the output xml file.
+ /// </para>
+ /// <para>
+ /// The output xml file contains a stylesheet link to render the results as html.
+ /// How that works depends on the default application configured for XML files:
+ /// </para>
+ /// <para><list type="bullet">
+ /// <item><term>Visual Studio</term>
+ /// <description>Will open in XML source view. To view the html result, click menu
+ /// 'XML' => 'Start XSLT without debugging'</description></item>
+ /// <item><term>Internet Explorer</term>
+ /// <description>XSL transform will be applied automatically.</description></item>
+ /// <item><term>Firefox</term>
+ /// <description>XSL transform will be applied automatically.</description></item>
+ /// <item><term>Chrome</term>
+ /// <description>Does not work. Chrome is unable/unwilling to apply xslt transforms from local files.</description></item>
+ /// </list></para>
+ /// </remarks>
+ [TestClass]
+ public class StringUsageReporter
+ {
+ /// <summary>
+ /// Root path of the web application
+ /// </summary>
+ /// <remarks>
+ /// Can be an absolute path or a path relative to the binaries folder (bin\Debug).
+ /// </remarks>
+ public const string WebFolder = @"..\..\..\MediaBrowser.WebDashboard\dashboard-ui";
+
+ /// <summary>
+ /// Path to the strings file, relative to <see cref="WebFolder"/>.
+ /// </summary>
+ public const string StringsFile = @"strings\en-US.json";
+
+ /// <summary>
+ /// Path to the output folder
+ /// </summary>
+ /// <remarks>
+ /// Can be an absolute path or a path relative to the binaries folder (bin\Debug).
+ /// Important: When changing the output path, make sure that "StringCheck.xslt" is present
+ /// to make the XML transform work.
+ /// </remarks>
+ public const string OutputPath = @".";
+
+ /// <summary>
+ /// List of file extension to search.
+ /// </summary>
+ public static string[] TargetExtensions = new[] { "js", "html" };
+
+ /// <summary>
+ /// List of paths to exclude from search.
+ /// </summary>
+ public static string[] ExcludePaths = new[] { @"\bower_components\", @"\thirdparty\" };
+
+ private TestContext testContextInstance;
+
+ /// <summary>
+ ///Gets or sets the test context which provides
+ ///information about and functionality for the current test run.
+ ///</summary>
+ public TestContext TestContext
+ {
+ get
+ {
+ return testContextInstance;
+ }
+ set
+ {
+ testContextInstance = value;
+ }
+ }
+
+ [TestMethod]
+ public void ReportStringUsage()
+ {
+ this.CheckDashboardStrings(false);
+ }
+
+ [TestMethod]
+ public void ReportUnusedStrings()
+ {
+ this.CheckDashboardStrings(true);
+ }
+
+ private void CheckDashboardStrings(Boolean unusedOnly)
+ {
+ // Init Folders
+ var currentDir = System.IO.Directory.GetCurrentDirectory();
+ Trace("CurrentDir: {0}", currentDir);
+
+ var rootFolderInfo = ResolveFolder(currentDir, WebFolder);
+ Trace("Web Root: {0}", rootFolderInfo.FullName);
+
+ var outputFolderInfo = ResolveFolder(currentDir, OutputPath);
+ Trace("Output Path: {0}", outputFolderInfo.FullName);
+
+ // Load Strings
+ var stringsFileName = Path.Combine(rootFolderInfo.FullName, StringsFile);
+
+ if (!File.Exists(stringsFileName))
+ {
+ throw new Exception(string.Format("Strings file not found: {0}", stringsFileName));
+ }
+
+ int lineNumbers;
+ var stringsDic = this.CreateStringsDictionary(new FileInfo(stringsFileName), out lineNumbers);
+
+ Trace("Loaded {0} strings from strings file containing {1} lines", stringsDic.Count, lineNumbers);
+
+ var allFiles = rootFolderInfo.GetFiles("*", SearchOption.AllDirectories);
+
+ var filteredFiles1 = allFiles.Where(f => TargetExtensions.Any(e => f.Name.EndsWith(e)));
+ var filteredFiles2 = filteredFiles1.Where(f => !ExcludePaths.Any(p => f.FullName.Contains(p)));
+
+ var selectedFiles = filteredFiles2.OrderBy(f => f.FullName).ToList();
+
+ var wordIndex = IndexBuilder.BuildIndexFromFiles(selectedFiles, rootFolderInfo.FullName);
+
+ Trace("Created word index from {0} files containing {1} individual words", selectedFiles.Count, wordIndex.Keys.Count);
+
+ var outputFileName = Path.Combine(outputFolderInfo.FullName, string.Format("StringCheck_{0:yyyyMMddHHmmss}.xml", DateTime.Now));
+ var settings = new XmlWriterSettings
+ {
+ Indent = true,
+ Encoding = Encoding.UTF8,
+ WriteEndDocumentOnClose = true
+ };
+
+ Trace("Output file: {0}", outputFileName);
+
+ using (XmlWriter writer = XmlWriter.Create(outputFileName, settings))
+ {
+ writer.WriteStartDocument(true);
+
+ // Write the Processing Instruction node.
+ string xslText = "type=\"text/xsl\" href=\"StringCheck.xslt\"";
+ writer.WriteProcessingInstruction("xml-stylesheet", xslText);
+
+ writer.WriteStartElement("StringUsages");
+ writer.WriteAttributeString("ReportTitle", unusedOnly ? "Unused Strings Report" : "String Usage Report");
+ writer.WriteAttributeString("Mode", unusedOnly ? "UnusedOnly" : "All");
+
+ foreach (var kvp in stringsDic)
+ {
+ var occurences = wordIndex.Find(kvp.Key);
+
+ if (occurences == null || !unusedOnly)
+ {
+ ////Trace("{0}: {1}", kvp.Key, kvp.Value);
+ writer.WriteStartElement("Dictionary");
+ writer.WriteAttributeString("Token", kvp.Key);
+ writer.WriteAttributeString("Text", kvp.Value);
+
+ if (occurences != null && !unusedOnly)
+ {
+ foreach (var occurence in occurences)
+ {
+ writer.WriteStartElement("Occurence");
+ writer.WriteAttributeString("FileName", occurence.FileName);
+ writer.WriteAttributeString("FullPath", occurence.FullPath);
+ writer.WriteAttributeString("LineNumber", occurence.LineNumber.ToString());
+ writer.WriteEndElement();
+ ////Trace(" {0}:{1}", occurence.FileName, occurence.LineNumber);
+ }
+ }
+
+ writer.WriteEndElement();
+ }
+ }
+ }
+
+ TestContext.AddResultFile(outputFileName);
+ TestContext.AddResultFile(outputFolderInfo.FullName);
+ }
+
+ private SortedDictionary<string, string> CreateStringsDictionary(FileInfo file, out int lineNumbers)
+ {
+ var dic = new SortedDictionary<string, string>();
+ lineNumbers = 0;
+
+ using (var reader = file.OpenText())
+ {
+ while (!reader.EndOfStream)
+ {
+ lineNumbers++;
+ var words = reader
+ .ReadLine()
+ .Split(new[] { "\":" }, StringSplitOptions.RemoveEmptyEntries);
+
+
+ if (words.Length == 2)
+ {
+ var token = words[0].Replace("\"", string.Empty).Trim();
+ var text = words[1].Replace("\",", string.Empty).Replace("\"", string.Empty).Trim();
+
+ if (dic.Keys.Contains(token))
+ {
+ throw new Exception(string.Format("Double string entry found: {0}", token));
+ }
+
+ dic.Add(token, text);
+ }
+ }
+ }
+
+ return dic;
+ }
+
+ private DirectoryInfo ResolveFolder(string currentDir, string folderPath)
+ {
+ if (folderPath.IndexOf(@"\:") != 1)
+ {
+ folderPath = Path.Combine(currentDir, folderPath);
+ }
+
+ var folderInfo = new DirectoryInfo(folderPath);
+
+ if (!folderInfo.Exists)
+ {
+ throw new Exception(string.Format("Folder not found: {0}", folderInfo.FullName));
+ }
+
+ return folderInfo;
+ }
+
+
+ private void Trace(string message, params object[] parameters)
+ {
+ var formatted = string.Format(message, parameters);
+ System.Diagnostics.Trace.WriteLine(formatted);
+ }
+ }
+}