PromptFlow.cpp (7051B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "WorkflowCommon.h" 5 #include <Commands/InstallCommand.h> 6 #include <Workflows/PromptFlow.h> 7 8 using namespace TestCommon; 9 using namespace AppInstaller::CLI; 10 using namespace AppInstaller::Settings; 11 12 TEST_CASE("PromptFlow_InteractivityDisabled", "[PromptFlow][workflow]") 13 { 14 TestCommon::TempFile installResultPath("TestExeInstalled.txt"); 15 TestCommon::TestUserSettings testSettings; 16 17 std::ostringstream installOutput; 18 TestContext context{ installOutput, std::cin }; 19 auto previousThreadGlobals = context.SetForCurrentThread(); 20 context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_LicenseAgreement.yaml").GetPath().u8string()); 21 22 SECTION("Disabled by setting") 23 { 24 testSettings.Set<Setting::InteractivityDisable>(true); 25 } 26 SECTION("Disabled by arg") 27 { 28 context.Args.AddArg(Execution::Args::Type::DisableInteractivity); 29 } 30 31 InstallCommand install({}); 32 install.Execute(context); 33 INFO(installOutput.str()); 34 35 // Verify prompt is not shown 36 REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::PackageAgreementsPrompt).get()) == std::string::npos); 37 38 // Verify installation failed 39 REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_PACKAGE_AGREEMENTS_NOT_ACCEPTED); 40 REQUIRE_FALSE(std::filesystem::exists(installResultPath.GetPath())); 41 REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::PackageAgreementsNotAgreedTo).get()) != std::string::npos); 42 } 43 44 TEST_CASE("PromptFlow_InstallerAbortsTerminal_Proceed", "[PromptFlow][workflow]") 45 { 46 TestCommon::TempFile installResultPath("TestExeInstalled.txt"); 47 48 // Accept that the installer may abort the terminal by saying "Yes" at the prompt 49 std::istringstream installInput{ "y" }; 50 51 std::ostringstream installOutput; 52 TestContext context{ installOutput, installInput }; 53 auto previousThreadGlobals = context.SetForCurrentThread(); 54 OverrideForShellExecute(context); 55 context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_AbortsTerminal.yaml").GetPath().u8string()); 56 57 InstallCommand install({}); 58 install.Execute(context); 59 INFO(installOutput.str()); 60 61 // Verify prompt is shown 62 REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallerAbortsTerminal).get()) != std::string::npos); 63 64 // Verify Installer is called. 65 REQUIRE(std::filesystem::exists(installResultPath.GetPath())); 66 } 67 68 TEST_CASE("PromptFlow_InstallerAbortsTerminal_Cancel", "[PromptFlow][workflow]") 69 { 70 TestCommon::TempFile installResultPath("TestExeInstalled.txt"); 71 72 // Cancel the installation by saying "No" at the prompt that the installer may abort the terminal 73 std::istringstream installInput{ "n" }; 74 75 std::ostringstream installOutput; 76 TestContext context{ installOutput, installInput }; 77 auto previousThreadGlobals = context.SetForCurrentThread(); 78 context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_AbortsTerminal.yaml").GetPath().u8string()); 79 80 InstallCommand install({}); 81 install.Execute(context); 82 INFO(installOutput.str()); 83 84 // Verify prompt is shown 85 REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallerAbortsTerminal).get()) != std::string::npos); 86 87 // Verify installation failed 88 REQUIRE_TERMINATED_WITH(context, E_ABORT); 89 REQUIRE_FALSE(std::filesystem::exists(installResultPath.GetPath())); 90 } 91 92 TEST_CASE("PromptFlow_InstallLocationRequired", "[PromptFlow][workflow]") 93 { 94 TestCommon::TempDirectory installLocation("TempDirectory"); 95 TestCommon::TestUserSettings testSettings; 96 97 std::istringstream installInput; 98 std::ostringstream installOutput; 99 TestContext context{ installOutput, installInput }; 100 auto previousThreadGlobals = context.SetForCurrentThread(); 101 OverrideForShellExecute(context); 102 context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_InstallLocationRequired.yaml").GetPath().u8string()); 103 104 bool shouldShowPrompt = false; 105 std::filesystem::path installResultPath = installLocation.GetPath() / "TestExeInstalled.txt"; 106 SECTION("From argument") 107 { 108 context.Args.AddArg(Execution::Args::Type::InstallLocation, installLocation.GetPath().string()); 109 } 110 SECTION("From settings") 111 { 112 testSettings.Set<Setting::InstallDefaultRoot>(installLocation.GetPath().string()); 113 114 // When using the default location from settings, the Package ID is appended to the root 115 auto installLocationWithPackageId = installLocation.GetPath() / "AppInstallerCliTest.TestInstaller"; 116 std::filesystem::create_directory(installLocationWithPackageId); 117 installResultPath = installLocationWithPackageId / "TestExeInstalled.txt"; 118 } 119 SECTION("From prompt") 120 { 121 installInput.str(installLocation.GetPath().string()); 122 shouldShowPrompt = true; 123 } 124 125 InstallCommand install({}); 126 install.Execute(context); 127 INFO(installOutput.str()); 128 129 bool promptShown = installOutput.str().find(Resource::LocString(Resource::String::InstallerRequiresInstallLocation).get()) != std::string::npos; 130 REQUIRE(shouldShowPrompt == promptShown); 131 132 // Verify Installer is called with the right parameters 133 REQUIRE(std::filesystem::exists(installResultPath)); 134 std::ifstream installResultFile(installResultPath); 135 REQUIRE(installResultFile.is_open()); 136 std::string installResultStr; 137 std::getline(installResultFile, installResultStr); 138 const auto installDirArgument = "/InstallDir " + installLocation.GetPath().string(); 139 REQUIRE(installResultStr.find(installDirArgument) != std::string::npos); 140 } 141 142 TEST_CASE("PromptFlow_InstallLocationRequired_Missing", "[PromptFlow][workflow]") 143 { 144 TestCommon::TempFile installResultPath("TestExeInstalled.txt"); 145 146 std::ostringstream installOutput; 147 TestContext context{ installOutput, std::cin }; 148 auto previousThreadGlobals = context.SetForCurrentThread(); 149 context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_InstallLocationRequired.yaml").GetPath().u8string()); 150 // Disable interactivity so that there is not prompt and we cannot get the required location 151 context.Args.AddArg(Execution::Args::Type::DisableInteractivity); 152 153 InstallCommand install({}); 154 install.Execute(context); 155 INFO(installOutput.str()); 156 157 // Verify prompt is shown 158 REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallerRequiresInstallLocation).get()) != std::string::npos); 159 160 // Verify installation failed 161 REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_INSTALL_LOCATION_REQUIRED); 162 REQUIRE_FALSE(std::filesystem::exists(installResultPath.GetPath())); 163 }