Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions FileDeduplicator.Test/MenuTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,31 @@ public void MenuRunsTheChosenVerbAndOffersTheMenuAgain()
Assert.AreEqual(2, output.Split("0. Exit").Length - 1, "The menu was not offered again after the verb ran.");
}

/// <summary>
/// <c>FileDeduplicator -p dir</c> with no verb lands on the menu, and the verb chosen from it
/// works on that directory rather than asking for a path again -- every time it is chosen.
/// </summary>
[TestMethod]
public void MenuHandsItsPathToTheChosenVerb()
{
// Arrange
using TempTree tree = new();
_ = tree.Write("a.txt", "alpha");
_ = tree.Write("aa.txt", "alpha");
Type[] menuVerbs = [.. Program.Verbs.Where(t => t != typeof(Menu))];
int scan = Array.IndexOf(menuVerbs, typeof(Scan)) + 1;
Assert.IsGreaterThan(0, scan, "Scan is not on the menu.");

// Act -- no path on stdin: the only path is the one the menu was given
string output = ConsoleCapture.Run(new Menu { PathString = tree.Root.WeakString }, $"{scan}\n{scan}\n0\n", out int exitCode);

// Assert
Assert.AreEqual(0, exitCode, output);
Assert.DoesNotContain("Enter the path", output, $"The menu dropped its -p path and asked again. Output was:\n{output}");
Assert.AreEqual(2, output.Split($"Scanning: {tree.Root}").Length - 1, $"Scan did not work on the menu's path both times. Output was:\n{output}");
Assert.AreEqual(2, output.Split("Found 1 group(s) of duplicate files").Length - 1, output);
}

[TestMethod]
public void MenuRejectsAChoiceThatIsNotOnIt()
{
Expand Down
38 changes: 33 additions & 5 deletions FileDeduplicator/Verbs/Menu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ internal ControlRepeater BuildScrollMenu(out ExitCommand exit)

private void RunNumberedMenu()
{
BaseVerb[] verbs = [.. MenuVerbs.Select(CreateVerb)];
ICommand[] verbs = [.. MenuVerbs.Select(CreateCommand)];
string[] texts = [.. MenuVerbs.Select(DescribeVerb)];

while (true)
Expand Down Expand Up @@ -105,11 +105,23 @@ private void RunNumberedMenu()
}
}

private static BaseVerb CreateVerb(Type verbType)
/// <summary>
/// Creates the command a menu entry runs: the verb, handed the menu's own <c>-p</c> path each
/// time it is chosen.
/// </summary>
/// <remarks>
/// The menu is the default verb, so <c>FileDeduplicator -p dir</c> lands here with the path set.
/// Dropping it would ask for the path again and invite a different one into a flow that can end
/// in deletion. It is re-applied on every run because <see cref="BaseVerb{T}.Run()"/> clears the
/// verb's path when it finishes.
/// </remarks>
/// <param name="verbType">The verb to run.</param>
/// <returns>The command that runs it.</returns>
private PresetPathCommand CreateCommand(Type verbType)
{
BaseVerb? verb = Activator.CreateInstance(verbType) as BaseVerb;
Debug.Assert(verb != null);
return verb;
return new(verb, () => PathString);
}

private static string DescribeVerb(Type verbType)
Expand All @@ -119,13 +131,29 @@ private static string DescribeVerb(Type verbType)
return string.IsNullOrEmpty(helpText) ? name : $"{name} - {helpText}";
}

private static LabelMenuItem CreateMenuItem(Type verbType) => new()
private LabelMenuItem CreateMenuItem(Type verbType) => new()
{
Text = DescribeVerb(verbType),
Command = CreateVerb(verbType),
Command = CreateCommand(verbType),
IsEnabled = true,
};

/// <summary>
/// Runs a verb with the path the menu was given, or with none so that the verb asks for one.
/// </summary>
/// <param name="verb">The verb to run.</param>
/// <param name="path">Reads the menu's path at the moment the verb runs.</param>
private sealed class PresetPathCommand(BaseVerb verb, Func<string?> path) : ICommand
{
public bool IsActive => verb.IsActive;

public void Execute()
{
verb.PathString = path();
verb.Execute();
}
}

/// <summary>
/// The menu item that leaves the menu, which otherwise could only be left with Ctrl+C.
/// </summary>
Expand Down
Loading