Compare commits

...

2 Commits

9 changed files with 380 additions and 76 deletions

View File

@@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<Version>6.3.3.1</Version>
<Version>6.3.4.1</Version>
</PropertyGroup>
<ItemGroup>

View File

@@ -185,6 +185,9 @@ namespace DataLayer
{
get
{
if (_seriesLink is null)
return "";
// first: alphabetical by name
var withNames = _seriesLink
.Where(s => !string.IsNullOrWhiteSpace(s.Series.Name))

View File

@@ -45,8 +45,20 @@ namespace FileManager
.Replace(">", "");
private string formatValue(object value)
=> ParameterMaxSize.HasValue && ParameterMaxSize.Value > 0
? value?.ToString().Truncate(ParameterMaxSize.Value)
: value?.ToString();
{
if (value is null)
return "";
// Other illegal characters will be taken care of later. Must take care of slashes now so params can't introduce new folders.
// Esp important for file templates.
var val = value
.ToString()
.Replace($"{System.IO.Path.DirectorySeparatorChar}", IllegalCharacterReplacements)
.Replace($"{System.IO.Path.AltDirectorySeparatorChar}", IllegalCharacterReplacements);
return
ParameterMaxSize.HasValue && ParameterMaxSize.Value > 0
? val.Truncate(ParameterMaxSize.Value)
: val;
}
}
}

View File

