blob: c1ca188238b2df6380dae0412473b1c1fcde9ccc [file] [log] [blame]
// Copyright 2018 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 "base/bind.h"
#include "base/synchronization/waitable_event.h"
#include "chrome/chrome_cleaner/interfaces/string16_embedded_nulls.mojom.h"
#include "chrome/chrome_cleaner/interfaces/test_string16_embedded_nulls.mojom.h"
#include "chrome/chrome_cleaner/ipc/ipc_test_util.h"
#include "chrome/chrome_cleaner/ipc/mojo_task_runner.h"
#include "chrome/chrome_cleaner/strings/string16_embedded_nulls.h"
#include "chrome/chrome_cleaner/strings/string_test_helpers.h"
#include "chrome/chrome_cleaner/test/test_util.h"
#include "mojo/core/embedder/embedder.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
namespace chrome_cleaner {
namespace {
using base::WaitableEvent;
class TestString16EmbeddedNullsImpl : public mojom::TestString16EmbeddedNulls {
public:
explicit TestString16EmbeddedNullsImpl(
mojom::TestString16EmbeddedNullsRequest request)
: binding_(this, std::move(request)) {}
void Echo(const String16EmbeddedNulls& path, EchoCallback callback) override {
std::move(callback).Run(path);
}
private:
mojo::Binding<mojom::TestString16EmbeddedNulls> binding_;
};
class SandboxParentProcess : public chrome_cleaner::ParentProcess {
public:
explicit SandboxParentProcess(scoped_refptr<MojoTaskRunner> mojo_task_runner)
: ParentProcess(std::move(mojo_task_runner)) {}
protected:
void CreateImpl(mojo::ScopedMessagePipeHandle mojo_pipe) override {
mojom::TestString16EmbeddedNullsRequest request(std::move(mojo_pipe));
impl_ = std::make_unique<TestString16EmbeddedNullsImpl>(std::move(request));
}
void DestroyImpl() override { impl_.reset(); }
private:
~SandboxParentProcess() override = default;
std::unique_ptr<TestString16EmbeddedNullsImpl> impl_;
};
class SandboxChildProcess : public chrome_cleaner::ChildProcess {
public:
explicit SandboxChildProcess(scoped_refptr<MojoTaskRunner> mojo_task_runner)
: ChildProcess(mojo_task_runner),
ptr_(std::make_unique<mojom::TestString16EmbeddedNullsPtr>()) {}
void BindToPipe(mojo::ScopedMessagePipeHandle mojo_pipe,
WaitableEvent* event) {
ptr_->Bind(chrome_cleaner::mojom::TestString16EmbeddedNullsPtrInfo(
std::move(mojo_pipe), 0));
event->Signal();
}
bool SuccessfulEcho(const String16EmbeddedNulls& input) {
String16EmbeddedNulls output;
WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
mojo_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&SandboxChildProcess::RunEcho, base::Unretained(this),
input, GetSaveAndSignalCallback(&output, &event)));
event.Wait();
return input == output;
}
private:
~SandboxChildProcess() override {
mojo_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
[](std::unique_ptr<mojom::TestString16EmbeddedNullsPtr> ptr) {
ptr.reset();
},
base::Passed(&ptr_)));
}
template <typename EchoedType>
base::OnceCallback<void(const EchoedType&)> GetSaveAndSignalCallback(
EchoedType* output,
WaitableEvent* event) {
return base::BindOnce(
[](EchoedType* value_holder, WaitableEvent* event,
const EchoedType& value) {
*value_holder = value;
event->Signal();
},
output, event);
}
void RunEcho(const String16EmbeddedNulls& input,
mojom::TestString16EmbeddedNulls::EchoCallback callback) {
(*ptr_)->Echo(input, std::move(callback));
}
std::unique_ptr<mojom::TestString16EmbeddedNullsPtr> ptr_;
};
scoped_refptr<SandboxChildProcess> InitChildProcess() {
auto mojo_task_runner = MojoTaskRunner::Create();
auto child_process =
base::MakeRefCounted<SandboxChildProcess>(mojo_task_runner);
auto message_pipe_handle = child_process->CreateMessagePipeFromCommandLine();
WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
mojo_task_runner->PostTask(
FROM_HERE, base::BindOnce(&SandboxChildProcess::BindToPipe, child_process,
base::Passed(&message_pipe_handle), &event));
event.Wait();
return child_process;
}
MULTIPROCESS_TEST_MAIN(EchoMain) {
scoped_refptr<SandboxChildProcess> child_process = InitChildProcess();
// Empty string.
EXPECT_TRUE(child_process->SuccessfulEcho(String16EmbeddedNulls()));
EXPECT_TRUE(child_process->SuccessfulEcho(String16EmbeddedNulls(nullptr)));
EXPECT_TRUE(child_process->SuccessfulEcho(String16EmbeddedNulls(nullptr, 0)));
EXPECT_TRUE(child_process->SuccessfulEcho(String16EmbeddedNulls(nullptr, 1)));
EXPECT_TRUE(child_process->SuccessfulEcho(String16EmbeddedNulls(L"", 0)));
EXPECT_TRUE(child_process->SuccessfulEcho(
String16EmbeddedNulls(std::vector<wchar_t>{})));
EXPECT_TRUE(
child_process->SuccessfulEcho(String16EmbeddedNulls(base::string16())));
EXPECT_TRUE(child_process->SuccessfulEcho(
String16EmbeddedNulls(base::string16(L""))));
EXPECT_TRUE(child_process->SuccessfulEcho(
String16EmbeddedNulls(base::StringPiece16())));
EXPECT_TRUE(child_process->SuccessfulEcho(
String16EmbeddedNulls(base::StringPiece16(L""))));
// Null-terminated strings. Zeroes will be replaced with null characters.
constexpr wchar_t kStringWithNulls[] = L"string0with0nulls";
const std::vector<wchar_t> vec1 = CreateVectorWithNulls(kStringWithNulls);
EXPECT_TRUE(child_process->SuccessfulEcho(
String16EmbeddedNulls(vec1.data(), vec1.size())));
EXPECT_TRUE(child_process->SuccessfulEcho(String16EmbeddedNulls(vec1)));
EXPECT_TRUE(child_process->SuccessfulEcho(
String16EmbeddedNulls(base::string16(vec1.data(), vec1.size()))));
EXPECT_TRUE(child_process->SuccessfulEcho(
String16EmbeddedNulls(base::StringPiece16(vec1.data(), vec1.size()))));
// Non null-terminated strings.
const std::vector<wchar_t> vec2(vec1.begin(), vec1.end() - 1);
EXPECT_TRUE(child_process->SuccessfulEcho(
String16EmbeddedNulls(vec2.data(), vec2.size())));
EXPECT_TRUE(child_process->SuccessfulEcho(String16EmbeddedNulls(vec2)));
EXPECT_TRUE(child_process->SuccessfulEcho(
String16EmbeddedNulls(base::string16(vec2.data(), vec2.size()))));
EXPECT_TRUE(child_process->SuccessfulEcho(
String16EmbeddedNulls(base::StringPiece16(vec2.data(), vec2.size()))));
return ::testing::Test::HasNonfatalFailure();
}
class String16EmbeddedNullsTypemapTest : public ::testing::Test {
public:
void SetUp() override {
mojo_task_runner_ = MojoTaskRunner::Create();
parent_process_ =
base::MakeRefCounted<SandboxParentProcess>(mojo_task_runner_);
}
protected:
scoped_refptr<MojoTaskRunner> mojo_task_runner_;
scoped_refptr<SandboxParentProcess> parent_process_;
};
TEST_F(String16EmbeddedNullsTypemapTest, Echo) {
int32_t exit_code = -1;
EXPECT_TRUE(
parent_process_->LaunchConnectedChildProcess("EchoMain", &exit_code));
EXPECT_EQ(0, exit_code);
}
} // namespace
} // namespace chrome_cleaner