.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

129 lines
4.8 KiB

// Copyright (c) 2026 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.IO;
using AwesomeAssertions;
using ICSharpCode.ILSpy.AppEnv;
using NUnit.Framework;
namespace ICSharpCode.ILSpy.Tests;
[TestFixture]
public class SingleInstanceTests
{
[Test]
public void InstanceId_Option_Is_Parsed()
{
CommandLineArguments.Create(new[] { "--instanceid", "Foo" }).InstanceId.Should().Be("Foo");
}
[Test]
public void InstanceId_Defaults_To_Null_When_Absent()
{
CommandLineArguments.Create(Array.Empty<string>()).InstanceId.Should().BeNull();
}
[Test]
public void NewInstance_Option_Forces_SingleInstance_False()
{
// --newinstance is what disables single-instance for a single launch; lock in the mapping
// now that a consumer finally reads CommandLineArguments.SingleInstance.
CommandLineArguments.Create(new[] { "--newinstance" }).SingleInstance.Should().BeFalse();
CommandLineArguments.Create(Array.Empty<string>()).SingleInstance.Should().BeNull();
}
[Test]
public void GetInstanceName_Is_Deterministic_And_Contains_No_Path_Separators()
{
// The mutex/pipe namespace is derived from machine + user only -- no executable location --
// so every launch of the same user shares one instance regardless of which exe path it came
// from. The name feeds a Mutex/pipe identifier, so it must carry no path separators.
var name = SingleInstance.GetInstanceName();
name.Should().Be(SingleInstance.GetInstanceName());
name.Should().StartWith("ILSpy.");
name.Should().NotContain("/");
name.Should().NotContain("\\");
}
[Test]
public void ShouldReuse_When_No_Id_Requested()
{
// A launcher without --instanceid (Explorer "Open with", a plain CLI launch) reuses whatever
// instance is running, regardless of that instance's identity.
SingleInstance.ShouldReuse(null, null, "/opt/ilspy/ILSpy").Should().BeTrue();
SingleInstance.ShouldReuse("", "launch-id", "/opt/ilspy/ILSpy").Should().BeTrue();
}
[Test]
public void ShouldReuse_When_Requested_Id_Matches_Running_Launch_Id()
{
SingleInstance.ShouldReuse("Foo", "Foo", "/opt/ilspy/ILSpy").Should().BeTrue();
}
[Test]
public void ShouldReuse_When_Requested_Id_Matches_Running_Executable_Path()
{
// The only-AddIn-installed case: VS requests its bundled exe path, and the running instance
// was started WITHOUT an id (e.g. by a shell "Open with ILSpy" on that same exe). It must
// still match, because the running instance reports the executable it actually is.
SingleInstance.ShouldReuse("/opt/ilspy/ILSpy", null, "/opt/ilspy/ILSpy").Should().BeTrue();
}
[Test]
public void ShouldReuse_False_When_Requested_Id_Matches_Neither()
{
SingleInstance.ShouldReuse("/opt/other/ILSpy", "Foo", "/opt/ilspy/ILSpy").Should().BeFalse();
}
[Test]
public void FullyQualifyPath_Roots_An_Existing_Relative_File_Against_The_Base_Directory()
{
var dir = Path.Combine(Path.GetTempPath(), "ILSpySingleInstance_" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(dir);
try
{
var file = Path.Combine(dir, "some.dll");
File.WriteAllText(file, "");
SingleInstance.FullyQualifyPath("some.dll", dir).Should().Be(file);
}
finally
{
Directory.Delete(dir, recursive: true);
}
}
[Test]
public void FullyQualifyPath_Leaves_Non_File_Tokens_Unchanged()
{
var dir = Path.GetTempPath();
var missing = "definitely-not-a-file-" + Guid.NewGuid().ToString("N");
var rooted = Path.Combine(dir, "does-not-exist-" + Guid.NewGuid().ToString("N") + ".dll");
// An option flag, a value that is not an existing file, and an already-rooted path all pass
// through untouched so the receiving instance re-parses them exactly as typed.
SingleInstance.FullyQualifyPath("--navigateto", dir).Should().Be("--navigateto");
SingleInstance.FullyQualifyPath(missing, dir).Should().Be(missing);
SingleInstance.FullyQualifyPath(rooted, dir).Should().Be(rooted);
}
}