Commit e7daf77d authored by David Maunder's avatar David Maunder Committed by Commit Bot

Endpoint Fetcher

This is a generic component for making GET and POST requests and supports
authentication.

Error reporting is not included which will be added in a separate patch.

Bug: 985015
Change-Id: I42f4ee93c680be26e9bbe388ba52808c915532d3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1706874
Commit-Queue: David Maunder <davidjm@chromium.org>
Reviewed-by: default avatarTommy Nyquist <nyquist@chromium.org>
Reviewed-by: default avatarYusuf Ozuysal <yusufo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#692041}
parent 291798b8
...@@ -2437,6 +2437,8 @@ generate_jni("chrome_jni_headers") { ...@@ -2437,6 +2437,8 @@ generate_jni("chrome_jni_headers") {
"java/src/org/chromium/chrome/browser/childaccounts/ChildAccountFeedbackReporter.java", "java/src/org/chromium/chrome/browser/childaccounts/ChildAccountFeedbackReporter.java",
"java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java", "java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java",
"java/src/org/chromium/chrome/browser/complex_tasks/TaskTabHelper.java", "java/src/org/chromium/chrome/browser/complex_tasks/TaskTabHelper.java",
"java/src/org/chromium/chrome/browser/complex_tasks/endpoint_fetcher/EndpointFetcher.java",
"java/src/org/chromium/chrome/browser/complex_tasks/endpoint_fetcher/EndpointResponse.java",
"java/src/org/chromium/chrome/browser/component_updater/UpdateScheduler.java", "java/src/org/chromium/chrome/browser/component_updater/UpdateScheduler.java",
"java/src/org/chromium/chrome/browser/component_updater/VrAssetsComponentInstaller.java", "java/src/org/chromium/chrome/browser/component_updater/VrAssetsComponentInstaller.java",
"java/src/org/chromium/chrome/browser/compositor/CompositorView.java", "java/src/org/chromium/chrome/browser/compositor/CompositorView.java",
......
...@@ -174,6 +174,8 @@ chrome_java_sources = [ ...@@ -174,6 +174,8 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/childaccounts/ChildAccountFeedbackReporter.java", "java/src/org/chromium/chrome/browser/childaccounts/ChildAccountFeedbackReporter.java",
"java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java", "java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java",
"java/src/org/chromium/chrome/browser/complex_tasks/TaskTabHelper.java", "java/src/org/chromium/chrome/browser/complex_tasks/TaskTabHelper.java",
"java/src/org/chromium/chrome/browser/complex_tasks/endpoint_fetcher/EndpointFetcher.java",
"java/src/org/chromium/chrome/browser/complex_tasks/endpoint_fetcher/EndpointResponse.java",
"java/src/org/chromium/chrome/browser/component_updater/UpdateScheduler.java", "java/src/org/chromium/chrome/browser/component_updater/UpdateScheduler.java",
"java/src/org/chromium/chrome/browser/component_updater/UpdateTask.java", "java/src/org/chromium/chrome/browser/component_updater/UpdateTask.java",
"java/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManager.java", "java/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManager.java",
......
// Copyright 2019 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.
package org.chromium.chrome.browser.complex_tasks.endpoint_fetcher;
import android.support.annotation.MainThread;
import org.chromium.base.Callback;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.chrome.browser.profiles.Profile;
/**
* EndpointFetcher uses native EndpointFetcher to call a HTTPS endpoint and return
* the response. The call to native EndpointFetcher is achieved over a static call
* over JNI. The native EndpointFetcher is created during the static call and
* destroyed in the callback function.
* EndpointFetcher currently doesn't support incognito mode.
* If the request times out an empty response will be returned. There will also
* be an error code indicating timeout once more detailed error messaging is added
* TODO(crbug.com/993393).
*/
public final class EndpointFetcher {
private EndpointFetcher() {}
/**
* Calls an endpoint and returns the response via a callback
* @param callback callback function response is returned in
* @param profile profile via which the endpoint is called
* @param oathConsumerName consumer name for OAuth
* @param url endpoint URL called
* @param httpsMethod the HTTPS method used e.g. "GET" or "POST"
* @param contentType the content type e.g. "application/json"
* @param scopes scopes used as part of authentication
* @param postData data for a "POST" request
* @param timeout time after which the request will terminate in the event a response hasn't
* been received
*/
@MainThread
public static void fetch(Callback<EndpointResponse> callback, Profile profile,
String oathConsumerName, String url, String httpsMethod, String contentType,
String[] scopes, String postData, long timeout) {
// EndpointFetcher currently does not support incognito mode
assert !profile.isOffTheRecord();
EndpointFetcherJni.get().nativeFetch(profile, oathConsumerName, url, httpsMethod,
contentType, scopes, postData, timeout, callback);
}
@NativeMethods
interface Natives {
void nativeFetch(Profile profile, String oathConsumerName, String url, String httpsMethod,
String contentType, String[] scopes, String postData, long timeout,
Callback callback);
}
}
// Copyright 2019 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.
package org.chromium.chrome.browser.complex_tasks.endpoint_fetcher;
import org.chromium.base.annotations.CalledByNative;
/**
* Encapsulates the response from the {@Link EndpointFetcher}
*/
public class EndpointResponse {
private final String mResponseString;
/**
* Create the EndpointResponse
* @param responseString the response string acquired from the endpoint
*/
public EndpointResponse(String responseString) {
mResponseString = responseString;
}
/**
* Response string acquired from calling an endpoint
*/
public String getResponseString() {
return mResponseString;
}
@CalledByNative
private static EndpointResponse createEndpointResponse(String response) {
return new EndpointResponse(response);
}
}
...@@ -305,6 +305,8 @@ jumbo_split_static_library("browser") { ...@@ -305,6 +305,8 @@ jumbo_split_static_library("browser") {
"command_updater_delegate.h", "command_updater_delegate.h",
"command_updater_impl.cc", "command_updater_impl.cc",
"command_updater_impl.h", "command_updater_impl.h",
"complex_tasks/endpoint_fetcher/endpoint_fetcher.cc",
"complex_tasks/endpoint_fetcher/endpoint_fetcher.h",
"complex_tasks/task_tab_helper.cc", "complex_tasks/task_tab_helper.cc",
"complex_tasks/task_tab_helper.h", "complex_tasks/task_tab_helper.h",
"component_updater/chrome_component_updater_configurator.cc", "component_updater/chrome_component_updater_configurator.cc",
......
// Copyright 2019 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 "chrome/browser/complex_tasks/endpoint_fetcher/endpoint_fetcher.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "components/signin/public/identity_manager/access_token_info.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "content/public/browser/storage_partition.h"
#include "google_apis/gaia/gaia_urls.h"
#include "services/network/public/cpp/simple_url_loader.h"
#if defined(OS_ANDROID)
#include "base/android/callback_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "chrome/android/chrome_jni_headers/EndpointFetcher_jni.h"
#include "chrome/android/chrome_jni_headers/EndpointResponse_jni.h"
#include "chrome/browser/profiles/profile_android.h"
#endif // defined(OS_ANDROID)
namespace {
const char kContentTypeKey[] = "Content-Type";
const char kDeveloperKey[] = "X-Developer-Key";
const int kNumRetries = 3;
} // namespace
EndpointFetcher::EndpointFetcher(
Profile* const profile,
const std::string& oauth_consumer_name,
const GURL& url,
const std::string& http_method,
const std::string& content_type,
const std::vector<std::string>& scopes,
int64_t timeout_ms,
const std::string& post_data,
const net::NetworkTrafficAnnotationTag& annotation_tag)
: EndpointFetcher(
oauth_consumer_name,
url,
http_method,
content_type,
scopes,
timeout_ms,
post_data,
annotation_tag,
content::BrowserContext::GetDefaultStoragePartition(profile)
->GetURLLoaderFactoryForBrowserProcess(),
IdentityManagerFactory::GetForProfile(profile)) {}
EndpointFetcher::EndpointFetcher(
const std::string& oauth_consumer_name,
const GURL& url,
const std::string& http_method,
const std::string& content_type,
const std::vector<std::string>& scopes,
int64_t timeout_ms,
const std::string& post_data,
const net::NetworkTrafficAnnotationTag& annotation_tag,
const scoped_refptr<network::SharedURLLoaderFactory>& url_loader_factory,
signin::IdentityManager* const identity_manager)
: oauth_consumer_name_(oauth_consumer_name),
url_(url),
http_method_(http_method),
content_type_(content_type),
timeout_ms_(timeout_ms),
post_data_(post_data),
annotation_tag_(annotation_tag),
url_loader_factory_(url_loader_factory),
identity_manager_(identity_manager) {
for (auto scope : scopes) {
oauth_scopes_.insert(scope);
}
}
EndpointFetcher::~EndpointFetcher() = default;
void EndpointFetcher::Fetch(EndpointFetcherCallback endpoint_fetcher_callback) {
signin::AccessTokenFetcher::TokenCallback token_callback = base::BindOnce(
&EndpointFetcher::OnAuthTokenFetched, weak_ptr_factory_.GetWeakPtr(),
std::move(endpoint_fetcher_callback));
DCHECK(!access_token_fetcher_);
DCHECK(!simple_url_loader_);
// TODO(crbug.com/997018) Make access_token_fetcher_ local variable passed to
// callback
access_token_fetcher_ =
std::make_unique<signin::PrimaryAccountAccessTokenFetcher>(
oauth_consumer_name_, identity_manager_, oauth_scopes_,
std::move(token_callback),
signin::PrimaryAccountAccessTokenFetcher::Mode::kImmediate);
}
void EndpointFetcher::OnAuthTokenFetched(
EndpointFetcherCallback endpoint_fetcher_callback,
GoogleServiceAuthError error,
signin::AccessTokenInfo access_token_info) {
access_token_fetcher_.reset();
if (error.state() != GoogleServiceAuthError::NONE) {
auto response = std::make_unique<EndpointResponse>();
response->response = "There was an authentication error";
// TODO(crbug.com/993393) Add more detailed error messaging
std::move(endpoint_fetcher_callback).Run(std::move(response));
return;
}
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->method = http_method_;
resource_request->url = url_;
resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
if (base::EqualsCaseInsensitiveASCII(http_method_, "POST")) {
resource_request->headers.SetHeader(kContentTypeKey, content_type_);
}
resource_request->headers.SetHeader(
kDeveloperKey, GaiaUrls::GetInstance()->oauth2_chrome_client_id());
resource_request->headers.SetHeader(
net::HttpRequestHeaders::kAuthorization,
base::StringPrintf("Bearer %s", access_token_info.token.c_str()));
// TODO(crbug.com/997018) Make simple_url_loader_ local variable passed to
// callback
simple_url_loader_ = network::SimpleURLLoader::Create(
std::move(resource_request), annotation_tag_);
if (base::EqualsCaseInsensitiveASCII(http_method_, "POST")) {
simple_url_loader_->AttachStringForUpload(post_data_, content_type_);
}
simple_url_loader_->SetRetryOptions(kNumRetries,
network::SimpleURLLoader::RETRY_ON_5XX);
simple_url_loader_->SetTimeoutDuration(
base::TimeDelta::FromMilliseconds(timeout_ms_));
network::SimpleURLLoader::BodyAsStringCallback body_as_string_callback =
base::BindOnce(&EndpointFetcher::OnResponseFetched,
weak_ptr_factory_.GetWeakPtr(),
std::move(endpoint_fetcher_callback));
simple_url_loader_->DownloadToString(
url_loader_factory_.get(), std::move(body_as_string_callback),
network::SimpleURLLoader::kMaxBoundedStringDownloadSize);
}
void EndpointFetcher::OnResponseFetched(
EndpointFetcherCallback endpoint_fetcher_callback,
std::unique_ptr<std::string> response_body) {
simple_url_loader_.reset();
auto response = std::make_unique<EndpointResponse>();
// TODO(crbug.com/993393) Add more detailed error messaging
response->response =
response_body ? std::move(*response_body) : "There was a response error";
std::move(endpoint_fetcher_callback).Run(std::move(response));
}
#if defined(OS_ANDROID)
namespace {
static void OnEndpointFetcherComplete(
const base::android::JavaRef<jobject>& jcaller,
// Passing the endpoint_fetcher ensures the endpoint_fetcher's
// lifetime extends to the callback and is not destroyed
// prematurely (which would result in cancellation of the request).
std::unique_ptr<EndpointFetcher> endpoint_fetcher,
std::unique_ptr<EndpointResponse> endpoint_response) {
base::android::RunObjectCallbackAndroid(
jcaller, Java_EndpointResponse_createEndpointResponse(
base::android::AttachCurrentThread(),
base::android::ConvertUTF8ToJavaString(
base::android::AttachCurrentThread(),
std::move(endpoint_response->response))));
}
} // namespace
static void JNI_EndpointFetcher_NativeFetch(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jprofile,
const base::android::JavaParamRef<jstring>& joauth_consumer_name,
const base::android::JavaParamRef<jstring>& jurl,
const base::android::JavaParamRef<jstring>& jhttps_method,
const base::android::JavaParamRef<jstring>& jcontent_type,
const base::android::JavaParamRef<jobjectArray>& jscopes,
const base::android::JavaParamRef<jstring>& jpost_data,
jlong jtimeout,
const base::android::JavaParamRef<jobject>& jcallback) {
std::vector<std::string> scopes;
base::android::AppendJavaStringArrayToStringVector(env, jscopes, &scopes);
auto endpoint_fetcher = std::make_unique<EndpointFetcher>(
ProfileAndroid::FromProfileAndroid(jprofile),
base::android::ConvertJavaStringToUTF8(env, joauth_consumer_name),
GURL(base::android::ConvertJavaStringToUTF8(env, jurl)),
base::android::ConvertJavaStringToUTF8(env, jhttps_method),
base::android::ConvertJavaStringToUTF8(env, jcontent_type), scopes,
jtimeout, base::android::ConvertJavaStringToUTF8(env, jpost_data),
// TODO(crbug.com/995852) Create a traffic annotation tag and configure it
// as part of the EndpointFetcher call over JNI.
NO_TRAFFIC_ANNOTATION_YET);
endpoint_fetcher->Fetch(
base::BindOnce(&OnEndpointFetcherComplete,
base::android::ScopedJavaGlobalRef<jobject>(jcallback),
// unique_ptr endpoint_fetcher is passed until the callback
// to ensure its lifetime across the request.
std::move(endpoint_fetcher)));
}
#endif // defined(OS_ANDROID)
// Copyright 2019 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.
#ifndef CHROME_BROWSER_COMPLEX_TASKS_ENDPOINT_FETCHER_ENDPOINT_FETCHER_H_
#define CHROME_BROWSER_COMPLEX_TASKS_ENDPOINT_FETCHER_ENDPOINT_FETCHER_H_
#include <string>
#include <vector>
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
namespace network {
struct ResourceRequest;
class SimpleURLLoader;
} // namespace network
namespace signin {
struct AccessTokenInfo;
class IdentityManager;
} // namespace signin
class GoogleServiceAuthError;
class GURL;
class Profile;
struct EndpointResponse {
std::string response;
// TODO(crbug.com/993393) Add more detailed error messaging
};
using EndpointFetcherCallback =
base::OnceCallback<void(std::unique_ptr<EndpointResponse>)>;
// EndpointFetcher calls an endpoint and returns the response.
// EndpointFetcher is not thread safe and it is up to the caller
// to wait until the callback function passed to Fetch() completes
// before invoking Fetch() again.
// Destroying an EndpointFetcher will result in the in-flight request being
// cancelled.
// EndpointFetcher performs authentication via the signed in user to
// Chrome.
// If the request times out an empty response will be returned. There will also
// be an error code indicating timeout once more detailed error messaging is
// added TODO(crbug.com/993393).
class EndpointFetcher {
public:
// Preferred constructor - forms identity_manager and url_loader_factory.
EndpointFetcher(Profile* const profile,
const std::string& oauth_consumer_name,
const GURL& url,
const std::string& http_method,
const std::string& content_type,
const std::vector<std::string>& scopes,
int64_t timeout_ms,
const std::string& post_data,
const net::NetworkTrafficAnnotationTag& annotation_tag);
// Used for tests. Can be used if caller constructs their own
// url_loader_factory and identity_manager.
EndpointFetcher(
const std::string& oauth_consumer_name,
const GURL& url,
const std::string& http_method,
const std::string& content_type,
const std::vector<std::string>& scopes,
int64_t timeout_ms,
const std::string& post_data,
const net::NetworkTrafficAnnotationTag& annotation_tag,
const scoped_refptr<network::SharedURLLoaderFactory>& url_loader_factory,
signin::IdentityManager* const identity_manager);
EndpointFetcher(const EndpointFetcher& endpoint_fetcher) = delete;
EndpointFetcher& operator=(const EndpointFetcher& endpoint_fetcher) = delete;
~EndpointFetcher();
// TODO(crbug.com/999256) enable cancellation support
void Fetch(EndpointFetcherCallback callback);
private:
void OnAuthTokenFetched(EndpointFetcherCallback callback,
GoogleServiceAuthError error,
signin::AccessTokenInfo access_token_info);
void OnResponseFetched(EndpointFetcherCallback callback,
std::unique_ptr<std::string> response_body);
// Members set in constructor to be passed to network::ResourceRequest or
// network::SimpleURLLoader.
const std::string oauth_consumer_name_;
const GURL url_;
const std::string http_method_;
const std::string content_type_;
int64_t timeout_ms_;
const std::string post_data_;
const net::NetworkTrafficAnnotationTag annotation_tag_;
identity::ScopeSet oauth_scopes_;
// Members set in constructor
const scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
signin::IdentityManager* const identity_manager_;
// Members set in Fetch
std::unique_ptr<const signin::PrimaryAccountAccessTokenFetcher>
access_token_fetcher_;
std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
base::WeakPtrFactory<EndpointFetcher> weak_ptr_factory_{this};
};
#endif // CHROME_BROWSER_COMPLEX_TASKS_ENDPOINT_FETCHER_ENDPOINT_FETCHER_H_
// Copyright 2019 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 "chrome/browser/complex_tasks/endpoint_fetcher/endpoint_fetcher.h"
#include <string>
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "net/http/http_util.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using MockEndpointFetcherCallback = base::MockCallback<EndpointFetcherCallback>;
namespace {
const char kContentType[] = "mock_content_type";
const char kEmail[] = "mock_email";
const char kEndpoint[] = "https://my-endpoint.com";
const char kExpectedResponse[] = "mock_response";
const char kExpectedAuthError[] = "There was an authentication error";
const char kExpectedResponseError[] = "There was a response error";
const char kHttpMethod[] = "POST";
const char kMockPostData[] = "mock_post_data";
int64_t kMockTimeoutMs = 1000000;
const char kOAuthConsumerName[] = "mock_oauth_consumer_name";
const char kScope[] = "mock_scope";
} // namespace
using ::testing::Field;
using ::testing::Pointee;
class EndpointFetcherTest : public testing::Test {
protected:
EndpointFetcherTest() {}
EndpointFetcherTest(const EndpointFetcherTest& endpoint_fetcher_test) =
delete;
EndpointFetcherTest& operator=(
const EndpointFetcherTest& endpoint_fetcher_test) = delete;
~EndpointFetcherTest() override {}
void SetUp() override {
scoped_refptr<network::SharedURLLoaderFactory> test_url_loader_factory =
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_);
endpoint_fetcher_ = std::make_unique<EndpointFetcher>(
kOAuthConsumerName, GURL(kEndpoint), kHttpMethod, kContentType,
std::vector<std::string>{kScope}, kMockTimeoutMs, kMockPostData,
TRAFFIC_ANNOTATION_FOR_TESTS, test_url_loader_factory,
identity_test_env_.identity_manager());
SignIn();
}
void SignIn() {
identity_test_env_.MakePrimaryAccountAvailable(kEmail);
identity_test_env_.SetAutomaticIssueOfAccessTokens(true);
}
MockEndpointFetcherCallback& endpoint_fetcher_callback() {
return mock_callback_;
}
EndpointFetcher* endpoint_fetcher() { return endpoint_fetcher_.get(); }
network::TestURLLoaderFactory* test_url_loader_factory() {
return &test_url_loader_factory_;
}
signin::IdentityTestEnvironment& identity_test_env() {
return identity_test_env_;
}
void SetMockResponse(const GURL& request_url,
const std::string& response_data,
net::HttpStatusCode response_code,
net::Error error) {
network::ResourceResponseHead head;
std::string headers(base::StringPrintf(
"HTTP/1.1 %d %s\nContent-type: application/json\n\n",
static_cast<int>(response_code), GetHttpReasonPhrase(response_code)));
head.headers = base::MakeRefCounted<net::HttpResponseHeaders>(
net::HttpUtil::AssembleRawHeaders(headers));
head.mime_type = "application/json";
network::URLLoaderCompletionStatus status(error);
status.decoded_body_length = response_data.size();
test_url_loader_factory_.AddResponse(request_url, head, response_data,
status);
}
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
signin::IdentityTestEnvironment identity_test_env_;
MockEndpointFetcherCallback mock_callback_;
network::TestURLLoaderFactory test_url_loader_factory_;
std::unique_ptr<EndpointFetcher> endpoint_fetcher_;
};
TEST_F(EndpointFetcherTest, FetchResponse) {
SetMockResponse(GURL(kEndpoint), kExpectedResponse, net::HTTP_OK, net::OK);
EXPECT_CALL(
endpoint_fetcher_callback(),
Run(Pointee(Field(&EndpointResponse::response, kExpectedResponse))));
endpoint_fetcher()->Fetch(endpoint_fetcher_callback().Get());
base::RunLoop().RunUntilIdle();
}
TEST_F(EndpointFetcherTest, FetchEndpointResponseError) {
SetMockResponse(GURL(kEndpoint), "", net::HTTP_BAD_REQUEST, net::ERR_FAILED);
EXPECT_CALL(
endpoint_fetcher_callback(),
Run(Pointee(Field(&EndpointResponse::response, kExpectedResponseError))));
endpoint_fetcher()->Fetch(endpoint_fetcher_callback().Get());
base::RunLoop().RunUntilIdle();
}
TEST_F(EndpointFetcherTest, FetchOAuthError) {
identity_test_env().SetAutomaticIssueOfAccessTokens(false);
EXPECT_CALL(
endpoint_fetcher_callback(),
Run(Pointee(Field(&EndpointResponse::response, kExpectedAuthError))));
endpoint_fetcher()->Fetch(endpoint_fetcher_callback().Get());
identity_test_env().WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
base::RunLoop().RunUntilIdle();
}
...@@ -2849,6 +2849,7 @@ test("unit_tests") { ...@@ -2849,6 +2849,7 @@ test("unit_tests") {
"../browser/chrome_content_browser_client_unittest.cc", "../browser/chrome_content_browser_client_unittest.cc",
"../browser/chrome_process_singleton_win_unittest.cc", "../browser/chrome_process_singleton_win_unittest.cc",
"../browser/command_updater_impl_unittest.cc", "../browser/command_updater_impl_unittest.cc",
"../browser/complex_tasks/endpoint_fetcher/endpoint_fetcher_unittest.cc",
"../browser/complex_tasks/task_tab_helper_unittest.cc", "../browser/complex_tasks/task_tab_helper_unittest.cc",
"../browser/component_updater/chrome_component_updater_configurator_unittest.cc", "../browser/component_updater/chrome_component_updater_configurator_unittest.cc",
"../browser/component_updater/optimization_hints_component_installer_unittest.cc", "../browser/component_updater/optimization_hints_component_installer_unittest.cc",
......
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