Skip to content
Closed
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
15 changes: 15 additions & 0 deletions src/LogExpert.Configuration/ConfigManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,21 @@ public void RemoveFromFileHistory (string fileName)
Save(SettingsFlags.FileHistory);
}

/// <summary>
/// Removes every entry from the file history list and persists the result immediately.
/// </summary>
[SupportedOSPlatform("windows")]
public void ClearFileHistory ()
{
// Held across the save so a concurrent save cannot serialise the list while it is being cleared.
lock (_loadSaveLock)
{
Instance.Settings.FileHistoryList.Clear();

Save(SettingsFlags.FileHistory);
}
}

public void ClearLastOpenFilesList ()
{
lock (_loadSaveLock)
Expand Down
9 changes: 9 additions & 0 deletions src/LogExpert.Core/Interfaces/IConfigManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,15 @@ public interface IConfigManager
/// <param name="fileName">The name of the file to remove from the file history list. Comparison is case-insensitive.</param>
void RemoveFromFileHistory (string fileName);

/// <summary>
/// Removes all entries from the file history list.
/// </summary>
/// <remarks>Only the file history list is cleared — the other history lists (search, filter, filter
/// range, URI, regex, columnizer) keep their entries. The settings are saved immediately; because
/// the save writes the whole settings file, the flag passed along names the change rather than
/// narrowing what is written.</remarks>
void ClearFileHistory ();

/// <summary>
/// Clears the list of recently opened files.
/// </summary>
Expand Down
9 changes: 9 additions & 0 deletions src/LogExpert.Resources/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/LogExpert.Resources/Resources.de.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1240,6 +1240,9 @@ Ein ausgewähltes Tool erscheint in der Iconbar. Alle anderen verfügbaren Tools
<data name="LogTabWindow_UI_ToolStripMenuItem_lastUsedToolStripMenuItem" xml:space="preserve">
<value>Zuletzt benutzt</value>
</data>
<data name="LogTabWindow_UI_ToolStripMenuItem_clearFileHistoryToolStripMenuItem" xml:space="preserve">
<value>Zuletzt benutzte löschen</value>
</data>
<data name="LogTabWindow_UI_ToolStripMenuItem_exitToolStripMenuItem" xml:space="preserve">
<value>Exit</value>
</data>
Expand Down
3 changes: 3 additions & 0 deletions src/LogExpert.Resources/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1176,6 +1176,9 @@ Checked tools will appear in the icon bar. All other tools are available in the
<data name="LogTabWindow_UI_ToolStripMenuItem_lastUsedToolStripMenuItem" xml:space="preserve">
<value>Last used</value>
</data>
<data name="LogTabWindow_UI_ToolStripMenuItem_clearFileHistoryToolStripMenuItem" xml:space="preserve">
<value>Clear recently used</value>
</data>
<data name="LogTabWindow_UI_ToolStripMenuItem_exitToolStripMenuItem" xml:space="preserve">
<value>Exit</value>
</data>
Expand Down
3 changes: 3 additions & 0 deletions src/LogExpert.Resources/Resources.zh-CN.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,9 @@
<data name="LogTabWindow_UI_ToolStripMenuItem_lastUsedToolStripMenuItem" xml:space="preserve">
<value>最近使用</value>
</data>
<data name="LogTabWindow_UI_ToolStripMenuItem_clearFileHistoryToolStripMenuItem" xml:space="preserve">
<value>清除最近使用</value>
</data>
<data name="LogTabWindow_UI_ToolStripMenuItem_exitToolStripMenuItem" xml:space="preserve">
<value>退出</value>
</data>
Expand Down
157 changes: 157 additions & 0 deletions src/LogExpert.Tests/ConfigManagerTests/ClearFileHistoryTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
using System.Reflection;

using LogExpert.Core.Config;

using Newtonsoft.Json;

using NUnit.Framework;

namespace LogExpert.Tests.ConfigManagerTests;

/// <summary>
/// Tests for the "clear recently used" action of the file history menu.
/// </summary>
[TestFixture]
public class ClearFileHistoryTests
{
private string _testDir;
private Configuration.ConfigManager _configManager;
private object? _originalSettings;
private bool _originalIsInitialized;
private string _originalConfigDir;

[SetUp]
public void SetUp ()
{
_configManager = Configuration.ConfigManager.Instance;

// Capture the singleton state before touching it. Other fixtures in this assembly rely on
// ConfigManager.Instance still pointing at the directory ConfigDirIsolationSetup prepared.
_originalIsInitialized = GetPrivateField("_isInitialized") is true;
_originalSettings = GetPrivateField("_settings");
_originalConfigDir = _configManager.ConfigDir;

_testDir = Path.Join(Path.GetTempPath(), "LogExpert_ClearHistoryTest_" + Guid.NewGuid().ToString("N"));
_ = Directory.CreateDirectory(_testDir);

ResetConfigManagerInitialization();
_configManager.Initialize(_testDir, new Rectangle(0, 0, 1920, 1080));
_configManager.ConfigDir = _testDir;
}

[TearDown]
public void TearDown ()
{
// Restore the singleton before removing the directory. While ConfigDir still points at this
// test's directory, the next fixture that saves recreates it and writes there instead of the
// shared isolation directory.
SetPrivateField("_isInitialized", _originalIsInitialized);
SetPrivateField("_settings", _originalSettings);
_configManager.ConfigDir = _originalConfigDir;

try
{
if (Directory.Exists(_testDir))
{
Directory.Delete(_testDir, recursive: true);
}
}
catch (IOException)
{
// Ignore IO errors during cleanup
}
catch (UnauthorizedAccessException)
{
// Ignore access errors during cleanup
}
}

[Test]
public void ClearFileHistory_RemovesAllEntries ()
{
_configManager.AddToFileHistory(@"C:\logs\one.log");
_configManager.AddToFileHistory(@"C:\logs\two.log");
Assert.That(_configManager.Settings.FileHistoryList, Has.Count.EqualTo(2));

_configManager.ClearFileHistory();

Assert.That(_configManager.Settings.FileHistoryList, Is.Empty);
}

[Test]
public void ClearFileHistory_WritesEmptyListToSettingsFile ()
{
_configManager.AddToFileHistory(@"C:\logs\one.log");

_configManager.ClearFileHistory();

var settingsFile = Path.Join(_configManager.ActiveConfigDir, "settings.json");
Assert.That(File.Exists(settingsFile), Is.True, "clearing the history has to persist immediately");

var saved = JsonConvert.DeserializeObject<Settings>(File.ReadAllText(settingsFile));
Assert.That(saved, Is.Not.Null);
Assert.That(saved.FileHistoryList, Is.Empty);
}

/// <summary>
/// Clearing the recent files list leaves every other history list alone — including the ones the
/// settings dialog and the regex test dialog keep for their own inputs.
/// </summary>
[Test]
public void ClearFileHistory_LeavesOtherHistoryListsUntouched ()
{
_configManager.AddToFileHistory(@"C:\logs\one.log");
_configManager.Settings.LastOpenFilesList.Add(@"C:\logs\one.log");
_configManager.Settings.SearchHistoryList.Add("error");
_configManager.Settings.FilterHistoryList.Add("WARN");
_configManager.Settings.FilterRangeHistoryList.Add("10");
_configManager.Settings.UriHistoryList.Add("https://example.org/one.log");
_configManager.Settings.RegexHistory.ExpressionHistoryList.Add(@"^\d+");
_configManager.Settings.RegexHistory.TesttextHistoryList.Add("42");
_configManager.Settings.ColumnizerHistoryList.Add(new ColumnizerHistoryEntry(@"C:\logs\one.log", "JsonColumnizer"));

_configManager.ClearFileHistory();

Assert.Multiple(() =>
{
Assert.That(_configManager.Settings.FileHistoryList, Is.Empty);
Assert.That(_configManager.Settings.LastOpenFilesList, Has.Count.EqualTo(1));
Assert.That(_configManager.Settings.SearchHistoryList, Has.Count.EqualTo(1));
Assert.That(_configManager.Settings.FilterHistoryList, Has.Count.EqualTo(1));
Assert.That(_configManager.Settings.FilterRangeHistoryList, Has.Count.EqualTo(1));
Assert.That(_configManager.Settings.UriHistoryList, Has.Count.EqualTo(1));
Assert.That(_configManager.Settings.RegexHistory.ExpressionHistoryList, Has.Count.EqualTo(1));
Assert.That(_configManager.Settings.RegexHistory.TesttextHistoryList, Has.Count.EqualTo(1));
Assert.That(_configManager.Settings.ColumnizerHistoryList, Has.Count.EqualTo(1));
});
}

/// <summary>
/// Resets the singleton so each test loads its settings from its own temp directory.
/// </summary>
private void ResetConfigManagerInitialization ()
{
SetPrivateField("_isInitialized", false);
SetPrivateField("_settings", null);
}

private object? GetPrivateField (string fieldName)
{
return Field(fieldName).GetValue(_configManager);
}

private void SetPrivateField (string fieldName, object? value)
{
Field(fieldName).SetValue(_configManager, value);
}

/// <summary>
/// Throws instead of returning null: a renamed field would otherwise make the reset silently do
/// nothing and the whole fixture pass against settings it never re-loaded.
/// </summary>
private static FieldInfo Field (string fieldName)
{
return typeof(Configuration.ConfigManager).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance)
?? throw new InvalidOperationException($"ConfigManager.{fieldName} is gone; update this fixture's reset.");
}
}
39 changes: 38 additions & 1 deletion src/LogExpert.UI.Tests/Services/MenuToolbarControllerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,45 @@ public void PopulateFileHistory_CreatesMenuItems ()
_controller.PopulateFileHistory(history);

