summaryrefslogtreecommitdiff
path: root/ipc/ipc_mojo_bootstrap_unittest.cc
blob: 77728967f9a1311fabd0a050af71b5f7bd09a7b4 (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
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
// 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 "ipc/ipc_mojo_bootstrap.h"

#include <cstdint>
#include <memory>
#include <utility>

#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/threading/thread_task_runner_handle.h"
#include "ipc/ipc.mojom.h"
#include "ipc/ipc_test_base.h"
#include "mojo/core/test/multiprocess_test_helper.h"
#include "mojo/public/cpp/bindings/associated_binding.h"

namespace {

constexpr int32_t kTestServerPid = 42;
constexpr int32_t kTestClientPid = 4242;

class Connection {
 public:
  explicit Connection(std::unique_ptr<IPC::MojoBootstrap> bootstrap,
                      int32_t sender_id)
      : bootstrap_(std::move(bootstrap)) {
    bootstrap_->Connect(&sender_, &receiver_);
    sender_->SetPeerPid(sender_id);
  }

  void TakeReceiver(IPC::mojom::ChannelAssociatedRequest* receiver) {
    *receiver = std::move(receiver_);
  }

  IPC::mojom::ChannelAssociatedPtr& GetSender() { return sender_; }

 private:
  IPC::mojom::ChannelAssociatedPtr sender_;
  IPC::mojom::ChannelAssociatedRequest receiver_;
  std::unique_ptr<IPC::MojoBootstrap> bootstrap_;
};

class PeerPidReceiver : public IPC::mojom::Channel {
 public:
  enum class MessageExpectation {
    kNotExpected,
    kExpectedValid,
    kExpectedInvalid
  };

  PeerPidReceiver(
      IPC::mojom::ChannelAssociatedRequest request,
      const base::Closure& on_peer_pid_set,
      MessageExpectation message_expectation = MessageExpectation::kNotExpected)
      : binding_(this, std::move(request)),
        on_peer_pid_set_(on_peer_pid_set),
        message_expectation_(message_expectation) {
    binding_.set_connection_error_handler(disconnect_run_loop_.QuitClosure());
  }

  ~PeerPidReceiver() override {
    bool expected_message =
        message_expectation_ != MessageExpectation::kNotExpected;
    EXPECT_EQ(expected_message, received_message_);
  }

  // mojom::Channel:
  void SetPeerPid(int32_t pid) override {
    peer_pid_ = pid;
    on_peer_pid_set_.Run();
  }

  void Receive(IPC::MessageView message_view) override {
    ASSERT_NE(MessageExpectation::kNotExpected, message_expectation_);
    received_message_ = true;

    IPC::Message message(message_view.data(), message_view.size());
    bool expected_valid =
        message_expectation_ == MessageExpectation::kExpectedValid;
    EXPECT_EQ(expected_valid, message.IsValid());
  }

  void GetAssociatedInterface(
      const std::string& name,
      IPC::mojom::GenericInterfaceAssociatedRequest request) override {}

  int32_t peer_pid() const { return peer_pid_; }

  void RunUntilDisconnect() { disconnect_run_loop_.Run(); }

 private:
  mojo::AssociatedBinding<IPC::mojom::Channel> binding_;
  const base::Closure on_peer_pid_set_;
  MessageExpectation message_expectation_;
  int32_t peer_pid_ = -1;
  bool received_message_ = false;
  base::RunLoop disconnect_run_loop_;

  DISALLOW_COPY_AND_ASSIGN(PeerPidReceiver);
};

class IPCMojoBootstrapTest : public testing::Test {
 protected:
  mojo::core::test::MultiprocessTestHelper helper_;
};

TEST_F(IPCMojoBootstrapTest, Connect) {
  base::MessageLoop message_loop;
  Connection connection(
      IPC::MojoBootstrap::Create(
          helper_.StartChild("IPCMojoBootstrapTestClient"),
          IPC::Channel::MODE_SERVER, base::ThreadTaskRunnerHandle::Get(),
          base::ThreadTaskRunnerHandle::Get()),
      kTestServerPid);

  IPC::mojom::ChannelAssociatedRequest receiver;
  connection.TakeReceiver(&receiver);

  base::RunLoop run_loop;
  PeerPidReceiver impl(std::move(receiver), run_loop.QuitClosure());
  run_loop.Run();

  EXPECT_EQ(kTestClientPid, impl.peer_pid());

  impl.RunUntilDisconnect();
  EXPECT_TRUE(helper_.WaitForChildTestShutdown());
}

// A long running process that connects to us.
MULTIPROCESS_TEST_MAIN_WITH_SETUP(
    IPCMojoBootstrapTestClientTestChildMain,
    ::mojo::core::test::MultiprocessTestHelper::ChildSetup) {
  base::MessageLoop message_loop;
  Connection connection(
      IPC::MojoBootstrap::Create(
          std::move(mojo::core::test::MultiprocessTestHelper::primordial_pipe),
          IPC::Channel::MODE_CLIENT, base::ThreadTaskRunnerHandle::Get(),
          base::ThreadTaskRunnerHandle::Get()),
      kTestClientPid);

  IPC::mojom::ChannelAssociatedRequest receiver;
  connection.TakeReceiver(&receiver);

  base::RunLoop run_loop;
  PeerPidReceiver impl(std::move(receiver), run_loop.QuitClosure());
  run_loop.Run();

  EXPECT_EQ(kTestServerPid, impl.peer_pid());

  return 0;
}

TEST_F(IPCMojoBootstrapTest, ReceiveEmptyMessage) {
  base::MessageLoop message_loop;
  Connection connection(
      IPC::MojoBootstrap::Create(
          helper_.StartChild("IPCMojoBootstrapTestEmptyMessage"),
          IPC::Channel::MODE_SERVER, base::ThreadTaskRunnerHandle::Get(),
          base::ThreadTaskRunnerHandle::Get()),
      kTestServerPid);

  IPC::mojom::ChannelAssociatedRequest receiver;
  connection.TakeReceiver(&receiver);

  base::RunLoop run_loop;
  PeerPidReceiver impl(std::move(receiver), run_loop.QuitClosure(),
                       PeerPidReceiver::MessageExpectation::kExpectedInvalid);
  run_loop.Run();

  // Wait for the Channel to be disconnected so we can reasonably assert that
  // the child's empty message must have been received before we pass the test.
  impl.RunUntilDisconnect();

  EXPECT_TRUE(helper_.WaitForChildTestShutdown());
}

// A long running process that connects to us.
MULTIPROCESS_TEST_MAIN_WITH_SETUP(
    IPCMojoBootstrapTestEmptyMessageTestChildMain,
    ::mojo::core::test::MultiprocessTestHelper::ChildSetup) {
  base::MessageLoop message_loop;
  Connection connection(
      IPC::MojoBootstrap::Create(
          std::move(mojo::core::test::MultiprocessTestHelper::primordial_pipe),
          IPC::Channel::MODE_CLIENT, base::ThreadTaskRunnerHandle::Get(),
          base::ThreadTaskRunnerHandle::Get()),
      kTestClientPid);

  IPC::mojom::ChannelAssociatedRequest receiver;
  connection.TakeReceiver(&receiver);
  auto& sender = connection.GetSender();

  uint8_t data = 0;
  sender->Receive(
      IPC::MessageView(mojo_base::BigBufferView(base::make_span(&data, 0)),
                       base::nullopt /* handles */));

  base::RunLoop run_loop;
  PeerPidReceiver impl(std::move(receiver), run_loop.QuitClosure());
  run_loop.Run();

  return 0;
}

}  // namespace