| // 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 |