text: // Copyright (c) 2010 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 "content/browser/geolocation/access_token_store.h"
  
  #include "base/string_util.h"
  #include "base/utf_string_conversions.h"
  #include "chrome/test/in_process_browser_test.h"
  #include "chrome/test/ui_test_utils.h"
  #include "content/browser/browser_thread.h"
  
  namespace {
  
  // The token store factory implementation expects to be used from any well-known
  // chrome thread other than UI. We could use any arbitrary thread; IO is
  // a good choice as this is the expected usage.
  const BrowserThread::ID kExpectedClientThreadId = BrowserThread::IO;
  const char* kRefServerUrl1 = "https://test.domain.example/foo?id=bar.bar";
  const char* kRefServerUrl2 = "http://another.domain.example/foo?id=bar.bar#2";
  
  class GeolocationAccessTokenStoreTest
      : public InProcessBrowserTest {
   public:
    GeolocationAccessTokenStoreTest()
        : token_to_expect_(NULL), token_to_set_(NULL) {}
  
    void DoTestStepAndWaitForResults(
        const char* ref_url, const string16* token_to_expect,
        const string16* token_to_set);
  
    void OnAccessTokenStoresLoaded(
          AccessTokenStore::AccessTokenSet access_token_set);
  
    scoped_refptr<AccessTokenStore> token_store_;
    CancelableRequestConsumer request_consumer_;
    GURL ref_url_;
    const string16* token_to_expect_;
    const string16* token_to_set_;
  };
  
  void StartTestStepFromClientThread(
      scoped_refptr<AccessTokenStore>* store,
      CancelableRequestConsumerBase* consumer,
      AccessTokenStore::LoadAccessTokensCallbackType* callback) {
    ASSERT_TRUE(BrowserThread::CurrentlyOn(kExpectedClientThreadId));
    if (*store == NULL)
      (*store) = NewChromePrefsAccessTokenStore();
    (*store)->LoadAccessTokens(consumer, callback);
  }
  
  struct TokenLoadClientForTest {
    void NotReachedCallback(AccessTokenStore::AccessTokenSet /*tokens*/) {
      NOTREACHED() << "This request should have been canceled before callback";
    }
  };
  
  void RunCancelTestInClientTread() {
    ASSERT_TRUE(BrowserThread::CurrentlyOn(kExpectedClientThreadId));
    scoped_refptr<AccessTokenStore> store(NewChromePrefsAccessTokenStore());
    CancelableRequestConsumer consumer;
    TokenLoadClientForTest load_client;
  
    // Single request, canceled explicitly
    CancelableRequestProvider::Handle first_handle =
        store->LoadAccessTokens(&consumer, NewCallback(
            &load_client, &TokenLoadClientForTest::NotReachedCallback));
    EXPECT_TRUE(consumer.HasPendingRequests());
    // Test this handle is valid.
    consumer.GetClientData(store.get(), first_handle);
    store->CancelRequest(first_handle);
    EXPECT_FALSE(consumer.HasPendingRequests());
  
    // 2 requests, canceled globally.
    store->LoadAccessTokens(&consumer, NewCallback(
        &load_client, &TokenLoadClientForTest::NotReachedCallback));
    store->LoadAccessTokens(&consumer, NewCallback(
        &load_client, &TokenLoadClientForTest::NotReachedCallback));
    EXPECT_TRUE(consumer.HasPendingRequests());
    EXPECT_EQ(2u, consumer.PendingRequestCount());
    consumer.CancelAllRequests();
    EXPECT_FALSE(consumer.HasPendingRequests());
    EXPECT_EQ(0u, consumer.PendingRequestCount());
  
    BrowserThread::PostTask(
        BrowserThread::UI, FROM_HERE, new MessageLoop::QuitTask);
  }
  
  void GeolocationAccessTokenStoreTest::DoTestStepAndWaitForResults(
      const char* ref_url, const string16* token_to_expect,
      const string16* token_to_set) {
    ref_url_ = GURL(ref_url);
    token_to_expect_ = token_to_expect;
    token_to_set_ = token_to_set;
  
    BrowserThread::PostTask(
        kExpectedClientThreadId, FROM_HERE, NewRunnableFunction(
            &StartTestStepFromClientThread, &token_store_, &request_consumer_,
            NewCallback(this,
                &GeolocationAccessTokenStoreTest::OnAccessTokenStoresLoaded)));
    ui_test_utils::RunMessageLoop();
  }
  
  void GeolocationAccessTokenStoreTest::OnAccessTokenStoresLoaded(
      AccessTokenStore::AccessTokenSet access_token_set) {
    ASSERT_TRUE(BrowserThread::CurrentlyOn(kExpectedClientThreadId))
        << "Callback from token factory should be from the same thread as the "
           "LoadAccessTokenStores request was made on";
    EXPECT_TRUE(token_to_set_ || token_to_expect_) << "No work to do?";
    AccessTokenStore::AccessTokenSet::const_iterator item =
        access_token_set.find(ref_url_);
    if (!token_to_expect_) {
      EXPECT_TRUE(item == access_token_set.end());
    } else {
      EXPECT_FALSE(item == access_token_set.end());
      EXPECT_EQ(*token_to_expect_, item->second);
    }
  
    if (token_to_set_) {
      scoped_refptr<AccessTokenStore> store(
          NewChromePrefsAccessTokenStore());
      store->SaveAccessToken(ref_url_, *token_to_set_);
    }
    BrowserThread::PostTask(
        BrowserThread::UI, FROM_HERE, new MessageLoop::QuitTask);
  }
  
  IN_PROC_BROWSER_TEST_F(GeolocationAccessTokenStoreTest, SetAcrossInstances) {
    const string16 ref_token1 = ASCIIToUTF16("jksdfo90,'s#\"#1*(");
    const string16 ref_token2 = ASCIIToUTF16("\1\2\3\4\5\6\7\10\11\12=023");
    ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
  
    DoTestStepAndWaitForResults(kRefServerUrl1, NULL, &ref_token1);
    // Check it was set, and change to new value.
    DoTestStepAndWaitForResults(kRefServerUrl1, &ref_token1, &ref_token2);
    // And change back.
    DoTestStepAndWaitForResults(kRefServerUrl1, &ref_token2, &ref_token1);
    DoTestStepAndWaitForResults(kRefServerUrl1, &ref_token1, NULL);
  
    // Set a second server URL
    DoTestStepAndWaitForResults(kRefServerUrl2, NULL, &ref_token2);
    DoTestStepAndWaitForResults(kRefServerUrl2, &ref_token2, NULL);
    DoTestStepAndWaitForResults(kRefServerUrl1, &ref_token1, NULL);
  }
  
  IN_PROC_BROWSER_TEST_F(GeolocationAccessTokenStoreTest, CancelRequest) {
    BrowserThread::PostTask(
        kExpectedClientThreadId, FROM_HERE, NewRunnableFunction(
            RunCancelTestInClientTread));
    ui_test_utils::RunMessageLoop();
  }
  
  }  // namespace
  