blob: 01b3929a6e176d68a1b5c2374aa4ebc6462c1750 [file] [log] [blame]
Patrick Monettef8fd8b22018-05-10 20:58:041// Copyright 2017 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/conflicts/installed_applications_win.h"
6
7#include <map>
8
9#include "base/macros.h"
10#include "base/strings/stringprintf.h"
11#include "base/test/test_reg_util_win.h"
12#include "base/win/registry.h"
13#include "chrome/browser/conflicts/msi_util_win.h"
14#include "testing/gtest/include/gtest/gtest.h"
15
16namespace {
17
18static const wchar_t kRegistryKeyPathFormat[] =
19 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\%ls";
20
21struct CommonInfo {
22 base::string16 product_id;
23 bool is_system_component;
24 bool is_microsoft_published;
25 base::string16 display_name;
26 base::string16 uninstall_string;
27};
28
29struct InstallLocationApplicationInfo {
30 CommonInfo common_info;
31 base::string16 install_location;
32};
33
34struct MsiApplicationInfo {
35 CommonInfo common_info;
36 std::vector<base::string16> components;
37};
38
39class MockMsiUtil : public MsiUtil {
40 public:
41 MockMsiUtil(const std::map<base::string16, std::vector<base::string16>>&
42 component_paths_map)
43 : component_paths_map_(component_paths_map) {}
44
45 bool GetMsiComponentPaths(
46 const base::string16& product_guid,
47 std::vector<base::string16>* component_paths) const override {
48 auto iter = component_paths_map_.find(product_guid);
49 if (iter == component_paths_map_.end())
50 return false;
51
52 *component_paths = iter->second;
53 return true;
54 }
55
56 private:
57 const std::map<base::string16, std::vector<base::string16>>&
58 component_paths_map_;
59};
60
61class TestInstalledApplications : public InstalledApplications {
62 public:
63 explicit TestInstalledApplications(std::unique_ptr<MsiUtil> msi_util)
64 : InstalledApplications(std::move(msi_util)) {}
65};
66
67class InstalledApplicationsTest : public testing::Test {
68 public:
69 InstalledApplicationsTest() = default;
70 ~InstalledApplicationsTest() override = default;
71
72 // ASSERT_NO_FATAL_FAILURE cannot be used in a constructor so the registry
73 // hive overrides are done here.
74 void SetUp() override {
75 ASSERT_NO_FATAL_FAILURE(
76 registry_override_manager_.OverrideRegistry(HKEY_LOCAL_MACHINE));
77 ASSERT_NO_FATAL_FAILURE(
78 registry_override_manager_.OverrideRegistry(HKEY_CURRENT_USER));
79 }
80
81 void AddCommonInfo(const CommonInfo& common_info,
82 base::win::RegKey* registry_key) {
83 registry_key->WriteValue(L"SystemComponent",
84 common_info.is_system_component ? 1 : 0);
85 registry_key->WriteValue(L"UninstallString",
86 common_info.uninstall_string.c_str());
87 if (common_info.is_microsoft_published)
88 registry_key->WriteValue(L"Publisher", L"Microsoft Corporation");
89 registry_key->WriteValue(L"DisplayName", common_info.display_name.c_str());
90 }
91
92 void AddFakeApplication(const MsiApplicationInfo& application_info) {
93 const base::string16 registry_key_path =
94 base::StringPrintf(kRegistryKeyPathFormat,
95 application_info.common_info.product_id.c_str());
96 base::win::RegKey registry_key(HKEY_CURRENT_USER, registry_key_path.c_str(),
97 KEY_WRITE);
98
99 AddCommonInfo(application_info.common_info, &registry_key);
100
101 component_paths_map_.insert(
102 {application_info.common_info.product_id, application_info.components});
103 }
104
105 void AddFakeApplication(
106 const InstallLocationApplicationInfo& application_info) {
107 const base::string16 registry_key_path =
108 base::StringPrintf(kRegistryKeyPathFormat,
109 application_info.common_info.product_id.c_str());
110 base::win::RegKey registry_key(HKEY_CURRENT_USER, registry_key_path.c_str(),
111 KEY_WRITE);
112
113 AddCommonInfo(application_info.common_info, &registry_key);
114
115 registry_key.WriteValue(L"InstallLocation",
116 application_info.install_location.c_str());
117 }
118
119 TestInstalledApplications& installed_applications() {
120 return *installed_applications_;
121 }
122
123 void InitializeInstalledApplications() {
124 installed_applications_ = std::make_unique<TestInstalledApplications>(
125 std::make_unique<MockMsiUtil>(component_paths_map_));
126 }
127
128 private:
129 registry_util::RegistryOverrideManager registry_override_manager_;
130
131 std::unique_ptr<TestInstalledApplications> installed_applications_;
132
133 std::map<base::string16, std::vector<base::string16>> component_paths_map_;
134
135 DISALLOW_COPY_AND_ASSIGN(InstalledApplicationsTest);
136};
137
138} // namespace
139
140// Checks that registry entries with invalid information are skipped.
141TEST_F(InstalledApplicationsTest, InvalidEntries) {
142 const wchar_t kValidDisplayName[] = L"ADisplayName";
143 const wchar_t kValidUninstallString[] = L"c:\\an\\UninstallString.exe";
144 const wchar_t kInstallLocation[] = L"c:\\application files\\application\\";
145
146 InstallLocationApplicationInfo kTestCases[] = {
147 {
148 {
149 L"Is SystemComponent", true, false, kValidDisplayName,
150 kValidUninstallString,
151 },
152 kInstallLocation,
153 },
154 {
155 {
156 L"Is Microsoft published", false, true, kValidDisplayName,
157 kValidUninstallString,
158 },
159 kInstallLocation,
160 },
161 {
162 {
163 L"Missing DisplayName", false, false, L"", kValidUninstallString,
164 },
165 kInstallLocation,
166 },
167 {
168 {
169 L"Missing UninstallString", false, false, kValidDisplayName, L"",
170 },
171 kInstallLocation,
172 },
173 };
174
175 for (const auto& test_case : kTestCases)
176 AddFakeApplication(test_case);
177
178 InitializeInstalledApplications();
179
180 // None of the invalid entries were picked up.
181 const base::FilePath valid_child_file =
182 base::FilePath(kInstallLocation).Append(L"file.dll");
183 std::vector<InstalledApplications::ApplicationInfo> applications;
184 EXPECT_FALSE(installed_applications().GetInstalledApplications(
185 valid_child_file, &applications));
186}
187
188// Tests InstalledApplications on a valid entry with an InstallLocation.
189TEST_F(InstalledApplicationsTest, InstallLocation) {
190 const wchar_t kValidDisplayName[] = L"ADisplayName";
191 const wchar_t kValidUninstallString[] = L"c:\\an\\UninstallString.exe";
192 const wchar_t kInstallLocation[] = L"c:\\application files\\application\\";
193
194 InstallLocationApplicationInfo kTestCase = {
195 {
196 L"Completely valid", false, false, kValidDisplayName,
197 kValidUninstallString,
198 },
199 kInstallLocation,
200 };
201
202 AddFakeApplication(kTestCase);
203
204 InitializeInstalledApplications();
205
206 // Child file path.
207 const base::FilePath valid_child_file =
208 base::FilePath(kInstallLocation).Append(L"file.dll");
209 std::vector<InstalledApplications::ApplicationInfo> applications;
210 EXPECT_TRUE(installed_applications().GetInstalledApplications(
211 valid_child_file, &applications));
212 ASSERT_EQ(1u, applications.size());
213 EXPECT_EQ(kTestCase.common_info.display_name, applications[0].name);
214 EXPECT_EQ(HKEY_CURRENT_USER, applications[0].registry_root);
215 EXPECT_FALSE(applications[0].registry_key_path.empty());
216 EXPECT_EQ(0u, applications[0].registry_wow64_access);
217
218 // Non-child file path.
219 const base::FilePath invalid_child_file(
220 L"c:\\application files\\another application\\test.dll");
221 EXPECT_FALSE(installed_applications().GetInstalledApplications(
222 invalid_child_file, &applications));
223}
224
225// Tests InstalledApplications on a valid MSI entry.
226TEST_F(InstalledApplicationsTest, Msi) {
227 const wchar_t kValidDisplayName[] = L"ADisplayName";
228 const wchar_t kValidUninstallString[] = L"c:\\an\\UninstallString.exe";
229
230 MsiApplicationInfo kTestCase = {
231 {
232 L"Completely valid", false, false, kValidDisplayName,
233 kValidUninstallString,
234 },
235 {
236 L"c:\\application files\\application\\file1.dll",
237 L"c:\\application files\\application\\file2.dll",
238 L"c:\\application files\\application\\sub\\file3.dll",
239 L"c:\\windows\\system32\\file4.dll",
240 },
241 };
242
243 AddFakeApplication(kTestCase);
244
245 InitializeInstalledApplications();
246
247 // Checks that all the files match the application.
248 for (const auto& component : kTestCase.components) {
249 std::vector<InstalledApplications::ApplicationInfo> applications;
250 EXPECT_TRUE(installed_applications().GetInstalledApplications(
251 base::FilePath(component), &applications));
252 ASSERT_EQ(1u, applications.size());
253 EXPECT_EQ(kTestCase.common_info.display_name, applications[0].name);
254 EXPECT_EQ(HKEY_CURRENT_USER, applications[0].registry_root);
255 EXPECT_FALSE(applications[0].registry_key_path.empty());
256 EXPECT_EQ(0u, applications[0].registry_wow64_access);
257 }
258
259 // Any other file shouldn't work.
260 const base::FilePath invalid_child_file(
261 L"c:\\application files\\another application\\test.dll");
262 std::vector<InstalledApplications::ApplicationInfo> applications;
263 EXPECT_FALSE(installed_applications().GetInstalledApplications(
264 invalid_child_file, &applications));
265}
266
267// Checks that if a file matches an InstallLocation and an MSI component, only
268// the MSI application will be considered.
269TEST_F(InstalledApplicationsTest, PrioritizeMsi) {
270 const wchar_t kValidUninstallString[] = L"c:\\an\\UninstallString.exe";
271 const wchar_t kInstallLocationDisplayName[] = L"InstallLocation DisplayName";
272 const wchar_t kMsiDisplayName[] = L"Msi DisplayName";
273 const wchar_t kInstallLocation[] = L"c:\\application files\\application\\";
274 const wchar_t kMsiComponent[] =
275 L"c:\\application files\\application\\file.dll";
276
277 InstallLocationApplicationInfo kInstallLocationFakeApplication = {
278 {
279 L"GUID1", false, false, kInstallLocationDisplayName,
280 kValidUninstallString,
281 },
282 kInstallLocation,
283 };
284
285 MsiApplicationInfo kMsiFakeApplication = {
286 {
287 L"GUID2", false, false, kMsiDisplayName, kValidUninstallString,
288 },
289 {
290 kMsiComponent,
291 },
292 };
293
294 AddFakeApplication(kInstallLocationFakeApplication);
295 AddFakeApplication(kMsiFakeApplication);
296
297 InitializeInstalledApplications();
298
299 std::vector<InstalledApplications::ApplicationInfo> applications;
300 EXPECT_TRUE(installed_applications().GetInstalledApplications(
301 base::FilePath(kMsiComponent), &applications));
302 ASSERT_EQ(1u, applications.size());
303 EXPECT_NE(kInstallLocationDisplayName, applications[0].name);
304 EXPECT_EQ(kMsiDisplayName, applications[0].name);
305}
306
307// Tests that if 2 entries with conflicting InstallLocation exist, both are
308// ignored.
309TEST_F(InstalledApplicationsTest, ConflictingInstallLocations) {
310 const wchar_t kValidUninstallString[] = L"c:\\an\\UninstallString.exe";
311 const wchar_t kDisplayName1[] = L"DisplayName1";
312 const wchar_t kDisplayName2[] = L"DisplayName2";
313 const wchar_t kInstallLocationParent[] = L"c:\\application files\\company\\";
314 const wchar_t kInstallLocationChild[] =
315 L"c:\\application files\\company\\application";
316 const wchar_t kFile[] =
317 L"c:\\application files\\company\\application\\file.dll";
318
319 InstallLocationApplicationInfo kFakeApplication1 = {
320 {
321 L"GUID1", false, false, kDisplayName1, kValidUninstallString,
322 },
323 kInstallLocationParent,
324 };
325 InstallLocationApplicationInfo kFakeApplication2 = {
326 {
327 L"GUID2", false, false, kDisplayName2, kValidUninstallString,
328 },
329 kInstallLocationChild,
330 };
331
332 AddFakeApplication(kFakeApplication1);
333 AddFakeApplication(kFakeApplication2);
334
335 InitializeInstalledApplications();
336
337 std::vector<InstalledApplications::ApplicationInfo> applications;
338 EXPECT_FALSE(installed_applications().GetInstalledApplications(
339 base::FilePath(kFile), &applications));
340}