forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsetup_singleton_unittest.cc
222 lines (180 loc) · 7.78 KB
/
setup_singleton_unittest.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
// Copyright 2016 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/installer/setup/setup_singleton.h"
#include <windows.h>
#include <functional>
#include <string>
#include <vector>
#include "base/command_line.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/multiprocess_test.h"
#include "base/test/test_timeouts.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "chrome/installer/setup/installer_state.h"
#include "chrome/installer/util/initial_preferences.h"
#include "chrome/installer/util/installation_state.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
namespace installer {
namespace {
constexpr char kInstallDirSwitch[] = "install-dir";
constexpr base::FilePath::CharType kSentinelFileName[] =
FILE_PATH_LITERAL("sentinel.txt");
constexpr wchar_t kTestProcessReadyEventName[] =
L"Local\\ChromeSetupSingletonTestProcessReady";
enum ErrorCode {
SUCCESS,
SETUP_SINGLETON_ACQUISITION_FAILED,
SENTINEL_FILE_CREATE_ERROR,
WAIT_RETURNED_FALSE,
};
base::CommandLine GetDummyCommandLine() {
return base::CommandLine(base::FilePath(FILE_PATH_LITERAL("dummy.exe")));
}
std::wstring HashFilePath(const base::FilePath& path) {
return base::NumberToWString(
std::hash<base::FilePath::StringType>()(path.value()));
}
ErrorCode CreateAndDeleteSentinelFile(const base::FilePath& install_dir) {
const base::FilePath sentinel_file_path =
install_dir.Append(kSentinelFileName);
base::File file(sentinel_file_path, base::File::FLAG_CREATE |
base::File::FLAG_WRITE |
base::File::FLAG_DELETE_ON_CLOSE);
if (!file.IsValid())
return SENTINEL_FILE_CREATE_ERROR;
base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
return SUCCESS;
}
MULTIPROCESS_TEST_MAIN(SetupSingletonTestExclusiveAccessProcessMain) {
base::CommandLine* const command_line =
base::CommandLine::ForCurrentProcess();
const base::FilePath install_dir =
command_line->GetSwitchValuePath(kInstallDirSwitch);
InstallationState original_state;
InstallerState installer_state;
installer_state.set_target_path_for_testing(install_dir);
// Acquire the exclusive right to modify the Chrome installation.
std::unique_ptr<SetupSingleton> setup_singleton(SetupSingleton::Acquire(
GetDummyCommandLine(), InitialPreferences::ForCurrentProcess(),
&original_state, &installer_state));
if (!setup_singleton)
return SETUP_SINGLETON_ACQUISITION_FAILED;
// Create a sentinel file and delete it after a few milliseconds. This will
// fail if the sentinel file already exists (which shouldn't be the case since
// we are in the scope of a SetupSingleton).
return CreateAndDeleteSentinelFile(install_dir);
}
MULTIPROCESS_TEST_MAIN(SetupSingletonTestWaitForInterruptProcessMain) {
base::CommandLine* const command_line =
base::CommandLine::ForCurrentProcess();
const base::FilePath install_dir =
command_line->GetSwitchValuePath(kInstallDirSwitch);
InstallationState original_state;
InstallerState installer_state;
installer_state.set_target_path_for_testing(install_dir);
// Acquire the exclusive right to modify the Chrome installation.
std::unique_ptr<SetupSingleton> setup_singleton(SetupSingleton::Acquire(
GetDummyCommandLine(), InitialPreferences::ForCurrentProcess(),
&original_state, &installer_state));
if (!setup_singleton)
return SETUP_SINGLETON_ACQUISITION_FAILED;
// Signal an event to indicate that this process has acquired the
// SetupSingleton.
base::WaitableEvent ready_event(base::win::ScopedHandle(::CreateEvent(
nullptr, FALSE, FALSE,
(kTestProcessReadyEventName + HashFilePath(install_dir)).c_str())));
ready_event.Signal();
// Wait indefinitely. This should only return when another SetupSingleton is
// instantiated for |install_dir|.
if (!setup_singleton->WaitForInterrupt(base::TimeDelta::Max()))
return WAIT_RETURNED_FALSE;
// Create a sentinel file and delete it after a few milliseconds. This will
// fail if the sentinel file already exists (which shouldn't be the case since
// we are in the scope of a SetupSingleton).
return CreateAndDeleteSentinelFile(install_dir);
}
class SetupSingletonTest : public base::MultiProcessTest {
public:
SetupSingletonTest() = default;
SetupSingletonTest(const SetupSingletonTest&) = delete;
SetupSingletonTest& operator=(const SetupSingletonTest&) = delete;
void SetUp() override { ASSERT_TRUE(install_dir_.CreateUniqueTempDir()); }
base::CommandLine MakeCmdLine(const std::string& procname) override {
base::CommandLine command_line =
base::MultiProcessTest::MakeCmdLine(procname);
command_line.AppendSwitchPath(kInstallDirSwitch, install_dir_path());
return command_line;
}
base::Process SpawnChildProcess(const std::string& process_name) {
base::LaunchOptions options;
options.start_hidden = true;
return SpawnChildWithOptions(process_name, options);
}
const base::FilePath& install_dir_path() const {
return install_dir_.GetPath();
}
private:
base::ScopedTempDir install_dir_;
};
} // namespace
// Verify that a single SetupSingleton can be active at a time for a given
// Chrome installation.
TEST_F(SetupSingletonTest, ExclusiveAccess) {
constexpr int kNumProcesses = 10;
std::vector<base::Process> processes;
for (int i = 0; i < kNumProcesses; ++i) {
processes.push_back(
SpawnChildProcess("SetupSingletonTestExclusiveAccessProcessMain"));
}
for (base::Process& process : processes) {
int exit_code = 0;
EXPECT_TRUE(process.WaitForExit(&exit_code));
EXPECT_EQ(SUCCESS, exit_code);
}
}
// Verify that WaitForInterrupt() returns false when its delay expires before
TEST_F(SetupSingletonTest, WaitForInterruptNoInterrupt) {
InstallationState original_state;
InstallerState installer_state;
installer_state.set_target_path_for_testing(install_dir_path());
std::unique_ptr<SetupSingleton> setup_singleton(SetupSingleton::Acquire(
GetDummyCommandLine(), InitialPreferences::ForCurrentProcess(),
&original_state, &installer_state));
ASSERT_TRUE(setup_singleton);
EXPECT_FALSE(setup_singleton->WaitForInterrupt(TestTimeouts::tiny_timeout()));
}
// Verify that WaitForInterrupt() returns true immediately when another process
// tries to acquire a SetupSingleton.
TEST_F(SetupSingletonTest, WaitForInterruptWithInterrupt) {
base::Process wait_process =
SpawnChildProcess("SetupSingletonTestWaitForInterruptProcessMain");
// Wait until the other process acquires the SetupSingleton.
base::WaitableEvent ready_event(base::win::ScopedHandle(::CreateEvent(
nullptr, FALSE, FALSE,
(kTestProcessReadyEventName + HashFilePath(install_dir_path()))
.c_str())));
ready_event.Wait();
// Acquire the SetupSingleton.
InstallationState original_state;
InstallerState installer_state;
installer_state.set_target_path_for_testing(install_dir_path());
std::unique_ptr<SetupSingleton> setup_singleton(SetupSingleton::Acquire(
GetDummyCommandLine(), InitialPreferences::ForCurrentProcess(),
&original_state, &installer_state));
ASSERT_TRUE(setup_singleton);
// Create a sentinel file and delete it after a few milliseconds. This will
// fail if the sentinel file already exists (which shouldn't be the case since
// we are in the scope of a SetupSingleton).
EXPECT_EQ(SUCCESS, CreateAndDeleteSentinelFile(install_dir_path()));
// Join |wait_process|.
int exit_code = 0;
EXPECT_TRUE(wait_process.WaitForExit(&exit_code));
EXPECT_EQ(SUCCESS, exit_code);
}
} // namespace installer