From f239934937e5b2faf80c6c46f4bf98e6d69f9dba Mon Sep 17 00:00:00 2001 From: John Andrews Date: Thu, 22 Aug 2024 17:09:36 +1200 Subject: [PATCH] unit tests --- BasicNodes/BasicNodes.csproj | 1 + BasicNodes/File/FileSizeCompare.cs | 2 +- BasicNodes/File/HasHardLinks.cs | 54 +++--- BasicNodes/Tests/CopyTests.cs | 78 --------- BasicNodes/Tests/ExecutorTests.cs | 60 ++++--- BasicNodes/Tests/FileSizeCompareTests.cs | 176 ++++++++++---------- BasicNodes/Tests/HasHardLinksTest.cs | 81 ++++++++- BasicNodes/Tests/MoveTests.cs | 47 +++--- BasicNodes/Tests/WriteTextTests.cs | 8 +- BasicNodes/Tests/_TestBase.cs | 14 ++ BasicNodes/Tools/Executor.cs | 200 ++++++++++++----------- FileFlows.Plugin.dll | Bin 152064 -> 152064 bytes FileFlows.Plugin.pdb | Bin 38080 -> 38096 bytes 13 files changed, 370 insertions(+), 351 deletions(-) delete mode 100644 BasicNodes/Tests/CopyTests.cs diff --git a/BasicNodes/BasicNodes.csproj b/BasicNodes/BasicNodes.csproj index 1ee7afc4..8b948ea9 100644 --- a/BasicNodes/BasicNodes.csproj +++ b/BasicNodes/BasicNodes.csproj @@ -24,6 +24,7 @@ This plugin is required for FileFlows to work. + diff --git a/BasicNodes/File/FileSizeCompare.cs b/BasicNodes/File/FileSizeCompare.cs index 7b046e32..c333c33b 100644 --- a/BasicNodes/File/FileSizeCompare.cs +++ b/BasicNodes/File/FileSizeCompare.cs @@ -27,7 +27,7 @@ public class FileSizeCompare : Node if (result.IsFailed) { // try get from variables - if (args.Variables.ContainsKey("file.Orig.Size") && args.Variables["file.Orig.Size"] is long tSize && tSize > 0) + if (args.Variables.TryGetValue("file.Orig.Size", out object? value) && value is long tSize && tSize > 0) { origSize = tSize; } diff --git a/BasicNodes/File/HasHardLinks.cs b/BasicNodes/File/HasHardLinks.cs index 77a6e270..9bc154dc 100644 --- a/BasicNodes/File/HasHardLinks.cs +++ b/BasicNodes/File/HasHardLinks.cs @@ -134,22 +134,22 @@ public class HasHardLinks: Node if(string.IsNullOrWhiteSpace(error) == false) args.Logger?.ILog("Error Output: " + error); - if (int.TryParse(output.Trim(), out var linkCount)) + if (int.TryParse(output.Trim(), out var linkCount) == false) { - if (linkCount > 0) - { - args.Logger?.ILog($"The file has {linkCount} hard links."); - if (linkCount >= count) - return true; - } - args.Logger?.ILog("The file does not have the required number of hard links."); + + args.Logger?.ILog("Failed to retrieve link count."); return false; } + + --linkCount; - if(string.IsNullOrWhiteSpace(error)) - args.Logger?.ELog(error); - - args.Logger?.ILog("Failed to retrieve link count."); + if (linkCount > 0) + { + args.Logger?.ILog($"The file has {linkCount} hard links."); + if (linkCount >= count) + return true; + } + args.Logger?.ILog("The file does not have the required number of hard links."); return false; } catch (Exception ex) @@ -184,26 +184,28 @@ public class HasHardLinks: Node string output = process.StandardOutput.ReadToEnd(); string error = process.StandardError.ReadToEnd(); process.WaitForExit(); - - if(string.IsNullOrWhiteSpace(output) == false) + + if (string.IsNullOrWhiteSpace(output) == false) args.Logger?.ILog("Standard Output: " + output); - if(string.IsNullOrWhiteSpace(error) == false) + if (string.IsNullOrWhiteSpace(error) == false) args.Logger?.ILog("Error Output: " + error); - if (int.TryParse(output.Trim(), out var linkCount)) + if (int.TryParse(output.Trim(), out var linkCount) == false) { - if (linkCount > 0) - { - args.Logger?.ILog($"The file has {linkCount} hard links."); - if (linkCount >= count) - return true; - } - args.Logger?.ILog("The file does not have hard links."); - return false; + args.Logger?.ILog("The file does not have the required number of hard links."); + return false; } + + linkCount -= 1; // every file has at least one hard link we want the additional hard links - args.Logger?.ILog("The file does not have the required number of hard links."); - return false; + if (linkCount > 0) + { + args.Logger?.ILog($"The file has {linkCount} hard links."); + if (linkCount >= count) + return true; + } + args.Logger?.ILog("The file does not have hard links."); + return false; } catch (Exception ex) { diff --git a/BasicNodes/Tests/CopyTests.cs b/BasicNodes/Tests/CopyTests.cs deleted file mode 100644 index 1b361806..00000000 --- a/BasicNodes/Tests/CopyTests.cs +++ /dev/null @@ -1,78 +0,0 @@ -using FileFlows.Plugin; - -#if(DEBUG) - -namespace BasicNodes.Tests; - -using FileFlows.BasicNodes.File; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Text.RegularExpressions; - -[TestClass] -public class CopyTests -{ - List> Mappings = new List> - { - new KeyValuePair("/usr/local/bin/ffmpeg", @"C:\Users\username\AppData\Roaming\FileFlows\Tools\ffmpeg.exe"), - new KeyValuePair("/mnt/tempNAS/media/dvd/sorted", @"\\192.168.1.22\Media\dvd\sorted"), - new KeyValuePair("/mnt/tempNAS/media/dvd/output", @"\\192.168.1.22\Media\dvd\output"), - }; - char DirectorySeperatorChar = System.IO.Path.DirectorySeparatorChar; - - [TestMethod] - public void CopyTests_Dir_Mapping() - { - var logger = new TestLogger(); - var args = new FileFlows.Plugin.NodeParameters(@"c:\test\testfile.mkv", logger, false, string.Empty, null); - args.PathMapper = s => Map(s); - - CopyFile node = new (); - node.CopyFolder = true; - node.DestinationPath = "/mnt/tempNAS/media/dvd/output"; - node.DestinationFile = "{file.Orig.FileName}p.DVD.x264.slow.CRF16{ext}"; - var result = node.Execute(args); - Assert.AreEqual(2, result); - } - - [TestMethod] - public void CopyTests_Dir_DateReplacements() - { - var logger = new TestLogger(); - var args = new FileFlows.Plugin.NodeParameters(@"D:\videos\testfiles\bigbuckbunny_480p_30s.mp4", logger, false, string.Empty, null); - args.PathMapper = s => Map(s); - - CopyFile node = new(); - node.CopyFolder = true; - node.DestinationPath = @"D:\videos\converted"; - node.DestinationFile = "{file.Orig.FileName}-{file.Create.Month:00}-{file.Create.Year}{ext!}"; - var result = node.Execute(args); - Assert.AreEqual(1, result); - } - - string Map(string path) - { - if (string.IsNullOrEmpty(path)) - return string.Empty; - if (Mappings != null && Mappings.Count > 0) - { - // convert all \ to / for now - path = path.Replace("\\", "/"); - foreach (var mapping in Mappings) - { - if (string.IsNullOrEmpty(mapping.Value) || string.IsNullOrEmpty(mapping.Key)) - continue; - string pattern = Regex.Escape(mapping.Key.Replace("\\", "/")); - string replacement = mapping.Value.Replace("\\", "/"); - path = Regex.Replace(path, "^" + pattern, replacement, RegexOptions.IgnoreCase); - } - // now convert / to path charcter - if (DirectorySeperatorChar != '/') - path = path.Replace('/', DirectorySeperatorChar); - if (path.StartsWith("//")) // special case for SMB paths - path = path.Replace('/', '\\'); - } - return path; - } -} - -#endif \ No newline at end of file diff --git a/BasicNodes/Tests/ExecutorTests.cs b/BasicNodes/Tests/ExecutorTests.cs index 4c23426a..65fe28ac 100644 --- a/BasicNodes/Tests/ExecutorTests.cs +++ b/BasicNodes/Tests/ExecutorTests.cs @@ -1,35 +1,45 @@ #if(DEBUG) -namespace BasicNodes.Tests +using FileFlows.BasicNodes.File; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Text.RegularExpressions; +using FileFlows.Plugin; +using Moq; + +namespace BasicNodes.Tests; + +[TestClass] +public class ExecutorTests : TestBase { - using FileFlows.BasicNodes.File; - using Microsoft.VisualStudio.TestTools.UnitTesting; - using System.Text.RegularExpressions; + private Mock mockProcessHelper = new(); - [TestClass] - public class ExecutorTests + protected override void TestStarting() { - [TestMethod] - public void Executor_OutputVariable() + mockProcessHelper.Setup(x => x.ExecuteShellCommand(It.IsAny())).Returns(Task.FromResult(new ProcessResult() { - var logger = new TestLogger(); - var args = new FileFlows.Plugin.NodeParameters(@"c:\test\testfile.mkv", logger, false, string.Empty, null); + ExitCode = 0, + Completed = true, + Output = "this is the output", + StandardOutput = "this is the standard output", + StandardError = "this is the standard error" + })); + } - Executor node = new Executor(); - string file = @"D:\Videos\dummy.mkv"; - node.FileName = @"C:\utils\ffmpeg\ffmpeg.exe"; - node.Arguments = "-i \"" + file + "\""; - node.OutputVariable = "ExecOutput"; - var result = node.Execute(args); - Assert.IsTrue(args.Variables.ContainsKey("ExecOutput")); - string output = args.Variables["ExecOutput"] as string; - Assert.IsNotNull(output); - } - [TestMethod] - public void Executor_VariablePattern_Tests() - { - Assert.IsFalse(Regex.IsMatch(string.Empty, Executor.VariablePattern)); - } + [TestMethod] + public void Executor_OutputVariable() + { + var args = new NodeParameters(@"c:\test\testfile.mkv", Logger, false, string.Empty, MockFileService.Object); + args.Process = mockProcessHelper.Object; + + Executor node = new Executor(); + string file = @"D:\Videos\dummy.mkv"; + node.FileName = @"C:\utils\ffmpeg\ffmpeg.exe"; + node.Arguments = "-i \"" + file + "\""; + node.OutputVariable = "ExecOutput"; + var result = node.Execute(args); + Assert.IsTrue(args.Variables.ContainsKey("ExecOutput")); + string output = args.Variables["ExecOutput"] as string; + Assert.IsNotNull(output); } } diff --git a/BasicNodes/Tests/FileSizeCompareTests.cs b/BasicNodes/Tests/FileSizeCompareTests.cs index 8ec357c2..adbe9dac 100644 --- a/BasicNodes/Tests/FileSizeCompareTests.cs +++ b/BasicNodes/Tests/FileSizeCompareTests.cs @@ -1,116 +1,114 @@ #if(DEBUG) using System.IO; +using FileFlows.BasicNodes.File; +using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace BasicNodes.Tests +namespace BasicNodes.Tests; + +[TestClass] +public class FileSizeCompareTests : TestBase { - using FileFlows.BasicNodes.File; - using Microsoft.VisualStudio.TestTools.UnitTesting; - - [TestClass] - public class FileSizeCompareTests + private string CreateFile(int size) { - private string CreateFile(int size) - { - string tempFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - System.IO.File.WriteAllText(tempFile, new string('a', size)); - return tempFile; + string tempFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".tmp"); + System.IO.File.WriteAllText(tempFile, new string('a', size * 1000)); + return tempFile; + } + + [TestMethod] + public void FileSize_Shrunk() + { + string tempFile = CreateFile(2); + var args = new FileFlows.Plugin.NodeParameters(tempFile, Logger, false, string.Empty, new LocalFileService()); + args.InitFile(tempFile); - } - [TestMethod] - public void FileSize_Shrunk() - { - string tempFile = CreateFile(2); - var logger = new TestLogger(); - var args = new FileFlows.Plugin.NodeParameters(tempFile, logger, false, string.Empty, null); + string wfFile = CreateFile(1); + args.SetWorkingFile(wfFile); - string wfFile = CreateFile(1); - args.SetWorkingFile(wfFile); + FileSizeCompare node = new(); + int result = node.Execute(args); + Assert.AreEqual(1, result); + } - FileSizeCompare node = new(); - int result = node.Execute(args); - Assert.AreEqual(1, result); - } + [TestMethod] + public void FileSize_Grew() + { + string tempFile = CreateFile(2); + var args = new FileFlows.Plugin.NodeParameters(tempFile, Logger, false, string.Empty, new LocalFileService()); + args.InitFile(tempFile); - [TestMethod] - public void FileSize_Grew() - { - string tempFile = CreateFile(2); - var logger = new TestLogger(); - var args = new FileFlows.Plugin.NodeParameters(tempFile, logger, false, string.Empty, null); + string wfFile = CreateFile(20); + args.SetWorkingFile(wfFile); - string wfFile = CreateFile(20); - args.SetWorkingFile(wfFile); + FileSizeCompare node = new (); + int result = node.Execute(args); + Assert.AreEqual(3, result); + } - FileSizeCompare node = new (); - int result = node.Execute(args); - Assert.AreEqual(3, result); - } + [TestMethod] + public void FileSize_SameSize() + { + string tempFile = CreateFile(2); + var args = new FileFlows.Plugin.NodeParameters(tempFile, Logger, false, string.Empty, new LocalFileService()); + args.InitFile(tempFile); - [TestMethod] - public void FileSize_SameSize() - { - string tempFile = CreateFile(2); - var logger = new TestLogger(); - var args = new FileFlows.Plugin.NodeParameters(tempFile, logger, false, string.Empty, null); + string wfFile = CreateFile(2); + args.SetWorkingFile(wfFile); - string wfFile = CreateFile(2); - args.SetWorkingFile(wfFile); - - FileSizeCompare node = new(); - int result = node.Execute(args); - Assert.AreEqual(2, result); - } + FileSizeCompare node = new(); + int result = node.Execute(args); + Assert.AreEqual(2, result); + } - [TestMethod] - public void FileSize_Shrunk_OriginalDeleted() - { - string tempFile = CreateFile(2); - var logger = new TestLogger(); - var args = new FileFlows.Plugin.NodeParameters(tempFile, logger, false, string.Empty, null); - Assert.IsTrue(args.WorkingFileSize > 0); - System.IO.File.Delete(tempFile); + [TestMethod] + public void FileSize_Shrunk_OriginalDeleted() + { + string tempFile = CreateFile(2); + var args = new FileFlows.Plugin.NodeParameters(tempFile, Logger, false, string.Empty, new LocalFileService()); + args.InitFile(tempFile); + Assert.IsTrue(args.WorkingFileSize > 0); + System.IO.File.Delete(tempFile); - string wfFile = CreateFile(1); - args.SetWorkingFile(wfFile); + string wfFile = CreateFile(1); + args.SetWorkingFile(wfFile); - FileSizeCompare node = new(); - int result = node.Execute(args); - Assert.AreEqual(1, result); - } + FileSizeCompare node = new(); + int result = node.Execute(args); + Assert.AreEqual(1, result); + } - [TestMethod] - public void FileSize_Grew_OriginalDeleted() - { - string tempFile = CreateFile(2); - var logger = new TestLogger(); - var args = new FileFlows.Plugin.NodeParameters(tempFile, logger, false, string.Empty, null); - System.IO.File.Delete(tempFile); + [TestMethod] + public void FileSize_Grew_OriginalDeleted() + { + string tempFile = CreateFile(2); + var args = new FileFlows.Plugin.NodeParameters(tempFile, Logger, false, string.Empty, new LocalFileService()); + args.InitFile(tempFile); + System.IO.File.Delete(tempFile); - string wfFile = CreateFile(20); - args.SetWorkingFile(wfFile); + string wfFile = CreateFile(20); + args.SetWorkingFile(wfFile); - FileSizeCompare node = new(); - int result = node.Execute(args); - Assert.AreEqual(3, result); - } + FileSizeCompare node = new(); + int result = node.Execute(args); + Assert.AreEqual(3, result); + } - [TestMethod] - public void FileSize_SameSize_OriginalDeleted() - { - string tempFile = CreateFile(2); - var logger = new TestLogger(); - var args = new FileFlows.Plugin.NodeParameters(tempFile, logger, false, string.Empty, null); - System.IO.File.Delete(tempFile); + [TestMethod] + public void FileSize_SameSize_OriginalDeleted() + { + string tempFile = CreateFile(2); + var args = new FileFlows.Plugin.NodeParameters(tempFile, Logger, false, string.Empty, new LocalFileService()); + args.InitFile(tempFile); + System.IO.File.Delete(tempFile); - string wfFile = CreateFile(2); - args.SetWorkingFile(wfFile); + string wfFile = CreateFile(2); + args.SetWorkingFile(wfFile); - FileSizeCompare node = new(); - int result = node.Execute(args); - Assert.AreEqual(2, result); - } + FileSizeCompare node = new(); + int result = node.Execute(args); + Assert.AreEqual(2, result); } } diff --git a/BasicNodes/Tests/HasHardLinksTest.cs b/BasicNodes/Tests/HasHardLinksTest.cs index 289d8a51..f885c790 100644 --- a/BasicNodes/Tests/HasHardLinksTest.cs +++ b/BasicNodes/Tests/HasHardLinksTest.cs @@ -1,18 +1,87 @@ #if(DEBUG) -namespace BasicNodes.Tests; +using System.Diagnostics; +using System.IO; using FileFlows.BasicNodes.File; using Microsoft.VisualStudio.TestTools.UnitTesting; +namespace BasicNodes.Tests; + [TestClass] -public class HasHardLinksTest +public class HasHardLinksTest : TestBase { + private string? testFile; + + + protected override void TestCleanUp() + { + // Clean up the original file and any hard links created + if (string.IsNullOrEmpty(testFile) == false && System.IO.File.Exists(testFile)) + { + try + { + // Delete the original file + System.IO.File.Delete(testFile); + + // Delete the hard links + for (int i = 1; ; i++) + { + var linkName = $"{testFile}_link{i}"; + if (System.IO.File.Exists(linkName)) + System.IO.File.Delete(linkName); + else + break; // Stop if the link doesn't exist + } + } + catch (Exception ex) + { + // Log or handle the cleanup exception if necessary + Logger.ILog($"Failed to clean up files: {ex.Message}"); + } + } + } + public static void CreateHardLinkFile(string filename, int hardlinks = 1) + { + // Ensure the file exists + System.IO.File.WriteAllText(filename, "Test content"); + + // Create the specified number of hard links + for (int i = 1; i <= hardlinks; i++) + { + string linkName = $"{filename}_link{i}"; + + // Use the `ln` command to create a hard link + Process process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = "ln", + Arguments = $"{filename} {linkName}", + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + } + }; + process.Start(); + process.WaitForExit(); + + if (process.ExitCode != 0) + { + string error = process.StandardError.ReadToEnd(); + throw new InvalidOperationException($"Failed to create hard link: {error}"); + } + } + } + [TestMethod] public void HasHardLink() { - var logger = new TestLogger(); - var args = new FileFlows.Plugin.NodeParameters(@"/home/john/temp/test.file", logger, false, string.Empty, new LocalFileService());; + testFile = Path.GetTempFileName(); + CreateHardLinkFile(testFile, 2); + + var args = new FileFlows.Plugin.NodeParameters(testFile, Logger, false, string.Empty, MockFileService.Object); HasHardLinks element = new (); @@ -23,8 +92,8 @@ public class HasHardLinksTest [TestMethod] public void NoHardLinks() { - var logger = new TestLogger(); - var args = new FileFlows.Plugin.NodeParameters(@"/home/john/temp/other.test", logger, false, string.Empty, new LocalFileService());; + testFile = Path.GetTempFileName(); + var args = new FileFlows.Plugin.NodeParameters(testFile, Logger, false, string.Empty, MockFileService.Object); HasHardLinks element = new (); diff --git a/BasicNodes/Tests/MoveTests.cs b/BasicNodes/Tests/MoveTests.cs index 8a582f5a..9fa9c5e3 100644 --- a/BasicNodes/Tests/MoveTests.cs +++ b/BasicNodes/Tests/MoveTests.cs @@ -1,33 +1,31 @@  #if(DEBUG) -namespace BasicNodes.Tests; +using System.IO; using FileFlows.Plugin; using FileFlows.BasicNodes.File; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Text.RegularExpressions; +namespace BasicNodes.Tests; [TestClass] -public class MoveTests +public class MoveTests : TestBase { - [TestMethod] public void MoveTests_Variable_Filename() { - var logger = new TestLogger(); - var args = new FileFlows.Plugin.NodeParameters(@"/home/user/test/tv4a-starwarsrebels.s01e15-1080p.mkv", logger, false, string.Empty, null); + var args = new NodeParameters(@"/home/user/test/tv4a-starwarsrebels.s01e15-1080p.mkv", Logger, false, string.Empty, new LocalFileService()); string dest = MoveFile.GetDestinationPath(args, @"D:\test", "{file.Name}"); Assert.AreEqual(@"D:/test/tv4a-starwarsrebels.s01e15-1080p.mkv", dest); } + [TestMethod] public void MoveTests_Variable_FilenameExt() { - var logger = new TestLogger(); - var args = new FileFlows.Plugin.NodeParameters(@"/home/user/test/tv4a-starwarsrebels.s01e15-1080p.mkv", logger, false, string.Empty, null); + var args = new NodeParameters(@"/home/user/test/tv4a-starwarsrebels.s01e15-1080p.mkv", Logger, false, string.Empty, new LocalFileService()); // ensure we dont double up the extension after FF-154 string dest = MoveFile.GetDestinationPath(args, @"D:\test", "{file.Name}{file.Extension}"); @@ -38,20 +36,22 @@ public class MoveTests [TestMethod] public void MoveTests_Variable_FilenameNoExtension() { - var logger = new TestLogger(); - var args = new FileFlows.Plugin.NodeParameters(@"/home/user/test/tv4a-starwarsrebels.s01e15-1080p.mkv", logger, false, string.Empty, null); + var tempFileNoExtension = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempFile = tempFileNoExtension + ".tmp"; + System.IO.File.WriteAllText(tempFile, "this is a temp file"); + var args = new NodeParameters(tempFile, Logger, false, string.Empty, new LocalFileService()); + args.InitFile(tempFile); // ensure we dont double up the extension after FF-154 - string dest = MoveFile.GetDestinationPath(args, @"D:\test", "{file.NameNoExtension}"); + string dest = MoveFile.GetDestinationPath(args, Path.GetTempPath(), "{file.NameNoExtension}"); - Assert.AreEqual(@"D:/test/tv4a-starwarsrebels.mkv", dest); + Assert.AreEqual(tempFileNoExtension, dest); } [TestMethod] public void MoveTests_Variable_Ext() { - var logger = new TestLogger(); - var args = new NodeParameters(@"/home/user/test/tv4a-starwarsrebels.s01e15-1080p.mkv", logger, false, string.Empty, null); + var args = new NodeParameters(@"/home/user/test/tv4a-starwarsrebels.s01e15-1080p.mkv", Logger, false, string.Empty, new LocalFileService()); // ensure we dont double up the extension after FF-154 string dest = MoveFile.GetDestinationPath(args, @"D:\test", "{file.Name}{ext}"); @@ -62,18 +62,17 @@ public class MoveTests [TestMethod] public void MoveTests_Variable_Original_Filename() { - var logger = new TestLogger(); - var args = new FileFlows.Plugin.NodeParameters(@"/home/user/test/tv4a-starwarsrebels.s01e15-1080p.mkv", logger, false, string.Empty, null); + var args = new NodeParameters(@"/home/user/test/tv4a-starwarsrebels.s01e15-1080p.mkv", Logger, false, string.Empty, new LocalFileService()); string dest = MoveFile.GetDestinationPath(args, @"D:\test", "{file.Orig.FileName}"); Assert.AreEqual(@"D:/test/tv4a-starwarsrebels.s01e15-1080p.mkv", dest); } + [TestMethod] public void MoveTests_Variable_Original_FilenameExt() { - var logger = new TestLogger(); - var args = new FileFlows.Plugin.NodeParameters(@"/home/user/test/tv4a-starwarsrebels.s01e15-1080p.mkv", logger, false, string.Empty, null); + var args = new NodeParameters(@"/home/user/test/tv4a-starwarsrebels.s01e15-1080p.mkv", Logger, false, string.Empty, new LocalFileService()); // ensure we dont double up the extension after FF-154 string dest = MoveFile.GetDestinationPath(args, @"D:\test", "{file.Orig.FileName}{file.Orig.Extension}"); @@ -83,8 +82,8 @@ public class MoveTests [TestMethod] public void MoveTests_Variable_Original_NoExtension() { - var logger = new TestLogger(); - var args = new FileFlows.Plugin.NodeParameters(@"/home/user/test/tv4a-starwarsrebels.s01e15-1080p.mkv", logger, false, string.Empty, null); + + var args = new NodeParameters(@"/home/user/test/tv4a-starwarsrebels.s01e15-1080p.mkv", Logger, false, string.Empty, new LocalFileService()); // ensure we dont double up the extension after FF-154 string dest = MoveFile.GetDestinationPath(args, @"D:\test", "{file.Orig.FileNameNoExtension}"); @@ -95,8 +94,8 @@ public class MoveTests [TestMethod] public void MoveTests_MoveFolder() { - var logger = new TestLogger(); - var args = new NodeParameters(@"\\tower\downloads\downloaded\tv\The.Walking.Dead.Dead.City.S01E04\some-file.mkv", logger, false, string.Empty, null); + + var args = new NodeParameters(@"\\tower\downloads\downloaded\tv\The.Walking.Dead.Dead.City.S01E04\some-file.mkv", Logger, false, string.Empty, new LocalFileService()); args.RelativeFile = @"The.Walking.Dead.Dead.City.S01E04\some-file.mkv"; string dest = MoveFile.GetDestinationPath(args, @"\\tower\downloads\converted\tv", null, moveFolder:true); @@ -111,14 +110,12 @@ public class MoveTests [TestMethod] public void MoveTests_AdditionalFiles() { - var logger = new TestLogger(); - var args = new NodeParameters(@"/home/john/Videos/move-me/dir/basic.mkv", logger, false, string.Empty, null); + var args = new NodeParameters(@"/home/john/Videos/move-me/dir/basic.mkv", Logger, false, string.Empty, new LocalFileService()); var ele = new MoveFile(); ele.AdditionalFiles = new[] { "*.srt" }; ele.DestinationPath = "/home/john/Videos/converted"; var result = ele.Execute(args); - var log = logger.ToString(); Assert.AreEqual(1, result); } } diff --git a/BasicNodes/Tests/WriteTextTests.cs b/BasicNodes/Tests/WriteTextTests.cs index 48c97465..197868a6 100644 --- a/BasicNodes/Tests/WriteTextTests.cs +++ b/BasicNodes/Tests/WriteTextTests.cs @@ -12,7 +12,7 @@ public class WriteTextTests : TestBase [TestMethod] public void WorkingFile_Csv() { - var args = new FileFlows.Plugin.NodeParameters(@"/test/file.mkv", new TestLogger(), false, string.Empty, new LocalFileService()); + var args = new FileFlows.Plugin.NodeParameters(@"/test/file.mkv", new TestLogger(), false, string.Empty, MockFileService.Object); var output = WriteText.GetText(args, "", "file.csv"); Assert.AreEqual("\"/test/file.mkv\"", output); @@ -21,7 +21,7 @@ public class WriteTextTests : TestBase [TestMethod] public void WorkingFile_Text() { - var args = new FileFlows.Plugin.NodeParameters(@"/test/file.mkv", new TestLogger(), false, string.Empty, new LocalFileService()); + var args = new FileFlows.Plugin.NodeParameters(@"/test/file.mkv", new TestLogger(), false, string.Empty, MockFileService.Object); var output = WriteText.GetText(args, "", "file.txt"); Assert.AreEqual("/test/file.mkv", output); @@ -30,7 +30,7 @@ public class WriteTextTests : TestBase [TestMethod] public void CsvArgs() { - var args = new FileFlows.Plugin.NodeParameters(@"/test/file.mkv", new TestLogger(), false, string.Empty, new LocalFileService()); + var args = new FileFlows.Plugin.NodeParameters(@"/test/file.mkv", new TestLogger(), false, string.Empty, MockFileService.Object); args.Variables["file.Name"] = "file.mkv"; args.Variables["ext"] = "mkv"; @@ -41,7 +41,7 @@ public class WriteTextTests : TestBase [TestMethod] public void CsvArg() { - var args = new FileFlows.Plugin.NodeParameters(@"/test/file.mkv", new TestLogger(), false, string.Empty, new LocalFileService()); + var args = new FileFlows.Plugin.NodeParameters(@"/test/file.mkv", new TestLogger(), false, string.Empty, MockFileService.Object); args.Variables["file.Name"] = "file.mkv"; var output = WriteText.GetText(args, "{file.Name}", "file.csv"); diff --git a/BasicNodes/Tests/_TestBase.cs b/BasicNodes/Tests/_TestBase.cs index 2f749502..7b5edc10 100644 --- a/BasicNodes/Tests/_TestBase.cs +++ b/BasicNodes/Tests/_TestBase.cs @@ -4,6 +4,9 @@ using System.Runtime.InteropServices; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.IO; using FileFlows.BasicNodes; +using FileFlows.Plugin.Helpers; +using FileFlows.Plugin.Services; +using Moq; namespace BasicNodes.Tests; @@ -20,6 +23,8 @@ public abstract class TestBase internal TestLogger Logger = new(); + protected Mock MockFileService = new(); + /// /// Gets or sets the test context /// @@ -38,6 +43,8 @@ public abstract class TestBase [TestInitialize] public void TestInitialize() { + FileHelper.DontChangeOwner = true; + FileHelper.DontSetPermissions = true; Logger.Writer = (msg) => TestContext.WriteLine(msg); this.TestPath = this.TestPath?.EmptyAsNull() ?? (IsLinux ? "~/src/ff-files/test-files/videos" : @"d:\videos\testfiles"); @@ -48,11 +55,14 @@ public abstract class TestBase if (Directory.Exists(this.TempPath) == false) Directory.CreateDirectory(this.TempPath); + + TestStarting(); } [TestCleanup] public void CleanUp() { + TestCleanUp(); TestContext.WriteLine(Logger.ToString()); } @@ -60,6 +70,10 @@ public abstract class TestBase { } + protected virtual void TestCleanUp() + { + + } } diff --git a/BasicNodes/Tools/Executor.cs b/BasicNodes/Tools/Executor.cs index dc9c5b14..58ca69d5 100644 --- a/BasicNodes/Tools/Executor.cs +++ b/BasicNodes/Tools/Executor.cs @@ -1,102 +1,108 @@ -namespace FileFlows.BasicNodes.File +using System.ComponentModel.DataAnnotations; +using System.Text.RegularExpressions; +using FileFlows.Plugin; +using FileFlows.Plugin.Attributes; + +namespace FileFlows.BasicNodes.File; + +/// +/// Executes a command +/// +public class Executor : Node { - using System.ComponentModel; - using System.ComponentModel.DataAnnotations; - using System.Diagnostics; - using System.Text; - using System.Text.RegularExpressions; - using FileFlows.Plugin; - using FileFlows.Plugin.Attributes; + /// + public override int Inputs => 1; + /// + public override int Outputs => 2; + /// + public override FlowElementType Type => FlowElementType.Process; + /// + public override string Icon => "fas fa-terminal"; + /// + public override string HelpUrl => "https://fileflows.com/docs/plugins/basic-nodes/executor"; - public class Executor : Node + internal const string VariablePattern = @"(^[\s]*$)|(^([a-zA-Z_]+)[a-zA-Z_0-9]*$)"; + + [Required] + [File(1)] + public string FileName { get; set; } + + [Required] + [TextVariable(2)] + public string Arguments { get; set; } + + [Folder(3)] + public string WorkingDirectory { get; set; } + + [Required] + [NumberInt(4)] + public int SuccessCode { get; set; } + + [NumberInt(5)] + public int Timeout { get; set; } + + [Text(6)] + [System.ComponentModel.DataAnnotations.RegularExpression(VariablePattern)] + public string OutputVariable { get; set; } + + [Text(7)] + [System.ComponentModel.DataAnnotations.RegularExpression(VariablePattern)] + public string OutputErrorVariable { get; set; } + + private NodeParameters args; + + /// + public override Task Cancel() { - public override int Inputs => 1; - public override int Outputs => 2; - public override FlowElementType Type => FlowElementType.Process; - public override string Icon => "fas fa-terminal"; - public override string HelpUrl => "https://fileflows.com/docs/plugins/basic-nodes/executor"; - - internal const string VariablePattern = @"(^[\s]*$)|(^([a-zA-Z_]+)[a-zA-Z_0-9]*$)"; - - [Required] - [File(1)] - public string FileName { get; set; } - - [Required] - [TextVariable(2)] - public string Arguments { get; set; } - - [Folder(3)] - public string WorkingDirectory { get; set; } - - [Required] - [NumberInt(4)] - public int SuccessCode { get; set; } - - [NumberInt(5)] - public int Timeout { get; set; } - - [Text(6)] - [System.ComponentModel.DataAnnotations.RegularExpression(VariablePattern)] - public string OutputVariable { get; set; } - - [Text(7)] - [System.ComponentModel.DataAnnotations.RegularExpression(VariablePattern)] - public string OutputErrorVariable { get; set; } - - private NodeParameters args; - - public override Task Cancel() - { - args?.Process?.Cancel(); - return Task.CompletedTask; - } - - public override int Execute(NodeParameters args) - { - this.args = args; - string pArgs = args.ReplaceVariables(Arguments ?? string.Empty); - string filename = args.ReplaceVariables(FileName ?? string.Empty, stripMissing: true); - string workingDirectory = args.ReplaceVariables(WorkingDirectory ?? string.Empty, stripMissing: true); - var task = args.Process.ExecuteShellCommand(new ExecuteArgs - { - Command = filename, - Arguments = pArgs, - Timeout = Timeout, - WorkingDirectory = workingDirectory - }); - - task.Wait(); - - if(task.Result.Completed == false) - { - args.Logger?.ELog("Process failed to complete"); - return -1; - } - bool success = task.Result.ExitCode == this.SuccessCode; - if(string.IsNullOrWhiteSpace(OutputVariable) == false && Regex.IsMatch(OutputVariable, VariablePattern)) - { - args.UpdateVariables(new Dictionary - { - { OutputVariable, task.Result.StandardOutput } - }); - } - if (string.IsNullOrWhiteSpace(OutputErrorVariable) == false && Regex.IsMatch(OutputErrorVariable ?? string.Empty, VariablePattern)) - { - args.UpdateVariables(new Dictionary - { - { OutputErrorVariable, task.Result.StandardError } - }); - } - if (success) - return 1; - else - { - args.Logger?.ILog("Unsuccesful exit code returned: " + task.Result.ExitCode); - return 2; - } - } - - + args?.Process?.Cancel(); + return Task.CompletedTask; } + + /// + public override int Execute(NodeParameters args) + { + this.args = args; + string pArgs = args.ReplaceVariables(Arguments ?? string.Empty); + string filename = args.ReplaceVariables(FileName ?? string.Empty, stripMissing: true); + string workingDirectory = args.ReplaceVariables(WorkingDirectory ?? string.Empty, stripMissing: true); + var task = args.Process.ExecuteShellCommand(new ExecuteArgs + { + Command = filename, + Arguments = pArgs, + Timeout = Timeout, + WorkingDirectory = workingDirectory + }); + + task.Wait(); + + if(task.Result.Completed == false) + { + args.Logger?.ELog("Process failed to complete"); + return -1; + } + bool success = task.Result.ExitCode == this.SuccessCode; + if(string.IsNullOrWhiteSpace(OutputVariable) == false && Regex.IsMatch(OutputVariable, VariablePattern)) + { + args.UpdateVariables(new Dictionary + { + { OutputVariable, task.Result.StandardOutput } + }); + } + if (string.IsNullOrWhiteSpace(OutputErrorVariable) == false && Regex.IsMatch(OutputErrorVariable ?? string.Empty, VariablePattern)) + { + args.UpdateVariables(new Dictionary + { + { OutputErrorVariable, task.Result.StandardError } + }); + } + if (success) + return 1; + else + { + args.Logger?.ILog("Unsuccesful exit code returned: " + task.Result.ExitCode); + return 2; + } + } + + } \ No newline at end of file diff --git a/FileFlows.Plugin.dll b/FileFlows.Plugin.dll index 5ade98062fe045b838dbdcd92aabd307121abca8..b8f493e50bd9bd02a8b89fe61eac3d865b8bc6c6 100644 GIT binary patch delta 57232 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 dH6gkYss(y$&RJ)+ntO`C0 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 H0y6#&1@61Q delta 4621 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 J$T$)n{|5@owu%4%