winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

TestCommand.cpp (13175B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 
      5 #ifndef AICLI_DISABLE_TEST_HOOKS
      6 
      7 #include "TestCommand.h"
      8 #include "AppInstallerRuntime.h"
      9 #include "TableOutput.h"
     10 #include "Public/ConfigurationSetProcessorFactoryRemoting.h"
     11 #include "Public/ShutdownMonitoring.h"
     12 #include "Workflows/ConfigurationFlow.h"
     13 #include "Workflows/MSStoreInstallerHandler.h"
     14 #include <winget/RepositorySource.h>
     15 #include <winrt/Microsoft.Management.Configuration.h>
     16 
     17 using namespace AppInstaller::CLI::Workflow;
     18 using namespace AppInstaller::Utility::literals;
     19 
     20 namespace AppInstaller::CLI
     21 {
     22     namespace
     23     {
     24         void LogAndReport(Execution::Context& context, std::string_view message)
     25         {
     26             context.Reporter.Info() << message << std::endl;
     27             AICLI_LOG(CLI, Info, << message);
     28         }
     29 
     30         HRESULT WaitForShutdown(Execution::Context& context)
     31         {
     32             LogAndReport(context, "Waiting for app shutdown event");
     33             if (!ShutdownMonitoring::TerminationSignalHandler::Instance()->WaitForAppShutdownEvent())
     34             {
     35                 LogAndReport(context, "Failed getting app shutdown event");
     36                 return APPINSTALLER_CLI_ERROR_INTERNAL_ERROR;
     37             }
     38 
     39             LogAndReport(context, "Succeeded waiting for app shutdown event");
     40             return S_OK;
     41         }
     42 
     43         HRESULT AppShutdownWindowMessage(Execution::Context& context)
     44         {
     45             auto windowHandle = ShutdownMonitoring::TerminationSignalHandler::Instance()->GetWindowHandle();
     46 
     47             if (windowHandle == NULL)
     48             {
     49                 LogAndReport(context, "Window was not created");
     50                 return APPINSTALLER_CLI_ERROR_INTERNAL_ERROR;
     51             }
     52 
     53             if (context.Args.Contains(Execution::Args::Type::Force))
     54             {
     55                 LogAndReport(context, "Sending WM_QUERYENDSESSION message");
     56                 THROW_LAST_ERROR_IF(!SendMessageTimeout(
     57                     windowHandle,
     58                     WM_QUERYENDSESSION,
     59                     NULL,
     60                     ENDSESSION_CLOSEAPP,
     61                     (SMTO_ABORTIFHUNG | SMTO_ERRORONEXIT),
     62                     5000,
     63                     NULL));
     64             }
     65 
     66             HRESULT hr = WaitForShutdown(context);
     67 
     68             if (context.Args.Contains(Execution::Args::Type::Force))
     69             {
     70                 LogAndReport(context, "Sending WM_ENDSESSION message");
     71                 THROW_LAST_ERROR_IF(!SendMessageTimeout(
     72                     windowHandle,
     73                     WM_ENDSESSION,
     74                     NULL,
     75                     ENDSESSION_CLOSEAPP,
     76                     (SMTO_ABORTIFHUNG | SMTO_ERRORONEXIT),
     77                     5000,
     78                     NULL));
     79             }
     80 
     81             return hr;
     82         }
     83 
     84         void EnsureDSCv3Processor(Execution::Context& context)
     85         {
     86             auto& configurationSet = context.Get<Execution::Data::ConfigurationContext>().Set();
     87             configurationSet.Environment().ProcessorIdentifier(L"dscv3");
     88         }
     89 
     90         void InvokeGetAllUnits(Execution::Context& context)
     91         {
     92             auto& configurationContext = context.Get<Execution::Data::ConfigurationContext>();
     93 
     94             winrt::Microsoft::Management::Configuration::ConfigurationUnit unit;
     95             unit.Type(Utility::ConvertToUTF16(context.Args.GetArg(Execution::Args::Type::ConfigurationExportResource)));
     96 
     97             auto result = configurationContext.Processor().GetAllUnits(unit);
     98 
     99             if (FAILED(result.ResultInformation().ResultCode()))
    100             {
    101                 context.Reporter.Error() << "Failed to export: " << WINGET_OSTREAM_FORMAT_HRESULT(result.ResultInformation().ResultCode()) << std::endl;
    102                 AICLI_TERMINATE_CONTEXT(result.ResultInformation().ResultCode());
    103             }
    104 
    105             for (const auto& resultUnit : result.Units())
    106             {
    107                 configurationContext.Set().Units().Append(resultUnit);
    108             }
    109         }
    110 
    111         // Command to directly invoke the export flow.
    112         struct TestConfigurationExportCommand final : public Command
    113         {
    114             TestConfigurationExportCommand(std::string_view parent) : Command("config-export-units", {}, parent) {}
    115 
    116             std::vector<Argument> GetArguments() const override
    117             {
    118                 return {
    119                     Argument{ Execution::Args::Type::OutputFile, Resource::String::OutputFileArgumentDescription, true },
    120                     Argument{ Execution::Args::Type::ConfigurationExportResource, Resource::String::ConfigureExportResource },
    121                 };
    122             }
    123 
    124             Resource::LocString ShortDescription() const override
    125             {
    126                 return "Run config export"_lis;
    127             }
    128 
    129             Resource::LocString LongDescription() const override
    130             {
    131                 return "Runs the GetAllUnits configuration method to test export on a DSC v3 directly."_lis;
    132             }
    133 
    134         protected:
    135             void ExecuteInternal(Execution::Context& context) const override
    136             {
    137                 context <<
    138                     VerifyIsFullPackage <<
    139                     CreateConfigurationProcessorWithoutFactory <<
    140                     CreateOrOpenConfigurationSet{ "0.3" } <<
    141                     EnsureDSCv3Processor <<
    142                     CreateConfigurationProcessor <<
    143                     InvokeGetAllUnits <<
    144                     WriteConfigFile;
    145             }
    146         };
    147 
    148         void InvokeFindUnitProcessors(Execution::Context& context)
    149         {
    150             auto& configurationContext = context.Get<Execution::Data::ConfigurationContext>();
    151 
    152             winrt::Microsoft::Management::Configuration::FindUnitProcessorsOptions findOptions;
    153 
    154             if (context.Args.Contains(Execution::Args::Type::InstallLocation))
    155             {
    156                 findOptions.SearchPaths(Utility::ConvertToUTF16(context.Args.GetArg(Execution::Args::Type::InstallLocation)));
    157                 findOptions.SearchPathsExclusive(true);
    158                 findOptions.UnitDetailFlags(winrt::Microsoft::Management::Configuration::ConfigurationUnitDetailFlags::Local);
    159             }
    160 
    161             auto result = configurationContext.Processor().FindUnitProcessors(findOptions);
    162 
    163             if (result.Size() > 0)
    164             {
    165                 Execution::TableOutput<2> table(context.Reporter,
    166                     {
    167                         "Type"_lis,
    168                         "Description"_lis
    169                     });
    170 
    171                 for (const auto& resultUnitProcessor : result)
    172                 {
    173                     table.OutputLine({
    174                         Utility::ConvertToUTF8(resultUnitProcessor.UnitType()),
    175                         Utility::ConvertToUTF8(resultUnitProcessor.UnitDescription())
    176                         });
    177                 }
    178 
    179                 table.Complete();
    180             }
    181             else
    182             {
    183                 context.Reporter.Info() << "No unit processors found."_lis << std::endl;
    184             }
    185         }
    186 
    187         // Command to directly invoke find unit processors.
    188         struct TestConfigurationFindUnitProcessorsCommand final : public Command
    189         {
    190             TestConfigurationFindUnitProcessorsCommand(std::string_view parent) : Command("config-find-unit-processors", {}, parent) {}
    191 
    192             std::vector<Argument> GetArguments() const override
    193             {
    194                 return {
    195                     Argument{ Execution::Args::Type::InstallLocation, Resource::String::LocationArgumentDescription },
    196                 };
    197             }
    198 
    199             Resource::LocString ShortDescription() const override
    200             {
    201                 return "Run find unit processors"_lis;
    202             }
    203 
    204             Resource::LocString LongDescription() const override
    205             {
    206                 return "Runs find unit processors. Search paths could be provided."_lis;
    207             }
    208 
    209         protected:
    210             void ExecuteInternal(Execution::Context& context) const override
    211             {
    212                 context <<
    213                     VerifyIsFullPackage <<
    214                     CreateConfigurationProcessorWithoutFactory <<
    215                     CreateOrOpenConfigurationSet{ "0.3" } <<
    216                     EnsureDSCv3Processor <<
    217                     CreateConfigurationProcessor <<
    218                     InvokeFindUnitProcessors;
    219             }
    220         };
    221 
    222         struct TestCanUnloadNowCommand final : public Command
    223         {
    224             TestCanUnloadNowCommand(std::string_view parent) : Command("can-unload-now", {}, parent, Visibility::Hidden) {}
    225 
    226             Resource::LocString ShortDescription() const override
    227             {
    228                 return "Test DllCanUnloadNow"_lis;
    229             }
    230 
    231             Resource::LocString LongDescription() const override
    232             {
    233                 return "Verifies that the function that implements the inproc DllCanUnloadNow properly blocks unload due to static storage object."_lis;
    234             }
    235 
    236         protected:
    237             void ExecuteInternal(Execution::Context& context) const override
    238             {
    239                 Repository::Source source{ Repository::PredefinedSource::Installed };
    240 
    241                 ProgressCallback progress;
    242                 source.Open(progress);
    243 
    244                 HMODULE self = GetModuleHandle(L"WindowsPackageManager.dll");
    245                 if (!self)
    246                 {
    247                     LogAndReport(context, "Couldn't get WindowsPackageManager module");
    248                     return;
    249                 }
    250 
    251                 auto WindowsPackageManagerInProcModuleTerminate = reinterpret_cast<bool (__stdcall *)()>(GetProcAddress(self, "WindowsPackageManagerInProcModuleTerminate"));
    252 
    253                 // Report the object counts, attempt to terminate, report the object counts again
    254                 ReportObjectCounts(context);
    255                 LogAndReport(context, WindowsPackageManagerInProcModuleTerminate() ? "DllCanUnloadNow" : "DllCannotUnloadNow");
    256                 ReportObjectCounts(context);
    257             }
    258 
    259         private:
    260             void ReportObjectCounts(Execution::Context& context) const
    261             {
    262                 std::ostringstream stream;
    263                 stream << "Internal objects: " << GetInternalObjectCount() << '\n';
    264                 stream << "External objects: " << GetExternalObjectCount();
    265 
    266                 LogAndReport(context, stream.str());
    267             }
    268 
    269             uint32_t GetInternalObjectCount() const
    270             {
    271                 return winrt::get_module_lock().operator unsigned int();
    272             }
    273 
    274             unsigned long GetExternalObjectCount() const
    275             {
    276                 auto module = Microsoft::WRL::GetModuleBase();
    277                 return module ? module->GetObjectCount() : 0;
    278             }
    279         };
    280     }
    281 
    282     std::vector<std::unique_ptr<Command>> TestCommand::GetCommands() const
    283     {
    284         return InitializeFromMoveOnly<std::vector<std::unique_ptr<Command>>>(
    285             {
    286                 std::make_unique<TestAppShutdownCommand>(FullName()),
    287                 std::make_unique<TestConfigurationExportCommand>(FullName()),
    288                 std::make_unique<TestConfigurationFindUnitProcessorsCommand>(FullName()),
    289                 std::make_unique<TestCanUnloadNowCommand>(FullName()),
    290             });
    291     }
    292 
    293     void TestCommand::ExecuteInternal(Execution::Context& context) const
    294     {
    295         UNREFERENCED_PARAMETER(context);
    296         Sleep(INFINITE);
    297     }
    298 
    299     Resource::LocString TestCommand::ShortDescription() const
    300     {
    301         return Utility::LocIndString("Waits infinitely"sv);
    302     }
    303 
    304     Resource::LocString TestCommand::LongDescription() const
    305     {
    306         return Utility::LocIndString("Waits infinitely. Use this if you want winget to wait forever while something is going on"sv);
    307     }
    308 
    309     std::vector<Argument> TestAppShutdownCommand::GetArguments() const
    310     {
    311         return {
    312             Argument::ForType(Execution::Args::Type::Force)
    313         };
    314     }
    315 
    316     void TestAppShutdownCommand::ExecuteInternal(Execution::Context& context) const
    317     {
    318         HRESULT hr = E_FAIL;
    319 
    320         // Only package context and admin won't create the window message.
    321         if (!Runtime::IsRunningInPackagedContext() || !Runtime::IsRunningAsAdmin())
    322         {
    323             hr = AppShutdownWindowMessage(context);
    324         }
    325         else
    326         {
    327             hr = WaitForShutdown(context);
    328         }
    329 
    330         AICLI_TERMINATE_CONTEXT(hr);
    331     }
    332 
    333     Resource::LocString TestAppShutdownCommand::ShortDescription() const
    334     {
    335         return Utility::LocIndString("Test command to verify appshutdown event."sv);
    336     }
    337 
    338     Resource::LocString TestAppShutdownCommand::LongDescription() const
    339     {
    340         return Utility::LocIndString("Test command for appshutdown. Verifies the window was created and waits for the app shutdown event"sv);
    341     }
    342 
    343 }
    344 
    345 #endif