blob: 329e8461440dc1946ce8058cc6e9a349692180d8 [file] [log] [blame]
// Copyright 2012 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 "ios/net/cookies/cookie_store_ios.h"
#import <Foundation/Foundation.h>
#include <memory>
#include "base/bind_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/sys_string_conversions.h"
#import "ios/net/cookies/cookie_store_ios_test_util.h"
#import "net/base/mac/url_conversions.h"
#include "net/cookies/cookie_store_unittest.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
struct CookieStoreIOSTestTraits {
static std::unique_ptr<net::CookieStore> Create() {
ClearCookies();
return base::MakeUnique<CookieStoreIOS>(
[NSHTTPCookieStorage sharedHTTPCookieStorage]);
}
static const bool supports_http_only = false;
static const bool supports_non_dotted_domains = false;
static const bool preserves_trailing_dots = false;
static const bool filters_schemes = false;
static const bool has_path_prefix_bug = true;
static const int creation_time_granularity_in_ms = 1000;
base::MessageLoop loop_;
};
INSTANTIATE_TYPED_TEST_CASE_P(CookieStoreIOS,
CookieStoreTest,
CookieStoreIOSTestTraits);
namespace {
// Helper callback to be passed to CookieStore::GetAllCookiesForURLAsync().
class GetAllCookiesCallback {
public:
GetAllCookiesCallback() : did_run_(false) {}
// Returns true if the callback has been run.
bool did_run() { return did_run_; }
// Returns the parameter of the callback.
const net::CookieList& cookie_list() { return cookie_list_; }
void Run(const net::CookieList& cookie_list) {
ASSERT_FALSE(did_run_);
did_run_ = true;
cookie_list_ = cookie_list;
}
private:
bool did_run_;
net::CookieList cookie_list_;
};
void IgnoreBoolean(bool ignored) {
}
void IgnoreString(const std::string& ignored) {
}
} // namespace
// Test fixture to exersize net::CookieStoreIOS created without backend and
// synchronized with |[NSHTTPCookieStorage sharedHTTPCookieStorage]|.
class CookieStoreIOSTest : public testing::Test {
public:
CookieStoreIOSTest()
: kTestCookieURL("http://foo.google.com/bar"),
kTestCookieURL2("http://foo.google.com/baz"),
kTestCookieURL3("http://foo.google.com"),
kTestCookieURL4("http://bar.google.com/bar"),
backend_(new TestPersistentCookieStore),
store_(base::MakeUnique<net::CookieStoreIOS>(
[NSHTTPCookieStorage sharedHTTPCookieStorage])) {
cookie_changed_callback_ = store_->AddCallbackForCookie(
kTestCookieURL, "abc",
base::Bind(&RecordCookieChanges, &cookies_changed_, &cookies_removed_));
}
~CookieStoreIOSTest() override {}
// Gets the cookies. |callback| will be called on completion.
void GetCookies(const net::CookieStore::GetCookiesCallback& callback) {
net::CookieOptions options;
options.set_include_httponly();
store_->GetCookiesWithOptionsAsync(kTestCookieURL, options, callback);
}
// Sets a cookie.
void SetCookie(const std::string& cookie_line) {
net::SetCookie(cookie_line, kTestCookieURL, store_.get());
}
void SetSystemCookie(const GURL& url,
const std::string& name,
const std::string& value) {
NSHTTPCookieStorage* storage =
[NSHTTPCookieStorage sharedHTTPCookieStorage];
[storage setCookie:[NSHTTPCookie cookieWithProperties:@{
NSHTTPCookiePath : base::SysUTF8ToNSString(url.path()),
NSHTTPCookieName : base::SysUTF8ToNSString(name),
NSHTTPCookieValue : base::SysUTF8ToNSString(value),
NSHTTPCookieDomain : base::SysUTF8ToNSString(url.host()),
}]];
net::CookieStoreIOS::NotifySystemCookiesChanged();
base::RunLoop().RunUntilIdle();
}
void DeleteSystemCookie(const GURL& gurl, const std::string& name) {
NSHTTPCookieStorage* storage =
[NSHTTPCookieStorage sharedHTTPCookieStorage];
NSURL* nsurl = net::NSURLWithGURL(gurl);
NSArray* cookies = [storage cookiesForURL:nsurl];
for (NSHTTPCookie* cookie in cookies) {
if (cookie.name.UTF8String == name) {
[storage deleteCookie:cookie];
break;
}
}
net::CookieStoreIOS::NotifySystemCookiesChanged();
base::RunLoop().RunUntilIdle();
}
protected:
const GURL kTestCookieURL;
const GURL kTestCookieURL2;
const GURL kTestCookieURL3;
const GURL kTestCookieURL4;
base::MessageLoop loop_;
scoped_refptr<TestPersistentCookieStore> backend_;
std::unique_ptr<net::CookieStoreIOS> store_;
std::unique_ptr<net::CookieStore::CookieChangedSubscription>
cookie_changed_callback_;
std::vector<net::CanonicalCookie> cookies_changed_;
std::vector<bool> cookies_removed_;
};
TEST_F(CookieStoreIOSTest, SetCookieCallsHookWhenSynchronized) {
GetCookies(base::Bind(&IgnoreString));
ClearCookies();
SetCookie("abc=def");
EXPECT_EQ(1U, cookies_changed_.size());
EXPECT_EQ(1U, cookies_removed_.size());
EXPECT_EQ("abc", cookies_changed_[0].Name());
EXPECT_EQ("def", cookies_changed_[0].Value());
EXPECT_FALSE(cookies_removed_[0]);
SetCookie("abc=ghi");
EXPECT_EQ(3U, cookies_changed_.size());
EXPECT_EQ(3U, cookies_removed_.size());
EXPECT_EQ("abc", cookies_changed_[1].Name());
EXPECT_EQ("def", cookies_changed_[1].Value());
EXPECT_TRUE(cookies_removed_[1]);
EXPECT_EQ("abc", cookies_changed_[2].Name());
EXPECT_EQ("ghi", cookies_changed_[2].Value());
EXPECT_FALSE(cookies_removed_[2]);
DeleteSystemCookie(kTestCookieURL, "abc");
}
TEST_F(CookieStoreIOSTest, DeleteCallsHook) {
GetCookies(base::Bind(&IgnoreString));
ClearCookies();
SetCookie("abc=def");
EXPECT_EQ(1U, cookies_changed_.size());
EXPECT_EQ(1U, cookies_removed_.size());
store_->DeleteCookieAsync(kTestCookieURL, "abc",
base::Bind(&IgnoreBoolean, false));
CookieStoreIOS::NotifySystemCookiesChanged();
base::RunLoop().RunUntilIdle();
}
TEST_F(CookieStoreIOSTest, SameValueDoesNotCallHook) {
GetCookieCallback callback;
GetCookies(base::Bind(&IgnoreString));
ClearCookies();
SetCookie("abc=def");
EXPECT_EQ(1U, cookies_changed_.size());
SetCookie("abc=def");
EXPECT_EQ(1U, cookies_changed_.size());
}
TEST(CookieStoreIOS, GetAllCookiesForURLAsync) {
base::MessageLoop loop;
const GURL kTestCookieURL("http://foo.google.com/bar");
ClearCookies();
std::unique_ptr<CookieStoreIOS> cookie_store(base::MakeUnique<CookieStoreIOS>(
[NSHTTPCookieStorage sharedHTTPCookieStorage]));
// Add a cookie.
net::CookieOptions options;
options.set_include_httponly();
cookie_store->SetCookieWithOptionsAsync(
kTestCookieURL, "a=b", options, net::CookieStore::SetCookiesCallback());
// Check we can get the cookie.
GetAllCookiesCallback callback;
cookie_store->GetAllCookiesForURLAsync(
kTestCookieURL,
base::Bind(&GetAllCookiesCallback::Run, base::Unretained(&callback)));
EXPECT_TRUE(callback.did_run());
EXPECT_EQ(1u, callback.cookie_list().size());
net::CanonicalCookie cookie = callback.cookie_list()[0];
EXPECT_EQ("a", cookie.Name());
EXPECT_EQ("b", cookie.Value());
}
TEST_F(CookieStoreIOSTest, NoInitialNotifyWithNoCookie) {
std::vector<net::CanonicalCookie> cookies;
store_->AddCallbackForCookie(
kTestCookieURL, "abc",
base::Bind(&RecordCookieChanges, &cookies, nullptr));
EXPECT_EQ(0U, cookies.size());
}
TEST_F(CookieStoreIOSTest, NoInitialNotifyWithSystemCookie) {
SetSystemCookie(kTestCookieURL, "abc", "def");
std::vector<net::CanonicalCookie> cookies;
store_->AddCallbackForCookie(
kTestCookieURL, "abc",
base::Bind(&RecordCookieChanges, &cookies, nullptr));
EXPECT_EQ(0U, cookies.size());
DeleteSystemCookie(kTestCookieURL, "abc");
}
TEST_F(CookieStoreIOSTest, NotifyOnAdd) {
std::vector<net::CanonicalCookie> cookies;
std::vector<bool> removes;
std::unique_ptr<net::CookieStore::CookieChangedSubscription> handle =
store_->AddCallbackForCookie(
kTestCookieURL, "abc",
base::Bind(&RecordCookieChanges, &cookies, &removes));
EXPECT_EQ(0U, cookies.size());
EXPECT_EQ(0U, removes.size());
SetSystemCookie(kTestCookieURL, "abc", "def");
EXPECT_EQ(1U, cookies.size());
EXPECT_EQ(1U, removes.size());
EXPECT_EQ("abc", cookies[0].Name());
EXPECT_EQ("def", cookies[0].Value());
EXPECT_FALSE(removes[0]);
SetSystemCookie(kTestCookieURL, "ghi", "jkl");
EXPECT_EQ(1U, cookies.size());
EXPECT_EQ(1U, removes.size());
DeleteSystemCookie(kTestCookieURL, "abc");
DeleteSystemCookie(kTestCookieURL, "ghi");
}
TEST_F(CookieStoreIOSTest, NotifyOnChange) {
std::vector<net::CanonicalCookie> cookies;
std::vector<bool> removes;
std::unique_ptr<net::CookieStore::CookieChangedSubscription> handle =
store_->AddCallbackForCookie(
kTestCookieURL, "abc",
base::Bind(&RecordCookieChanges, &cookies, &removes));
EXPECT_EQ(0U, cookies.size());
SetSystemCookie(kTestCookieURL, "abc", "def");
EXPECT_EQ(1U, cookies.size());
SetSystemCookie(kTestCookieURL, "abc", "ghi");
EXPECT_EQ(3U, cookies.size());
EXPECT_EQ(3U, removes.size());
EXPECT_EQ("abc", cookies[1].Name());
EXPECT_EQ("def", cookies[1].Value());
EXPECT_TRUE(removes[1]);
EXPECT_EQ("abc", cookies[2].Name());
EXPECT_EQ("ghi", cookies[2].Value());
EXPECT_FALSE(removes[2]);
DeleteSystemCookie(kTestCookieURL, "abc");
}
TEST_F(CookieStoreIOSTest, NotifyOnDelete) {
std::vector<net::CanonicalCookie> cookies;
std::vector<bool> removes;
SetSystemCookie(kTestCookieURL, "abc", "def");
std::unique_ptr<net::CookieStore::CookieChangedSubscription> handle =
store_->AddCallbackForCookie(
kTestCookieURL, "abc",
base::Bind(&RecordCookieChanges, &cookies, &removes));
EXPECT_EQ(0U, cookies.size());
DeleteSystemCookie(kTestCookieURL, "abc");
EXPECT_EQ(1U, cookies.size());
EXPECT_EQ(1U, removes.size());
EXPECT_TRUE(removes[0]);
SetSystemCookie(kTestCookieURL, "abc", "def");
EXPECT_EQ(2U, cookies.size());
EXPECT_EQ(2U, removes.size());
EXPECT_FALSE(removes[1]);
DeleteSystemCookie(kTestCookieURL, "abc");
}
TEST_F(CookieStoreIOSTest, NoNotifyOnNoChange) {
std::vector<net::CanonicalCookie> cookies;
std::unique_ptr<net::CookieStore::CookieChangedSubscription> handle =
store_->AddCallbackForCookie(
kTestCookieURL, "abc",
base::Bind(&RecordCookieChanges, &cookies, nullptr));
EXPECT_EQ(0U, cookies.size());
SetSystemCookie(kTestCookieURL, "abc", "def");
EXPECT_EQ(1U, cookies.size());
SetSystemCookie(kTestCookieURL, "abc", "def");
EXPECT_EQ(1U, cookies.size());
DeleteSystemCookie(kTestCookieURL, "abc");
}
TEST_F(CookieStoreIOSTest, MultipleNotifies) {
std::vector<net::CanonicalCookie> cookies;
std::vector<net::CanonicalCookie> cookies2;
std::vector<net::CanonicalCookie> cookies3;
std::vector<net::CanonicalCookie> cookies4;
std::unique_ptr<net::CookieStore::CookieChangedSubscription> handle =
store_->AddCallbackForCookie(
kTestCookieURL, "abc",
base::Bind(&RecordCookieChanges, &cookies, nullptr));
std::unique_ptr<net::CookieStore::CookieChangedSubscription> handle2 =
store_->AddCallbackForCookie(
kTestCookieURL2, "abc",
base::Bind(&RecordCookieChanges, &cookies2, nullptr));
std::unique_ptr<net::CookieStore::CookieChangedSubscription> handle3 =
store_->AddCallbackForCookie(
kTestCookieURL3, "abc",
base::Bind(&RecordCookieChanges, &cookies3, nullptr));
std::unique_ptr<net::CookieStore::CookieChangedSubscription> handle4 =
store_->AddCallbackForCookie(
kTestCookieURL4, "abc",
base::Bind(&RecordCookieChanges, &cookies4, nullptr));
SetSystemCookie(kTestCookieURL, "abc", "def");
SetSystemCookie(kTestCookieURL2, "abc", "def");
SetSystemCookie(kTestCookieURL3, "abc", "def");
SetSystemCookie(kTestCookieURL4, "abc", "def");
EXPECT_EQ(1U, cookies.size());
EXPECT_EQ(1U, cookies2.size());
EXPECT_EQ(0U, cookies3.size());
EXPECT_EQ(1U, cookies4.size());
DeleteSystemCookie(kTestCookieURL, "abc");
DeleteSystemCookie(kTestCookieURL2, "abc");
DeleteSystemCookie(kTestCookieURL3, "abc");
DeleteSystemCookie(kTestCookieURL4, "abc");
}
TEST_F(CookieStoreIOSTest, LessSpecificNestedCookie) {
std::vector<net::CanonicalCookie> cookies;
SetSystemCookie(kTestCookieURL2, "abc", "def");
std::unique_ptr<net::CookieStore::CookieChangedSubscription> handle =
store_->AddCallbackForCookie(
kTestCookieURL2, "abc",
base::Bind(&RecordCookieChanges, &cookies, nullptr));
EXPECT_EQ(0U, cookies.size());
SetSystemCookie(kTestCookieURL3, "abc", "ghi");
EXPECT_EQ(1U, cookies.size());
DeleteSystemCookie(kTestCookieURL, "abc");
}
TEST_F(CookieStoreIOSTest, MoreSpecificNestedCookie) {
std::vector<net::CanonicalCookie> cookies;
SetSystemCookie(kTestCookieURL3, "abc", "def");
std::unique_ptr<net::CookieStore::CookieChangedSubscription> handle =
store_->AddCallbackForCookie(
kTestCookieURL2, "abc",
base::Bind(&RecordCookieChanges, &cookies, nullptr));
EXPECT_EQ(0U, cookies.size());
SetSystemCookie(kTestCookieURL2, "abc", "ghi");
EXPECT_EQ(2U, cookies.size());
DeleteSystemCookie(kTestCookieURL, "abc");
}
TEST_F(CookieStoreIOSTest, MoreSpecificNestedCookieWithSameValue) {
std::vector<net::CanonicalCookie> cookies;
SetSystemCookie(kTestCookieURL3, "abc", "def");
std::unique_ptr<net::CookieStore::CookieChangedSubscription> handle =
store_->AddCallbackForCookie(
kTestCookieURL2, "abc",
base::Bind(&RecordCookieChanges, &cookies, nullptr));
EXPECT_EQ(0U, cookies.size());
SetSystemCookie(kTestCookieURL2, "abc", "def");
EXPECT_EQ(2U, cookies.size());
DeleteSystemCookie(kTestCookieURL, "abc");
}
TEST_F(CookieStoreIOSTest, RemoveCallback) {
std::vector<net::CanonicalCookie> cookies;
SetSystemCookie(kTestCookieURL, "abc", "def");
std::unique_ptr<net::CookieStore::CookieChangedSubscription> handle =
store_->AddCallbackForCookie(
kTestCookieURL, "abc",
base::Bind(&RecordCookieChanges, &cookies, nullptr));
EXPECT_EQ(0U, cookies.size());
SetSystemCookie(kTestCookieURL, "abc", "ghi");
EXPECT_EQ(2U, cookies.size());
// this deletes the callback
handle.reset();
SetSystemCookie(kTestCookieURL, "abc", "jkl");
EXPECT_EQ(2U, cookies.size());
DeleteSystemCookie(kTestCookieURL, "abc");
}
} // namespace net