Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(258)

Unified Diff: components/policy/core/common/remote_commands/remote_commands_service_unittest.cc

Issue 879233003: Initial RemoteCommandService (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@remote-commands
Patch Set: rebase, fixes addressing #31 Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: components/policy/core/common/remote_commands/remote_commands_service_unittest.cc
diff --git a/components/policy/core/common/remote_commands/remote_commands_service_unittest.cc b/components/policy/core/common/remote_commands/remote_commands_service_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..712171c7be1904fb01f524eb3c957bd58b33b284
--- /dev/null
+++ b/components/policy/core/common/remote_commands/remote_commands_service_unittest.cc
@@ -0,0 +1,400 @@
+// Copyright 2015 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 <queue>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/time/clock.h"
+#include "components/policy/core/common/cloud/cloud_policy_client.h"
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/core/common/remote_commands/remote_command_job.h"
+#include "components/policy/core/common/remote_commands/remote_commands_factory.h"
+#include "components/policy/core/common/remote_commands/remote_commands_queue.h"
+#include "components/policy/core/common/remote_commands/remote_commands_service.h"
+#include "components/policy/core/common/remote_commands/test_remote_command_job.h"
+#include "components/policy/core/common/remote_commands/testing_remote_commands_server.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "policy/proto/device_management_backend.pb.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::ReturnNew;
+
+namespace policy {
+
+namespace {
+
+namespace em = enterprise_management;
+
+const char kTestPayload[] = "_testing_payload_";
+const int kTestCommandExecutionTimeInSeconds = 1;
+const int kTestClientServerCommunicationDelayInSeconds = 3;
+
+void ExpectSucceededJob(const std::string& expected_payload,
+ const em::RemoteCommandResult& command_result) {
+ EXPECT_EQ(em::RemoteCommandResult_ResultType_RESULT_SUCCESS,
+ command_result.result());
+ EXPECT_EQ(expected_payload, command_result.payload());
+}
+
+} // namespace
+
+// Mocked RemoteCommand factory to allow us to build test commands.
+class MockTestRemoteCommandFactory : public RemoteCommandsFactory {
+ public:
+ MockTestRemoteCommandFactory() {
+ ON_CALL(*this, BuildTestCommand())
+ .WillByDefault(ReturnNew<TestRemoteCommandJob>(
+ true,
+ base::TimeDelta::FromSeconds(kTestCommandExecutionTimeInSeconds)));
+ }
+
+ MOCK_METHOD0(BuildTestCommand, TestRemoteCommandJob*());
+
+ private:
+ // RemoteCommandJobsFactory:
+ scoped_ptr<RemoteCommandJob> BuildJobForType(
+ em::RemoteCommand_Type type) override {
+ if (type != em::RemoteCommand_Type_COMMAND_ECHO_TEST) {
+ ADD_FAILURE();
+ return nullptr;
+ }
+ return make_scoped_ptr<RemoteCommandJob>(BuildTestCommand());
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(MockTestRemoteCommandFactory);
+};
+
+// A mocked CloudPolicyClient to interact with a TestingRemoteCommandsServer.
+class TestingCloudPolicyClientForRemoteCommands : public CloudPolicyClient {
+ public:
+ explicit TestingCloudPolicyClientForRemoteCommands(
+ TestingRemoteCommandsServer* server)
+ : CloudPolicyClient(std::string(), /* machine_id */
+ std::string(), /* machine_model */
+ std::string(), /* verification_key_hash */
+ USER_AFFILIATION_NONE,
+ nullptr,
+ nullptr),
+ server_(server) {}
+
+ ~TestingCloudPolicyClientForRemoteCommands() override {
+ EXPECT_TRUE(expected_fetch_commands_calls_.empty());
+ }
+
+ // Expect a FetchRemoteCommands() call with |expected_command_results|
+ // commands results sent and |expected_fetched_commands| commands fetched.
+ // |commands_fetched_callback| will be executed after the fetch is processed.
+ void ExpectFetchCommands(size_t expected_command_results,
+ size_t expected_fetched_commands,
+ const base::Closure& commands_fetched_callback) {
+ expected_fetch_commands_calls_.push(FetchCallExpectation(
+ expected_command_results, expected_fetched_commands,
+ commands_fetched_callback));
+ }
+
+ private:
+ // Expectations for a single FetchRemoteCommands() call.
+ struct FetchCallExpectation {
+ FetchCallExpectation(size_t expected_command_results,
+ size_t expected_fetched_commands,
+ const base::Closure& commands_fetched_callback)
+ : expected_command_results(expected_command_results),
+ expected_fetched_commands(expected_fetched_commands),
+ commands_fetched_callback(commands_fetched_callback) {}
+ virtual ~FetchCallExpectation() {}
+
+ const size_t expected_command_results;
+ const size_t expected_fetched_commands;
+ const base::Closure commands_fetched_callback;
+ };
+
+ void FetchRemoteCommands(
+ scoped_ptr<RemoteCommandJob::UniqueIDType> last_command_id,
+ const std::vector<em::RemoteCommandResult>& command_results,
+ const RemoteCommandCallback& callback) override {
+ ASSERT_FALSE(expected_fetch_commands_calls_.empty());
+
+ const FetchCallExpectation fetch_call_expectation =
+ expected_fetch_commands_calls_.front();
+ expected_fetch_commands_calls_.pop();
+
+ // Simulate delay from client to DMServer.
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(
+ &TestingCloudPolicyClientForRemoteCommands::DoFetchRemoteCommands,
+ base::Unretained(this), base::Passed(&last_command_id),
+ command_results, callback, fetch_call_expectation),
+ base::TimeDelta::FromSeconds(
+ kTestClientServerCommunicationDelayInSeconds));
+ }
+
+ void DoFetchRemoteCommands(
+ scoped_ptr<RemoteCommandJob::UniqueIDType> last_command_id,
+ const std::vector<em::RemoteCommandResult>& command_results,
+ const RemoteCommandCallback& callback,
+ const FetchCallExpectation& fetch_call_expectation) {
+ const std::vector<em::RemoteCommand> fetched_commands =
+ server_->FetchCommands(last_command_id.Pass(), command_results);
+
+ EXPECT_EQ(fetch_call_expectation.expected_command_results,
+ command_results.size());
+ EXPECT_EQ(fetch_call_expectation.expected_fetched_commands,
+ fetched_commands.size());
+
+ if (!fetch_call_expectation.commands_fetched_callback.is_null())
+ fetch_call_expectation.commands_fetched_callback.Run();
+
+ // Simulate delay from DMServer back to client.
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, base::Bind(callback, DM_STATUS_SUCCESS, fetched_commands),
+ base::TimeDelta::FromSeconds(
+ kTestClientServerCommunicationDelayInSeconds));
+ }
+
+ std::queue<FetchCallExpectation> expected_fetch_commands_calls_;
+ TestingRemoteCommandsServer* server_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestingCloudPolicyClientForRemoteCommands);
+};
+
+// A scoped TestMockTimeTaskRunner capable to run tasks until Quit() is called.
+class ScopedMockTimeTaskRunner : public base::TestMockTimeTaskRunner {
+ public:
+ class ScopedRunner {
+ public:
+ explicit ScopedRunner(
+ const scoped_refptr<ScopedMockTimeTaskRunner>& task_runner)
+ : task_runner_(task_runner) {
+ DCHECK(!task_runner_->attached_runner_);
+ task_runner_->attached_runner_ = this;
+ }
+
+ virtual ~ScopedRunner() {
+ DCHECK_EQ(this, task_runner_->attached_runner_);
+ DCHECK(run_called_);
+ DCHECK(quit_called_);
+
+ task_runner_->attached_runner_ = nullptr;
+ }
+
+ void Run() {
+ DCHECK(!run_called_);
+ run_called_ = true;
+
+ // It's okay to call Quit() before calling Run().
+ if (quit_called_)
+ return;
+ task_runner_->FastForwardUntilNoTasksRemain();
+ }
+
+ void Quit() {
+ DCHECK(!quit_called_);
+ quit_called_ = true;
+ }
+
+ base::Closure QuitClosure() {
+ // It's safe to use Unretained here since Quit() is required to be
+ // called before dtor is called.
+ return base::Bind(&ScopedRunner::Quit, base::Unretained(this));
+ }
+
+ private:
+ friend class ScopedMockTimeTaskRunner;
+
+ bool run_called_ = false;
+ bool quit_called_ = false;
+
+ scoped_refptr<ScopedMockTimeTaskRunner> task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedRunner);
+ };
+
+ ScopedMockTimeTaskRunner() {}
+
+ private:
+ ~ScopedMockTimeTaskRunner() override { DCHECK(!attached_runner_); }
+
+ bool IsElapsingStopped() override {
+ return attached_runner_ && attached_runner_->quit_called_;
+ }
+
+ // Points to the current attached ScopedRunner, and is null if no runner is
+ // attached.
+ ScopedRunner* attached_runner_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedMockTimeTaskRunner);
+};
+
+// Base class for unit tests regarding remote commands service.
+class RemoteCommandsServiceTest : public testing::Test {
+ protected:
+ RemoteCommandsServiceTest()
+ : task_runner_(new ScopedMockTimeTaskRunner()),
+ runner_handle_(task_runner_) {}
+
+ void SetUp() override {
+ server_.reset(new TestingRemoteCommandsServer());
+ server_->SetClock(task_runner_->GetMockClock().Pass());
+ cloud_policy_client_.reset(
+ new TestingCloudPolicyClientForRemoteCommands(server_.get()));
+ }
+
+ void TearDown() override {
+ remote_commands_service_.reset();
+ cloud_policy_client_.reset();
+ server_.reset();
+ }
+
+ void StartService(scoped_ptr<RemoteCommandsFactory> factory) {
+ remote_commands_service_.reset(
+ new RemoteCommandsService(factory.Pass(), cloud_policy_client_.get()));
+ remote_commands_service_->SetClockForTesting(task_runner_->GetMockClock());
+ }
+
+ void FlushAllTasks() {
+ task_runner_->FastForwardUntilNoTasksRemain();
+ }
+
+ scoped_ptr<TestingRemoteCommandsServer> server_;
+ scoped_ptr<TestingCloudPolicyClientForRemoteCommands> cloud_policy_client_;
+ scoped_ptr<RemoteCommandsService> remote_commands_service_;
+
+ scoped_refptr<ScopedMockTimeTaskRunner> task_runner_;
+
+ private:
+ bool service_started_ = false;
+
+ base::ThreadTaskRunnerHandle runner_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(RemoteCommandsServiceTest);
+};
+
+// Tests that no command will be fetched if no commands is issued.
+TEST_F(RemoteCommandsServiceTest, NoCommands) {
+ scoped_ptr<MockTestRemoteCommandFactory> factory(
+ new MockTestRemoteCommandFactory());
+ EXPECT_CALL(*factory, BuildTestCommand()).Times(0);
+
+ StartService(factory.Pass());
+
+ // A fetch requst should get nothing from server.
+ cloud_policy_client_->ExpectFetchCommands(0u, 0u, base::Closure());
+ EXPECT_TRUE(remote_commands_service_->FetchRemoteCommands());
+
+ FlushAllTasks();
+}
+
+// Tests that existing commands issued before service started will be fetched.
+TEST_F(RemoteCommandsServiceTest, ExistingCommand) {
+ scoped_ptr<MockTestRemoteCommandFactory> factory(
+ new MockTestRemoteCommandFactory());
+ EXPECT_CALL(*factory, BuildTestCommand()).Times(1);
+
+ {
+ ScopedMockTimeTaskRunner::ScopedRunner scoped_runner(task_runner_);
+
+ // Issue a command before service started.
+ server_->IssueCommand(em::RemoteCommand_Type_COMMAND_ECHO_TEST,
+ kTestPayload,
+ base::Bind(&ExpectSucceededJob, kTestPayload), false);
+
+ // Start the service, run until the command is fetched.
+ cloud_policy_client_->ExpectFetchCommands(0u, 1u,
+ scoped_runner.QuitClosure());
+ StartService(factory.Pass());
+ EXPECT_TRUE(remote_commands_service_->FetchRemoteCommands());
+
+ scoped_runner.Run();
+ }
+
+ // And run again so that the result can be reported.
+ cloud_policy_client_->ExpectFetchCommands(1u, 0u, base::Closure());
+
+ FlushAllTasks();
+
+ EXPECT_EQ(0u, server_->NumberOfCommandsPendingResult());
+}
+
+// Tests that commands issued after service started will be fetched.
+TEST_F(RemoteCommandsServiceTest, NewCommand) {
+ scoped_ptr<MockTestRemoteCommandFactory> factory(
+ new MockTestRemoteCommandFactory());
+ EXPECT_CALL(*factory, BuildTestCommand()).Times(1);
+
+ StartService(factory.Pass());
+
+ // Set up expectations on fetch commands calls. The first request will fetch
+ // one command, and the second will fetch none but provide result for the
+ // previous command instead.
+ cloud_policy_client_->ExpectFetchCommands(0u, 1u, base::Closure());
+ cloud_policy_client_->ExpectFetchCommands(1u, 0u, base::Closure());
+
+ // Issue a command and manually start a command fetch.
+ server_->IssueCommand(em::RemoteCommand_Type_COMMAND_ECHO_TEST, kTestPayload,
+ base::Bind(&ExpectSucceededJob, kTestPayload), false);
+ EXPECT_TRUE(remote_commands_service_->FetchRemoteCommands());
+
+ FlushAllTasks();
+
+ EXPECT_EQ(0u, server_->NumberOfCommandsPendingResult());
+}
+
+// Tests that commands issued after service started will be fetched, even if
+// the command is issued when a fetch request is ongoing.
+TEST_F(RemoteCommandsServiceTest, NewCommandFollwingFetch) {
+ scoped_ptr<MockTestRemoteCommandFactory> factory(
+ new MockTestRemoteCommandFactory());
+ EXPECT_CALL(*factory, BuildTestCommand()).Times(1);
+
+ StartService(factory.Pass());
+
+ {
+ ScopedMockTimeTaskRunner::ScopedRunner scoped_runner(task_runner_);
+
+ // Add a command which will be issued after first fetch.
+ server_->IssueCommand(em::RemoteCommand_Type_COMMAND_ECHO_TEST,
+ kTestPayload,
+ base::Bind(&ExpectSucceededJob, kTestPayload), true);
+
+ cloud_policy_client_->ExpectFetchCommands(0u, 0u,
+ scoped_runner.QuitClosure());
+
+ // Attempts to fetch commands.
+ EXPECT_TRUE(remote_commands_service_->FetchRemoteCommands());
+
+ // There should be no issued command at this point.
+ EXPECT_EQ(0u, server_->NumberOfCommandsPendingResult());
+
+ // The command fetch should be in progress.
+ EXPECT_TRUE(remote_commands_service_->IsCommandFetchInProgressForTesting());
+
+ // And a following up fetch request should be enqueued.
+ EXPECT_FALSE(remote_commands_service_->FetchRemoteCommands());
+
+ // Run until first fetch request is completed.
+ scoped_runner.Run();
+ }
+
+ // The command should be issued now. Note that this command was actually
+ // issued before the first fetch request completes in previous run loop.
+ EXPECT_EQ(1u, server_->NumberOfCommandsPendingResult());
+
+ cloud_policy_client_->ExpectFetchCommands(0u, 1u, base::Closure());
+ cloud_policy_client_->ExpectFetchCommands(1u, 0u, base::Closure());
+
+ // No further fetch request is made, but the new issued command should be
+ // fetched and executed.
+ FlushAllTasks();
+
+ EXPECT_EQ(0u, server_->NumberOfCommandsPendingResult());
+}
+
+} // namespace policy

Powered by Google App Engine
This is Rietveld 408576698