// Copyright (c) 2011 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 <vector>
#include "base/message_loop.h"
#include "base/string_split.h"
#include "chrome/browser/policy/device_management_backend_impl.h"
#include "chrome/browser/policy/device_management_backend_mock.h"
#include "chrome/browser/policy/device_management_service.h"
#include "chrome/browser/policy/proto/device_management_constants.h"
#include "chrome/common/net/test_url_fetcher_factory.h"
#include "chrome/test/test_url_request_context_getter.h"
#include "content/browser/browser_thread.h"
#include "net/base/escape.h"
#include "net/url_request/url_request_status.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::IgnoreResult;
using testing::InvokeWithoutArgs;
namespace policy {
const char kServiceUrl[] = "https://example.com/management_service";
// Encoded empty response messages for testing the error code paths.
const char kResponseEmpty[] = "\x08\x00";
#define PROTO_STRING(name) (std::string(name, arraysize(name) - 1))
// Some helper constants.
const char kAuthToken[] = "auth-token";
const char kDMToken[] = "device-management-token";
const char kDeviceId[] = "device-id";
// Unit tests for the device management policy service. The tests are run
// against a TestURLFetcherFactory that is used to short-circuit the request
// without calling into the actual network stack.
template<typename TESTBASE>
class DeviceManagementServiceTestBase : public TESTBASE {
protected:
DeviceManagementServiceTestBase()
: request_context_(new TestURLRequestContextGetter()),
io_thread_(BrowserThread::IO, &loop_) {
ResetService();
service_->Initialize(request_context_.get());
}
virtual void SetUp() {
URLFetcher::set_factory(&factory_);
}
virtual void TearDown() {
URLFetcher::set_factory(NULL);
backend_.reset();
service_.reset();
request_context_ = NULL;
loop_.RunAllPending();
}
void ResetService() {
backend_.reset();
service_.reset(new DeviceManagementService(kServiceUrl));
backend_.reset(service_->CreateBackend());
}
TestURLFetcherFactory factory_;
scoped_refptr<TestURLRequestContextGetter> request_context_;
scoped_ptr<DeviceManagementService> service_;
scoped_ptr<DeviceManagementBackend> backend_;
private:
MessageLoopForUI loop_;
BrowserThread io_thread_;
};
struct FailedRequestParams {
FailedRequestParams(DeviceManagementBackend::ErrorCode expected_error,
net::URLRequestStatus::Status request_status,
int http_status,
const std::string& response)
: expected_error_(expected_error),
request_status_(request_status, 0),
http_status_(http_status),
response_(response) {}
DeviceManagementBackend::ErrorCode expected_error_;
net::URLRequestStatus request_status_;
int http_status_;
std::string response_;
};
// A parameterized test case for erroneous response situations, they're mostly
// the same for all kinds of requests.
class DeviceManagementServiceFailedRequestTest
: public DeviceManagementServiceTestBase<
testing::TestWithParam<FailedRequestParams> > {
};
TEST_P(DeviceManagementServiceFailedRequestTest, RegisterRequest) {
DeviceRegisterResponseDelegateMock mock;
EXPECT_CALL(mock, OnError(GetParam().expected_error_));
em::DeviceRegisterRequest request;
backend_->ProcessRegisterRequest(kAuthToken, kDeviceId, request, &mock);
TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
ASSERT_TRUE(fetcher);
fetcher->delegate()->OnURLFetchComplete(fetcher,
GURL(kServiceUrl),
GetParam().request_status_,
GetParam().http_status_,
ResponseCookies(),
GetParam().response_);
}
TEST_P(DeviceManagementServiceFailedRequestTest, UnregisterRequest) {
DeviceUnregisterResponseDelegateMock mock;
EXPECT_CALL(mock, OnError(GetParam().expected_error_));
em::DeviceUnregisterRequest request;
backend_->ProcessUnregisterRequest(kDMToken, kDeviceId, request, &mock);
TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
ASSERT_TRUE(fetcher);
fetcher->delegate()->OnURLFetchComplete(fetcher,
GURL(kServiceUrl),
GetParam().request_status_,
GetParam().http_status_,
ResponseCookies(),
GetParam().response_);
}
TEST_P(DeviceManagementServiceFailedRequestTest, PolicyRequest) {
DevicePolicyResponseDelegateMock mock;
EXPECT_CALL(mock, OnError(GetParam().expected_error_));
em::DevicePolicyRequest request;
request.set_policy_scope(kChromePolicyScope);
em::DevicePolicySettingRequest* setting_request =
request.add_setting_request();
setting_request->set_key(kChromeDevicePolicySettingKey);
backend_->ProcessPolicyRequest(kDMToken, kDeviceId, request, &mock);
TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
ASSERT_TRUE(fetcher);
fetcher->delegate()->OnURLFetchComplete(fetcher,
GURL(kServiceUrl),
GetParam().request_status_,
GetParam().http_status_,
ResponseCookies(),
GetParam().response_);
}
INSTANTIATE_TEST_CASE_P(
DeviceManagementServiceFailedRequestTestInstance,
DeviceManagementServiceFailedRequestTest,
testing::Values(
FailedRequestParams(
DeviceManagementBackend::kErrorRequestFailed,
net::URLRequestStatus::FAILED,
200,
PROTO_STRING(kResponseEmpty)),
FailedRequestParams(
DeviceManagementBackend::kErrorHttpStatus,
net::URLRequestStatus::SUCCESS,
666,
PROTO_STRING(kResponseEmpty)),
FailedRequestParams(
DeviceManagementBackend::kErrorResponseDecoding,
net::URLRequestStatus::SUCCESS,
200,
PROTO_STRING("Not a protobuf.")),
FailedRequestParams(
DeviceManagementBackend::kErrorServiceManagementNotSupported,
net::URLRequestStatus::SUCCESS,
403,
PROTO_STRING(kResponseEmpty)),
FailedRequestParams(
DeviceManagementBackend::kErrorServiceDeviceNotFound,
net::URLRequestStatus::SUCCESS,
901,
PROTO_STRING(kResponseEmpty)),
FailedRequestParams(
DeviceManagementBackend::kErrorServiceManagementTokenInvalid,
net::URLRequestStatus::SUCCESS,
401,
PROTO_STRING(kResponseEmpty)),
FailedRequestParams(
DeviceManagementBackend::kErrorRequestInvalid,
net::URLRequestStatus::SUCCESS,
400,
PROTO_STRING(kResponseEmpty)),
FailedRequestParams(
DeviceManagementBackend::kErrorTemporaryUnavailable,
net::URLRequestStatus::SUCCESS,
404,
PROTO_STRING(kResponseEmpty)),
FailedRequestParams(
DeviceManagementBackend::kErrorServiceActivationPending,
net::URLRequestStatus::SUCCESS,
491,
PROTO_STRING(kResponseEmpty))));
// Simple query parameter parser for testing.
class QueryParams {
public:
explicit QueryParams(const std::string& query) {
base::SplitStringIntoKeyValuePairs(query, '=', '&', ¶ms_);
}
bool Check(const std::string& name, const std::string& expected_value) {
bool found = false;
for (ParamMap::const_iterator i(params_.begin()); i != params_.end(); ++i) {
std::string unescaped_name(
UnescapeURLComponent(i->first,
UnescapeRule::NORMAL |
UnescapeRule::SPACES |
UnescapeRule::URL_SPECIAL_CHARS |
UnescapeRule::CONTROL_CHARS |
UnescapeRule::REPLACE_PLUS_WITH_SPACE));
if (unescaped_name == name) {
if (found)
return false;
found = true;
std::string unescaped_value(
UnescapeURLComponent(i->second,
UnescapeRule::NORMAL |
UnescapeRule::SPACES |
UnescapeRule::URL_SPECIAL_CHARS |
UnescapeRule::CONTROL_CHARS |
UnescapeRule::REPLACE_PLUS_WITH_SPACE));
if (unescaped_value != expected_value)
return false;
}
}
return found;
}
private:
typedef std::vector<std::pair<std::string, std::string> > ParamMap;
ParamMap params_;
};
class DeviceManagementServiceTest
: public DeviceManagementServiceTestBase<testing::Test> {
public:
void ResetBackend() {
backend_.reset();
}
protected:
void CheckURLAndQueryParams(const GURL& request_url,
const std::string& request_type,
const std::string& device_id) {
const GURL service_url(kServiceUrl);
EXPECT_EQ(service_url.scheme(), request_url.scheme());
EXPECT_EQ(service_url.host(), request_url.host());
EXPECT_EQ(service_url.port(), request_url.port());
EXPECT_EQ(service_url.path(), request_url.path());
QueryParams query_params(request_url.query());
EXPECT_TRUE(query_params.Check(
DeviceManagementBackendImpl::kParamRequest, request_type));
EXPECT_TRUE(query_params.Check(
DeviceManagementBackendImpl::kParamDeviceID, device_id));
EXPECT_TRUE(query_params.Check(
DeviceManagementBackendImpl::kParamDeviceType,
DeviceManagementBackendImpl::kValueDeviceType));
EXPECT_TRUE(query_params.Check(
DeviceManagementBackendImpl::kParamAppType,
DeviceManagementBackendImpl::kValueAppType));
EXPECT_TRUE(query_params.Check(
DeviceManagementBackendImpl::kParamAgent,
DeviceManagementBackendImpl::GetAgentString()));
}
};
MATCHER_P(MessageEquals, reference, "") {
std::string reference_data;
std::string arg_data;
return arg.SerializeToString(&arg_data) &&
reference.SerializeToString(&reference_data) &&
arg_data == reference_data;
}
TEST_F(DeviceManagementServiceTest, RegisterRequest) {
DeviceRegisterResponseDelegateMock mock;
em::DeviceRegisterResponse expected_response;
expected_response.set_device_management_token(kDMToken);
EXPECT_CALL(mock, HandleRegisterResponse(MessageEquals(expected_response)));
em::DeviceRegisterRequest request;
backend_->ProcessRegisterRequest(kDMToken, kDeviceId, request, &mock);
TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
ASSERT_TRUE(fetcher);
CheckURLAndQueryParams(fetcher->original_url(),
DeviceManagementBackendImpl::kValueRequestRegister,
kDeviceId);
em::DeviceManagementRequest expected_request_wrapper;
expected_request_wrapper.mutable_register_request()->CopyFrom(request);
std::string expected_request_data;
ASSERT_TRUE(expected_request_wrapper.SerializeToString(
&expected_request_data));
EXPECT_EQ(expected_request_data, fetcher->upload_data());
// Generate the response.
std::string response_data;
em::DeviceManagementResponse response_wrapper;
response_wrapper.mutable_register_response()->CopyFrom(expected_response);
ASSERT_TRUE(response_wrapper.SerializeToString(&response_data));
net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, 0);
fetcher->delegate()->OnURLFetchComplete(fetcher,
GURL(kServiceUrl),
status,
200,
ResponseCookies(),
response_data);
}
TEST_F(DeviceManagementServiceTest, UnregisterRequest) {
DeviceUnregisterResponseDelegateMock mock;
em::DeviceUnregisterResponse expected_response;
EXPECT_CALL(mock, HandleUnregisterResponse(MessageEquals(expected_response)));
em::DeviceUnregisterRequest request;
backend_->ProcessUnregisterRequest(kDMToken, kDeviceId, request, &mock);
TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
ASSERT_TRUE(fetcher);
// Check the data the fetcher received.
const GURL& request_url(fetcher->original_url());
const GURL service_url(kServiceUrl);
EXPECT_EQ(service_url.scheme(), request_url.scheme());
EXPECT_EQ(service_url.host(), request_url.host());
EXPECT_EQ(service_url.port(), request_url.port());
EXPECT_EQ(service_url.path(), request_url.path());
CheckURLAndQueryParams(fetcher->original_url(),
DeviceManagementBackendImpl::kValueRequestUnregister,
kDeviceId);
em::DeviceManagementRequest expected_request_wrapper;
expected_request_wrapper.mutable_unregister_request()->CopyFrom(request);
std::string expected_request_data;
ASSERT_TRUE(expected_request_wrapper.SerializeToString(
&expected_request_data));
EXPECT_EQ(expected_request_data, fetcher->upload_data());
// Generate the response.
std::string response_data;
em::DeviceManagementResponse response_wrapper;
response_wrapper.mutable_unregister_response()->CopyFrom(expected_response);
ASSERT_TRUE(response_wrapper.SerializeToString(&response_data));
net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, 0);
fetcher->delegate()->OnURLFetchComplete(fetcher,
GURL(kServiceUrl),
status,
200,
ResponseCookies(),
response_data);
}
TEST_F(DeviceManagementServiceTest, CancelRegisterRequest) {
DeviceRegisterResponseDelegateMock mock;
EXPECT_CALL(mock, HandleRegisterResponse(_)).Times(0);
em::DeviceRegisterRequest request;
backend_->ProcessRegisterRequest(kAuthToken, kDeviceId, request, &mock);
TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
ASSERT_TRUE(fetcher);
// There shouldn't be any callbacks.
backend_.reset();
}
TEST_F(DeviceManagementServiceTest, CancelUnregisterRequest) {
DeviceUnregisterResponseDelegateMock mock;
EXPECT_CALL(mock, HandleUnregisterResponse(_)).Times(0);
em::DeviceUnregisterRequest request;
backend_->ProcessUnregisterRequest(kDMToken, kDeviceId, request, &mock);
TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
ASSERT_TRUE(fetcher);
// There shouldn't be any callbacks.
backend_.reset();
}
TEST_F(DeviceManagementServiceTest, CancelPolicyRequest) {
DevicePolicyResponseDelegateMock mock;
EXPECT_CALL(mock, HandlePolicyResponse(_)).Times(0);
em::DevicePolicyRequest request;
request.set_policy_scope(kChromePolicyScope);
em::DevicePolicySettingRequest* setting_request =
request.add_setting_request();
setting_request->set_key(kChromeDevicePolicySettingKey);
setting_request->set_watermark("stale");
backend_->ProcessPolicyRequest(kDMToken, kDeviceId, request, &mock);
TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
ASSERT_TRUE(fetcher);
// There shouldn't be any callbacks.
backend_.reset();
}
TEST_F(DeviceManagementServiceTest, JobQueueing) {
// Start with a non-initialized service.
ResetService();
// Make a request. We should not see any fetchers being created.
DeviceRegisterResponseDelegateMock mock;
em::DeviceRegisterResponse expected_response;
expected_response.set_device_management_token(kDMToken);
EXPECT_CALL(mock, HandleRegisterResponse(MessageEquals(expected_response)));
em::DeviceRegisterRequest request;
backend_->ProcessRegisterRequest(kAuthToken, kDeviceId, request, &mock);
TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
ASSERT_FALSE(fetcher);
// Now initialize the service. That should start the job.
service_->Initialize(request_context_.get());
fetcher = factory_.GetFetcherByID(0);
ASSERT_TRUE(fetcher);
factory_.RemoveFetcherFromMap(0);
// Check that the request is processed as expected.
std::string response_data;
em::DeviceManagementResponse response_wrapper;
response_wrapper.mutable_register_response()->CopyFrom(expected_response);
ASSERT_TRUE(response_wrapper.SerializeToString(&response_data));
net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, 0);
fetcher->delegate()->OnURLFetchComplete(fetcher,
GURL(kServiceUrl),
status,
200,
ResponseCookies(),
response_data);
}
TEST_F(DeviceManagementServiceTest, CancelRequestAfterShutdown) {
DevicePolicyResponseDelegateMock mock;
EXPECT_CALL(mock, HandlePolicyResponse(_)).Times(0);
em::DevicePolicyRequest request;
request.set_policy_scope(kChromePolicyScope);
em::DevicePolicySettingRequest* setting_request =
request.add_setting_request();
setting_request->set_key(kChromeDevicePolicySettingKey);
setting_request->set_watermark("stale");
backend_->ProcessPolicyRequest(kDMToken, kDeviceId, request, &mock);
TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
ASSERT_TRUE(fetcher);
// Shutdown the service and cancel the job afterwards.
service_->Shutdown();
backend_.reset();
}
TEST_F(DeviceManagementServiceTest, CancelDuringCallback) {
// Make a request.
DeviceRegisterResponseDelegateMock mock;
EXPECT_CALL(mock, OnError(_))
.WillOnce(InvokeWithoutArgs(this,
&DeviceManagementServiceTest::ResetBackend))
.RetiresOnSaturation();
em::DeviceRegisterRequest request;
backend_->ProcessRegisterRequest(kAuthToken, kDeviceId, request, &mock);
TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
ASSERT_TRUE(fetcher);
// Generate a callback.
net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, 0);
fetcher->delegate()->OnURLFetchComplete(fetcher,
GURL(kServiceUrl),
status,
500,
ResponseCookies(),
"");
// Backend should have been reset.
EXPECT_FALSE(backend_.get());
}
} // namespace policy