var lastUsed = FindMenuItem("lastUsedToolStripMenuItem");
Assert.That(lastUsed.DropDownItems, Has.Count.EqualTo(2));
Assert.That(lastUsed.DropDownItems, Has.Count.EqualTo(4), "2 files + separator + clear item");
Assert.That(lastUsed.DropDownItems[0].Text, Is.EqualTo(@"C:\log1.txt"));
Assert.That(lastUsed.DropDownItems[2], Is.TypeOf<ToolStripSeparator>());
Assert.That(lastUsed.DropDownItems[3].Name, Is.EqualTo("clearFileHistoryToolStripMenuItem"));
Assert.That(lastUsed.DropDownItems[3].Enabled, Is.True);
}

[Test]
public void PopulateFileHistory_DisablesClearItem_WhenHistoryIsEmpty ()
{
_controller.PopulateFileHistory([]);

var lastUsed = FindMenuItem("lastUsedToolStripMenuItem");
Assert.That(lastUsed.DropDownItems, Has.Count.EqualTo(1), "clear item only, no separator");
Assert.That(lastUsed.DropDownItems[0].Name, Is.EqualTo("clearFileHistoryToolStripMenuItem"));
Assert.That(lastUsed.DropDownItems[0].Enabled, Is.False);
}

[Test]
public void ClearHistoryItem_RaisesHistoryClearRequested_AndNotFileClicked ()
{
var clearRequested = 0;
string clickedFile = null;
_controller.HistoryClearRequested += (_, _) => clearRequested++;
_controller.HistoryItemClicked += (_, e) => clickedFile = e.FileName;

_controller.PopulateFileHistory(["test.log"]);
var lastUsed = FindMenuItem("lastUsedToolStripMenuItem");
var clearItem = lastUsed.DropDownItems[^1];

var args = new ToolStripItemClickedEventArgs(clearItem);
var onItemClicked = typeof(ToolStrip).GetMethod("OnItemClicked", BindingFlags.Instance | BindingFlags.NonPublic);
_ = onItemClicked.Invoke(lastUsed.DropDown, [args]);

Assert.Multiple(() =>
{
Assert.That(clearRequested, Is.EqualTo(1));
Assert.That(clickedFile, Is.Null, "the clear item must not be treated as a file entry");
});
}

