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
12 changes: 12 additions & 0 deletions .github/actions/spelling/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ BFirst
bigcatalog
BITMAPINFOHEADER
bitmask
BKMG
bkup
blargle
blockedbypolicy
Expand Down Expand Up @@ -335,6 +336,7 @@ MINORVERSION
missingdependency
mkgmtime
MMmmbbbb
MODULEENTRY
mof
monicka
MPNS
Expand All @@ -347,6 +349,7 @@ MSIXSTRM
msstore
MSZIP
mszyml
mta
Mugiwara
Multideclaration
mysource
Expand Down Expand Up @@ -427,6 +430,7 @@ positionals
posix
postuninstall
powershellgallery
PPROCESS
pri
PRIMARYKEY
processthreads
Expand All @@ -435,6 +439,7 @@ PRODUCTICON
propkey
PROPVARIANT
proxystub
psapi
pscustomobject
pseudocode
PSHOST
Expand Down Expand Up @@ -474,6 +479,7 @@ rgp
rgpsz
rhs
riid
roapi
Roblox
ronomon
rowid
Expand Down Expand Up @@ -516,11 +522,14 @@ sid
Sideload
SIGNATUREHASH
silentpreferred
SINGLETHREADED
Skipx
sku
SLAPI
SMTO
SNAME
SNAPMODULE
SNAPTHREAD
sortof
sourceforge
SOURCESDIRECTORY
Expand Down Expand Up @@ -553,11 +562,14 @@ tellp
temppath
testexampleinstaller
thiscouldbeapc
THREADENTRY
threehundred
timespan
Tlg
tlhelp
TLSCAs
tombstoned
Toolhelp
transitioning
trimstart
ttl
Expand Down
1 change: 1 addition & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ jobs:
Contents: |
AppInstallerCLIE2ETests\**
AppInstallerCLITests\**
ComInprocTestbed\**
Microsoft.Management.Configuration\**
Microsoft.Management.Configuration.UnitTests\**
Microsoft.Management.Configuration.OutOfProc\**
Expand Down
27 changes: 27 additions & 0 deletions src/AppInstallerCLI.sln
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripts", "Scripts", "{F49C
PowerShell\scripts\Initialize-LocalWinGetModules.ps1 = PowerShell\scripts\Initialize-LocalWinGetModules.ps1
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ComInprocTestbed", "ComInprocTestbed\ComInprocTestbed.vcxproj", "{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
Expand Down Expand Up @@ -1022,6 +1024,30 @@ Global
{33745E4A-39E2-676F-7E23-50FB43848D25}.ReleaseStatic|x64.Build.0 = Release|x64
{33745E4A-39E2-676F-7E23-50FB43848D25}.ReleaseStatic|x86.ActiveCfg = Release|x86
{33745E4A-39E2-676F-7E23-50FB43848D25}.ReleaseStatic|x86.Build.0 = Release|x86
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Debug|ARM64.ActiveCfg = Debug|ARM64
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Debug|ARM64.Build.0 = Debug|ARM64
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Debug|x64.ActiveCfg = Debug|x64
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Debug|x64.Build.0 = Debug|x64
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Debug|x86.ActiveCfg = Debug|Win32
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Debug|x86.Build.0 = Debug|Win32
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Fuzzing|ARM64.ActiveCfg = Release|ARM64
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Fuzzing|ARM64.Build.0 = Release|ARM64
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Fuzzing|x64.ActiveCfg = Release|x64
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Fuzzing|x64.Build.0 = Release|x64
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Fuzzing|x86.ActiveCfg = Release|Win32
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Fuzzing|x86.Build.0 = Release|Win32
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Release|ARM64.ActiveCfg = Release|ARM64
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Release|ARM64.Build.0 = Release|ARM64
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Release|x64.ActiveCfg = Release|x64
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Release|x64.Build.0 = Release|x64
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Release|x86.ActiveCfg = Release|Win32
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Release|x86.Build.0 = Release|Win32
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.ReleaseStatic|ARM64.ActiveCfg = Release|ARM64
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.ReleaseStatic|ARM64.Build.0 = Release|ARM64
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.ReleaseStatic|x64.ActiveCfg = Release|x64
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.ReleaseStatic|x64.Build.0 = Release|x64
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.ReleaseStatic|x86.ActiveCfg = Release|Win32
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.ReleaseStatic|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1058,6 +1084,7 @@ Global
{A33223D2-550B-4D99-A53D-488B1F68683E} = {60618CAC-2995-4DF9-9914-45C6FC02C995}
{7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{F49C4C89-447E-4D15-B38B-5A8DCFB134AF} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9}
{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B6FDB70C-A751-422C-ACD1-E35419495857}
Expand Down
14 changes: 8 additions & 6 deletions src/AppInstallerCLICore/Public/ShutdownMonitoring.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,12 @@ namespace AppInstaller::ShutdownMonitoring
};

// Coordinates shutdown across server components
struct ServerShutdownSynchronization : public ICancellable
struct ServerShutdownSynchronization
{
using ShutdownCompleteCallback = void (*)();

// Initializes the monitoring system and sets up a callback to be invoked when shutdown is completed.
static void Initialize(ShutdownCompleteCallback callback);
static void Initialize(ShutdownCompleteCallback callback, bool createTerminationSignalHandler = true);

// "Interface" for a single component to synchronize with.
struct ComponentSystem
Expand All @@ -93,18 +93,20 @@ namespace AppInstaller::ShutdownMonitoring
// Waits for the shutdown to complete.
static void WaitForShutdown();

// Listens for a termination signal.
void Cancel(CancelReason reason, bool force) override;

private:
ServerShutdownSynchronization();
ServerShutdownSynchronization() = default;
~ServerShutdownSynchronization();

friend TerminationSignalHandler;

static ServerShutdownSynchronization& Instance();

// Runs the actual shutdown process and invokes the callback.
void SynchronizeShutdown(CancelReason reason);

// Listens for a termination signal.
void Signal(CancelReason reason);

ShutdownCompleteCallback m_callback = nullptr;
std::mutex m_componentsLock;
std::vector<ComponentSystem> m_components;
Expand Down
45 changes: 29 additions & 16 deletions src/AppInstallerCLICore/ShutdownMonitoring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,19 +183,22 @@ namespace AppInstaller::ShutdownMonitoring
// Returns FALSE if no contexts attached; TRUE otherwise.
BOOL TerminationSignalHandler::InformListeners(CancelReason reason, bool force)
{
std::lock_guard<std::mutex> lock{ m_listenersLock };
BOOL result = FALSE;

if (m_listeners.empty())
{
return FALSE;
}
std::lock_guard<std::mutex> lock{ m_listenersLock };
result = m_listeners.empty() ? FALSE : TRUE;

for (auto& listener : m_listeners)
{
listener->Cancel(reason, force);
for (auto& listener : m_listeners)
{
listener->Cancel(reason, force);
}
}

return TRUE;
// Notify shutdown synchronization as well
ServerShutdownSynchronization::Instance().Signal(reason);

return result;
}

void TerminationSignalHandler::CreateWindowAndStartMessageLoop()
Expand Down Expand Up @@ -283,9 +286,16 @@ namespace AppInstaller::ShutdownMonitoring
}
}