@@ -6,6 +6,17 @@ namespace LibationFileManager
{
public abstract class Templates
{
protected static string[] Valid => Array.Empty<string>();
public const string ERROR_NULL_IS_INVALID = "Null template is invalid.";
public const string ERROR_FULL_PATH_IS_INVALID = @"No full paths allowed. Eg: should not start with C:\";
public const string ERROR_INVALID_FILE_NAME_CHAR = @"Only file name friendly characters allowed. Eg: no colons or slashes";
public const string WARNING_EMPTY = "Template is empty.";
public const string WARNING_WHITE_SPACE = "Template is white space.";
public const string WARNING_NO_TAGS = "Should use tags. Eg: <title>";
public const string WARNING_HAS_CHAPTER_TAGS = "Chapter tags should only be used in the template used for naming files which are split by chapter. Eg: <ch title>";
public const string WARNING_NO_CHAPTER_NUMBER_TAG = "Should include chapter number tag in template used for naming files which are split by chapter. Ie: <ch#> or <ch# 0>";
public static Templates Folder { get; } = new FolderTemplate();
public static Templates File { get; } = new FileTemplate();
public static Templates ChapterFile { get; } = new ChapterFileTemplate();
@@ -13,10 +24,25 @@ namespace LibationFileManager
public abstract string Name { get; }
public abstract string Description { get; }
public abstract string DefaultTemplate { get; }
protected abstract bool IsChapterized { get; }
public abstract bool IsValid(string template);
public abstract bool IsRecommended(string template);
public abstract int TagCount(string template);
public abstract IEnumerable<string> GetErrors(string template);
public bool IsValid(string template) => !GetErrors(template).Any();
public abstract IEnumerable<string> GetWarnings(string template);
public bool HasWarnings(string template) => GetWarnings(template).Any();
public IEnumerable<TemplateTags> GetTemplateTags()
=> TemplateTags.GetAll()
// yeah, this line is a little funky but it works when you think through it. also: trust the unit tests
.Where(t => IsChapterized || !t.IsChapterOnly);
public int TagCount(string template)
=> GetTemplateTags()
// for <id><id> == 1, use:
// .Count(t => template.Contains($"<{t.TagName}>"))
// .Sum() impl: <id><id> == 2
.Sum(t => template.Split($"<{t.TagName}>").Length - 1);
public static bool ContainsChapterOnlyTags(string template)
=> TemplateTags.GetAll()
@@ -25,43 +51,64 @@ namespace LibationFileManager
public static bool ContainsTag(string template, string tag) => template.Contains($"<{tag}>");
protected static bool fileIsValid(string template)
protected static string[] getFileErrors(string template)
{
// File name only; not path. all other path chars are valid enough to pass this check and will be handled on final save.
// null is invalid. whitespace is valid but not recommended
=> template is not null
&& !template.Contains(':')
&& !template.Contains(System.IO.Path.DirectorySeparatorChar)
&& !template.Contains(System.IO.Path.AltDirectorySeparatorChar);
if (template is null)
return new[] { ERROR_NULL_IS_INVALID };
protected bool isRecommended(string template, bool isChapter)
=> IsValid(template)
&& !string.IsNullOrWhiteSpace(template)
&& TagCount(template) > 0
&& ContainsChapterOnlyTags(template) == isChapter;
if (template.Contains(':')
|| template.Contains(System.IO.Path.DirectorySeparatorChar)
|| template.Contains(System.IO.Path.AltDirectorySeparatorChar)
)
return new[] { ERROR_INVALID_FILE_NAME_CHAR };
protected static int tagCount(string template, Func<TemplateTags, bool> func)
=> TemplateTags.GetAll()
.Where(func)
// for <id><id> == 1, use:
// .Count(t => template.Contains($"<{t.TagName}>"))
// .Sum() impl: <id><id> == 2
.Sum(t => template.Split($"<{t.TagName}>").Length - 1);
return Valid;
}
protected IEnumerable<string> getWarnings(string template)
{
var warnings = GetErrors(template).ToList();
if (template is null)
return warnings;
if (string.IsNullOrEmpty(template))
warnings.Add(WARNING_EMPTY);
else if (string.IsNullOrWhiteSpace(template))
warnings.Add(WARNING_WHITE_SPACE);
if (TagCount(template) == 0)
warnings.Add(WARNING_NO_TAGS);
if (!IsChapterized && ContainsChapterOnlyTags(template))
warnings.Add(WARNING_HAS_CHAPTER_TAGS);
return warnings;
}
private class FolderTemplate : Templates
{
public override string Name => "Folder Template";
public override string Description => Configuration.GetDescription(nameof(Configuration.FolderTemplate));
public override string DefaultTemplate { get; } = "<title short> [<id>]";
protected override bool IsChapterized { get; } = false;
public override bool IsValid(string template)
// must be relative. no colons. all other path chars are valid enough to pass this check and will be handled on final save.
public override IEnumerable<string> GetErrors(string template)
{
// null is invalid. whitespace is valid but not recommended
=> template is not null
&& !template.Contains(':');
if (template is null)
return new[] { ERROR_NULL_IS_INVALID };
public override bool IsRecommended(string template) => isRecommended(template, false);
// must be relative. no colons. all other path chars are valid enough to pass this check and will be handled on final save.
if (template.Contains(':'))
return new[] { ERROR_FULL_PATH_IS_INVALID };
public override int TagCount(string template) => tagCount(template, t => !t.IsChapterOnly);
return Valid;
}
public override IEnumerable<string> GetWarnings(string template) => getWarnings(template);
}
private class FileTemplate : Templates
@@ -69,12 +116,11 @@ namespace LibationFileManager
public override string Name => "File Template";
public override string Description => Configuration.GetDescription(nameof(Configuration.FileTemplate));
public override string DefaultTemplate { get; } = "<title> [<id>]";
protected override bool IsChapterized { get; } = false;
public override bool IsValid(string template) => fileIsValid(template);
public override IEnumerable<string> GetErrors(string template) => getFileErrors(template);
public override bool IsRecommended(string template) => isRecommended(template, false);
public override int TagCount(string template) => tagCount(template, t => !t.IsChapterOnly);
public override IEnumerable<string> GetWarnings(string template) => getWarnings(template);
}
private class ChapterFileTemplate : Templates
@@ -82,15 +128,22 @@ namespace LibationFileManager
public override string Name => "Chapter File Template";
public override string Description => Configuration.GetDescription(nameof(Configuration.ChapterFileTemplate));
public override string DefaultTemplate { get; } = "<title> [<id>] - <ch# 0> - <ch title>";
protected override bool IsChapterized { get; } = true;
public override bool IsValid(string template) => fileIsValid(template);
public override IEnumerable<string> GetErrors(string template) => getFileErrors(template);
public override IEnumerable<string> GetWarnings(string template)
{
var warnings = getWarnings(template).ToList();
if (template is null)
return warnings;
public override bool IsRecommended(string template)
=> isRecommended(template, true)
// recommended to incl. <ch#> or <ch# 0>
&& (ContainsTag(template, TemplateTags.ChNumber.TagName) || ContainsTag(template, TemplateTags.ChNumber0.TagName));
if (!ContainsTag(template, TemplateTags.ChNumber.TagName) && !ContainsTag(template, TemplateTags.ChNumber0.TagName))
warnings.Add(WARNING_NO_CHAPTER_NUMBER_TAG);
public override int TagCount(string template) => tagCount(template, t => true);
return warnings;
}
}
}
}

