Skip to content
Open
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
20 changes: 20 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.

8 changes: 8 additions & 0 deletions src/LogExpert.Resources/Resources.de.resx
Original file line number Diff line number Diff line change
Expand Up @@ -2093,6 +2093,14 @@ Fortfahren?</value>
<value>Plugin-Vertrauenskonfiguration aktualisiert.

LogExpert neu starten, um die Änderungen zu übernehmen?</value>
</data>
<data name="LogTabWindow_UI_Message_UnresolvedShortcut_Single" xml:space="preserve">
<value>Die Verknüpfung konnte nicht aufgelöst werden und wurde als Datei geöffnet:
{0}</value>
</data>
<data name="LogTabWindow_UI_Message_UnresolvedShortcut_Multiple" xml:space="preserve">
<value>Die folgenden Verknüpfungen konnten nicht aufgelöst werden und wurden als Dateien geöffnet:
{0}</value>
</data>
<data name="LogExpert_Common_Error_Message_ServiceNotInitialized" xml:space="preserve">
<value>{0} nicht initialisiert</value>
Expand Down
8 changes: 8 additions & 0 deletions src/LogExpert.Resources/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -2094,6 +2094,14 @@ Continue?</value>
<value>Plugin trust configuration updated.

Restart LogExpert to apply changes?</value>
</data>
<data name="LogTabWindow_UI_Message_UnresolvedShortcut_Single" xml:space="preserve">
<value>Couldn't follow the shortcut, so it was opened as a file:
{0}</value>
</data>
<data name="LogTabWindow_UI_Message_UnresolvedShortcut_Multiple" xml:space="preserve">
<value>Couldn't follow these shortcuts, so they were opened as files:
{0}</value>
</data>
<data name="LogTabWindow_UI_Title_RestartRecommended" xml:space="preserve">
<value>Restart Recommended</value>
Expand Down
8 changes: 8 additions & 0 deletions src/LogExpert.Resources/Resources.zh-CN.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1906,6 +1906,14 @@ YY[YY] = 年
<value>插件信任配置已更新。

是否重启 LogExpert 以应用更改?</value>
</data>
<data name="LogTabWindow_UI_Message_UnresolvedShortcut_Single" xml:space="preserve">
<value>无法跟随该快捷方式,已按文件打开:
{0}</value>
</data>
<data name="LogTabWindow_UI_Message_UnresolvedShortcut_Multiple" xml:space="preserve">
<value>无法跟随以下快捷方式,已按文件打开:
{0}</value>
</data>
<data name="LogTabWindow_UI_Title_RestartRecommended" xml:space="preserve">
<value>建议重启</value>
Expand Down
184 changes: 183 additions & 1 deletion src/LogExpert.UI.Tests/Services/FileOperationServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ public void AddFileTab_RaisesFileOpenedEvent ()
// Assert
Assert.That(receivedArgs, Is.Not.Null);
Assert.That(receivedArgs!.LogWindow, Is.SameAs(_stubLogWindow));
Assert.That(receivedArgs.Request, Is.SameAs(request));
Assert.That(receivedArgs.Request, Is.EqualTo(request));
Assert.That(receivedArgs.ResolvedFileName, Is.Not.Null.And.Not.Empty);
Assert.That(receivedArgs.EncodingOptions, Is.Not.Null);
}
Expand Down Expand Up @@ -657,6 +657,136 @@ public void LoadFilesWithOption_SingleFile_CallsAddFileTab ()
Assert.That(_factoryCalls[0].Request.FileName, Is.EqualTo("test.log"));
}

[Test]
public void LoadFilesWithOption_ShortcutToLxjFile_CallsProjectCallback ()
{
// Arrange — the .lxj check has to run on the resolved name, or the shortcut gets opened as a raw log.
var (directory, shortcut, session) = CreateShortcutToSession();

try
{
// Act
var decision = _sut.LoadFilesWithOption(new[] { shortcut }, false);

// Assert
Assert.That(decision, Is.EqualTo(MultiFileDecision.Cancel));
Assert.That(_projectCallbackCalls, Has.Count.EqualTo(1));
Assert.That(_projectCallbackCalls[0].FileName, Is.EqualTo(session));
Assert.That(_factoryCalls, Is.Empty, "Should not open the shortcut as a log");
}
finally
{
Directory.Delete(directory, true);
}
}

[Test]
public void AddFileTabs_ShortcutToLxjFile_CallsProjectCallback ()
{
// Arrange
var (directory, shortcut, session) = CreateShortcutToSession();

try
{
// Act
_sut.AddFileTabs(new[] { shortcut });

// Assert
Assert.That(_projectCallbackCalls, Has.Count.EqualTo(1));
Assert.That(_projectCallbackCalls[0].FileName, Is.EqualTo(session));
Assert.That(_factoryCalls, Is.Empty, "Should not open the shortcut as a log");
}
finally
{
Directory.Delete(directory, true);
}
}

