// Copyright 2016 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.
#ifndef MOJO_EDK_TEST_MOJO_TEST_BASE_H_
#define MOJO_EDK_TEST_MOJO_TEST_BASE_H_
#include <memory>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "mojo/edk/embedder/embedder.h"
#include "mojo/edk/test/multiprocess_test_helper.h"
#include "mojo/public/c/system/types.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace edk {
namespace test {
class MojoTestBase : public testing::Test {
public:
MojoTestBase();
~MojoTestBase() override;
using LaunchType = MultiprocessTestHelper::LaunchType;
protected:
using HandlerCallback = base::Callback<void(ScopedMessagePipeHandle)>;
class ClientController {
public:
ClientController(const std::string& client_name,
MojoTestBase* test,
const ProcessErrorCallback& process_error_callback,
LaunchType launch_type);
~ClientController();
MojoHandle pipe() const { return pipe_.get().value(); }
void ClosePeerConnection();
int WaitForShutdown();
private:
friend class MojoTestBase;
#if !defined(OS_IOS)
MultiprocessTestHelper helper_;
#endif
ScopedMessagePipeHandle pipe_;
bool was_shutdown_ = false;
DISALLOW_COPY_AND_ASSIGN(ClientController);
};
// Set the callback to handle bad messages received from test client
// processes. This can be set to a different callback before starting each
// client.
void set_process_error_callback(const ProcessErrorCallback& callback) {
process_error_callback_ = callback;
}
ClientController& StartClient(const std::string& client_name);
template <typename HandlerFunc>
void StartClientWithHandler(const std::string& client_name,
HandlerFunc handler) {
int expected_exit_code = 0;
ClientController& c = StartClient(client_name);
handler(c.pipe(), &expected_exit_code);
EXPECT_EQ(expected_exit_code, c.WaitForShutdown());
}
// Closes a handle and expects success.
static void CloseHandle(MojoHandle h);
////// Message pipe test utilities ///////
// Creates a new pipe, returning endpoint handles in |p0| and |p1|.
static void CreateMessagePipe(MojoHandle* p0, MojoHandle* p1);
// Writes a string to the pipe, transferring handles in the process.
static void WriteMessageWithHandles(MojoHandle mp,
const std::string& message,
const MojoHandle* handles,
uint32_t num_handles);
// Writes a string to the pipe with no handles.
static void WriteMessage(MojoHandle mp, const std::string& message);
// Reads a string from the pipe, expecting to read an exact number of handles
// in the process. Returns the read string.
static std::string ReadMessageWithHandles(MojoHandle mp,
MojoHandle* handles,
uint32_t expected_num_handles);
// Reads a string from the pipe, expecting either zero or one handles.
// If no handle is read, |handle| will be reset.
static std::string ReadMessageWithOptionalHandle(MojoHandle mp,
MojoHandle* handle);
// Reads a string from the pipe, expecting to read no handles.
// Returns the string.
static std::string ReadMessage(MojoHandle mp);
// Reads a string from the pipe, expecting to read no handles and exactly
// |num_bytes| bytes, which are read into |data|.
static void ReadMessage(MojoHandle mp, char* data, size_t num_bytes);
// Writes |message| to |in| and expects to read it back from |out|.
static void VerifyTransmission(MojoHandle in,
MojoHandle out,
const std::string& message);
// Writes |message| to |mp| and expects to read it back from the same handle.
static void VerifyEcho(MojoHandle mp, const std::string& message);
//////// Shared buffer test utilities /////////
// Creates a new shared buffer.
static MojoHandle CreateBuffer(uint64_t size);
// Duplicates a shared buffer to a new handle.
static MojoHandle DuplicateBuffer(MojoHandle h, bool read_only);
// Maps a buffer, writes some data into it, and unmaps it.
static void WriteToBuffer(MojoHandle h,
size_t offset,
const base::StringPiece& s);
// Maps a buffer, tests the value of some of its contents, and unmaps it.
static void ExpectBufferContents(MojoHandle h,
size_t offset,
const base::StringPiece& s);
//////// Data pipe test utilities /////////
// Creates a new data pipe.
static void CreateDataPipe(MojoHandle* producer,
MojoHandle* consumer,
size_t capacity);
// Writes data to a data pipe.
static void WriteData(MojoHandle producer, const std::string& data);
// Reads data from a data pipe.
static std::string ReadData(MojoHandle consumer, size_t size);
void set_launch_type(LaunchType launch_type) { launch_type_ = launch_type; }
private:
friend class ClientController;
std::vector<std::unique_ptr<ClientController>> clients_;
ProcessErrorCallback process_error_callback_;
LaunchType launch_type_ = LaunchType::CHILD;
DISALLOW_COPY_AND_ASSIGN(MojoTestBase);
};
// Launches a new child process running the test client |client_name| connected
// to a new message pipe bound to |pipe_name|. |pipe_name| is automatically
// closed on test teardown.
#define RUN_CHILD_ON_PIPE(client_name, pipe_name) \
StartClientWithHandler( \
#client_name, \
[&](MojoHandle pipe_name, int *expected_exit_code) { {
// Waits for the client to terminate and expects a return code of zero.
#define END_CHILD() \
} \
*expected_exit_code = 0; \
});
// Wait for the client to terminate with a specific return code.
#define END_CHILD_AND_EXPECT_EXIT_CODE(code) \
} \
*expected_exit_code = code; \
});
// Use this to declare the child process's "main()" function for tests using
// MojoTestBase and MultiprocessTestHelper. It returns an |int|, which will
// will be the process's exit code (but see the comment about
// WaitForChildShutdown()).
//
// The function is defined as a subclass of |test_base| to facilitate shared
// code between test clients and to allow clients to spawn children themselves.
//
// |pipe_name| will be bound to the MojoHandle of a message pipe connected
// to the parent process (see RUN_CHILD_ON_PIPE above.) This pipe handle is
// automatically closed on test client teardown.
#if !defined(OS_IOS)
#define DEFINE_TEST_CLIENT_WITH_PIPE(client_name, test_base, pipe_name) \
class client_name##_MainFixture : public test_base { \
void TestBody() override {} \
public: \
int Main(MojoHandle); \
}; \
MULTIPROCESS_TEST_MAIN_WITH_SETUP( \
client_name##TestChildMain, \
::mojo::edk::test::MultiprocessTestHelper::ChildSetup) { \
client_name##_MainFixture test; \
return ::mojo::edk::test::MultiprocessTestHelper::RunClientMain( \
base::Bind(&client_name##_MainFixture::Main, \
base::Unretained(&test))); \
} \
int client_name##_MainFixture::Main(MojoHandle pipe_name)
// This is a version of DEFINE_TEST_CLIENT_WITH_PIPE which can be used with
// gtest ASSERT/EXPECT macros.
#define DEFINE_TEST_CLIENT_TEST_WITH_PIPE(client_name, test_base, pipe_name) \
class client_name##_MainFixture : public test_base { \
void TestBody() override {} \
public: \
void Main(MojoHandle); \
}; \
MULTIPROCESS_TEST_MAIN_WITH_SETUP( \
client_name##TestChildMain, \
::mojo::edk::test::MultiprocessTestHelper::ChildSetup) { \
client_name##_MainFixture test; \
return ::mojo::edk::test::MultiprocessTestHelper::RunClientTestMain( \
base::Bind(&client_name##_MainFixture::Main, \
base::Unretained(&test))); \
} \
void client_name##_MainFixture::Main(MojoHandle pipe_name)
#else // !defined(OS_IOS)
#define DEFINE_TEST_CLIENT_WITH_PIPE(client_name, test_base, pipe_name)
#define DEFINE_TEST_CLIENT_TEST_WITH_PIPE(client_name, test_base, pipe_name)
#endif // !defined(OS_IOS)
} // namespace test
} // namespace edk
} // namespace mojo
#endif // MOJO_EDK_TEST_MOJO_TEST_BASE_H_