blob: f9953f9a63c40ee5d036365c986c80dd3855f617 [file] [log] [blame]
Mike Frysinger1dad0972019-02-23 18:36:37 -05001// Copyright 2019 The Chromium OS 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 "dev-install/dev_install.h"
6
Mike Frysinger60260f62019-02-24 02:28:23 -05007#include <unistd.h>
8
9#include <istream>
10#include <sstream>
11#include <string>
12
Mike Frysingerb9c9f6c2019-02-24 02:32:32 -050013#include <base/files/file_path.h>
14#include <base/files/file_util.h>
15#include <base/files/scoped_temp_dir.h>
Mike Frysinger1dad0972019-02-23 18:36:37 -050016#include <gmock/gmock.h>
17#include <gtest/gtest.h>
18
19using ::testing::_;
20using ::testing::Return;
21
22namespace dev_install {
23
24namespace {
25
26class DevInstallMock : public DevInstall {
27 public:
Ben Chan1ea2ba12019-09-19 11:58:03 -070028 MOCK_METHOD(int, Exec, (const std::vector<const char*>&), (override));
29 MOCK_METHOD(bool, IsDevMode, (), (const, override));
30 MOCK_METHOD(bool,
31 PromptUser,
32 (std::istream&, const std::string&),
33 (override));
Mike Frysinger69c167f2019-02-24 05:14:57 -050034 MOCK_METHOD(bool, ClearStateDir, (const base::FilePath&), (override));
Mike Frysinger3d44f782019-02-25 23:17:08 -050035 MOCK_METHOD(bool,
36 InitializeStateDir,
37 (const base::FilePath& dir),
38 (override));
Mike Frysinger1dad0972019-02-23 18:36:37 -050039};
40
41class DevInstallTest : public ::testing::Test {
Mike Frysingeree5af6e2019-02-23 23:47:03 -050042 public:
43 void SetUp() override {
44 // Set the default to dev mode enabled. Most tests want that.
45 ON_CALL(dev_install_, IsDevMode()).WillByDefault(Return(true));
Mike Frysinger69c167f2019-02-24 05:14:57 -050046
Mike Frysinger3d44f782019-02-25 23:17:08 -050047 // Ignore stateful setup for most tests.
48 ON_CALL(dev_install_, InitializeStateDir(_)).WillByDefault(Return(true));
49
Mike Frysinger69c167f2019-02-24 05:14:57 -050050 // Most tests should run with a path that doesn't exist.
51 dev_install_.SetStateDirForTest(base::FilePath("/.path-does-not-exist"));
Mike Frysingeree5af6e2019-02-23 23:47:03 -050052 }
53
Mike Frysinger1dad0972019-02-23 18:36:37 -050054 protected:
55 DevInstallMock dev_install_;
56};
57
58} // namespace
59
60// Check default run through.
61TEST_F(DevInstallTest, Run) {
62 EXPECT_CALL(dev_install_, Exec(_)).WillOnce(Return(1234));
63 EXPECT_EQ(1234, dev_install_.Run());
64}
65
Mike Frysingeree5af6e2019-02-23 23:47:03 -050066// Systems not in dev mode should abort.
67TEST_F(DevInstallTest, NonDevMode) {
68 EXPECT_CALL(dev_install_, IsDevMode()).WillOnce(Return(false));
Mike Frysinger69c167f2019-02-24 05:14:57 -050069 EXPECT_CALL(dev_install_, ClearStateDir(_)).Times(0);
Mike Frysingeree5af6e2019-02-23 23:47:03 -050070 EXPECT_CALL(dev_install_, Exec(_)).Times(0);
71 EXPECT_EQ(2, dev_install_.Run());
72}
73
Mike Frysinger69c167f2019-02-24 05:14:57 -050074// Check system has been initialized.
75TEST_F(DevInstallTest, AlreadyInitialized) {
76 dev_install_.SetStateDirForTest(base::FilePath("/"));
77 EXPECT_CALL(dev_install_, Exec(_)).Times(0);
78 ASSERT_EQ(4, dev_install_.Run());
79}
80
81// Check --reinstall passed.
82TEST_F(DevInstallTest, RunReinstallWorked) {
83 dev_install_.SetReinstallForTest(true);
84 EXPECT_CALL(dev_install_, ClearStateDir(_)).WillOnce(Return(true));
85 EXPECT_CALL(dev_install_, Exec(_)).WillOnce(Return(1234));
86 ASSERT_EQ(1234, dev_install_.Run());
87}
88
89// Check when --reinstall is requested but clearing fails.
90TEST_F(DevInstallTest, RunReinstallFails) {
91 dev_install_.SetReinstallForTest(true);
92 EXPECT_CALL(dev_install_, ClearStateDir(_)).WillOnce(Return(false));
93 EXPECT_CALL(dev_install_, Exec(_)).Times(0);
94 ASSERT_EQ(1, dev_install_.Run());
95}
96
97// Check --uninstall passed.
98TEST_F(DevInstallTest, RunUninstall) {
99 dev_install_.SetUninstallForTest(true);
100 EXPECT_CALL(dev_install_, ClearStateDir(_)).WillOnce(Return(true));
101 EXPECT_CALL(dev_install_, Exec(_)).Times(0);
102 ASSERT_EQ(0, dev_install_.Run());
103}
104
Mike Frysinger3d44f782019-02-25 23:17:08 -0500105// Stateful setup failures.
106TEST_F(DevInstallTest, StatefulSetupFailure) {
107 EXPECT_CALL(dev_install_, InitializeStateDir(_)).WillOnce(Return(false));
108 EXPECT_CALL(dev_install_, Exec(_)).Times(0);
109 ASSERT_EQ(5, dev_install_.Run());
110}
111
Mike Frysinger60260f62019-02-24 02:28:23 -0500112namespace {
113
114class PromptUserTest : public ::testing::Test {
115 protected:
116 DevInstall dev_install_;
117};
118
119} // namespace
120
121// The --yes flag should pass w/out prompting the user.
122TEST_F(PromptUserTest, Forced) {
123 dev_install_.SetYesForTest(true);
124 std::stringstream stream("");
125 EXPECT_TRUE(dev_install_.PromptUser(stream, ""));
126}
127
128// EOF input should fail.
129TEST_F(PromptUserTest, Eof) {
130 std::stringstream stream("");
131 EXPECT_FALSE(dev_install_.PromptUser(stream, ""));
132}
133
134// Default input (hitting enter) should fail.
135TEST_F(PromptUserTest, Default) {
136 std::stringstream stream("\n");
137 EXPECT_FALSE(dev_install_.PromptUser(stream, ""));
138}
139
140// Entering "n" should fail.
141TEST_F(PromptUserTest, No) {
142 std::stringstream stream("n\n");
143 EXPECT_FALSE(dev_install_.PromptUser(stream, ""));
144}
145
146// Entering "y" should pass.
147TEST_F(PromptUserTest, Yes) {
148 std::stringstream stream("y\n");
149 EXPECT_TRUE(dev_install_.PromptUser(stream, ""));
150}
151
Mike Frysingerb9c9f6c2019-02-24 02:32:32 -0500152namespace {
153
154class DeletePathTest : public ::testing::Test {
155 public:
156 void SetUp() override {
157 ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
158 test_dir_ = scoped_temp_dir_.GetPath();
159 dev_install_.SetStateDirForTest(test_dir_);
160 }
161
162 protected:
163 DevInstall dev_install_;
164 base::FilePath test_dir_;
165 base::ScopedTempDir scoped_temp_dir_;
166};
167
168} // namespace
169
170// Check missing dir.
171TEST_F(DeletePathTest, Missing) {
172 struct stat st = {};
173 EXPECT_TRUE(dev_install_.DeletePath(st, test_dir_.Append("foo")));
174}
175
176// Check deleting dir contents leaves the dir alone.
177TEST_F(DeletePathTest, Empty) {
178 struct stat st = {};
179 EXPECT_TRUE(dev_install_.DeletePath(st, test_dir_));
180 EXPECT_TRUE(base::PathExists(test_dir_));
181}
182
183// Check mounted deletion.
184TEST_F(DeletePathTest, Mounted) {
185 struct stat st = {};
186 const base::FilePath subdir = test_dir_.Append("subdir");
187 EXPECT_TRUE(base::CreateDirectory(subdir));
188 EXPECT_FALSE(dev_install_.DeletePath(st, test_dir_));
189 EXPECT_TRUE(base::PathExists(subdir));
190}
191
192// Check recursive deletion.
193TEST_F(DeletePathTest, Works) {
194 struct stat st;
195 EXPECT_EQ(0, stat(test_dir_.value().c_str(), &st));
196
197 EXPECT_EQ(3, base::WriteFile(test_dir_.Append("file"), "123", 3));
198 EXPECT_EQ(0, symlink("x", test_dir_.Append("broken-sym").value().c_str()));
199 EXPECT_EQ(0, symlink("file", test_dir_.Append("file-sym").value().c_str()));
200 EXPECT_EQ(0, symlink(".", test_dir_.Append("dir-sym").value().c_str()));
201 EXPECT_EQ(0, symlink("subdir", test_dir_.Append("dir-sym2").value().c_str()));
202 const base::FilePath subdir = test_dir_.Append("subdir");
203 EXPECT_TRUE(base::CreateDirectory(subdir));
204 EXPECT_EQ(3, base::WriteFile(subdir.Append("file"), "123", 3));
205 const base::FilePath subsubdir = test_dir_.Append("subdir");
206 EXPECT_TRUE(base::CreateDirectory(subsubdir));
207 EXPECT_EQ(3, base::WriteFile(subsubdir.Append("file"), "123", 3));
208
209 EXPECT_TRUE(dev_install_.DeletePath(st, test_dir_));
210 EXPECT_TRUE(base::PathExists(test_dir_));
211 EXPECT_EQ(0, rmdir(test_dir_.value().c_str()));
212}
213
Mike Frysinger69c167f2019-02-24 05:14:57 -0500214namespace {
215
216// We could mock out DeletePath, but it's easy to lightly validate it.
217class ClearStateDirMock : public DevInstall {
218 public:
219 MOCK_METHOD(bool,
220 PromptUser,
221 (std::istream&, const std::string&),
222 (override));
223};
224
225class ClearStateDirTest : public ::testing::Test {
226 public:
227 void SetUp() {
228 ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
229 test_dir_ = scoped_temp_dir_.GetPath();
230 }
231
232 protected:
233 ClearStateDirMock dev_install_;
234 base::FilePath test_dir_;
235 base::ScopedTempDir scoped_temp_dir_;
236};
237
238} // namespace
239
240// Check user rejecting things.
241TEST_F(ClearStateDirTest, Cancel) {
242 EXPECT_CALL(dev_install_, PromptUser(_, _)).WillOnce(Return(false));
243 const base::FilePath subdir = test_dir_.Append("subdir");
244 ASSERT_TRUE(base::CreateDirectory(subdir));
245 ASSERT_FALSE(dev_install_.ClearStateDir(test_dir_));
246 ASSERT_TRUE(base::PathExists(subdir));
247}
248
249// Check missing dir is handled.
250TEST_F(ClearStateDirTest, Missing) {
251 EXPECT_CALL(dev_install_, PromptUser(_, _)).WillOnce(Return(true));
252 ASSERT_TRUE(dev_install_.ClearStateDir(test_dir_.Append("subdir")));
253 ASSERT_TRUE(base::PathExists(test_dir_));
254}
255
256// Check empty dir is handled.
257TEST_F(ClearStateDirTest, Empty) {
258 EXPECT_CALL(dev_install_, PromptUser(_, _)).WillOnce(Return(true));
259 ASSERT_TRUE(dev_install_.ClearStateDir(test_dir_));
260 ASSERT_TRUE(base::PathExists(test_dir_));
261}
262
263// Check dir with contents is cleared.
264TEST_F(ClearStateDirTest, Works) {
265 EXPECT_CALL(dev_install_, PromptUser(_, _)).WillOnce(Return(true));
266 const base::FilePath subdir = test_dir_.Append("subdir");
267 ASSERT_TRUE(base::CreateDirectory(subdir));
268 ASSERT_TRUE(dev_install_.ClearStateDir(test_dir_));
269 ASSERT_FALSE(base::PathExists(subdir));
270}
271
Mike Frysinger3d44f782019-02-25 23:17:08 -0500272namespace {
273
274class InitializeStateDirTest : public ::testing::Test {
275 public:
276 void SetUp() {
277 ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
278 test_dir_ = scoped_temp_dir_.GetPath();
279 }
280
281 protected:
282 DevInstall dev_install_;
283 base::FilePath test_dir_;
284 base::ScopedTempDir scoped_temp_dir_;
285};
286
287} // namespace
288
289// Check stateful is set up correctly.
290TEST_F(InitializeStateDirTest, Works) {
291 // Make sure we fully set things up.
292 ASSERT_TRUE(dev_install_.InitializeStateDir(test_dir_));
293 ASSERT_TRUE(base::IsLink(test_dir_.Append("usr")));
294 ASSERT_TRUE(base::IsLink(test_dir_.Append("local")));
295 ASSERT_TRUE(base::IsLink(test_dir_.Append("local")));
296 const base::FilePath etc = test_dir_.Append("etc");
297 ASSERT_TRUE(base::PathExists(etc));
298 ASSERT_TRUE(base::IsLink(etc.Append("passwd")));
299 ASSERT_TRUE(base::IsLink(etc.Append("group")));
300
301 // Calling a second time should be fine.
302 ASSERT_TRUE(dev_install_.InitializeStateDir(test_dir_));
303}
304
305// Check we handle errors gracefully.
306TEST_F(InitializeStateDirTest, Fails) {
307 // Create a broken /etc symlink.
308 ASSERT_TRUE(
309 base::CreateSymbolicLink(base::FilePath("foo"), test_dir_.Append("etc")));
310 ASSERT_FALSE(dev_install_.InitializeStateDir(test_dir_));
311}
312
Mike Frysinger1dad0972019-02-23 18:36:37 -0500313} // namespace dev_install