diff --git a/doc/distrib/NodeHelpFiles/HNVVP7HSR2IM5H5AFWWLLJBXSX2WTG5FEWESXLGORW2CL2CG7C4Q.md b/doc/distrib/NodeHelpFiles/HNVVP7HSR2IM5H5AFWWLLJBXSX2WTG5FEWESXLGORW2CL2CG7C4Q.md new file mode 100644 index 00000000000..dde79357e24 --- /dev/null +++ b/doc/distrib/NodeHelpFiles/HNVVP7HSR2IM5H5AFWWLLJBXSX2WTG5FEWESXLGORW2CL2CG7C4Q.md @@ -0,0 +1,7 @@ + + +## ByBoxLengths(origin, width, length, height, xSpans, ySpans, zSpans, symmetry, inSmoothMode) - Documentation +This documentation file is auto generated by NodeDocumentationMarkdownGenerator, Version=2.14.0.3986, Culture=neutral, PublicKeyToken=null. + +For more information about adding documentation to nodes see https://github.com/DynamoDS/Dynamo/wiki/Create-and-Add-Custom-Documentation-to-Nodes + diff --git a/doc/distrib/NodeHelpFiles/VFK33PBU2AHJIHDOMW3NS2YJNGRTQADZ2RUGALOLEWRXK4DM4DTA.md b/doc/distrib/NodeHelpFiles/VFK33PBU2AHJIHDOMW3NS2YJNGRTQADZ2RUGALOLEWRXK4DM4DTA.md new file mode 100644 index 00000000000..c90fc2b5fd6 --- /dev/null +++ b/doc/distrib/NodeHelpFiles/VFK33PBU2AHJIHDOMW3NS2YJNGRTQADZ2RUGALOLEWRXK4DM4DTA.md @@ -0,0 +1,7 @@ + +## ByBoxLengths(cs, width, length, height, xSpans, ySpans, zSpans, symmetry, inSmoothMode) - Documentation +This documentation file is auto generated by NodeDocumentationMarkdownGenerator, Version=2.14.0.3986, Culture=neutral, PublicKeyToken=null. + +For more information about adding documentation to nodes see https://github.com/DynamoDS/Dynamo/wiki/Create-and-Add-Custom-Documentation-to-Nodes + diff --git a/doc/distrib/NodeHelpFiles/WN6BWNG6A6KOPFMMRBHLV7XBOOEGXTSO5I5FZXHKKUG5YO6MNNYA.md b/doc/distrib/NodeHelpFiles/WN6BWNG6A6KOPFMMRBHLV7XBOOEGXTSO5I5FZXHKKUG5YO6MNNYA.md new file mode 100644 index 00000000000..fa9488b5acf --- /dev/null +++ b/doc/distrib/NodeHelpFiles/WN6BWNG6A6KOPFMMRBHLV7XBOOEGXTSO5I5FZXHKKUG5YO6MNNYA.md @@ -0,0 +1,7 @@ + + +## ByBoxLengths(width, length, height, xSpans, ySpans, zSpans, symmetry, inSmoothMode) - Documentation +This documentation file is auto generated by NodeDocumentationMarkdownGenerator, Version=2.14.0.3986, Culture=neutral, PublicKeyToken=null. + +For more information about adding documentation to nodes see https://github.com/DynamoDS/Dynamo/wiki/Create-and-Add-Custom-Documentation-to-Nodes + diff --git a/src/DocumentationBrowserViewExtension/DocumentationBrowserViewModel.cs b/src/DocumentationBrowserViewExtension/DocumentationBrowserViewModel.cs index c4b68667910..104eefa2016 100644 --- a/src/DocumentationBrowserViewExtension/DocumentationBrowserViewModel.cs +++ b/src/DocumentationBrowserViewExtension/DocumentationBrowserViewModel.cs @@ -86,6 +86,7 @@ private set private Uri link; private string graphPath; private string content; + private string name; private MarkdownHandler MarkdownHandlerInstance => markdownHandler ?? (markdownHandler = new MarkdownHandler()); public bool HasContent => !string.IsNullOrWhiteSpace(this.content); @@ -184,6 +185,7 @@ private void HandleLocalResource(OpenDocumentationLinkEventArgs e) { string targetContent; string graph; + string graphName; Uri link; switch (e) { @@ -195,12 +197,14 @@ private void HandleLocalResource(OpenDocumentationLinkEventArgs e) link = string.IsNullOrEmpty(mdLink) ? new Uri(String.Empty, UriKind.Relative) : new Uri(mdLink); graph = GetGraphLinkFromMDLocation(link); targetContent = CreateNodeAnnotationContent(openNodeAnnotationEventArgs); + graphName = openNodeAnnotationEventArgs.MinimumQualifiedName; break; case OpenDocumentationLinkEventArgs openDocumentationLink: link = openDocumentationLink.Link; graph = GetGraphLinkFromMDLocation(link); targetContent = ResourceUtilities.LoadContentFromResources(openDocumentationLink.Link.ToString(), GetType().Assembly); + graphName = null; break; default: @@ -208,6 +212,7 @@ private void HandleLocalResource(OpenDocumentationLinkEventArgs e) targetContent = null; graph = null; link = null; + graphName = null; break; } @@ -220,6 +225,7 @@ private void HandleLocalResource(OpenDocumentationLinkEventArgs e) this.content = targetContent; this.Link = link; this.graphPath = graph; + this.name = graphName; } } catch (FileNotFoundException) @@ -358,7 +364,8 @@ internal void InsertGraph() { if (graphPath != null) { - raiseInsertGraph(this, new InsertDocumentationLinkEventArgs(graphPath, Path.GetFileNameWithoutExtension(graphPath))); + var graphName = this.name ?? Path.GetFileNameWithoutExtension(graphPath); + raiseInsertGraph(this, new InsertDocumentationLinkEventArgs(graphPath, graphName)); } else { diff --git a/src/DocumentationBrowserViewExtension/PackageDocumentationManager.cs b/src/DocumentationBrowserViewExtension/PackageDocumentationManager.cs index 030b565e628..5b5b74557de 100644 --- a/src/DocumentationBrowserViewExtension/PackageDocumentationManager.cs +++ b/src/DocumentationBrowserViewExtension/PackageDocumentationManager.cs @@ -5,6 +5,7 @@ using System.Threading; using Dynamo.Interfaces; using Dynamo.Logging; +using Dynamo.Utilities; namespace Dynamo.DocumentationBrowser { @@ -95,10 +96,13 @@ public string GetAnnotationDoc(string nodeNamespace, string packageName) return output; } + var shortName = Hash.GetHashFilenameFromString(nodeNamespace); + FileInfo matchingDoc = null; if (hostDynamoFallbackDocPath != null) { - matchingDoc = hostDynamoFallbackDocPath.GetFiles($"{nodeNamespace}.md").FirstOrDefault(); + matchingDoc = hostDynamoFallbackDocPath.GetFiles($"{shortName}.md").FirstOrDefault() ?? + hostDynamoFallbackDocPath.GetFiles($"{nodeNamespace}.md").FirstOrDefault(); if (matchingDoc != null) { return matchingDoc.FullName; @@ -107,7 +111,8 @@ public string GetAnnotationDoc(string nodeNamespace, string packageName) if (dynamoCoreFallbackDocPath != null) { - matchingDoc = dynamoCoreFallbackDocPath.GetFiles($"{nodeNamespace}.md").FirstOrDefault(); + matchingDoc = dynamoCoreFallbackDocPath.GetFiles($"{shortName}.md").FirstOrDefault() ?? + dynamoCoreFallbackDocPath.GetFiles($"{nodeNamespace}.md").FirstOrDefault(); } return matchingDoc is null ? string.Empty : matchingDoc.FullName; diff --git a/src/DynamoUtilities/Hash.cs b/src/DynamoUtilities/Hash.cs new file mode 100644 index 00000000000..7ec17e7407b --- /dev/null +++ b/src/DynamoUtilities/Hash.cs @@ -0,0 +1,97 @@ +using System; +using System.Linq; +using System.Security.Cryptography; +using System.Text; + +namespace Dynamo.Utilities +{ + internal class Hash + { + /// + /// Get the hash value + /// + /// input as a byte array + /// hash as a byte array + internal static byte[] GetHash(byte[] bytes) + { + using (var hashAlgorithm = SHA256.Create()) + { + return hashAlgorithm.ComputeHash(bytes); + } + } + + /// + /// Get the hash value + /// + /// input as a string + /// hash as a byte array + internal static byte[] GetHashFromString(string str) + { + var bytes = Encoding.UTF8.GetBytes(str); + return GetHash(bytes); + } + + /// + /// Get a valid filename for a hash + /// + /// hash as a byte array + /// hash as a valid filename string + internal static string GetFilenameFromHash(byte[] bytes) + { + return ToBase32String(bytes); + } + + /// + /// Get hash file name + /// + /// inout as a string + /// hash as a valid filename + internal static string GetHashFilenameFromString(string str) + { + var hash = GetHashFromString(str); + return GetFilenameFromHash(hash); + } + + /// + /// /// The different characters allowed in Base32 encoding. + /// + /// + /// This is a 32-character subset of the twenty-six letters A–Z and six digits 2–7. + /// + /// + internal static string Base32AllowedCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + + /// /// Converts a byte array into a Base32 string. + /// + /// The string to convert to Base32. + /// Whether or not to add RFC3548 '='-padding to the string. + /// A Base32 string. + /// + /// https://tools.ietf.org/html/rfc3548#section-2.2 indicates padding MUST be added unless the reference to the RFC tells us otherswise. + /// https://github.com/google/google-authenticator/wiki/Key-Uri-Format indicates that padding SHOULD be omitted. + /// To meet both requirements, you can omit padding when required. + /// + internal static string ToBase32String(byte[] input, bool addPadding = false) + { + if (input == null || input.Length == 0) + { + return string.Empty; + } + + var bits = input.Select(b => Convert.ToString(b, 2).PadLeft(8, '0')).Aggregate((a, b) => a + b) + .PadRight((int)(Math.Ceiling((input.Length * 8) / 5d) * 5), '0'); + var result = Enumerable.Range(0, bits.Length / 5) + .Select(i => Base32AllowedCharacters.Substring(Convert.ToInt32(bits.Substring(i * 5, 5), 2), 1)) + .Aggregate((a, b) => a + b); + + if (addPadding) + { + result = result.PadRight((int)(Math.Ceiling(result.Length / 8d) * 8), '='); + } + + return result; + } + } +} + + diff --git a/src/DynamoUtilities/Properties/AssemblyInfo.cs b/src/DynamoUtilities/Properties/AssemblyInfo.cs index 98fad63197b..279839de1bb 100644 --- a/src/DynamoUtilities/Properties/AssemblyInfo.cs +++ b/src/DynamoUtilities/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -20,4 +20,5 @@ [assembly: InternalsVisibleTo("DocumentationBrowserViewExtension")] [assembly: InternalsVisibleTo("DynamoPackages")] [assembly: InternalsVisibleTo("ProtoScript")] -[assembly: InternalsVisibleTo("ProtoCore")] \ No newline at end of file +[assembly: InternalsVisibleTo("ProtoCore")] +[assembly: InternalsVisibleTo("NodeDocumentationMarkdownGenerator")] diff --git a/src/Tools/NodeDocumentationMarkdownGenerator/CommandHandler.cs b/src/Tools/NodeDocumentationMarkdownGenerator/CommandHandler.cs index 0c028d1b549..bb0f4ab6cac 100644 --- a/src/Tools/NodeDocumentationMarkdownGenerator/CommandHandler.cs +++ b/src/Tools/NodeDocumentationMarkdownGenerator/CommandHandler.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Text; using Dynamo.Logging; using NodeDocumentationMarkdownGenerator.Commands; @@ -36,6 +36,20 @@ internal static string HandleFromDirectory(FromDirectoryOptions opts) return ""; } + internal static string HandleRename(RenameOptions opts) + { + try + { + RenameCommand.HandleRename(opts); + } + catch (Exception e) + { + LogExceptionToConsole(e); + } + + return ""; + } + internal static void LogExceptionToConsole(Exception e) { var strBuilder = new StringBuilder(); @@ -76,4 +90,4 @@ public void LogWarning(string warning, WarningLevel level) throw new NotImplementedException(); } } -} \ No newline at end of file +} diff --git a/src/Tools/NodeDocumentationMarkdownGenerator/Commands/RenameCommand.cs b/src/Tools/NodeDocumentationMarkdownGenerator/Commands/RenameCommand.cs new file mode 100644 index 00000000000..1711fe9e693 --- /dev/null +++ b/src/Tools/NodeDocumentationMarkdownGenerator/Commands/RenameCommand.cs @@ -0,0 +1,90 @@ +using System; +using System.IO; +using System.Linq; +using NodeDocumentationMarkdownGenerator.Verbs; + +namespace NodeDocumentationMarkdownGenerator.Commands +{ + internal static class RenameCommand + { + internal static void HandleRename(RenameOptions opts) + { + if (opts.InputMdFile is null && opts.InputMdDirectory != null) + { + RenameDirectory(opts.InputMdDirectory, opts.MaxLength); + } + else if (opts.InputMdFile != null && opts.InputMdDirectory is null) + { + RenameFile(opts.InputMdFile); + } + else + { + Console.WriteLine("Invalid options: You can rename a single file using the file option\nor rename multiple files in a directory (if they are longer than max length)\nusing the directory option"); + } + } + + private static void RenameFile(string file) + { + var extension = Path.GetExtension(file); + if (!extension.Equals(".md", StringComparison.InvariantCultureIgnoreCase)) + { + Console.WriteLine($"Can only rename MD files: {file}"); + return; + } + + if (!File.Exists(file)) + { + Console.WriteLine($"File not found: {file}"); + return; + } + + var baseName = Path.GetFileNameWithoutExtension(file); + var shortName = Dynamo.Utilities.Hash.GetHashFilenameFromString(baseName); + + RenameFile(file, baseName, shortName); + } + + private static void RenameFile(string file, string baseName, string shortName) + { + var content = File.ReadAllText(file); + content = content.Replace(baseName, shortName); + var path = Path.GetDirectoryName(file); + var newFile = Path.Combine(path, shortName + ".md"); + File.WriteAllText(newFile, $"\n\n" + content); + File.Delete(file); + + var allSupportFiles = Directory.GetFiles(path, baseName + ".*", SearchOption.TopDirectoryOnly) + .Select(x => new FileInfo(x)).ToList(); + allSupportFiles.AddRange(Directory.GetFiles(path, baseName + "_img.*", SearchOption.TopDirectoryOnly) + .Select(x => new FileInfo(x)).ToList()); + + foreach (var supportFile in allSupportFiles) + { + var newName = Path.Combine(supportFile.DirectoryName, + supportFile.Name.Replace(baseName, shortName)); + supportFile.MoveTo(newName); + } + } + + private static void RenameDirectory(string directory, int maxLength) + { + if (!Directory.Exists(directory)) + { + Console.WriteLine($"Directory not found: {directory}"); + return; + } + + var allMdFiles = Directory.GetFiles(directory, "*.md", SearchOption.TopDirectoryOnly).Select(x => new FileInfo(x)).ToList(); + + foreach (var mdFile in allMdFiles) + { + if (mdFile.Name.Length > maxLength) + { + var baseName = Path.GetFileNameWithoutExtension(mdFile.Name); + var shortName = Dynamo.Utilities.Hash.GetHashFilenameFromString(baseName); + RenameFile(mdFile.FullName, baseName, shortName); + } + } + } + } +} diff --git a/src/Tools/NodeDocumentationMarkdownGenerator/Program.cs b/src/Tools/NodeDocumentationMarkdownGenerator/Program.cs index 51bb83614f5..0e21d35afab 100644 --- a/src/Tools/NodeDocumentationMarkdownGenerator/Program.cs +++ b/src/Tools/NodeDocumentationMarkdownGenerator/Program.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -50,11 +50,12 @@ internal static void Main(string[] args) ShowWelcomeMessages(); - var result = Parser.Default.ParseArguments(args); + var result = Parser.Default.ParseArguments(args); var text = result .MapResult( (FromDirectoryOptions opts) => CommandHandler.HandleFromDirectory(opts), (FromPackageOptions opts) => CommandHandler.HandleFromPackage(opts), + (RenameOptions opts) => CommandHandler.HandleRename(opts), err => "1"); Console.WriteLine($"docs generation tool {sw.Elapsed.TotalSeconds}"); # if DEBUG diff --git a/src/Tools/NodeDocumentationMarkdownGenerator/Verbs/RenameOptions.cs b/src/Tools/NodeDocumentationMarkdownGenerator/Verbs/RenameOptions.cs new file mode 100644 index 00000000000..e92b2f73927 --- /dev/null +++ b/src/Tools/NodeDocumentationMarkdownGenerator/Verbs/RenameOptions.cs @@ -0,0 +1,25 @@ +using CommandLine.Text; +using CommandLine; +using System.Collections.Generic; + +namespace NodeDocumentationMarkdownGenerator.Verbs +{ + [Verb("rename", HelpText = "Renaming utilities for fallback MD files")] + internal class RenameOptions + { + [Option('f', "file", HelpText = "Input MD file. Renames a single MD file including any support files to a shorter length (~56-60 characters) base file name.", Required = false)] + public string InputMdFile { get; set; } + [Option('d', "directory", HelpText = "Input directory. Inspects all MD files in a directory and renames all MD files with a base name longer that maxlength (see below).", Required = false)] + public string InputMdDirectory { get; set; } + [Option('m', "maxlength", HelpText = "Max length of the base file name before renaming to a shorter length (~56-60 characters) base file name.", Required = false, Default = 65)] + public int MaxLength { get; set; } + [Usage(ApplicationAlias = "Dynamo docs generator")] + public static IEnumerable Examples + { + get + { + yield return new Example("Renaming utilities for fallback MD files", new RenameOptions()); + } + } + } +} diff --git a/src/Tools/NodeDocumentationMarkdownGenerator/readme.md b/src/Tools/NodeDocumentationMarkdownGenerator/readme.md index 25013218321..c4fe33e993b 100644 --- a/src/Tools/NodeDocumentationMarkdownGenerator/readme.md +++ b/src/Tools/NodeDocumentationMarkdownGenerator/readme.md @@ -3,9 +3,11 @@ The NodeDocumentationGenerator is a CLI tool to generate node documentation markdown stubs. The tool creates markdown files. Depending on the mode and option flags used, the file content can be default content, or extracted from the DynamoDictionary. -There are two different high level commands: +There are three different high level commands: 1. Create Documentation From Directory 2. Create Documentation From Package +3. Rename an existing Documentaion package to a shorter name + ## Use Cases 1. One time dynamo dictionary migration to .md files. The resulting files will be stored in the Dynamo and host specific repos. (DynamoRevit) @@ -15,6 +17,8 @@ There are two different high level commands: 3. Used by package authors to stub out documentation for nodes. third parties can run the --FromPackage command to stub out empty markdown files in the /doc folder of their package. Then the package author can manually fill in the markddown file with detailed documentation, images, gifs etc. +4. The name/path of a resulting documentaion package sometimes gets too long and causes problems for CI/CD and installers. The rename options can then be used on a case by case basis for shorten the base filename to a name based on a hash value. The MD file and all support files gets renamed and the original base name is added to the new MD file as a comment making it searchable. + ## How to use the Generated Docs ### Dynamo @@ -33,6 +37,7 @@ Eventually Dictionary website can be refactored to consume markdown files from c | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `fromdirectory` | Generates documentation for all dlls (or only those specified in the filter) and all dyfs (if specified by the `--includedyfs` flag) in the input directory | | `frompackage` | Looks up node libraries in the pkg.json file and creates documentation for those dlls and creates documentation for all dyfs in the `/dyf` folder. All generated files gets saved in `path/to/package/docs` | +| `rename` | Renaming utilities for fallback MD files | ### fromdirectory - flags | Short name | Long name | Optional | Description | @@ -95,6 +100,31 @@ frompackage -w ``` +### rename - flags +| Short name | Long name | Optional | Description | +| ---------- | -------------- | :------: | ------------------------------------------------------------------------------- | +| `-f` | `--file` | ✅ | Input MD file. Renames a single MD file including any support files to a shorter length (~56-60 characters) base file name. | +| `-d` | `--directory` | ✅ | Input directory. Inspects all MD files in a directory and renames all MD files with a base name longer that maxlength (see below). | +| `-m` | `--maxlength` | ✅ | Max length of the base file name before renaming to a shorter length (~56-60 characters) base file name. Defaults to 70. | + + +### Rename examples + +rename a single MD file including any support files + +```bash +rename +-f "C:\...\fallback_docs\Autodesk.DesignScript.Geometry.CoordinateSystem.ByOriginVectors(origin, xAxis, yAxis).md" +``` + +rename all MD files in a directory with a base file name longer then 70 characters. + +```bash +rename +-d "C:\...\fallback_docs" +-m 70 +``` + ## Known Issues: * The nodedocsgenerator.exe tool currently requires being able to load the types used by the binaries being inspected. For example if you are trying to generate docs for a package which depends on Revit you will need to use the `-references flag (-r)` to give the tool access to the RevitAPI and Revit binaries. diff --git a/test/DynamoCoreTests/Utilities/UtilitiesTests.cs b/test/DynamoCoreTests/Utilities/UtilitiesTests.cs index 203ab31c334..628a89639dc 100644 --- a/test/DynamoCoreTests/Utilities/UtilitiesTests.cs +++ b/test/DynamoCoreTests/Utilities/UtilitiesTests.cs @@ -1,6 +1,7 @@ -using System; +using System; using Dynamo.Utilities; using NUnit.Framework; +using SharpDX.Text; namespace Dynamo.Tests.Core { @@ -60,5 +61,18 @@ public void ResourceLoaderLoadManyTest() resourceNames = new string[] { "TestInt" }; Assert.Throws(() => ResourceLoader.Load(typeof(TestResource), "TestInt")); } + /// + /// Test the Hash class + /// + [Test] + [Category("UnitTests")] + public void HashTest() + { + var testStr = "Test"; + + var filename = Hash.GetHashFilenameFromString(testStr); + + Assert.AreEqual("KMXKVPMVOSEA3P3WXG4MYAEDFQQKN3ARHVUCFGKVBV5G4DZULYSQ", filename); + } } -} \ No newline at end of file +} diff --git a/test/Tools/NodeDocumentationMarkdownGeneratorTests/MarkdownGeneratorCommandTests.cs b/test/Tools/NodeDocumentationMarkdownGeneratorTests/MarkdownGeneratorCommandTests.cs index 61d8f77c216..1c53873ebdf 100644 --- a/test/Tools/NodeDocumentationMarkdownGeneratorTests/MarkdownGeneratorCommandTests.cs +++ b/test/Tools/NodeDocumentationMarkdownGeneratorTests/MarkdownGeneratorCommandTests.cs @@ -483,6 +483,75 @@ public void ReferencesFlagAddsReferencePaths() Assert.IsTrue(Program.ReferenceAssemblyPaths.Select(x => new FileInfo(x).Name).Contains("SampleLibraryUI.dll")); } + [Test] + public void CanRenameFile() + { + // Arrange + var originalOutDirName = "fallback_docs"; + var originalOutDir = new DirectoryInfo(Path.Combine(toolsTestFilesDirectory, originalOutDirName)); + + var targetMdFile = "CoreNodeModels.HigherOrder.Map.md"; + var renamedTargetMdFile = "SVLKFMPW6YIPCHS5TA2H3KJQQTSPUZOGUBWJG3VEPVFVB7DMGFDQ.md"; + + tempDirectory = CreateTempOutputDirectory(); + Assert.That(tempDirectory.Exists); + + CopyFilesRecursively(originalOutDir, tempDirectory); + var mdFile = Path.Combine(tempDirectory.FullName, targetMdFile); + var renamedMdFile = Path.Combine(tempDirectory.FullName, renamedTargetMdFile); + + // Act + var opts = new RenameOptions + { + InputMdFile = mdFile + }; + + RenameCommand.HandleRename(opts); + + // Assert + var mdFiles = tempDirectory.GetFiles("*.md", SearchOption.TopDirectoryOnly) + .Select(x => x.Name); + + var content = File.ReadAllText(renamedMdFile); + + Assert.IsTrue(mdFiles.Contains(renamedTargetMdFile)); + Assert.IsTrue(content.Contains("CoreNodeModels.HigherOrder.Map")); + } + + [Test] + public void CanRenameFilesInADirectory() + { + // Arrange + var originalOutDirName = "fallback_docs"; + var originalOutDir = new DirectoryInfo(Path.Combine(toolsTestFilesDirectory, originalOutDirName)); + + var expectedFileNames = new List + { + "FGRJU5ZIMM4EKNHFEXZGHJTKI73262KTH4CSUBI2IEXVH46TACRA.md", + "HEG35EENB6LZZUAB4OKNCYCDHDTBEF7IR2YWCH7I4EOIQPFOJGFQ.md", + "SVLKFMPW6YIPCHS5TA2H3KJQQTSPUZOGUBWJG3VEPVFVB7DMGFDQ.md", + "list.rank.md", + "loopwhile.md" + }; + + tempDirectory = CreateTempOutputDirectory(); + Assert.That(tempDirectory.Exists); + + CopyFilesRecursively(originalOutDir, tempDirectory); + + // Act + var opts = new RenameOptions + { + InputMdDirectory = tempDirectory.FullName, + MaxLength = 15 + }; + + RenameCommand.HandleRename(opts); + + // Assert + CollectionAssert.AreEquivalent(expectedFileNames, tempDirectory.GetFiles().Select(x => x.Name)); + } + #region Helpers internal void AssertMdFileInfos(List mdFileInfos, FileInfo[] coreNodeModelMdFiles) {