157 lines
5.0 KiB
C#
157 lines
5.0 KiB
C#
using CursorLang.Core.Models;
|
|
using CursorLang.Core.Services;
|
|
using CursorLang.Tests.Shared;
|
|
using Microsoft.Win32;
|
|
|
|
namespace CursorLang.Core.Tests.Services;
|
|
|
|
/// <summary>
|
|
/// Startup of a build unpacked into a folder: a value under the Run key. The tests
|
|
/// keep to a root of their own, so the startup list of the machine is untouched.
|
|
/// </summary>
|
|
public sealed class RegistryStartupTests
|
|
{
|
|
private const string RunPath = @"Software\Microsoft\Windows\CurrentVersion\Run";
|
|
|
|
private const string ApprovedPath =
|
|
@"Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run";
|
|
|
|
private const string ValueName = "CursorLang";
|
|
|
|
private const string Command = @"""C:\Apps\CursorLang\CursorLang.exe""";
|
|
|
|
[Fact]
|
|
public void With_nothing_written_down_startup_is_off()
|
|
{
|
|
using var root = new TempRegistryKey();
|
|
|
|
Assert.Equal(StartupState.Disabled, new RegistryStartup(root.Key, Command).GetState());
|
|
}
|
|
|
|
[Fact]
|
|
public void Switching_startup_on_writes_the_path_of_the_app()
|
|
{
|
|
using var root = new TempRegistryKey();
|
|
var startup = new RegistryStartup(root.Key, Command);
|
|
|
|
Assert.Equal(StartupState.Enabled, startup.SetEnabled(true));
|
|
Assert.Equal(Command, ReadRunValue(root));
|
|
}
|
|
|
|
[Fact]
|
|
public void Switching_startup_off_takes_the_entry_away()
|
|
{
|
|
using var root = new TempRegistryKey();
|
|
var startup = new RegistryStartup(root.Key, Command);
|
|
|
|
startup.SetEnabled(true);
|
|
|
|
Assert.Equal(StartupState.Disabled, startup.SetEnabled(false));
|
|
Assert.Null(ReadRunValue(root));
|
|
}
|
|
|
|
// Switching off what is already off is what happens when Windows and the app
|
|
// disagree about the state, and it is no reason to fail
|
|
[Fact]
|
|
public void Switching_off_startup_that_is_already_off_passes_quietly()
|
|
{
|
|
using var root = new TempRegistryKey();
|
|
|
|
Assert.Equal(StartupState.Disabled, new RegistryStartup(root.Key, Command).SetEnabled(false));
|
|
}
|
|
|
|
[Fact]
|
|
public void The_path_is_written_afresh_every_time()
|
|
{
|
|
using var root = new TempRegistryKey();
|
|
|
|
new RegistryStartup(root.Key, @"""C:\Old\CursorLang.exe""").SetEnabled(true);
|
|
new RegistryStartup(root.Key, Command).SetEnabled(true);
|
|
|
|
Assert.Equal(Command, ReadRunValue(root));
|
|
}
|
|
|
|
[Fact]
|
|
public void An_entry_the_user_has_banned_counts_as_off()
|
|
{
|
|
using var root = new TempRegistryKey();
|
|
var startup = new RegistryStartup(root.Key, Command);
|
|
|
|
startup.SetEnabled(true);
|
|
Ban(root);
|
|
|
|
Assert.Equal(StartupState.DisabledByUser, startup.GetState());
|
|
}
|
|
|
|
// The ban outlives the request: the entry is written, and Windows still ignores it
|
|
[Fact]
|
|
public void The_ban_of_the_user_survives_a_request_to_switch_startup_on()
|
|
{
|
|
using var root = new TempRegistryKey();
|
|
var startup = new RegistryStartup(root.Key, Command);
|
|
|
|
Ban(root);
|
|
|
|
Assert.Equal(StartupState.DisabledByUser, startup.SetEnabled(true));
|
|
Assert.Equal(Command, ReadRunValue(root));
|
|
}
|
|
|
|
[Fact]
|
|
public void A_verdict_of_the_user_in_favour_leaves_startup_on()
|
|
{
|
|
using var root = new TempRegistryKey();
|
|
var startup = new RegistryStartup(root.Key, Command);
|
|
|
|
startup.SetEnabled(true);
|
|
WriteVerdict(root, 0x02);
|
|
|
|
Assert.Equal(StartupState.Enabled, startup.GetState());
|
|
}
|
|
|
|
// An empty blob is not a ban: Windows writes twelve bytes, but a value cut
|
|
// short says nothing about the will of the user
|
|
[Fact]
|
|
public void A_verdict_with_no_bytes_in_it_is_no_ban()
|
|
{
|
|
using var root = new TempRegistryKey();
|
|
var startup = new RegistryStartup(root.Key, Command);
|
|
|
|
startup.SetEnabled(true);
|
|
|
|
using RegistryKey approved = root.Key.CreateSubKey(ApprovedPath);
|
|
approved.SetValue(ValueName, Array.Empty<byte>(), RegistryValueKind.Binary);
|
|
|
|
Assert.Equal(StartupState.Enabled, startup.GetState());
|
|
}
|
|
|
|
[Fact]
|
|
public void With_no_path_to_the_app_startup_is_unavailable()
|
|
{
|
|
using var root = new TempRegistryKey();
|
|
var startup = new RegistryStartup(root.Key, command: null);
|
|
|
|
Assert.Equal(StartupState.Unavailable, startup.GetState());
|
|
Assert.Equal(StartupState.Unavailable, startup.SetEnabled(true));
|
|
Assert.Equal(StartupState.Unavailable, startup.SetEnabled(false));
|
|
Assert.Null(ReadRunValue(root));
|
|
}
|
|
|
|
private static string? ReadRunValue(TempRegistryKey root)
|
|
{
|
|
using RegistryKey? run = root.Key.OpenSubKey(RunPath);
|
|
return run?.GetValue(ValueName) as string;
|
|
}
|
|
|
|
/// <summary>The mark Windows leaves after the user switches the entry off.</summary>
|
|
private static void Ban(TempRegistryKey root) => WriteVerdict(root, 0x03);
|
|
|
|
private static void WriteVerdict(TempRegistryKey root, byte first)
|
|
{
|
|
var verdict = new byte[12];
|
|
verdict[0] = first;
|
|
|
|
using RegistryKey approved = root.Key.CreateSubKey(ApprovedPath);
|
|
approved.SetValue(ValueName, verdict, RegistryValueKind.Binary);
|
|
}
|
|
}
|