View File

@@ -34,6 +34,9 @@
this.templateLbl = new System.Windows.Forms.Label();
this.resetToDefaultBtn = new System.Windows.Forms.Button();
this.outputTb = new System.Windows.Forms.TextBox();
this.listView1 = new System.Windows.Forms.ListView();
this.columnHeader1 = new System.Windows.Forms.ColumnHeader();
this.columnHeader2 = new System.Windows.Forms.ColumnHeader();
this.SuspendLayout();
//
// saveBtn
@@ -93,12 +96,41 @@
//
// outputTb
//
this.outputTb.Location = new System.Drawing.Point(12, 153);
this.outputTb.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.outputTb.Location = new System.Drawing.Point(346, 56);
this.outputTb.Multiline = true;
this.outputTb.Name = "outputTb";
this.outputTb.ReadOnly = true;
this.outputTb.Size = new System.Drawing.Size(759, 205);
this.outputTb.TabIndex = 100;
this.outputTb.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.outputTb.Size = new System.Drawing.Size(574, 434);
this.outputTb.TabIndex = 4;
//
// listView1
//
this.listView1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)));
this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.columnHeader1,
this.columnHeader2});
this.listView1.HideSelection = false;
this.listView1.Location = new System.Drawing.Point(12, 56);
this.listView1.Name = "listView1";
this.listView1.Size = new System.Drawing.Size(328, 434);
this.listView1.TabIndex = 100;
this.listView1.UseCompatibleStateImageBehavior = false;
this.listView1.View = System.Windows.Forms.View.Details;
//
// columnHeader1
//
this.columnHeader1.Text = "Tag";
this.columnHeader1.Width = 90;
//
// columnHeader2
//
this.columnHeader2.Text = "Description";
this.columnHeader2.Width = 230;
//
// EditTemplateDialog
//
@@ -107,6 +139,7 @@
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.cancelBtn;
this.ClientSize = new System.Drawing.Size(933, 539);
this.Controls.Add(this.listView1);
this.Controls.Add(this.outputTb);
this.Controls.Add(this.resetToDefaultBtn);
this.Controls.Add(this.templateLbl);
@@ -131,5 +164,8 @@
private System.Windows.Forms.Label templateLbl;
private System.Windows.Forms.Button resetToDefaultBtn;
private System.Windows.Forms.TextBox outputTb;
private System.Windows.Forms.ListView listView1;
private System.Windows.Forms.ColumnHeader columnHeader1;
private System.Windows.Forms.ColumnHeader columnHeader2;
}
}

View File