void ServerShutdownSynchronization::Initialize(ShutdownCompleteCallback callback)
void ServerShutdownSynchronization::Initialize(ShutdownCompleteCallback callback, bool createTerminationSignalHandler)
{
Instance().m_callback = callback;

// Force the creation of the TerminationSignalHandler singleton so that the process can listen for termination signals even if
// it never attempts to run anything that explicitly registers for cancellation callbacks.
if (createTerminationSignalHandler)
{
TerminationSignalHandler::Instance();
}
}

void ServerShutdownSynchronization::AddComponent(const ComponentSystem& component)
Expand Down Expand Up @@ -322,8 +332,17 @@ namespace AppInstaller::ShutdownMonitoring
instance.m_shutdownComplete.wait();
}

void ServerShutdownSynchronization::Cancel(CancelReason reason, bool)
void ServerShutdownSynchronization::Signal(CancelReason reason)
{
{
// Check for registered components before creating a thread to do nothing
std::lock_guard<std::mutex> lock{ m_componentsLock };
if (m_components.empty())
{
return;
}
}

std::lock_guard<std::mutex> lock{ m_threadLock };

if (!m_shutdownThread.joinable())
Expand All @@ -332,14 +351,8 @@ namespace AppInstaller::ShutdownMonitoring
}
}

ServerShutdownSynchronization::ServerShutdownSynchronization()
{
TerminationSignalHandler::Instance()->AddListener(this);
}

ServerShutdownSynchronization::~ServerShutdownSynchronization()
{
TerminationSignalHandler::Instance()->RemoveListener(this);
if (m_shutdownThread.joinable())
{
m_shutdownThread.detach();
Expand Down
2 changes: 2 additions & 0 deletions src/AppInstallerCLIE2ETests/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public class Constants
public const string PowerShellModulePathParameter = "PowerShellModulePath";
public const string SkipTestSourceParameter = "SkipTestSource";
public const string ForcedExperimentalFeaturesParameter = "ForcedExperimentalFeatures";
public const string InprocTestbedPathParameter = "InprocTestbedPath";
public const string InprocTestbedUseTestPackageParameter = "InprocTestbedUseTestPackage";

// Test Sources
public const string DefaultWingetSourceName = @"winget";
Expand Down
39 changes: 27 additions & 12 deletions src/AppInstallerCLIE2ETests/Helpers/TestCommon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,6 @@ public static RunCommandResult RunAICLICommand(string command, string parameters
}
}

string inputMsg =
"AICLI path: " + TestSetup.Parameters.AICLIPath +
" Command: " + command +
" Parameters: " + parameters + correlationParameter +
(string.IsNullOrEmpty(stdIn) ? string.Empty : " StdIn: " + stdIn) +
" Timeout: " + timeOut;

TestContext.Out.WriteLine($"Starting command run. {inputMsg}");

return RunAICLICommandViaDirectProcess(command, parameters + correlationParameter, stdIn, timeOut, throwOnTimeout);
}