[Test]
public void AddFileTab_ShortcutToLogFile_OpensTarget ()
{
// Arrange
var directory = TestShortcuts.CreateTempDirectory("ShortcutLog");

try
{
var log = Path.Join(directory, "actual.log");
File.WriteAllText(log, "line1");

var shortcut = Path.Join(directory, "actual.lnk");
TestShortcuts.Create(shortcut, log);

// Act
_sut.AddFileTab(new FileTabRequest { FileName = shortcut });

// Assert
Assert.That(_factoryCalls, Has.Count.EqualTo(1));
Assert.That(_factoryCalls[0].Request.FileName, Is.EqualTo(log), "the target file is opened, not the shortcut");
}
finally
{
Directory.Delete(directory, true);
}
}

[Test]
public void AddFileTab_UnresolvableShortcut_ReportsItInFileOpened ()
{
// Arrange
var directory = TestShortcuts.CreateTempDirectory("ShortcutBroken");

try
{
var shortcut = Path.Join(directory, "broken.lnk");
TestShortcuts.Create(shortcut, Path.Join(directory, "missing.log"));

FileOpenedEventArgs? receivedArgs = null;
_sut.FileOpened += (_, args) => receivedArgs = args;

// Act
_sut.AddFileTab(new FileTabRequest { FileName = shortcut });

// Assert
Assert.That(receivedArgs, Is.Not.Null);
Assert.That(receivedArgs!.UnresolvedShortcuts, Is.EqualTo(new[] { shortcut }),
"the user has to be told why the shortcut opens as binary data");
}
finally
{
Directory.Delete(directory, true);
}
}

[Test]
public void AddFileTab_ResolvedShortcut_DoesNotReportUnresolvedShortcut ()
{
// Arrange
var directory = TestShortcuts.CreateTempDirectory("ShortcutResolved");

try
{
var log = Path.Join(directory, "actual.log");
File.WriteAllText(log, "line1");

var shortcut = Path.Join(directory, "actual.lnk");
TestShortcuts.Create(shortcut, log);

FileOpenedEventArgs? receivedArgs = null;
_sut.FileOpened += (_, args) => receivedArgs = args;

// Act
_sut.AddFileTab(new FileTabRequest { FileName = shortcut });

// Assert
Assert.That(receivedArgs, Is.Not.Null);
Assert.That(receivedArgs!.UnresolvedShortcuts, Is.Null);
}
finally
{
Directory.Delete(directory, true);
}
}

[Test]
public void LoadFilesWithOption_SingleLxjFile_CallsProjectCallback ()
{
Expand Down Expand Up @@ -811,6 +941,41 @@ public void AddMultiFileTab_RaisesFileOpened_WithMultiFileNames ()
Assert.That(receivedArgs.EncodingOptions, Is.Not.Null);
}

[Test]
public void AddMultiFileTab_Shortcuts_SortsByResolvedTargetName ()
{
// Arrange
var directory = TestShortcuts.CreateTempDirectory("ShortcutSort");

try
{
var firstTarget = Path.Join(directory, "a.log");
var secondTarget = Path.Join(directory, "b.log");
File.WriteAllText(firstTarget, "first");
File.WriteAllText(secondTarget, "second");

// The shortcut names sort in the opposite order of their targets.
var zShortcut = Path.Join(directory, "z.lnk");
var aShortcut = Path.Join(directory, "a.lnk");
TestShortcuts.Create(zShortcut, firstTarget);
TestShortcuts.Create(aShortcut, secondTarget);

string[]? multiFileNames = null;
_sut.FileOpened += (_, e) => multiFileNames = e.MultiFileNames;

// Act
_ = _sut.AddMultiFileTab([zShortcut, aShortcut]);

// Assert
Assert.That(multiFileNames, Is.EqualTo(new[] { firstTarget, secondTarget }),
"multi-file order must follow the resolved target names, not the shortcut names");
}
finally
{
Directory.Delete(directory, true);
}
}

[Test]
public void AddFileTabs_SkipsEmptyStrings ()
{
Expand Down Expand Up @@ -1225,6 +1390,23 @@ public void LoadFiles_DelegatesToAddFileTabs ()
Assert.That(_factoryCalls[1].Request.FileName, Is.EqualTo("b.log"));
}

/// <summary>
/// Creates a temporary directory holding a session file and a shortcut to it, and returns the
/// three paths. The caller owns the directory and deletes it.
/// </summary>
private static (string Directory, string Shortcut, string Session) CreateShortcutToSession ()
{
var directory = TestShortcuts.CreateTempDirectory("ShortcutSession");

var session = Path.Join(directory, "project.lxj");
File.WriteAllText(session, "<Project></Project>");

var shortcut = Path.Join(directory, "session.lnk");
TestShortcuts.Create(shortcut, session);

return (directory, shortcut, session);
}

/// <summary>
/// Builds a second service on the shared config manager, standing in for another LogTabWindow
/// of the same session with its own tab controller.
Expand Down
Loading
Loading