Refactor valid path/filename. Centralize validaion. Universal templating is one step closer

This commit is contained in:
Robert McRackan 2021-10-18 13:36:55 -04:00
parent 7720110460
commit d08962cffa
18 changed files with 415 additions and 74 deletions

View file

@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.1.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.7" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.7" />

View file

@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Dinah.Core;
using FileManager;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace FileTemplateTests
{
[TestClass]
public class GetFilename
{
[TestMethod]
public void equiv_GetValidFilename()
{
var expected = @"C:\foo\bar\my_ book LONG_1234567890_1234567890_1234567890_123 [ID123456].txt";
var f1 = OLD_GetValidFilename(@"C:\foo\bar", "my: book LONG_1234567890_1234567890_1234567890_12345", "txt", "ID123456");
var f2 = NEW_GetValidFilename_FileTemplate(@"C:\foo\bar", "my: book LONG_1234567890_1234567890_1234567890_12345", "txt", "ID123456");
f1.Should().Be(expected);
f1.Should().Be(f2);
}
private static string OLD_GetValidFilename(string dirFullPath, string filename, string extension, string metadataSuffix)
{
ArgumentValidator.EnsureNotNullOrWhiteSpace(dirFullPath, nameof(dirFullPath));
filename ??= "";
// sanitize. omit invalid characters. exception: colon => underscore
filename = filename.Replace(":", "_");
filename = FileUtility.GetSafeFileName(filename);
if (filename.Length > 50)
filename = filename.Substring(0, 50);
if (!string.IsNullOrWhiteSpace(metadataSuffix))
filename += $" [{metadataSuffix}]";
// extension is null when this method is used for directory names
extension = FileUtility.GetStandardizedExtension(extension);
// ensure uniqueness
var fullfilename = Path.Combine(dirFullPath, filename + extension);
var i = 0;
while (File.Exists(fullfilename))
fullfilename = Path.Combine(dirFullPath, filename + $" ({++i})" + extension);
return fullfilename;
}
private static string NEW_GetValidFilename_FileTemplate(string dirFullPath, string filename, string extension, string metadataSuffix)
{
var template = $"<title> [<id>]";
var fullfilename = Path.Combine(dirFullPath, template + FileUtility.GetStandardizedExtension(extension));
var fileTemplate = new FileTemplate(fullfilename) { IllegalCharacterReplacements = "_" };
fileTemplate.AddParameterReplacement("title", filename);
fileTemplate.AddParameterReplacement("id", metadataSuffix);
return fileTemplate.GetFilename();
}
[TestMethod]
public void equiv_GetMultipartFileName()
{
var expected = @"C:\foo\bar\my file - 002 - title.txt";
var f1 = OLD_GetMultipartFileName(@"C:\foo\bar\my file.txt", 2, 100, "title");
var f2 = NEW_GetMultipartFileName_FileTemplate(@"C:\foo\bar\my file.txt", 2, 100, "title");
f1.Should().Be(expected);
f1.Should().Be(f2);
}
private static string OLD_GetMultipartFileName(string originalPath, int partsPosition, int partsTotal, string suffix)
{
// 1-9 => 1-9
// 10-99 => 01-99
// 100-999 => 001-999
var chapterCountLeadingZeros = partsPosition.ToString().PadLeft(partsTotal.ToString().Length, '0');
string extension = Path.GetExtension(originalPath);
var filenameBase = $"{Path.GetFileNameWithoutExtension(originalPath)} - {chapterCountLeadingZeros}";
if (!string.IsNullOrWhiteSpace(suffix))
filenameBase += $" - {suffix}";
// Replace illegal path characters with spaces
var fileName = FileUtility.GetSafeFileName(filenameBase, " ");
var path = Path.Combine(Path.GetDirectoryName(originalPath), fileName + extension);
return path;
}
private static string NEW_GetMultipartFileName_FileTemplate(string originalPath, int partsPosition, int partsTotal, string suffix)
{
// 1-9 => 1-9
// 10-99 => 01-99
// 100-999 => 001-999
var chapterCountLeadingZeros = partsPosition.ToString().PadLeft(partsTotal.ToString().Length, '0');
var t = Path.ChangeExtension(originalPath, null) + " - <chapter> - <title>" + Path.GetExtension(originalPath);
var fileTemplate = new FileTemplate(t) { IllegalCharacterReplacements = " " };
fileTemplate.AddParameterReplacement("chapter", chapterCountLeadingZeros);
fileTemplate.AddParameterReplacement("title", suffix);
return fileTemplate.GetFilename();
}
}
}

View file

