123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- 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.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 => string.Equals(e, f.Extension, StringComparison.OrdinalIgnoreCase)));
- 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);
- }
- }
- }
|