winget-cli

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

TestCommon.cpp (13539B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "TestCommon.h"
      5 #include "TestHooks.h"
      6 #include <winget/GroupPolicy.h>
      7 #include <winget/UserSettings.h>
      8 #include <AppInstallerMsixInfo.h>
      9 #include <AppInstallerDownloader.h>
     10 
     11 using namespace AppInstaller;
     12 
     13 namespace TestCommon
     14 {
     15     namespace
     16     {
     17         int initRand()
     18         {
     19             srand(static_cast<unsigned int>(time(NULL)));
     20             return rand();
     21         };
     22 
     23         inline int getRand()
     24         {
     25             static int randStart = initRand();
     26             return randStart++;
     27         }
     28 
     29         inline std::filesystem::path GetFilePath(std::filesystem::path path, const std::string& baseName, const std::string& baseExt)
     30         {
     31             path /= baseName + std::to_string(getRand()) + baseExt;
     32             return path;
     33         }
     34 
     35         inline std::filesystem::path GetTempFilePath(const std::string& baseName, const std::string& baseExt)
     36         {
     37             std::filesystem::path tempFilePath = std::filesystem::temp_directory_path();
     38             return GetFilePath(tempFilePath, baseName, baseExt);
     39         }
     40 
     41         static TempFileDestructionBehavior s_TempFileDestructorBehavior = TempFileDestructionBehavior::Delete;
     42         static std::vector<std::filesystem::path> s_TempFilesOnFile;
     43 
     44         static std::filesystem::path s_TestDataFileBasePath{};
     45 
     46         bool CleanVolatileTestRoot(HKEY root)
     47         {
     48             THROW_IF_WIN32_ERROR(RegDeleteTreeW(root, nullptr));
     49             return true;
     50         }
     51     }
     52 
     53     TempFile::TempFile(const std::string& baseName, const std::string& baseExt, std::optional<KeepTempFile> keepTempFile)
     54     {
     55         _filepath = GetTempFilePath(baseName, baseExt);
     56         if (!keepTempFile)
     57         {
     58             std::filesystem::remove(_filepath);
     59         }
     60     }
     61 
     62     TempFile::TempFile(const std::filesystem::path& parent, const std::string& baseName, const std::string& baseExt, std::optional<KeepTempFile> keepTempFile)
     63     {
     64         _filepath = GetFilePath(parent, baseName, baseExt);
     65         if (!keepTempFile)
     66         {
     67             std::filesystem::remove(_filepath);
     68         }
     69     }
     70 
     71     TempFile::TempFile(const std::filesystem::path& filePath, std::optional<KeepTempFile> keepTempFile)
     72     {
     73         if (filePath.is_relative())
     74         {
     75             _filepath = std::filesystem::temp_directory_path();
     76             _filepath /= filePath;
     77         }
     78         else
     79         {
     80             _filepath = filePath;
     81         }
     82         if (!keepTempFile)
     83         {
     84             std::filesystem::remove(_filepath);
     85         }
     86     }
     87 
     88     TempFile::~TempFile() try
     89     {
     90         if (m_destructionToken)
     91         {
     92             switch (s_TempFileDestructorBehavior)
     93             {
     94             case TempFileDestructionBehavior::Delete:
     95                 std::filesystem::remove_all(_filepath);
     96                 break;
     97             case TempFileDestructionBehavior::Keep:
     98                 break;
     99             case TempFileDestructionBehavior::ShellExecuteOnFailure:
    100                 s_TempFilesOnFile.emplace_back(std::move(_filepath));
    101                 break;
    102             }
    103         }
    104     }
    105     CATCH_LOG_RETURN()
    106 
    107     void TempFile::Rename(const std::filesystem::path& newFilePath)
    108     {
    109         std::filesystem::rename(GetPath(), newFilePath);
    110         _filepath = newFilePath;
    111     }
    112 
    113     void TempFile::Release()
    114     {
    115         m_destructionToken = false;
    116     }
    117 
    118     void TempFile::SetDestructorBehavior(TempFileDestructionBehavior behavior)
    119     {
    120         s_TempFileDestructorBehavior = behavior;
    121     }
    122 
    123     void TempFile::SetTestFailed(bool failed)
    124     {
    125         if (failed)
    126         {
    127             for (const auto& path : s_TempFilesOnFile)
    128             {
    129                 SHELLEXECUTEINFOW seinfo{};
    130                 seinfo.cbSize = sizeof(seinfo);
    131                 seinfo.lpVerb = L"open";
    132                 seinfo.lpFile = path.c_str();
    133 
    134                 ShellExecuteExW(&seinfo);
    135             }
    136         }
    137         else
    138         {
    139             s_TempFilesOnFile.clear();
    140         }
    141     }
    142 
    143     TempDirectory::TempDirectory(const std::string& baseName, bool create)
    144     {
    145         _filepath = GetTempFilePath(baseName, "");
    146         if (create)
    147         {
    148             if (std::filesystem::exists(_filepath))
    149             {
    150                 std::filesystem::remove_all(_filepath);
    151             }
    152             std::filesystem::create_directories(_filepath);
    153         }
    154     }
    155 
    156     std::filesystem::path TestDataFile::GetPath() const
    157     {
    158         std::filesystem::path result = s_TestDataFileBasePath;
    159         result /= m_path;
    160         return result;
    161     }
    162 
    163     void TestDataFile::SetTestDataBasePath(const std::filesystem::path& path)
    164     {
    165         s_TestDataFileBasePath = path;
    166     }
    167 
    168     void TestProgress::OnProgress(uint64_t current, uint64_t maximum, AppInstaller::ProgressType type)
    169     {
    170         if (m_OnProgress)
    171         {
    172             m_OnProgress(current, maximum, type);
    173         }
    174     }
    175 
    176     void TestProgress::SetProgressMessage(std::string_view)
    177     {
    178     }
    179 
    180     void TestProgress::BeginProgress()
    181     {
    182     }
    183 
    184     void TestProgress::EndProgress(bool)
    185     {
    186     }
    187 
    188     bool TestProgress::IsCancelledBy(AppInstaller::CancelReason)
    189     {
    190         return false;
    191     }
    192 
    193     AppInstaller::IProgressCallback::CancelFunctionRemoval TestProgress::SetCancellationFunction(std::function<void()>&&)
    194     {
    195         return {};
    196     }
    197 
    198     wil::unique_hkey RegCreateVolatileTestRoot()
    199     {
    200         // First create/open the real test root
    201         wil::unique_hkey root;
    202         THROW_IF_WIN32_ERROR(RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\WinGet\\TestRoot", 0, nullptr, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, nullptr, &root, nullptr));
    203 
    204         static bool s_ignored = CleanVolatileTestRoot(root.get());
    205 
    206         // Create a random name
    207         GUID name{};
    208         (void)CoCreateGuid(&name);
    209 
    210         wchar_t nameBuffer[256];
    211         (void)StringFromGUID2(name, nameBuffer, ARRAYSIZE(nameBuffer));
    212 
    213         return RegCreateVolatileSubKey(root.get(), nameBuffer);
    214     }
    215 
    216     wil::unique_hkey RegCreateVolatileSubKey(HKEY parent, const std::wstring& name)
    217     {
    218         wil::unique_hkey result;
    219         THROW_IF_WIN32_ERROR(RegCreateKeyExW(parent, name.c_str(), 0, nullptr, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, nullptr, &result, nullptr));
    220         return result;
    221     }
    222 
    223     void SetRegistryValue(HKEY key, const std::wstring& name, const std::wstring& value, DWORD type)
    224     {
    225         THROW_IF_WIN32_ERROR(RegSetValueExW(key, name.c_str(), 0, type, reinterpret_cast<const BYTE*>(value.c_str()), static_cast<DWORD>(sizeof(wchar_t) * (value.size() + 1))));
    226     }
    227 
    228     void SetRegistryValue(HKEY key, const std::wstring& name, const std::vector<BYTE>& value, DWORD type)
    229     {
    230         THROW_IF_WIN32_ERROR(RegSetValueExW(key, name.c_str(), 0, type, reinterpret_cast<const BYTE*>(value.data()), static_cast<DWORD>(value.size())));
    231     }
    232 
    233     void SetRegistryValue(HKEY key, const std::wstring& name, DWORD value)
    234     {
    235         THROW_IF_WIN32_ERROR(RegSetValueExW(key, name.c_str(), 0, REG_DWORD, reinterpret_cast<const BYTE*>(&value), sizeof(DWORD)));
    236     }
    237 
    238     void EnableDevMode(bool enable)
    239     {
    240         wil::unique_hkey result;
    241         THROW_IF_WIN32_ERROR(RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", 0, KEY_ALL_ACCESS|KEY_WOW64_64KEY, &result));
    242         SetRegistryValue(result.get(), L"AllowDevelopmentWithoutDevLicense", (enable ? 1 : 0));
    243     }
    244 
    245     TestUserSettings::TestUserSettings(bool keepFileSettings)
    246     {
    247         if (!keepFileSettings)
    248         {
    249             m_settings.clear();
    250         }
    251 
    252         AppInstaller::Settings::SetUserSettingsOverride(this);
    253     }
    254 
    255     TestUserSettings::~TestUserSettings()
    256     {
    257         AppInstaller::Settings::SetUserSettingsOverride(nullptr);
    258     }
    259 
    260     std::unique_ptr<TestUserSettings> TestUserSettings::EnableExperimentalFeature(Settings::ExperimentalFeature::Feature feature, bool keepFileSettings)
    261     {
    262         std::unique_ptr<TestUserSettings> result = std::make_unique<TestUserSettings>(keepFileSettings);
    263 
    264         // Due to the template usage, this needs to be updated for any features that want to use it.
    265         // Currently no feature is used. Uncomment below when a feature needs to be used.
    266         // switch (feature)
    267         // {
    268         // default:
    269         //     THROW_HR(E_NOTIMPL);
    270         // }
    271         UNREFERENCED_PARAMETER(feature);
    272 
    273         return result;
    274     }
    275 
    276     bool InstallCertFromSignedPackage(const std::filesystem::path& package)
    277     {
    278         auto [certContext, certStore] = AppInstaller::Msix::GetCertContextFromMsix(package);
    279 
    280         wil::unique_hcertstore trustedPeopleStore;
    281         trustedPeopleStore.reset(CertOpenStore(
    282             CERT_STORE_PROV_SYSTEM_W,
    283             PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
    284             NULL,
    285             CERT_SYSTEM_STORE_LOCAL_MACHINE,
    286             L"TrustedPeople"));
    287         THROW_LAST_ERROR_IF(!trustedPeopleStore.get());
    288 
    289         wil::unique_cert_context existingCert;
    290         existingCert.reset(CertFindCertificateInStore(
    291             trustedPeopleStore.get(),
    292             PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
    293             0,
    294             CERT_FIND_EXISTING,
    295             certContext.get(),
    296             nullptr));
    297 
    298         // Add if it does not already exist in the store
    299         if (!existingCert.get())
    300         {
    301             THROW_LAST_ERROR_IF(!CertAddCertificateContextToStore(
    302                 trustedPeopleStore.get(),
    303                 certContext.get(),
    304                 CERT_STORE_ADD_NEW,
    305                 nullptr));
    306 
    307             return true;
    308         }
    309 
    310         return false;
    311     }
    312 
    313     bool UninstallCertFromSignedPackage(const std::filesystem::path& package)
    314     {
    315         auto [certContext, certStore] = AppInstaller::Msix::GetCertContextFromMsix(package);
    316 
    317         wil::unique_hcertstore trustedPeopleStore;
    318         trustedPeopleStore.reset(CertOpenStore(
    319             CERT_STORE_PROV_SYSTEM_W,
    320             PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
    321             NULL,
    322             CERT_SYSTEM_STORE_LOCAL_MACHINE,
    323             L"TrustedPeople"));
    324         THROW_LAST_ERROR_IF(!trustedPeopleStore.get());
    325 
    326         wil::unique_cert_context existingCert;
    327         existingCert.reset(CertFindCertificateInStore(
    328             trustedPeopleStore.get(),
    329             PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
    330             0,
    331             CERT_FIND_EXISTING,
    332             certContext.get(),
    333             nullptr));
    334 
    335         // Remove if it exists in the store
    336         if (existingCert.get())
    337         {
    338             THROW_LAST_ERROR_IF(!CertDeleteCertificateFromStore(existingCert.get()));
    339 
    340             return true;
    341         }
    342 
    343         return false;
    344     }
    345 
    346     bool GetMsixPackageManifestReader(std::string_view testFileName, IAppxManifestReader** manifestReader)
    347     {
    348         // Locate test file
    349         TestDataFile testFile(testFileName);
    350         auto path = testFile.GetPath().u8string();
    351 
    352         // Get the stream for the test file
    353         auto stream = AppInstaller::Utility::GetReadOnlyStreamFromURI(path);
    354 
    355         // Get manifest from package reader
    356         Microsoft::WRL::ComPtr<IAppxPackageReader> packageReader;
    357         return  AppInstaller::Msix::GetPackageReader(stream.Get(), &packageReader)
    358             && SUCCEEDED(packageReader->GetManifest(manifestReader));
    359     }
    360 
    361     std::string RemoveConsoleFormat(const std::string& str)
    362     {
    363         // We are looking something that starts with "\x1b[0m"
    364         if (!str.empty() && str[0] == '\x1b')
    365         {
    366             // Find first m
    367             auto pos = str.find("m");
    368             if (pos != std::string::npos)
    369             {
    370                 return str.substr(pos + 1);
    371             }
    372         }
    373 
    374         return str;
    375     }
    376 
    377     Json::Value ConvertToJson(const std::string& content)
    378     {
    379         auto contentClean = RemoveConsoleFormat(content);
    380 
    381         Json::Value root;
    382         Json::CharReaderBuilder builder;
    383         const std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
    384         std::string error;
    385 
    386         if (!reader->parse(contentClean.c_str(), contentClean.c_str() + contentClean.size(), &root, &error))
    387         {
    388             throw error;
    389         }
    390 
    391         return root;
    392     }
    393 
    394     void SetTestPathOverrides()
    395     {
    396         // Force all tests to run against settings inside this container.
    397         // This prevents test runs from trashing the users actual settings.
    398         Runtime::TestHook_SetPathOverride(Runtime::PathName::LocalState, Runtime::GetPathTo(Runtime::PathName::LocalState) / "Tests");
    399         Runtime::TestHook_SetPathOverride(Runtime::PathName::UserFileSettings, Runtime::GetPathTo(Runtime::PathName::UserFileSettings) / "Tests");
    400         Runtime::TestHook_SetPathOverride(Runtime::PathName::StandardSettings, Runtime::GetPathTo(Runtime::PathName::StandardSettings) / "Tests");
    401         Runtime::TestHook_SetPathOverride(Runtime::PathName::SecureSettingsForRead, Runtime::GetPathTo(Runtime::PathName::StandardSettings) / "WinGet_SecureSettings_Tests");
    402         Runtime::TestHook_SetPathOverride(Runtime::PathName::SecureSettingsForWrite, Runtime::GetPathDetailsFor(Runtime::PathName::SecureSettingsForRead));
    403     }
    404 }