PortableInstaller.cpp (11371B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "TestCommon.h" 5 #include <PortableInstaller.h> 6 #include <Public/AppInstallerArchitecture.h> 7 #include <winget/Filesystem.h> 8 #include <winget/Manifest.h> 9 #include <winget/PathVariable.h> 10 #include <winget/PortableARPEntry.h> 11 #include <winget/SQLiteStorageBase.h> 12 #include <Microsoft/Schema/IPortableIndex.h> 13 #include <winget/PortableIndex.h> 14 15 using namespace std::string_literals; 16 using namespace AppInstaller::CLI::Portable; 17 using namespace AppInstaller::Filesystem; 18 using namespace AppInstaller::Manifest; 19 using namespace AppInstaller::Registry::Environment; 20 using namespace AppInstaller::Repository::Microsoft; 21 using namespace AppInstaller::SQLite; 22 using namespace AppInstaller::Repository::Microsoft::Schema; 23 using namespace AppInstaller::Utility; 24 using namespace AppInstaller::CLI::Workflow; 25 using namespace TestCommon; 26 27 TEST_CASE("PortableInstaller_InstallToRegistry", "[PortableInstaller]") 28 { 29 TempDirectory tempDirectory = TestCommon::TempDirectory("TempDirectory", false); 30 31 std::vector<PortableFileEntry> desiredTestState; 32 33 TestCommon::TempFile testPortable("testPortable.txt"); 34 std::ofstream file(testPortable, std::ofstream::out); 35 file.close(); 36 37 std::filesystem::path targetPath = tempDirectory.GetPath() / "testPortable.txt"; 38 std::filesystem::path symlinkPath = tempDirectory.GetPath() / "testSymlink.exe"; 39 40 desiredTestState.emplace_back(std::move(PortableFileEntry::CreateFileEntry(testPortable.GetPath(), targetPath, {}))); 41 desiredTestState.emplace_back(std::move(PortableFileEntry::CreateSymlinkEntry(symlinkPath, targetPath))); 42 43 PortableInstaller portableInstaller = PortableInstaller(ScopeEnum::User, Architecture::X64, "testProductCode"); 44 portableInstaller.TargetInstallLocation = tempDirectory.GetPath(); 45 portableInstaller.SetDesiredState(desiredTestState); 46 REQUIRE(portableInstaller.VerifyExpectedState()); 47 48 portableInstaller.Install(AppInstaller::CLI::Workflow::OperationType::Install); 49 50 auto entry = portableInstaller.GetAppsAndFeaturesEntry(); 51 REQUIRE(entry.ProductCode == portableInstaller.GetProductCode()); 52 53 PortableInstaller portableInstaller2 = PortableInstaller(ScopeEnum::User, Architecture::X64, "testProductCode"); 54 REQUIRE(portableInstaller2.ARPEntryExists()); 55 REQUIRE(std::filesystem::exists(portableInstaller2.PortableTargetFullPath)); 56 REQUIRE(AppInstaller::Filesystem::SymlinkExists(portableInstaller2.PortableSymlinkFullPath)); 57 58 portableInstaller2.Uninstall(); 59 REQUIRE_FALSE(std::filesystem::exists(portableInstaller2.PortableTargetFullPath)); 60 REQUIRE_FALSE(AppInstaller::Filesystem::SymlinkExists(portableInstaller2.PortableSymlinkFullPath)); 61 REQUIRE_FALSE(std::filesystem::exists(portableInstaller2.InstallLocation)); 62 } 63 64 TEST_CASE("PortableInstaller_InstallToIndex_CreateInstallRoot", "[PortableInstaller]") 65 { 66 TempDirectory installRootDirectory = TestCommon::TempDirectory("PortableInstallRoot", false); 67 68 std::vector<PortableFileEntry> desiredTestState; 69 70 TestCommon::TempFile testPortable("testPortable.txt"); 71 std::ofstream file1(testPortable, std::ofstream::out); 72 file1.close(); 73 74 TestCommon::TempFile testPortable2("testPortable2.txt"); 75 std::ofstream file2(testPortable2, std::ofstream::out); 76 file2.close(); 77 78 TestCommon::TempDirectory testDirectoryFolder("testDirectory", true); 79 80 std::filesystem::path installRootPath = installRootDirectory.GetPath(); 81 std::filesystem::path targetPath = installRootPath / "testPortable.txt"; 82 std::filesystem::path targetPath2 = installRootPath / "testPortable2.txt"; 83 std::filesystem::path symlinkPath = installRootPath / "testSymlink.exe"; 84 std::filesystem::path symlinkPath2 = installRootPath / "testSymlink2.exe"; 85 std::filesystem::path directoryPath = installRootPath / "testDirectory"; 86 87 desiredTestState.emplace_back(std::move(PortableFileEntry::CreateFileEntry(testPortable.GetPath(), targetPath, {}))); 88 desiredTestState.emplace_back(std::move(PortableFileEntry::CreateFileEntry(testPortable2.GetPath(), targetPath2, {}))); 89 desiredTestState.emplace_back(std::move(PortableFileEntry::CreateSymlinkEntry(symlinkPath, targetPath))); 90 desiredTestState.emplace_back(std::move(PortableFileEntry::CreateSymlinkEntry(symlinkPath2, targetPath2))); 91 desiredTestState.emplace_back(std::move(PortableFileEntry::CreateDirectoryEntry(testDirectoryFolder.GetPath(), directoryPath))); 92 93 PortableInstaller portableInstaller = PortableInstaller(ScopeEnum::User, Architecture::X64, "testProductCode"); 94 portableInstaller.TargetInstallLocation = installRootDirectory.GetPath(); 95 portableInstaller.RecordToIndex = true; 96 portableInstaller.SetDesiredState(desiredTestState); 97 REQUIRE(portableInstaller.VerifyExpectedState()); 98 99 portableInstaller.Install(AppInstaller::CLI::Workflow::OperationType::Install); 100 101 REQUIRE(std::filesystem::exists(installRootPath / portableInstaller.GetPortableIndexFileName())); 102 REQUIRE(std::filesystem::exists(targetPath)); 103 REQUIRE(std::filesystem::exists(targetPath2)); 104 REQUIRE(AppInstaller::Filesystem::SymlinkExists(symlinkPath)); 105 REQUIRE(AppInstaller::Filesystem::SymlinkExists(symlinkPath2)); 106 REQUIRE(std::filesystem::exists(directoryPath)); 107 108 PortableInstaller portableInstaller2 = PortableInstaller(ScopeEnum::User, Architecture::X64, "testProductCode"); 109 REQUIRE(portableInstaller2.ARPEntryExists()); 110 111 portableInstaller2.Uninstall(); 112 113 // Install root directory should be removed since it was created. 114 REQUIRE_FALSE(std::filesystem::exists(installRootPath)); 115 REQUIRE_FALSE(std::filesystem::exists(targetPath)); 116 REQUIRE_FALSE(std::filesystem::exists(targetPath2)); 117 REQUIRE_FALSE(AppInstaller::Filesystem::SymlinkExists(symlinkPath)); 118 REQUIRE_FALSE(AppInstaller::Filesystem::SymlinkExists(symlinkPath2)); 119 REQUIRE_FALSE(std::filesystem::exists(directoryPath)); 120 } 121 122 TEST_CASE("PortableInstaller_InstallToIndex_ExistingInstallRoot", "[PortableInstaller]") 123 { 124 TempDirectory installRootDirectory = TestCommon::TempDirectory("PortableInstallRoot", true); 125 126 std::vector<PortableFileEntry> desiredTestState; 127 128 TestCommon::TempFile testPortable("testPortable.txt"); 129 std::ofstream file1(testPortable, std::ofstream::out); 130 file1.close(); 131 132 TestCommon::TempFile testPortable2("testPortable2.txt"); 133 std::ofstream file2(testPortable2, std::ofstream::out); 134 file2.close(); 135 136 TestCommon::TempDirectory testDirectoryFolder("testDirectory", true); 137 138 std::filesystem::path installRootPath = installRootDirectory.GetPath(); 139 std::filesystem::path targetPath = installRootPath / "testPortable.txt"; 140 std::filesystem::path targetPath2 = installRootPath / "testPortable2.txt"; 141 std::filesystem::path symlinkPath = installRootPath / "testSymlink.exe"; 142 std::filesystem::path symlinkPath2 = installRootPath / "testSymlink2.exe"; 143 std::filesystem::path directoryPath = installRootPath / "testDirectory"; 144 145 desiredTestState.emplace_back(std::move(PortableFileEntry::CreateFileEntry(testPortable.GetPath(), targetPath, {}))); 146 desiredTestState.emplace_back(std::move(PortableFileEntry::CreateFileEntry(testPortable2.GetPath(), targetPath2, {}))); 147 desiredTestState.emplace_back(std::move(PortableFileEntry::CreateSymlinkEntry(symlinkPath, targetPath))); 148 desiredTestState.emplace_back(std::move(PortableFileEntry::CreateSymlinkEntry(symlinkPath2, targetPath2))); 149 desiredTestState.emplace_back(std::move(PortableFileEntry::CreateDirectoryEntry(testDirectoryFolder.GetPath(), directoryPath))); 150 151 PortableInstaller portableInstaller = PortableInstaller(ScopeEnum::User, Architecture::X64, "testProductCode"); 152 portableInstaller.TargetInstallLocation = installRootDirectory.GetPath(); 153 portableInstaller.RecordToIndex = true; 154 portableInstaller.SetDesiredState(desiredTestState); 155 REQUIRE(portableInstaller.VerifyExpectedState()); 156 157 portableInstaller.Install(AppInstaller::CLI::Workflow::OperationType::Install); 158 159 REQUIRE(std::filesystem::exists(installRootPath / portableInstaller.GetPortableIndexFileName())); 160 REQUIRE(std::filesystem::exists(targetPath)); 161 REQUIRE(std::filesystem::exists(targetPath2)); 162 REQUIRE(AppInstaller::Filesystem::SymlinkExists(symlinkPath)); 163 REQUIRE(AppInstaller::Filesystem::SymlinkExists(symlinkPath2)); 164 REQUIRE(std::filesystem::exists(directoryPath)); 165 166 PortableInstaller portableInstaller2 = PortableInstaller(ScopeEnum::User, Architecture::X64, "testProductCode"); 167 REQUIRE(portableInstaller2.ARPEntryExists()); 168 169 portableInstaller2.Uninstall(); 170 171 // Install root directory should still exist since it was created previously. 172 REQUIRE(std::filesystem::exists(installRootPath)); 173 REQUIRE_FALSE(std::filesystem::exists(targetPath)); 174 REQUIRE_FALSE(std::filesystem::exists(targetPath2)); 175 REQUIRE_FALSE(AppInstaller::Filesystem::SymlinkExists(symlinkPath)); 176 REQUIRE_FALSE(AppInstaller::Filesystem::SymlinkExists(symlinkPath2)); 177 REQUIRE_FALSE(std::filesystem::exists(directoryPath)); 178 } 179 180 TEST_CASE("PortableInstaller_UnicodeSymlinkPath", "[PortableInstaller]") 181 { 182 TempDirectory tempDirectory = TestCommon::TempDirectory("TempDirectory", false); 183 184 // Modify install location path to include unicode characters. 185 std::filesystem::path testInstallLocation = tempDirectory.GetPath() / std::filesystem::path{ ConvertToUTF16("романтический") }; 186 187 std::vector<PortableFileEntry> desiredTestState; 188 189 TestCommon::TempFile testPortable("testPortable.txt"); 190 std::ofstream file(testPortable, std::ofstream::out); 191 file.close(); 192 193 std::filesystem::path targetPath = testInstallLocation / "testPortable.txt"; 194 std::filesystem::path symlinkPath = tempDirectory.GetPath() / "testSymlink.exe"; 195 196 desiredTestState.emplace_back(std::move(PortableFileEntry::CreateFileEntry(testPortable.GetPath(), targetPath, {}))); 197 desiredTestState.emplace_back(std::move(PortableFileEntry::CreateSymlinkEntry(symlinkPath, targetPath))); 198 199 PortableInstaller portableInstaller = PortableInstaller(ScopeEnum::User, Architecture::X64, "testProductCode"); 200 portableInstaller.TargetInstallLocation = testInstallLocation; 201 portableInstaller.SetDesiredState(desiredTestState); 202 REQUIRE(portableInstaller.VerifyExpectedState()); 203 204 portableInstaller.Install(AppInstaller::CLI::Workflow::OperationType::Install); 205 206 PortableInstaller portableInstaller2 = PortableInstaller(ScopeEnum::User, Architecture::X64, "testProductCode"); 207 REQUIRE(portableInstaller2.ARPEntryExists()); 208 REQUIRE(std::filesystem::exists(portableInstaller2.PortableTargetFullPath)); 209 REQUIRE(AppInstaller::Filesystem::SymlinkExists(portableInstaller2.PortableSymlinkFullPath)); 210 211 portableInstaller2.Uninstall(); 212 REQUIRE_FALSE(std::filesystem::exists(portableInstaller2.PortableTargetFullPath)); 213 REQUIRE_FALSE(AppInstaller::Filesystem::SymlinkExists(portableInstaller2.PortableSymlinkFullPath)); 214 REQUIRE_FALSE(std::filesystem::exists(portableInstaller2.InstallLocation)); 215 }