[Test]
Expand Down
7 changes: 7 additions & 0 deletions src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ private void InitializeMenuToolbarControllerEvents ()
{
_menuToolbarController.HistoryItemClicked += OnMenuControllerHistoryItemClicked;
_menuToolbarController.HistoryItemRemoveRequested += OnMenuControllerHistoryItemRemoveRequested;
_menuToolbarController.HistoryClearRequested += OnMenuControllerHistoryClearRequested;
_menuToolbarController.HighlightGroupSelected += OnMenuControllerHighlightGroupSelected;
}

Expand All @@ -250,6 +251,12 @@ private void OnMenuControllerHistoryItemRemoveRequested (object? sender, History
FillHistoryMenu();
}

private void OnMenuControllerHistoryClearRequested (object? sender, EventArgs e)
{
ConfigManager.ClearFileHistory();
FillHistoryMenu();
}

private void OnMenuControllerHistoryItemClicked (object? sender, HistoryItemClickedEventArgs e)
{
_ = _fileOperationService.AddFileTab(new FileTabRequest { FileName = e.FileName });
Expand Down
1 change: 1 addition & 0 deletions src/LogExpert.UI/Interface/IMenuToolbarController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,6 @@ void InitializeMenus (MenuStrip mainMenu, ToolStrip buttonToolbar, ToolStrip ext
// Events
event EventHandler<HistoryItemClickedEventArgs> HistoryItemClicked;
event EventHandler<HistoryItemClickedEventArgs> HistoryItemRemoveRequested;
event EventHandler HistoryClearRequested;
event EventHandler<HighlightGroupSelectedEventArgs> HighlightGroupSelected;
}
Loading
Loading