Expand Down Expand Up @@ -1162,17 +1153,27 @@ public static string CopyInstallerFileToARPInstallSourceDirectory(string install
/// <summary>
/// Run winget command via direct process.
/// </summary>
/// <param name="executablePath">The executable to run.</param>
/// <param name="command">Command to run.</param>
/// <param name="parameters">Parameters.</param>
/// <param name="stdIn">Optional std in.</param>
/// <param name="timeOut">Optional timeout.</param>
/// <param name="throwOnTimeout">Throw on timeout.</param>
/// <returns>The result of the command.</returns>
private static RunCommandResult RunAICLICommandViaDirectProcess(string command, string parameters, string stdIn, int timeOut, bool throwOnTimeout)
public static RunCommandResult RunProcess(string executablePath, string command, string parameters, string stdIn, int timeOut, bool throwOnTimeout)
{
string inputMsg =
"Exe path: " + executablePath +
" Command: " + command +
" Parameters: " + parameters +
(string.IsNullOrEmpty(stdIn) ? string.Empty : " StdIn: " + stdIn) +
" Timeout: " + timeOut;

TestContext.Out.WriteLine($"Starting command run. {inputMsg}");

RunCommandResult result = new ();
Process p = new Process();
p.StartInfo = new ProcessStartInfo(TestSetup.Parameters.AICLIPath, command + ' ' + parameters);
p.StartInfo = new ProcessStartInfo(executablePath, command + ' ' + parameters);
p.StartInfo.UseShellExecute = false;

p.StartInfo.StandardOutputEncoding = Encoding.UTF8;
Expand Down Expand Up @@ -1236,12 +1237,26 @@ private static RunCommandResult RunAICLICommandViaDirectProcess(string command,
}
else if (throwOnTimeout)
{
throw new TimeoutException($"Direct winget command run timed out: {command} {parameters}");
throw new TimeoutException($"Direct command run timed out: {command} {parameters}");
}

return result;
}

/// <summary>
/// Run winget command via direct process.
/// </summary>
/// <param name="command">Command to run.</param>
/// <param name="parameters">Parameters.</param>
/// <param name="stdIn">Optional std in.</param>
/// <param name="timeOut">Optional timeout.</param>
/// <param name="throwOnTimeout">Throw on timeout.</param>
/// <returns>The result of the command.</returns>
private static RunCommandResult RunAICLICommandViaDirectProcess(string command, string parameters, string stdIn, int timeOut, bool throwOnTimeout)
{
return RunProcess(TestSetup.Parameters.AICLIPath, command, parameters, stdIn, timeOut, throwOnTimeout);
}

/// <summary>
/// Run command result.
/// </summary>
Expand Down
12 changes: 12 additions & 0 deletions src/AppInstallerCLIE2ETests/Helpers/TestSetup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ private TestSetup()
this.VerboseLogging = this.InitializeBoolParam(Constants.VerboseLoggingParameter, true);
this.LooseFileRegistration = this.InitializeBoolParam(Constants.LooseFileRegistrationParameter);
this.SkipTestSource = this.InitializeBoolParam(Constants.SkipTestSourceParameter, this.IsDefault);
this.InprocTestbedUseTestPackage = this.InitializeBoolParam(Constants.InprocTestbedUseTestPackageParameter);

// For packaged context, default to AppExecutionAlias
this.AICLIPath = this.InitializeStringParam(Constants.AICLIPathParameter, this.PackagedContext ? "WinGetDev.exe" : TestCommon.GetTestFile("winget.exe"));
Expand All @@ -45,6 +46,7 @@ private TestSetup()
this.MsixInstallerPath = this.InitializeFileParam(Constants.MsixInstallerPathParameter);
this.MsiInstallerV2Path = this.InitializeFileParam(Constants.MsiInstallerV2PathParameter);
this.FontPath = this.InitializeFileParam(Constants.FontPathParameter);
this.InprocTestbedPath = this.InitializeFileParam(Constants.InprocTestbedPathParameter);

this.ForcedExperimentalFeatures = this.InitializeStringArrayParam(Constants.ForcedExperimentalFeaturesParameter);
}
Expand Down Expand Up @@ -130,6 +132,16 @@ public static TestSetup Parameters
/// </summary>
public string PackageCertificatePath { get; }

/// <summary>
/// Gets the inproc testbed executable path.
/// </summary>
public string InprocTestbedPath { get; }

/// <summary>
/// Gets a value indicating whether to use the test package or not.
/// </summary>
public bool InprocTestbedUseTestPackage { get; }

/// <summary>
/// Gets a value indicating whether to skip creating test source.
/// </summary>
Expand Down
Loading