@ -1,13 +1,130 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Dinah.Core;
using FileManager;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace FileUtilityTests
{
[TestClass]
public class GetSafePath
{
[TestMethod]
public void null_path_throws() => Assert.ThrowsException<ArgumentNullException>(() => FileUtility.GetSafePath(null));
// needs separate method. middle null param not running correctly in TestExplorer when used in DataRow()
[TestMethod]
[DataRow("http://test.com/a/b/c", @"http\\test.com\a\b\c")]
public void null_replacement(string inStr, string outStr) => Tests(inStr, null, outStr);
[TestMethod]
// empty replacement
[DataRow("abc*abc.txt", "", "abcabc.txt")]
// non-empty replacement
[DataRow("abc*abc.txt", "ZZZ", "abcZZZabc.txt")]
// standardize slashes
[DataRow(@"a/b\c/d", "Z", @"a\b\c\d")]
// remove illegal chars
[DataRow("a*?:z.txt", "Z", "aZZZz.txt")]
// retain drive letter path colon
[DataRow(@"C:\az.txt", "Z", @"C:\az.txt")]
// replace all other colongs
[DataRow(@"a\b:c\d.txt", "ZZZ", @"a\bZZZc\d.txt")]
public void Tests(string inStr, string replacement, string outStr) => Assert.AreEqual(outStr, FileUtility.GetSafePath(inStr, replacement));
}
[TestClass]
public class GetSafeFileName
{
// needs separate method. middle null param not running correctly in TestExplorer when used in DataRow()
[TestMethod]
[DataRow("http://test.com/a/b/c", "httptest.comabc")]
public void url_null_replacement(string inStr, string outStr) => ReplacementTests(inStr, null, outStr);
[TestMethod]
// empty replacement
[DataRow("http://test.com/a/b/c", "", "httptest.comabc")]
// single char replace
[DataRow("http://test.com/a/b/c", "_", "http___test.com_a_b_c")]
// multi char replace
[DataRow("http://test.com/a/b/c", "!!!", "http!!!!!!!!!test.com!!!a!!!b!!!c")]
public void ReplacementTests(string inStr, string replacement, string outStr) => FileUtility.GetSafeFileName(inStr, replacement).Should().Be(outStr);
}
[TestClass]
public class GetValidFilename
{
[TestMethod]
public void TestMethod1()
{
}
[DataRow(null, "name", "ext", "suffix")]
[DataRow(@"C:\", null, "ext", "suffix")]
[ExpectedException(typeof(ArgumentNullException))]
public void arg_null_exception(string dirFullPath, string filename, string extension, string metadataSuffix)
=> FileUtility.GetValidFilename(dirFullPath, filename, extension, metadataSuffix);
[TestMethod]
[DataRow("", "name", "ext", "suffix")]
[DataRow(" ", "name", "ext", "suffix")]
[DataRow(@"C:\", "", "ext", "suffix")]
[DataRow(@"C:\", " ", "ext", "suffix")]
[ExpectedException(typeof(ArgumentException))]
public void arg_exception(string dirFullPath, string filename, string extension, string metadataSuffix)
=> FileUtility.GetValidFilename(dirFullPath, filename, extension, metadataSuffix);
[TestMethod]
public void null_extension() => Tests(@"C:\foo\bar", "my file", null, "meta", @"C:\foo\bar\my file [meta]");
[TestMethod]
public void null_metadataSuffix() => Tests(@"C:\foo\bar", "my file", "txt", null, @"C:\foo\bar\my file [].txt");
[TestMethod]
[DataRow(@"C:\foo\bar", "my file", "txt", "my id", @"C:\foo\bar\my file [my id].txt")]
[DataRow(@"C:\foo\bar", "my file", "txt", "", @"C:\foo\bar\my file [].txt")]
public void Tests(string dirFullPath, string filename, string extension, string metadataSuffix, string expected)
=> FileUtility.GetValidFilename(dirFullPath, filename, extension, metadataSuffix).Should().Be(expected);
}
[TestClass]
public class GetMultipartFileName
{
[TestMethod]
public void null_path() => Assert.ThrowsException<ArgumentNullException>(() => FileUtility.GetMultipartFileName(null, 1, 1, ""));
[TestMethod]
public void null_suffix() => Tests(@"C:\foo\bar\my file.txt", 2, 100, null, @"C:\foo\bar\my file - 002 - .txt");
[TestMethod]
public void negative_partsPosition() => Assert.ThrowsException<ArgumentException>(()
=> FileUtility.GetMultipartFileName("foo", -1, 2, "")
);
[TestMethod]
public void zero_partsPosition() => Assert.ThrowsException<ArgumentException>(()
=> FileUtility.GetMultipartFileName("foo", 0, 2, "")
);
[TestMethod]
public void negative_partsTotal() => Assert.ThrowsException<ArgumentException>(()
=> FileUtility.GetMultipartFileName("foo", 2, -1, "")
);
[TestMethod]
public void zero_partsTotal() => Assert.ThrowsException<ArgumentException>(()
=> FileUtility.GetMultipartFileName("foo", 2, 0, "")
);
[TestMethod]
public void partsPosition_greater_than_partsTotal() => Assert.ThrowsException<ArgumentException>(()
=> FileUtility.GetMultipartFileName("foo", 2, 1, "")
);
[TestMethod]
// only part
[DataRow(@"C:\foo\bar\my file.txt", 1, 1, "title", @"C:\foo\bar\my file - 1 - title.txt")]
// 3 digits
[DataRow(@"C:\foo\bar\my file.txt", 2, 100, "title", @"C:\foo\bar\my file - 002 - title.txt")]
// no suffix
[DataRow(@"C:\foo\bar\my file.txt", 2, 100, "", @"C:\foo\bar\my file - 002 - .txt")]
public void Tests(string originalPath, int partsPosition, int partsTotal, string suffix, string expected)
=> FileUtility.GetMultipartFileName(originalPath, partsPosition, partsTotal, suffix).Should().Be(expected);
}
}