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
67 changes: 3 additions & 64 deletions package/AgentWindowsManaged.Tests/AgentIdentityTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
using Property = WixSharp.Property;
using Record = Microsoft.Deployment.WindowsInstaller.Record;

using static DevolutionsAgent.Installer.Tests.InstallerSession;

namespace DevolutionsAgent.Installer.Tests;

[Collection(InstallerSession.Collection)]
public sealed class AgentIdentityTests
{
// A well-formed token (crates/agent-identity/src/token.rs) whose bag names https://example.test/dvls.
Expand Down Expand Up @@ -410,12 +413,6 @@ string ErrorIn(string productLanguage)
Assert.Equal(english, ErrorIn("1031"));
}

/// <summary>
/// The installer's action that runs the custom action <paramref name="methodName"/>.
/// </summary>
private static ManagedAction ActionOf(string methodName) =>
Assert.Single(AgentActions.Actions.OfType<ManagedAction>(), a => a.MethodName == methodName);

/// <summary>
/// Runs the custom action that reads the enrollment token file, in a session of a package in
/// <paramref name="folder"/> where <paramref name="properties"/> (<c>NAME=value</c>) are set.
Expand Down Expand Up @@ -454,62 +451,4 @@ private static (ActionResult Result, string Token, List<(InstallMessage Type, st
MsiInstaller.SetExternalUI(previous, InstallLogModes.None);
}
}

/// <summary>
/// Evaluates <paramref name="condition"/> with the Windows Installer engine, in a session where
/// <paramref name="properties"/> (<c>NAME=value</c>) are set.
/// </summary>
private static bool EvaluateCondition(string condition, string[] properties) =>
WithSession(properties, session => session.EvaluateCondition(condition));

private static T WithSession<T>(string[] properties, Func<Session, T> action) =>
WithSession(AppContext.BaseDirectory, properties, action);

/// <summary>
/// Runs <paramref name="action"/> in a Windows Installer session of an empty package in <paramref name="folder"/>
/// where <paramref name="properties"/> (<c>NAME=value</c>) are set.
/// </summary>
private static T WithSession<T>(string folder, string[] properties, Func<Session, T> action)
{
string path = Path.Combine(folder, $"session-{Guid.NewGuid():N}.msi");

try
{
using (Database database = new(path, DatabaseOpenMode.CreateDirect))
{
database.Execute("CREATE TABLE `Property` (`Property` CHAR(72) NOT NULL, `Value` CHAR(0) NOT NULL LOCALIZABLE PRIMARY KEY `Property`)");
database.Execute($"INSERT INTO `Property` (`Property`, `Value`) VALUES ('ProductCode', '{Guid.NewGuid():B}')");
database.SummaryInfo.Template = "x64;1033";
database.SummaryInfo.RevisionNumber = Guid.NewGuid().ToString("B");
database.SummaryInfo.PageCount = 500;
database.Commit();
}

using Session session = MsiInstaller.OpenPackage(path, ignoreMachineState: true);

foreach (string property in properties)
{
string[] parts = property.Split(['='], 2);
session[parts[0]] = parts[1];
}

return action(session);
}
finally
{
System.IO.File.Delete(path);
}
}

/// <summary>
/// A new, empty folder under the temporary directory, deleted with its content on disposal.
/// </summary>
private sealed class TempFolder : IDisposable
{
public string Path { get; } = System.IO.Path.Combine(System.IO.Path.GetTempPath(), $"agent-identity-{Guid.NewGuid():N}");

public TempFolder() => Directory.CreateDirectory(Path);

public void Dispose() => Directory.Delete(Path, recursive: true);
}
}
393 changes: 393 additions & 0 deletions package/AgentWindowsManaged.Tests/DataFolderTests.cs

Large diffs are not rendered by default.

82 changes: 82 additions & 0 deletions package/AgentWindowsManaged.Tests/InstallerSession.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System;
using System.IO;
using System.Linq;

using DevolutionsAgent.Actions;

using Microsoft.Deployment.WindowsInstaller;

using WixSharp;

using Xunit;

using MsiInstaller = Microsoft.Deployment.WindowsInstaller.Installer;

namespace DevolutionsAgent.Installer.Tests;

/// <summary>
/// The installer's actions, and Windows Installer sessions to evaluate them in.
/// </summary>
internal static class InstallerSession
{
/// <summary>
/// The test collection of every test class that opens a session.
/// </summary>
/// <remarks>
/// Windows Installer opens one package at a time in a process, and xUnit runs the tests of one collection one at a
/// time.
/// </remarks>
public const string Collection = "Windows Installer session";

/// <summary>
/// The installer's action that runs the custom action <paramref name="methodName"/>.
/// </summary>
public static ManagedAction ActionOf(string methodName) =>
Assert.Single(AgentActions.Actions.OfType<ManagedAction>(), a => a.MethodName == methodName);

/// <summary>
/// Evaluates <paramref name="condition"/> with the Windows Installer engine, in a session where
/// <paramref name="properties"/> (<c>NAME=value</c>) are set.
/// </summary>
public static bool EvaluateCondition(string condition, string[] properties) =>
WithSession(properties, session => session.EvaluateCondition(condition));

public static T WithSession<T>(string[] properties, Func<Session, T> action) =>
WithSession(AppContext.BaseDirectory, properties, action);

/// <summary>
/// Runs <paramref name="action"/> in a Windows Installer session of an empty package in <paramref name="folder"/>
/// where <paramref name="properties"/> (<c>NAME=value</c>) are set.
/// </summary>
public static T WithSession<T>(string folder, string[] properties, Func<Session, T> action)
{
string path = Path.Combine(folder, $"session-{Guid.NewGuid():N}.msi");

try
{
using (Database database = new(path, DatabaseOpenMode.CreateDirect))
{
database.Execute("CREATE TABLE `Property` (`Property` CHAR(72) NOT NULL, `Value` CHAR(0) NOT NULL LOCALIZABLE PRIMARY KEY `Property`)");
database.Execute($"INSERT INTO `Property` (`Property`, `Value`) VALUES ('ProductCode', '{Guid.NewGuid():B}')");
database.SummaryInfo.Template = "x64;1033";
database.SummaryInfo.RevisionNumber = Guid.NewGuid().ToString("B");
database.SummaryInfo.PageCount = 500;
database.Commit();
}

using Session session = MsiInstaller.OpenPackage(path, ignoreMachineState: true);

foreach (string property in properties)
{
string[] parts = property.Split(['='], 2);
session[parts[0]] = parts[1];
}

return action(session);
}
finally
{
System.IO.File.Delete(path);
}
}
}
15 changes: 15 additions & 0 deletions package/AgentWindowsManaged.Tests/OutputLogger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using DevolutionsAgent.Actions;

using Xunit.Abstractions;

namespace DevolutionsAgent.Installer.Tests;

/// <summary>
/// Writes the log of the code under test to the test's output.
/// </summary>
internal sealed class OutputLogger(ITestOutputHelper output) : ILogger
{
public void Log(string msg) => output.WriteLine(msg);

public void Log(string format, params object[] args) => output.WriteLine(format, args);
}
Loading
Loading