// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/updater/app/server/win/com_classes_legacy.h"
#include <windows.h>
#include <shellapi.h>
#include <wrl/client.h>
#include <string>
#include <vector>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/strings/strcat.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/win/scoped_variant.h"
#include "base/win/win_util.h"
#include "build/branding_buildflags.h"
#include "chrome/updater/test/integration_tests_impl.h"
#include "chrome/updater/test/test_scope.h"
#include "chrome/updater/test/unit_test_util.h"
#include "chrome/updater/test/unit_test_util_win.h"
#include "chrome/updater/util/util.h"
#include "chrome/updater/util/win_util.h"
#include "chrome/updater/win/setup/setup_util.h"
#include "chrome/updater/win/test/test_executables.h"
#include "chrome/updater/win/test/test_strings.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace updater::test {
namespace {
constexpr wchar_t kAppId1[] = L"{3B1A3CCA-0525-4418-93E6-A0DB3398EC9B}";
constexpr wchar_t kBadCmdLine[] = L"\"c:\\Program Files (x86)\\cmd.exe\"";
constexpr wchar_t kCmdLineValid[] =
L"\"C:\\Program Files\\Windows Media Player\\wmpnscfg.exe\" /Close";
constexpr wchar_t kCmdId1[] = L"command 1";
constexpr wchar_t kCmdId2[] = L"command 2";
} // namespace
class LegacyAppCommandWebImplTest : public testing::Test {
protected:
LegacyAppCommandWebImplTest()
: cmd_exe_command_line_(base::CommandLine::NO_PROGRAM) {}
~LegacyAppCommandWebImplTest() override = default;
void SetUp() override {
SetupCmdExe(GetUpdaterScopeForTesting(), cmd_exe_command_line_,
temp_programfiles_dir_);
}
void TearDown() override {
DeleteAppClientKey(GetUpdaterScopeForTesting(), kAppId1);
}
[[nodiscard]] HRESULT CreateAppCommandWeb(
const std::wstring& app_id,
const std::wstring& command_id,
const std::wstring& command_line_format,
LegacyAppCommandWebImpl::PingSender ping_sender,
Microsoft::WRL::ComPtr<LegacyAppCommandWebImpl>& app_command_web) {
CreateAppCommandRegistry(GetUpdaterScopeForTesting(), app_id, command_id,
command_line_format);
return MakeAndInitializeComObject<LegacyAppCommandWebImpl>(
app_command_web, GetUpdaterScopeForTesting(), app_id, command_id,
ping_sender);
}
void WaitForUpdateCompletion(
Microsoft::WRL::ComPtr<LegacyAppCommandWebImpl>& app_command_web) {
EXPECT_TRUE(test::WaitFor([&] {
UINT status = 0;
EXPECT_HRESULT_SUCCEEDED(app_command_web->get_status(&status));
return status == COMMAND_STATUS_COMPLETE;
}));
}
base::test::TaskEnvironment environment_;
base::CommandLine cmd_exe_command_line_;
base::ScopedTempDir temp_programfiles_dir_;
};
TEST_F(LegacyAppCommandWebImplTest, NoApp) {
Microsoft::WRL::ComPtr<LegacyAppCommandWebImpl> app_command_web;
EXPECT_HRESULT_FAILED(MakeAndInitializeComObject<LegacyAppCommandWebImpl>(
app_command_web, GetUpdaterScopeForTesting(), kAppId1, kCmdId1));
}
TEST_F(LegacyAppCommandWebImplTest, NoCmd) {
Microsoft::WRL::ComPtr<LegacyAppCommandWebImpl> app_command_web;
CreateAppCommandRegistry(GetUpdaterScopeForTesting(), kAppId1, kCmdId1,
kCmdLineValid);
EXPECT_HRESULT_FAILED(MakeAndInitializeComObject<LegacyAppCommandWebImpl>(
app_command_web, GetUpdaterScopeForTesting(), kAppId1, kCmdId2));
}
TEST_F(LegacyAppCommandWebImplTest, Execute) {
bool ping_sent = false;
Microsoft::WRL::ComPtr<LegacyAppCommandWebImpl> app_command_web;
ASSERT_HRESULT_SUCCEEDED(CreateAppCommandWeb(
kAppId1, kCmdId1,
base::StrCat(
{cmd_exe_command_line_.GetCommandLineString(), L" /c \"exit 7\""}),
base::BindLambdaForTesting(
[&ping_sent](UpdaterScope scope, const std::string& app_id,
const std::string& command_id,
LegacyAppCommandWebImpl::ErrorParams error_params) {
ping_sent = true;
EXPECT_EQ(GetUpdaterScopeForTesting(), scope);
EXPECT_EQ(app_id, base::WideToASCII(kAppId1));
EXPECT_EQ(command_id, base::WideToASCII(kCmdId1));
EXPECT_EQ(error_params.error_code, 7);
EXPECT_EQ(error_params.extra_code1, 0);
}),
app_command_web));
UINT status = 0;
EXPECT_HRESULT_SUCCEEDED(app_command_web->get_status(&status));
EXPECT_EQ(status, COMMAND_STATUS_INIT);
DWORD exit_code = 0;
EXPECT_EQ(app_command_web->get_exitCode(&exit_code), S_FALSE);
ASSERT_HRESULT_SUCCEEDED(
app_command_web->execute(base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant));
WaitForUpdateCompletion(app_command_web);
EXPECT_HRESULT_SUCCEEDED(app_command_web->get_status(&status));
EXPECT_EQ(status, COMMAND_STATUS_COMPLETE);
EXPECT_HRESULT_SUCCEEDED(app_command_web->get_exitCode(&exit_code));
EXPECT_EQ(exit_code, 7U);
EXPECT_TRUE(ping_sent);
}
TEST_F(LegacyAppCommandWebImplTest, ExecuteParameterizedCommand) {
bool ping_sent = false;
Microsoft::WRL::ComPtr<LegacyAppCommandWebImpl> app_command_web;
ASSERT_HRESULT_SUCCEEDED(CreateAppCommandWeb(
kAppId1, kCmdId1,
base::StrCat(
{cmd_exe_command_line_.GetCommandLineString(), L" /c \"exit %1\""}),
base::BindLambdaForTesting(
[&ping_sent](UpdaterScope scope, const std::string& app_id,
const std::string& command_id,
LegacyAppCommandWebImpl::ErrorParams error_params) {
ping_sent = true;
EXPECT_EQ(GetUpdaterScopeForTesting(), scope);
EXPECT_EQ(app_id, base::WideToASCII(kAppId1));
EXPECT_EQ(command_id, base::WideToASCII(kCmdId1));
EXPECT_EQ(error_params.error_code, 5420);
EXPECT_EQ(error_params.extra_code1, 0);
}),
app_command_web));
ASSERT_HRESULT_SUCCEEDED(
app_command_web->execute(base::win::ScopedVariant(L"5420"),
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant));
WaitForUpdateCompletion(app_command_web);
DWORD exit_code = 0;
EXPECT_HRESULT_SUCCEEDED(app_command_web->get_exitCode(&exit_code));
EXPECT_EQ(exit_code, 5420U);
EXPECT_TRUE(ping_sent);
}
TEST_F(LegacyAppCommandWebImplTest, FailedToLaunchStatus) {
bool ping_sent = false;
Microsoft::WRL::ComPtr<LegacyAppCommandWebImpl> app_command_web;
ASSERT_HRESULT_SUCCEEDED(CreateAppCommandWeb(
kAppId1, kCmdId1, kBadCmdLine,
base::BindLambdaForTesting(
[&ping_sent](UpdaterScope scope, const std::string& app_id,
const std::string& command_id,
LegacyAppCommandWebImpl::ErrorParams error_params) {
ping_sent = true;
EXPECT_EQ(GetUpdaterScopeForTesting(), scope);
EXPECT_EQ(app_id, base::WideToASCII(kAppId1));
EXPECT_EQ(command_id, base::WideToASCII(kCmdId1));
EXPECT_EQ(error_params.error_code,
HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
EXPECT_EQ(error_params.extra_code1, kErrorAppCommandLaunchFailed);
}),
app_command_web));
EXPECT_HRESULT_FAILED(
app_command_web->execute(base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant));
DWORD exit_code = 0;
EXPECT_EQ(app_command_web->get_exitCode(&exit_code), S_FALSE);
EXPECT_TRUE(ping_sent);
}
TEST_F(LegacyAppCommandWebImplTest, CommandRunningStatus) {
if (IsSystemInstall(GetUpdaterScopeForTesting())) {
return;
}
bool ping_sent = false;
Microsoft::WRL::ComPtr<LegacyAppCommandWebImpl> app_command_web;
base::CommandLine command_line = GetTestProcessCommandLine(
GetUpdaterScopeForTesting(), test::GetTestName());
command_line.AppendSwitchNative(kTestEventToWaitOn, L"%1");
command_line.AppendSwitchNative(kTestExitCode, L"%2");
ASSERT_HRESULT_SUCCEEDED(CreateAppCommandWeb(
kAppId1, kCmdId1,
command_line.GetCommandLineStringWithUnsafeInsertSequences(),
base::BindLambdaForTesting(
[&ping_sent](UpdaterScope scope, const std::string& app_id,
const std::string& command_id,
LegacyAppCommandWebImpl::ErrorParams error_params) {
ping_sent = true;
EXPECT_EQ(GetUpdaterScopeForTesting(), scope);
EXPECT_EQ(app_id, base::WideToASCII(kAppId1));
EXPECT_EQ(command_id, base::WideToASCII(kCmdId1));
EXPECT_EQ(error_params.error_code, 999);
EXPECT_EQ(error_params.extra_code1, 0);
}),
app_command_web));
test::EventHolder event_holder(test::CreateWaitableEventForTest());
ASSERT_HRESULT_SUCCEEDED(app_command_web->execute(
base::win::ScopedVariant(event_holder.name.c_str()),
base::win::ScopedVariant(L"999"), base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant,
base::win::ScopedVariant::kEmptyVariant));
UINT status = 0;
EXPECT_HRESULT_SUCCEEDED(app_command_web->get_status(&status));
EXPECT_EQ(status, COMMAND_STATUS_RUNNING);
event_holder.event.Signal();
WaitForUpdateCompletion(app_command_web);
DWORD exit_code = 0;
EXPECT_HRESULT_SUCCEEDED(app_command_web->get_exitCode(&exit_code));
EXPECT_EQ(exit_code, 999U);
EXPECT_TRUE(ping_sent);
}
TEST_F(LegacyAppCommandWebImplTest, CheckLegacyTypeLibAndInterfaceExist) {
base::FilePath typelib_path;
ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &typelib_path));
Microsoft::WRL::ComPtr<ITypeLib> type_lib;
ASSERT_HRESULT_SUCCEEDED(::LoadTypeLib(
typelib_path.Append(GetExecutableRelativePath())
.Append(GetComTypeLibResourceIndex(__uuidof(IAppCommandWeb)))
.value()
.c_str(),
&type_lib));
Microsoft::WRL::ComPtr<ITypeInfo> type_info;
EXPECT_HRESULT_SUCCEEDED(
type_lib->GetTypeInfoOfGuid(__uuidof(IAppCommandWeb), &type_info))
<< " Could not load type info for legacy interface IAppCommandWeb, "
"IID_IAppCommand: "
<< StringFromGuid(__uuidof(IAppCommandWeb));
}
TEST(LegacyCOMClassesTest, CheckLegacyInterfaceIDs) {
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
EXPECT_EQ(StringFromGuid(__uuidof(GoogleUpdate3WebUserClass)),
L"{22181302-A8A6-4F84-A541-E5CBFC70CC43}");
EXPECT_EQ(StringFromGuid(__uuidof(GoogleUpdate3WebSystemClass)),
L"{8A1D4361-2C08-4700-A351-3EAA9CBFF5E4}");
EXPECT_EQ(StringFromGuid(__uuidof(GoogleUpdate3WebServiceClass)),
L"{534F5323-3569-4F42-919D-1E1CF93E5BF6}");
EXPECT_EQ(StringFromGuid(__uuidof(PolicyStatusUserClass)),
L"{6DDCE70D-A4AE-4E97-908C-BE7B2DB750AD}");
EXPECT_EQ(StringFromGuid(__uuidof(PolicyStatusSystemClass)),
L"{521FDB42-7130-4806-822A-FC5163FAD983}");
EXPECT_EQ(StringFromGuid(__uuidof(ProcessLauncherClass)),
L"{ABC01078-F197-4B0B-ADBC-CFE684B39C82}");
EXPECT_EQ(StringFromGuid(__uuidof(IAppVersionWeb)),
L"{0CD01D1E-4A1C-489D-93B9-9B6672877C57}");
EXPECT_EQ(StringFromGuid(__uuidof(ICurrentState)),
L"{247954F9-9EDC-4E68-8CC3-150C2B89EADF}");
EXPECT_EQ(StringFromGuid(__uuidof(IGoogleUpdate3Web)),
L"{494B20CF-282E-4BDD-9F5D-B70CB09D351E}");
EXPECT_EQ(StringFromGuid(__uuidof(IAppBundleWeb)),
L"{DD42475D-6D46-496A-924E-BD5630B4CBBA}");
EXPECT_EQ(StringFromGuid(__uuidof(IAppWeb)),
L"{18D0F672-18B4-48E6-AD36-6E6BF01DBBC4}");
EXPECT_EQ(StringFromGuid(__uuidof(IAppCommandWeb)),
L"{8476CE12-AE1F-4198-805C-BA0F9B783F57}");
EXPECT_EQ(StringFromGuid(__uuidof(IPolicyStatus)),
L"{F63F6F8B-ACD5-413C-A44B-0409136D26CB}");
EXPECT_EQ(StringFromGuid(__uuidof(IPolicyStatus2)),
L"{34527502-D3DB-4205-A69B-789B27EE0414}");
EXPECT_EQ(StringFromGuid(__uuidof(IPolicyStatus3)),
L"{05A30352-EB25-45B6-8449-BCA7B0542CE5}");
EXPECT_EQ(StringFromGuid(__uuidof(IPolicyStatus4)),
L"{FD0FDA43-AF97-4F1C-BD68-3355FB4F1C92}");
EXPECT_EQ(StringFromGuid(__uuidof(IPolicyStatusValue)),
L"{27634814-8E41-4C35-8577-980134A96544}");
EXPECT_EQ(StringFromGuid(__uuidof(IProcessLauncher)),
L"{128C2DA6-2BC0-44C0-B3F6-4EC22E647964}");
EXPECT_EQ(StringFromGuid(__uuidof(IProcessLauncher2)),
L"{D106AB5F-A70E-400E-A21B-96208C1D8DBB}");
#else
EXPECT_EQ(StringFromGuid(__uuidof(GoogleUpdate3WebUserClass)),
L"{75828ED1-7BE8-45D0-8950-AA85CBF74510}");
EXPECT_EQ(StringFromGuid(__uuidof(GoogleUpdate3WebSystemClass)),
L"{283209B7-C761-41CA-BE8D-B5321CD78FD6}");
EXPECT_EQ(StringFromGuid(__uuidof(GoogleUpdate3WebServiceClass)),
L"{B52C8B56-9541-4B78-9B2F-665366B78A9C}");
EXPECT_EQ(StringFromGuid(__uuidof(PolicyStatusUserClass)),
L"{4DAC24AB-B340-4B7E-AD01-1504A7F59EEA}");
EXPECT_EQ(StringFromGuid(__uuidof(PolicyStatusSystemClass)),
L"{83FE19AC-72A6-4A72-B136-724444121586}");
EXPECT_EQ(StringFromGuid(__uuidof(ProcessLauncherClass)),
L"{811A664F-703E-407C-A323-E6E31D1EFFA0}");
EXPECT_EQ(StringFromGuid(__uuidof(IAppVersionWeb)),
L"{3057E1F8-2498-4C19-99B5-F7F207DA4DC7}");
EXPECT_EQ(StringFromGuid(__uuidof(ICurrentState)),
L"{BE5D3E90-A66C-4A0A-9B7B-1A6B9BF3971E}");
EXPECT_EQ(StringFromGuid(__uuidof(IGoogleUpdate3Web)),
L"{027234BD-61BB-4F5C-9386-7FE804171C8C}");
EXPECT_EQ(StringFromGuid(__uuidof(IAppBundleWeb)),
L"{D734C877-21F4-496E-B857-3E5B2E72E4CC}");
EXPECT_EQ(StringFromGuid(__uuidof(IAppWeb)),
L"{2C6218B9-088D-4D25-A4F8-570558124142}");
EXPECT_EQ(StringFromGuid(__uuidof(IAppCommandWeb)),
L"{87DBF75E-F590-4802-93FD-F8D07800E2E9}");
EXPECT_EQ(StringFromGuid(__uuidof(IPolicyStatus)),
L"{7D908375-C9D0-44C5-BB98-206F3C24A74C}");
EXPECT_EQ(StringFromGuid(__uuidof(IPolicyStatus2)),
L"{9D31EA63-2E06-4D41-98C7-CB1F307DB597}");
EXPECT_EQ(StringFromGuid(__uuidof(IPolicyStatus3)),
L"{5C674FC1-80E3-48D2-987B-79D9D286065B}");
EXPECT_EQ(StringFromGuid(__uuidof(IPolicyStatus4)),
L"{4F08E832-C4AF-4D77-840F-8884083E8324}");
EXPECT_EQ(StringFromGuid(__uuidof(IPolicyStatusValue)),
L"{47C8886A-A4B5-4F6C-865A-41A207074DFA}");
EXPECT_EQ(StringFromGuid(__uuidof(IProcessLauncher)),
L"{EED70106-3604-4385-866E-6D540E99CA1A}");
EXPECT_EQ(StringFromGuid(__uuidof(IProcessLauncher2)),
L"{BAEE6326-C925-4FA4-AFE9-5FA69902B021}");
#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)
}
} // namespace updater::test