@@ -39,16 +39,32 @@ namespace LibationWinForms.Dialogs
this.templateLbl.Text = template.Description;
this.templateTb.Text = inputTemplateText;
// populate list view
foreach (var tag in template.GetTemplateTags())
listView1.Items.Add(new ListViewItem(new[] { $"<{tag.TagName}>", tag.Description }));
}
private void resetToDefaultBtn_Click(object sender, EventArgs e) => templateTb.Text = template.DefaultTemplate;
private void templateTb_TextChanged(object sender, EventArgs e)
{
var t = templateTb.Text;
var warnings
= !template.HasWarnings(t)
? ""
: "Warnings:\r\n" +
template
.GetWarnings(t)
.Select(err => $"- {err}")
.Aggregate((a, b) => $"{a}\r\n{b}");
var books = config.Books;
var folderTemplate = template == Templates.Folder ? templateTb.Text : config.FolderTemplate;
var folderTemplate = template == Templates.Folder ? t : config.FolderTemplate;
folderTemplate = folderTemplate.Trim().Trim(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }).Trim();
var fileTemplate = template == Templates.Folder ? config.FileTemplate : templateTb.Text;
var fileTemplate = template == Templates.Folder ? config.FileTemplate : t;
fileTemplate = fileTemplate.Trim();
var ext = config.DecryptToLossy ? "mp3" : "m4b";
@@ -56,24 +72,67 @@ namespace LibationWinForms.Dialogs
// this logic should be external
path = path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
var sing = $"{Path.DirectorySeparatorChar}";
var dbl = $"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}";
while (path.Contains(dbl))
path = path.Replace(dbl, $"{Path.DirectorySeparatorChar}");
path = path.Replace(dbl, sing);
// once path is finalized
const char ZERO_WIDTH_SPACE = '\u200B';
path = path.Replace(sing, $"{ZERO_WIDTH_SPACE}{sing}");
// result: can wrap long paths. eg:
// |-- LINE WRAP BOUNDARIES --|
// \books\author with a very <= normal line break on space between words
// long name\narrator narrator
// \title <= line break on the zero-with space we added before slashes
var book = new DataLayer.Book(
new DataLayer.AudibleProductId("123456789"),
"A Study in Scarlet: A Sherlock Holmes Novel",
"Fake description",
1234,
DataLayer.ContentType.Product,
new List<DataLayer.Contributor>
{
new("Arthur Conan Doyle"),
new("Stephen Fry - introductions")
},
new List<DataLayer.Contributor> { new("Stephen Fry") },
new DataLayer.Category(new DataLayer.AudibleCategoryId("cat12345"), "Mystery"),
"us"
);
var libraryBook = new DataLayer.LibraryBook(book, DateTime.Now, "my account");
outputTb.Text = @$"
Example:
{books}
{folderTemplate}
{fileTemplate}
{ext}
{path}
";
{book.AudibleProductId}
{book.Title}
{book.AuthorNames}
{book.NarratorNames}
series: {"Sherlock Holmes"}
{warnings}
".Trim();
}
private void saveBtn_Click(object sender, EventArgs e)
{
if (!template.IsValid(templateTb.Text))
{
MessageBox.Show("This template text is not valid.", "Invalid", MessageBoxButtons.OK, MessageBoxIcon.Error);
var errors = template
.GetErrors(templateTb.Text)
.Select(err => $"- {err}")
.Aggregate((a, b) => $"{a}\r\n{b}");
MessageBox.Show($"This template text is not valid. Errors:\r\n{errors}", "Invalid", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}

View File

@@ -108,8 +108,10 @@ namespace LibationWinForms.Dialogs
private void chapterFileTemplateBtn_Click(object sender, EventArgs e) => editTemplate(Templates.ChapterFile, chapterFileTemplateTb);
private static void editTemplate(Templates template, TextBox textBox)
{
#if !DEBUG
TEMP_TEMP_TEMP();
return;
#endif
var form = new EditTemplateDialog(template, textBox.Text);
if (form.ShowDialog() == DialogResult.OK)
@@ -124,31 +126,34 @@ namespace LibationWinForms.Dialogs
var newBooks = booksSelectControl.SelectedDirectory;
#region validation
static void validationError(string text, string caption)
=> MessageBox.Show(text, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
if (string.IsNullOrWhiteSpace(newBooks))
{
MessageBox.Show("Cannot set Books Location to blank", "Location is blank", MessageBoxButtons.OK, MessageBoxIcon.Error);
validationError("Cannot set Books Location to blank", "Location is blank");
return;
}
if (!Directory.Exists(newBooks) && booksSelectControl.SelectedDirectoryIsCustom)
{
MessageBox.Show($"Not saving change to Books location. This folder does not exist:\r\n{newBooks}", "Folder does not exist", MessageBoxButtons.OK, MessageBoxIcon.Error);
validationError($"Not saving change to Books location. This folder does not exist:\r\n{newBooks}", "Folder does not exist");
return;
}
// these 3 should do nothing. Configuration will only init these with a valid value. EditTemplateDialog ensures valid before returning
if (!Templates.Folder.IsValid(folderTemplateTb.Text))
{
MessageBox.Show($"Not saving change to folder naming template. Invalid format.", "Invalid folder template", MessageBoxButtons.OK, MessageBoxIcon.Error);
validationError($"Not saving change to folder naming template. Invalid format.", "Invalid folder template");
return;
}
if (!Templates.File.IsValid(fileTemplateTb.Text))
{
MessageBox.Show($"Not saving change to file naming template. Invalid format.", "Invalid file template", MessageBoxButtons.OK, MessageBoxIcon.Error);
validationError($"Not saving change to file naming template. Invalid format.", "Invalid file template");
return;
}
if (!Templates.ChapterFile.IsValid(chapterFileTemplateTb.Text))
{
MessageBox.Show($"Not saving change to chapter file naming template. Invalid format.", "Invalid chapter file template", MessageBoxButtons.OK, MessageBoxIcon.Error);
validationError($"Not saving change to chapter file naming template. Invalid format.", "Invalid chapter file template");
return;
}
#endregion

View File

@@ -104,5 +104,13 @@ namespace FileTemplateTests
return fileTemplate.GetFilePath();
}
[TestMethod]
public void remove_slashes()
{
var fileTemplate = new FileTemplate(@"\foo\<title>.txt");
fileTemplate.AddParameterReplacement("title", @"s\l/a\s/h\e/s");
fileTemplate.GetFilePath().Should().Be(@"\foo\slashes.txt");
}
}
}

View File

@@ -33,6 +33,38 @@ namespace TemplatesTests
namespace Templates_Folder_Tests
{
[TestClass]
public class GetErrors
{
[TestMethod]
public void null_is_invalid() => Tests(null, new[] { Templates.ERROR_NULL_IS_INVALID });
[TestMethod]
public void empty_is_valid() => valid_tests("");
[TestMethod]
public void whitespace_is_valid() => valid_tests(" ");
[TestMethod]
[DataRow(@"foo")]
[DataRow(@"\foo")]
[DataRow(@"foo\")]
[DataRow(@"\foo\")]
[DataRow(@"foo\bar")]
[DataRow(@"<id>")]
[DataRow(@"<id>\<title>")]
public void valid_tests(string template) => Tests(template, Array.Empty<string>());
[TestMethod]
[DataRow(@"C:\", Templates.ERROR_FULL_PATH_IS_INVALID)]
public void Tests(string template, params string[] expected)
{
var result = Templates.Folder.GetErrors(template);
result.Count().Should().Be(expected.Length);
result.Should().BeEquivalentTo(expected);
}
}
[TestClass]
public class IsValid
{
@@ -58,36 +90,64 @@ namespace Templates_Folder_Tests
}
[TestClass]
public class IsRecommended
public class GetWarnings
{
[TestMethod]
public void null_is_not_recommended() => Tests(null, false);
public void null_is_invalid() => Tests(null, new[] { Templates.ERROR_NULL_IS_INVALID });
[TestMethod]
public void empty_is_not_recommended() => Tests("", false);
public void empty_has_warnings() => Tests("", Templates.WARNING_EMPTY, Templates.WARNING_NO_TAGS);
[TestMethod]
public void whitespace_is_not_recommended() => Tests(" ", false);
public void whitespace_has_warnings() => Tests(" ", Templates.WARNING_WHITE_SPACE, Templates.WARNING_NO_TAGS);
[TestMethod]
[DataRow(@"no tags", false)]
[DataRow(@"<id>\foo\bar", true)]
[DataRow("<ch#> <id>", false)]
[DataRow("<ch#> chapter tag", false)]
public void Tests(string template, bool expected) => Templates.Folder.IsRecommended(template).Should().Be(expected);
[DataRow(@"<id>\foo\bar")]
public void valid_tests(string template) => Tests(template, Array.Empty<string>());
[TestMethod]
[DataRow(@"no tags", Templates.WARNING_NO_TAGS)]
[DataRow("<ch#> <id>", Templates.WARNING_HAS_CHAPTER_TAGS)]
[DataRow("<ch#> chapter tag", Templates.WARNING_NO_TAGS, Templates.WARNING_HAS_CHAPTER_TAGS)]
public void Tests(string template, params string[] expected)
{
var result = Templates.Folder.GetWarnings(template);
result.Count().Should().Be(expected.Length);
result.Should().BeEquivalentTo(expected);
}
}
[TestClass]
public class HasWarnings
{
[TestMethod]
public void null_has_warnings() => Tests(null, true);
[TestMethod]
public void empty_has_warnings() => Tests("", true);
[TestMethod]
public void whitespace_has_warnings() => Tests(" ", true);
[TestMethod]
[DataRow(@"no tags", true)]
[DataRow(@"<id>\foo\bar", false)]
[DataRow("<ch#> <id>", true)]
[DataRow("<ch#> chapter tag", true)]
public void Tests(string template, bool expected) => Templates.Folder.HasWarnings(template).Should().Be(expected);
}
[TestClass]
public class TagCount
{
[TestMethod]
public void null_is_not_recommended() => Assert.ThrowsException<NullReferenceException>(() => Tests(null, -1));
public void null_throws() => Assert.ThrowsException<NullReferenceException>(() => Templates.Folder.TagCount(null));
[TestMethod]
public void empty_is_not_recommended() => Tests("", 0);
public void empty() => Tests("", 0);
[TestMethod]
public void whitespace_is_not_recommended() => Tests(" ", 0);
public void whitespace() => Tests(" ", 0);
[TestMethod]
[DataRow("no tags", 0)]
@@ -105,6 +165,37 @@ namespace Templates_Folder_Tests
namespace Templates_File_Tests
{
[TestClass]
public class GetErrors
{
[TestMethod]
public void null_is_invalid() => Tests(null, new[] { Templates.ERROR_NULL_IS_INVALID });
[TestMethod]
public void empty_is_valid() => valid_tests("");
[TestMethod]
public void whitespace_is_valid() => valid_tests(" ");
[TestMethod]
[DataRow(@"foo")]
[DataRow(@"<id>")]
public void valid_tests(string template) => Tests(template, Array.Empty<string>());
[TestMethod]
[DataRow(@"C:\", Templates.ERROR_INVALID_FILE_NAME_CHAR)]
[DataRow(@"\foo", Templates.ERROR_INVALID_FILE_NAME_CHAR)]
[DataRow(@"/foo", Templates.ERROR_INVALID_FILE_NAME_CHAR)]
[DataRow(@"C:\", Templates.ERROR_INVALID_FILE_NAME_CHAR)]
public void Tests(string template, params string[] expected)
{
var result = Templates.File.GetErrors(template);
result.Count().Should().Be(expected.Length);
result.Should().BeEquivalentTo(expected);
}
}
[TestClass]
public class IsValid
{
@@ -126,9 +217,13 @@ namespace Templates_File_Tests
public void Tests(string template, bool expected) => Templates.File.IsValid(template).Should().Be(expected);
}
// same as Templates.Folder.IsRecommended
// same as Templates.Folder.GetWarnings
//[TestClass]
//public class IsRecommended { }
//public class GetWarnings { }
// same as Templates.Folder.HasWarnings
//[TestClass]
//public class HasWarnings { }
// same as Templates.Folder.TagCount
//[TestClass]
@@ -137,29 +232,62 @@ namespace Templates_File_Tests
namespace Templates_ChapterFile_Tests
{
// same as Templates.File.GetErrors
//[TestClass]
//public class GetErrors { }
// same as Templates.File.IsValid
//[TestClass]
//public class IsValid { }
[TestClass]
public class IsRecommended
public class GetWarnings
{
[TestMethod]
public void null_is_not_recommended() => Tests(null, false);
public void null_is_invalid() => Tests(null, new[] { Templates.ERROR_NULL_IS_INVALID });
[TestMethod]
public void empty_is_not_recommended() => Tests("", false);
public void empty_has_warnings() => Tests("", Templates.WARNING_EMPTY, Templates.WARNING_NO_TAGS, Templates.WARNING_NO_CHAPTER_NUMBER_TAG);
[TestMethod]
public void whitespace_is_not_recommended() => Tests(" ", false);
public void whitespace_has_warnings() => Tests(" ", Templates.WARNING_WHITE_SPACE, Templates.WARNING_NO_TAGS, Templates.WARNING_NO_CHAPTER_NUMBER_TAG);
[TestMethod]
[DataRow(@"no tags", false)]
[DataRow(@"<id>\foo\bar", false)]
[DataRow("<ch#> <id>", true)]
[DataRow("<ch#> -- chapter tag", true)]
[DataRow("<chapter count> -- chapter tag but not ch# or ch_#", false)]
public void Tests(string template, bool expected) => Templates.ChapterFile.IsRecommended(template).Should().Be(expected);
[DataRow("<ch#>")]
[DataRow("<ch#> <id>")]
public void valid_tests(string template) => Tests(template, Array.Empty<string>());
[TestMethod]
[DataRow(@"no tags", Templates.WARNING_NO_TAGS, Templates.WARNING_NO_CHAPTER_NUMBER_TAG)]
[DataRow(@"<id>\foo\bar", Templates.ERROR_INVALID_FILE_NAME_CHAR, Templates.WARNING_NO_CHAPTER_NUMBER_TAG)]
[DataRow("<chapter count> -- chapter tag but not ch# or ch_#", Templates.WARNING_NO_TAGS, Templates.WARNING_NO_CHAPTER_NUMBER_TAG)]
public void Tests(string template, params string[] expected)
{
var result = Templates.ChapterFile.GetWarnings(template);
result.Count().Should().Be(expected.Length);
result.Should().BeEquivalentTo(expected);
}
}
[TestClass]
public class HasWarnings
{
[TestMethod]
public void null_has_warnings() => Tests(null, true);
[TestMethod]
public void empty_has_warnings() => Tests("", true);
[TestMethod]
public void whitespace_has_warnings() => Tests(" ", true);
[TestMethod]
[DataRow(@"no tags", true)]
[DataRow(@"<id>\foo\bar", true)]
[DataRow("<ch#> <id>", false)]
[DataRow("<ch#> -- chapter tag", false)]
[DataRow("<chapter count> -- chapter tag but not ch# or ch_#", true)]
public void Tests(string template, bool expected) => Templates.ChapterFile.HasWarnings(template).Should().Be(expected);
}
[TestClass]