Commit 1b38080e authored by msw's avatar msw Committed by Commit bot

Further refine mojo_example_apptests.

Destroy MojoMain's RunLoop before executing tests.
Reuse the shell handle for multiple test app instances.
Add ApplicationImpl::UnbindShell for message pipe reuse.

Add Apptest, a GTEST base class for application testing.

TODO: Support/Exemplify actual command line arg use.

BUG=392646
TEST=mojo_shell 'mojo:mojo_example_apptests <gtest_args>' still works.
R=viettrungluu@chromium.org

Review URL: https://codereview.chromium.org/654103002

Cr-Commit-Position: refs/heads/master@{#300155}
parent 978b5a5c
...@@ -18,32 +18,83 @@ ...@@ -18,32 +18,83 @@
#include "mojo/public/cpp/utility/run_loop.h" #include "mojo/public/cpp/utility/run_loop.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace { namespace {
// TODO(msw): Remove this once we can get ApplicationImpl from TLS. // This global shell handle is needed for repeated use by test applications.
mojo::ApplicationImpl* g_application_impl_hack = NULL; MessagePipeHandle g_shell_message_pipe_handle_hack;
} // namespace // Apptest is a GTEST base class for application testing executed in mojo_shell.
class Apptest : public testing::Test {
public:
explicit Apptest(Array<String> args)
: args_(args.Pass()),
application_impl_(nullptr) {
}
virtual ~Apptest() override {}
namespace mojo { protected:
ApplicationImpl* application_impl() { return application_impl_; }
namespace { // Get the ApplicationDelegate for the application to be tested.
virtual ApplicationDelegate* GetApplicationDelegate() = 0;
class ExampleApptest : public testing::Test { // testing::Test:
public: virtual void SetUp() override {
ExampleApptest() { // New applications are constructed for each test to avoid persisting state.
g_application_impl_hack->ConnectToService("mojo:mojo_example_service", MOJO_CHECK(g_shell_message_pipe_handle_hack.is_valid());
&example_service_); application_impl_ = new ApplicationImpl(
example_service_.set_client(&example_client_); GetApplicationDelegate(),
MakeScopedHandle(g_shell_message_pipe_handle_hack));
// Fake application initialization with the given command line arguments.
application_impl_->Initialize(args_.Clone());
} }
virtual void TearDown() override {
g_shell_message_pipe_handle_hack =
application_impl_->UnbindShell().release();
delete application_impl_;
}
private:
// The command line arguments supplied to each test application instance.
Array<String> args_;
// The application implementation instance, reconstructed for each test.
ApplicationImpl* application_impl_;
// A run loop is needed for ApplicationImpl initialization and communication.
RunLoop run_loop_;
MOJO_DISALLOW_COPY_AND_ASSIGN(Apptest);
};
// ExampleApptest exemplifies Apptest's application testing pattern.
class ExampleApptest : public Apptest {
public:
// TODO(msw): Exemplify the use of actual command line arguments.
ExampleApptest() : Apptest(Array<String>()) {}
virtual ~ExampleApptest() override {} virtual ~ExampleApptest() override {}
protected: protected:
// Apptest:
virtual ApplicationDelegate* GetApplicationDelegate() override {
return &example_client_application_;
}
virtual void SetUp() override {
Apptest::SetUp();
application_impl()->ConnectToService("mojo:mojo_example_service",
&example_service_);
example_service_.set_client(&example_client_);
}
ExampleServicePtr example_service_; ExampleServicePtr example_service_;
ExampleClientImpl example_client_; ExampleClientImpl example_client_;
private: private:
ExampleClientApplication example_client_application_;
MOJO_DISALLOW_COPY_AND_ASSIGN(ExampleApptest); MOJO_DISALLOW_COPY_AND_ASSIGN(ExampleApptest);
}; };
...@@ -64,9 +115,9 @@ TEST_F(ExampleApptest, PingServiceToPongClient) { ...@@ -64,9 +115,9 @@ TEST_F(ExampleApptest, PingServiceToPongClient) {
} }
template <typename T> template <typename T>
struct SetAndQuit : public Callback<void()>::Runnable { struct SetCallback : public Callback<void()>::Runnable {
SetAndQuit(T* val, T result) : val_(val), result_(result) {} SetCallback(T* val, T result) : val_(val), result_(result) {}
virtual ~SetAndQuit() {} virtual ~SetCallback() {}
virtual void Run() const override { *val_ = result_; } virtual void Run() const override { *val_ = result_; }
T* val_; T* val_;
T result_; T result_;
...@@ -75,7 +126,7 @@ struct SetAndQuit : public Callback<void()>::Runnable { ...@@ -75,7 +126,7 @@ struct SetAndQuit : public Callback<void()>::Runnable {
TEST_F(ExampleApptest, RunCallbackViaService) { TEST_F(ExampleApptest, RunCallbackViaService) {
// Test ExampleService callback functionality. // Test ExampleService callback functionality.
bool was_run = false; bool was_run = false;
example_service_->RunCallback(SetAndQuit<bool>(&was_run, true)); example_service_->RunCallback(SetCallback<bool>(&was_run, true));
EXPECT_TRUE(example_service_.WaitForIncomingMethodCall()); EXPECT_TRUE(example_service_.WaitForIncomingMethodCall());
EXPECT_TRUE(was_run); EXPECT_TRUE(was_run);
} }
...@@ -85,18 +136,22 @@ TEST_F(ExampleApptest, RunCallbackViaService) { ...@@ -85,18 +136,22 @@ TEST_F(ExampleApptest, RunCallbackViaService) {
} // namespace mojo } // namespace mojo
MojoResult MojoMain(MojoHandle shell_handle) { MojoResult MojoMain(MojoHandle shell_handle) {
mojo::Environment env; mojo::Environment environment;
// TODO(msw): Destroy this ambient RunLoop before running tests.
// Need to CancelWait() / PassMessagePipe() from the ShellPtr?
mojo::RunLoop loop;
mojo::ApplicationDelegate* delegate = new mojo::ExampleClientApplication();
mojo::ApplicationImpl app(delegate, shell_handle);
g_application_impl_hack = &app;
MOJO_CHECK(app.WaitForInitialize());
{ {
// This RunLoop is used for init, and then destroyed before running tests.
mojo::RunLoop run_loop;
// Construct an ApplicationImpl just for the GTEST commandline arguments.
// GTEST command line arguments are supported amid application arguments:
// mojo_shell 'mojo:mojo_example_apptest arg1 --gtest_filter=foo arg2'
mojo::ApplicationDelegate dummy_application_delegate;
mojo::ApplicationImpl app(&dummy_application_delegate, shell_handle);
MOJO_CHECK(app.WaitForInitialize());
// InitGoogleTest expects (argc + 1) elements, including a terminating NULL. // InitGoogleTest expects (argc + 1) elements, including a terminating NULL.
// It also removes GTEST arguments from |argv| and updates the |argc| count. // It also removes GTEST arguments from |argv| and updates the |argc| count.
// TODO(msw): Provide tests access to these actual command line arguments.
const std::vector<std::string>& args = app.args(); const std::vector<std::string>& args = app.args();
MOJO_CHECK(args.size() < INT_MAX); MOJO_CHECK(args.size() < INT_MAX);
int argc = static_cast<int>(args.size()); int argc = static_cast<int>(args.size());
...@@ -105,10 +160,14 @@ MojoResult MojoMain(MojoHandle shell_handle) { ...@@ -105,10 +160,14 @@ MojoResult MojoMain(MojoHandle shell_handle) {
argv[i] = const_cast<char*>(args[i].data()); argv[i] = const_cast<char*>(args[i].data());
argv[argc] = NULL; argv[argc] = NULL;
testing::InitGoogleTest(&argc, &argv[0]); testing::InitGoogleTest(&argc, &argv[0]);
mojo::g_shell_message_pipe_handle_hack = app.UnbindShell().release();
} }
mojo_ignore_result(RUN_ALL_TESTS()); mojo_ignore_result(RUN_ALL_TESTS());
delete delegate; MojoResult result = MojoClose(mojo::g_shell_message_pipe_handle_hack.value());
MOJO_ALLOW_UNUSED_LOCAL(result);
assert(result == MOJO_RESULT_OK);
return MOJO_RESULT_OK; return MOJO_RESULT_OK;
} }
...@@ -76,6 +76,12 @@ class ApplicationImpl : public InterfaceImpl<Application> { ...@@ -76,6 +76,12 @@ class ApplicationImpl : public InterfaceImpl<Application> {
// Wait for the ShellPtr's Initialize message. // Wait for the ShellPtr's Initialize message.
bool WaitForInitialize(); bool WaitForInitialize();
// Unbind the shell from this application and return its handle.
ScopedMessagePipeHandle UnbindShell();
// Application implementation.
virtual void Initialize(Array<String> args) override;
private: private:
class ShellPtrWatcher; class ShellPtrWatcher;
...@@ -90,7 +96,6 @@ class ApplicationImpl : public InterfaceImpl<Application> { ...@@ -90,7 +96,6 @@ class ApplicationImpl : public InterfaceImpl<Application> {
static void Terminate(); static void Terminate();
// Application implementation. // Application implementation.
virtual void Initialize(Array<String> args) override;
virtual void AcceptConnection(const String& requestor_url, virtual void AcceptConnection(const String& requestor_url,
ServiceProviderPtr provider) override; ServiceProviderPtr provider) override;
......
...@@ -54,13 +54,6 @@ ApplicationImpl::~ApplicationImpl() { ...@@ -54,13 +54,6 @@ ApplicationImpl::~ApplicationImpl() {
delete shell_watch_; delete shell_watch_;
} }
void ApplicationImpl::Initialize(Array<String> args) {
MOJO_CHECK(!initialized_);
initialized_ = true;
args_ = args.To<std::vector<std::string>>();
delegate_->Initialize(this);
}
ApplicationConnection* ApplicationImpl::ConnectToApplication( ApplicationConnection* ApplicationImpl::ConnectToApplication(
const String& application_url) { const String& application_url) {
MOJO_CHECK(initialized_); MOJO_CHECK(initialized_);
...@@ -84,6 +77,17 @@ bool ApplicationImpl::WaitForInitialize() { ...@@ -84,6 +77,17 @@ bool ApplicationImpl::WaitForInitialize() {
return result; return result;
} }
ScopedMessagePipeHandle ApplicationImpl::UnbindShell() {
return shell_.PassMessagePipe();
}
void ApplicationImpl::Initialize(Array<String> args) {
MOJO_CHECK(!initialized_);
initialized_ = true;
args_ = args.To<std::vector<std::string>>();
delegate_->Initialize(this);
}
void ApplicationImpl::BindShell(ScopedMessagePipeHandle shell_handle) { void ApplicationImpl::BindShell(ScopedMessagePipeHandle shell_handle) {
shell_watch_ = new ShellPtrWatcher(this); shell_watch_ = new ShellPtrWatcher(this);
shell_.Bind(shell_handle.Pass()); shell_.Bind(shell_handle.Pass());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment