summaryrefslogtreecommitdiff
path: root/sandbox/linux/services/scoped_process_unittest.cc
blob: 8bd284799708c5b63e1151aababb7565abd6b020 (plain)
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
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "sandbox/linux/services/scoped_process.h"

#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include "base/bind.h"
#include "base/callback.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "sandbox/linux/tests/unit_tests.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace sandbox {

namespace {

void DoExit() { _exit(0); }

void ExitWithCode(int exit_code) { _exit(exit_code); }

void RaiseAndExit(int signal) {
  PCHECK(0 == raise(signal));
  _exit(0);
}

void DoNothing() {}

TEST(ScopedProcess, ScopedProcessNormalExit) {
  const int kCustomExitCode = 12;
  ScopedProcess process(base::Bind(&ExitWithCode, kCustomExitCode));
  bool got_signaled = true;
  int exit_code = process.WaitForExit(&got_signaled);
  EXPECT_FALSE(got_signaled);
  EXPECT_EQ(kCustomExitCode, exit_code);

  // Verify that WaitForExit() can be called multiple times on the same
  // process.
  bool got_signaled2 = true;
  int exit_code2 = process.WaitForExit(&got_signaled2);
  EXPECT_FALSE(got_signaled2);
  EXPECT_EQ(kCustomExitCode, exit_code2);
}

// Disable this test on Android, SIGABRT is funky there.
TEST(ScopedProcess, DISABLE_ON_ANDROID(ScopedProcessAbort)) {
  PCHECK(SIG_ERR != signal(SIGABRT, SIG_DFL));
  ScopedProcess process(base::Bind(&RaiseAndExit, SIGABRT));
  bool got_signaled = false;
  int exit_code = process.WaitForExit(&got_signaled);
  EXPECT_TRUE(got_signaled);
  EXPECT_EQ(SIGABRT, exit_code);
}

TEST(ScopedProcess, ScopedProcessSignaled) {
  ScopedProcess process(base::Bind(&DoNothing));
  bool got_signaled = false;
  ASSERT_EQ(0, kill(process.GetPid(), SIGKILL));
  int exit_code = process.WaitForExit(&got_signaled);
  EXPECT_TRUE(got_signaled);
  EXPECT_EQ(SIGKILL, exit_code);
}

TEST(ScopedProcess, DiesForReal) {
  int pipe_fds[2];
  ASSERT_EQ(0, pipe(pipe_fds));
  base::ScopedFD read_end_closer(pipe_fds[0]);
  base::ScopedFD write_end_closer(pipe_fds[1]);

  { ScopedProcess process(base::Bind(&DoExit)); }

  // Close writing end of the pipe.
  write_end_closer.reset();
  pipe_fds[1] = -1;

  ASSERT_EQ(0, fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK));
  char c;
  // If the child process is dead for real, there will be no writing end
  // for this pipe left and read will EOF instead of returning EWOULDBLOCK.
  ASSERT_EQ(0, read(pipe_fds[0], &c, 1));
}

TEST(ScopedProcess, SynchronizationBasic) {
  ScopedProcess process1(base::Bind(&DoNothing));
  EXPECT_TRUE(process1.WaitForClosureToRun());

  ScopedProcess process2(base::Bind(&DoExit));
  // The closure didn't finish running normally. This case is simple enough
  // that process.WaitForClosureToRun() should return false, even though the
  // API does not guarantees that it will return at all.
  EXPECT_FALSE(process2.WaitForClosureToRun());
}

void SleepInMsAndWriteOneByte(int time_to_sleep, int fd) {
  base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(time_to_sleep));
  CHECK(1 == write(fd, "1", 1));
}

TEST(ScopedProcess, SynchronizationWorks) {
  int pipe_fds[2];
  ASSERT_EQ(0, pipe(pipe_fds));
  base::ScopedFD read_end_closer(pipe_fds[0]);
  base::ScopedFD write_end_closer(pipe_fds[1]);

  // Start a process with a closure that takes a little bit to run.
  ScopedProcess process(
      base::Bind(&SleepInMsAndWriteOneByte, 100, pipe_fds[1]));
  EXPECT_TRUE(process.WaitForClosureToRun());

  // Verify that the closure did, indeed, run.
  ASSERT_EQ(0, fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK));
  char c = 0;
  EXPECT_EQ(1, read(pipe_fds[0], &c, 1));
  EXPECT_EQ('1', c);
}

}  // namespace

}  // namespace sandbox