1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "google_apis/gcm/engine/gcm_store_impl.h"
10 #include "base/bind.h"
11 #include "base/files/file_path.h"
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/run_loop.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "google_apis/gcm/base/mcs_message.h"
18 #include "google_apis/gcm/base/mcs_util.h"
19 #include "google_apis/gcm/protocol/mcs.pb.h"
20 #include "testing/gtest/include/gtest/gtest.h"
26 // Number of persistent ids to use in tests.
27 const int kNumPersistentIds
= 10;
29 // Number of per-app messages in tests.
30 const int kNumMessagesPerApp
= 20;
32 // App name for testing.
33 const char kAppName
[] = "my_app";
35 // Category name for testing.
36 const char kCategoryName
[] = "my_category";
38 const uint64 kDeviceId
= 22;
39 const uint64 kDeviceToken
= 55;
41 class GCMStoreImplTest
: public testing::Test
{
44 virtual ~GCMStoreImplTest();
46 scoped_ptr
<GCMStore
> BuildGCMStore();
48 std::string
GetNextPersistentId();
52 void LoadCallback(scoped_ptr
<GCMStore::LoadResult
>* result_dst
,
53 scoped_ptr
<GCMStore::LoadResult
> result
);
54 void UpdateCallback(bool success
);
57 base::MessageLoop message_loop_
;
58 base::ScopedTempDir temp_directory_
;
59 bool expected_success_
;
60 scoped_ptr
<base::RunLoop
> run_loop_
;
63 GCMStoreImplTest::GCMStoreImplTest()
64 : expected_success_(true) {
65 EXPECT_TRUE(temp_directory_
.CreateUniqueTempDir());
66 run_loop_
.reset(new base::RunLoop());
69 GCMStoreImplTest::~GCMStoreImplTest() {}
71 scoped_ptr
<GCMStore
> GCMStoreImplTest::BuildGCMStore() {
72 return scoped_ptr
<GCMStore
>(new GCMStoreImpl(
74 temp_directory_
.path(),
75 message_loop_
.message_loop_proxy()));
78 std::string
GCMStoreImplTest::GetNextPersistentId() {
79 return base::Uint64ToString(base::Time::Now().ToInternalValue());
82 void GCMStoreImplTest::PumpLoop() { message_loop_
.RunUntilIdle(); }
84 void GCMStoreImplTest::LoadCallback(
85 scoped_ptr
<GCMStore::LoadResult
>* result_dst
,
86 scoped_ptr
<GCMStore::LoadResult
> result
) {
87 ASSERT_TRUE(result
->success
);
88 *result_dst
= result
.Pass();
90 run_loop_
.reset(new base::RunLoop());
93 void GCMStoreImplTest::UpdateCallback(bool success
) {
94 ASSERT_EQ(expected_success_
, success
);
97 // Verify creating a new database and loading it.
98 TEST_F(GCMStoreImplTest
, LoadNew
) {
99 scoped_ptr
<GCMStore
> gcm_store(BuildGCMStore());
100 scoped_ptr
<GCMStore::LoadResult
> load_result
;
101 gcm_store
->Load(base::Bind(
102 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
105 EXPECT_EQ(0U, load_result
->device_android_id
);
106 EXPECT_EQ(0U, load_result
->device_security_token
);
107 EXPECT_TRUE(load_result
->incoming_messages
.empty());
108 EXPECT_TRUE(load_result
->outgoing_messages
.empty());
109 EXPECT_EQ(1LL, load_result
->serial_number_mappings
.next_serial_number
);
110 EXPECT_TRUE(load_result
->serial_number_mappings
.user_serial_numbers
.empty());
113 TEST_F(GCMStoreImplTest
, DeviceCredentials
) {
114 scoped_ptr
<GCMStore
> gcm_store(BuildGCMStore());
115 scoped_ptr
<GCMStore::LoadResult
> load_result
;
116 gcm_store
->Load(base::Bind(
117 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
120 gcm_store
->SetDeviceCredentials(
123 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
126 gcm_store
= BuildGCMStore().Pass();
127 gcm_store
->Load(base::Bind(
128 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
131 ASSERT_EQ(kDeviceId
, load_result
->device_android_id
);
132 ASSERT_EQ(kDeviceToken
, load_result
->device_security_token
);
135 // Verify saving some incoming messages, reopening the directory, and then
136 // removing those incoming messages.
137 TEST_F(GCMStoreImplTest
, IncomingMessages
) {
138 scoped_ptr
<GCMStore
> gcm_store(BuildGCMStore());
139 scoped_ptr
<GCMStore::LoadResult
> load_result
;
140 gcm_store
->Load(base::Bind(
141 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
144 std::vector
<std::string
> persistent_ids
;
145 for (int i
= 0; i
< kNumPersistentIds
; ++i
) {
146 persistent_ids
.push_back(GetNextPersistentId());
147 gcm_store
->AddIncomingMessage(
148 persistent_ids
.back(),
149 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
153 gcm_store
= BuildGCMStore().Pass();
154 gcm_store
->Load(base::Bind(
155 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
158 ASSERT_EQ(persistent_ids
, load_result
->incoming_messages
);
159 ASSERT_TRUE(load_result
->outgoing_messages
.empty());
161 gcm_store
->RemoveIncomingMessages(
163 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
166 gcm_store
= BuildGCMStore().Pass();
167 load_result
->incoming_messages
.clear();
168 gcm_store
->Load(base::Bind(
169 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
172 ASSERT_TRUE(load_result
->incoming_messages
.empty());
173 ASSERT_TRUE(load_result
->outgoing_messages
.empty());
176 // Verify saving some outgoing messages, reopening the directory, and then
177 // removing those outgoing messages.
178 TEST_F(GCMStoreImplTest
, OutgoingMessages
) {
179 scoped_ptr
<GCMStore
> gcm_store(BuildGCMStore());
180 scoped_ptr
<GCMStore::LoadResult
> load_result
;
181 gcm_store
->Load(base::Bind(
182 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
185 std::vector
<std::string
> persistent_ids
;
186 const int kNumPersistentIds
= 10;
187 for (int i
= 0; i
< kNumPersistentIds
; ++i
) {
188 persistent_ids
.push_back(GetNextPersistentId());
189 mcs_proto::DataMessageStanza message
;
190 message
.set_from(kAppName
+ persistent_ids
.back());
191 message
.set_category(kCategoryName
+ persistent_ids
.back());
192 gcm_store
->AddOutgoingMessage(
193 persistent_ids
.back(),
195 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
199 gcm_store
= BuildGCMStore().Pass();
200 gcm_store
->Load(base::Bind(
201 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
204 ASSERT_TRUE(load_result
->incoming_messages
.empty());
205 ASSERT_EQ(load_result
->outgoing_messages
.size(), persistent_ids
.size());
206 for (int i
= 0; i
< kNumPersistentIds
; ++i
) {
207 std::string id
= persistent_ids
[i
];
208 ASSERT_TRUE(load_result
->outgoing_messages
[id
].get());
209 const mcs_proto::DataMessageStanza
* message
=
210 reinterpret_cast<mcs_proto::DataMessageStanza
*>(
211 load_result
->outgoing_messages
[id
].get());
212 ASSERT_EQ(message
->from(), kAppName
+ id
);
213 ASSERT_EQ(message
->category(), kCategoryName
+ id
);
216 gcm_store
->RemoveOutgoingMessages(
218 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
221 gcm_store
= BuildGCMStore().Pass();
222 load_result
->outgoing_messages
.clear();
223 gcm_store
->Load(base::Bind(
224 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
227 ASSERT_TRUE(load_result
->incoming_messages
.empty());
228 ASSERT_TRUE(load_result
->outgoing_messages
.empty());
231 // Verify incoming and outgoing messages don't conflict.
232 TEST_F(GCMStoreImplTest
, IncomingAndOutgoingMessages
) {
233 scoped_ptr
<GCMStore
> gcm_store(BuildGCMStore());
234 scoped_ptr
<GCMStore::LoadResult
> load_result
;
235 gcm_store
->Load(base::Bind(
236 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
239 std::vector
<std::string
> persistent_ids
;
240 const int kNumPersistentIds
= 10;
241 for (int i
= 0; i
< kNumPersistentIds
; ++i
) {
242 persistent_ids
.push_back(GetNextPersistentId());
243 gcm_store
->AddIncomingMessage(
244 persistent_ids
.back(),
245 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
248 mcs_proto::DataMessageStanza message
;
249 message
.set_from(kAppName
+ persistent_ids
.back());
250 message
.set_category(kCategoryName
+ persistent_ids
.back());
251 gcm_store
->AddOutgoingMessage(
252 persistent_ids
.back(),
254 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
258 gcm_store
= BuildGCMStore().Pass();
259 gcm_store
->Load(base::Bind(
260 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
263 ASSERT_EQ(persistent_ids
, load_result
->incoming_messages
);
264 ASSERT_EQ(load_result
->outgoing_messages
.size(), persistent_ids
.size());
265 for (int i
= 0; i
< kNumPersistentIds
; ++i
) {
266 std::string id
= persistent_ids
[i
];
267 ASSERT_TRUE(load_result
->outgoing_messages
[id
].get());
268 const mcs_proto::DataMessageStanza
* message
=
269 reinterpret_cast<mcs_proto::DataMessageStanza
*>(
270 load_result
->outgoing_messages
[id
].get());
271 ASSERT_EQ(message
->from(), kAppName
+ id
);
272 ASSERT_EQ(message
->category(), kCategoryName
+ id
);
275 gcm_store
->RemoveIncomingMessages(
277 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
279 gcm_store
->RemoveOutgoingMessages(
281 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
284 gcm_store
= BuildGCMStore().Pass();
285 load_result
->incoming_messages
.clear();
286 load_result
->outgoing_messages
.clear();
287 gcm_store
->Load(base::Bind(
288 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
291 ASSERT_TRUE(load_result
->incoming_messages
.empty());
292 ASSERT_TRUE(load_result
->outgoing_messages
.empty());
295 // Verify that the next serial number of persisted properly.
296 TEST_F(GCMStoreImplTest
, NextSerialNumber
) {
297 const int64 kNextSerialNumber
= 77LL;
298 scoped_ptr
<GCMStore
> gcm_store(BuildGCMStore());
299 scoped_ptr
<GCMStore::LoadResult
> load_result
;
300 gcm_store
->Load(base::Bind(
301 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
304 gcm_store
->SetNextSerialNumber(
306 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
309 gcm_store
= BuildGCMStore().Pass();
310 gcm_store
->Load(base::Bind(
311 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
314 EXPECT_EQ(kNextSerialNumber
,
315 load_result
->serial_number_mappings
.next_serial_number
);
318 // Verify that user serial number mappings are persisted properly.
319 TEST_F(GCMStoreImplTest
, UserSerialNumberMappings
) {
320 scoped_ptr
<GCMStore
> gcm_store(BuildGCMStore());
321 scoped_ptr
<GCMStore::LoadResult
> load_result
;
322 gcm_store
->Load(base::Bind(
323 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
326 std::string username1
= "username1";
327 int64 serial_number1
= 34LL;
328 gcm_store
->AddUserSerialNumber(
331 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
333 std::string username2
= "username2";
334 int64 serial_number2
= 56LL;
335 gcm_store
->AddUserSerialNumber(
338 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
341 gcm_store
= BuildGCMStore().Pass();
342 gcm_store
->Load(base::Bind(
343 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
346 ASSERT_EQ(2u, load_result
->serial_number_mappings
.user_serial_numbers
.size());
348 load_result
->serial_number_mappings
.user_serial_numbers
.end(),
349 load_result
->serial_number_mappings
.user_serial_numbers
.find(username1
));
350 EXPECT_EQ(serial_number1
,
351 load_result
->serial_number_mappings
.user_serial_numbers
[username1
]);
353 load_result
->serial_number_mappings
.user_serial_numbers
.end(),
354 load_result
->serial_number_mappings
.user_serial_numbers
.find(username2
));
355 EXPECT_EQ(serial_number2
,
356 load_result
->serial_number_mappings
.user_serial_numbers
[username2
]);
359 // Test that per-app message limits are enforced, persisted across restarts,
360 // and updated as messages are removed.
361 TEST_F(GCMStoreImplTest
, PerAppMessageLimits
) {
362 scoped_ptr
<GCMStore
> gcm_store(BuildGCMStore());
363 scoped_ptr
<GCMStore::LoadResult
> load_result
;
364 gcm_store
->Load(base::Bind(&GCMStoreImplTest::LoadCallback
,
365 base::Unretained(this),
368 // Add the initial (below app limit) messages.
369 for (int i
= 0; i
< kNumMessagesPerApp
; ++i
) {
370 mcs_proto::DataMessageStanza message
;
371 message
.set_from(kAppName
);
372 message
.set_category(kCategoryName
);
373 EXPECT_TRUE(gcm_store
->AddOutgoingMessage(
374 base::IntToString(i
),
376 base::Bind(&GCMStoreImplTest::UpdateCallback
,
377 base::Unretained(this))));
381 // Attempting to add some more should fail.
382 for (int i
= 0; i
< kNumMessagesPerApp
; ++i
) {
383 mcs_proto::DataMessageStanza message
;
384 message
.set_from(kAppName
);
385 message
.set_category(kCategoryName
);
386 EXPECT_FALSE(gcm_store
->AddOutgoingMessage(
387 base::IntToString(i
+ kNumMessagesPerApp
),
389 base::Bind(&GCMStoreImplTest::UpdateCallback
,
390 base::Unretained(this))));
394 // Tear down and restore the database.
395 gcm_store
= BuildGCMStore().Pass();
396 gcm_store
->Load(base::Bind(&GCMStoreImplTest::LoadCallback
,
397 base::Unretained(this),
401 // Adding more messages should still fail.
402 for (int i
= 0; i
< kNumMessagesPerApp
; ++i
) {
403 mcs_proto::DataMessageStanza message
;
404 message
.set_from(kAppName
);
405 message
.set_category(kCategoryName
);
406 EXPECT_FALSE(gcm_store
->AddOutgoingMessage(
407 base::IntToString(i
+ kNumMessagesPerApp
),
409 base::Bind(&GCMStoreImplTest::UpdateCallback
,
410 base::Unretained(this))));
414 // Remove the existing messages.
415 for (int i
= 0; i
< kNumMessagesPerApp
; ++i
) {
416 gcm_store
->RemoveOutgoingMessage(
417 base::IntToString(i
),
418 base::Bind(&GCMStoreImplTest::UpdateCallback
,
419 base::Unretained(this)));
423 // Successfully add new messages.
424 for (int i
= 0; i
< kNumMessagesPerApp
; ++i
) {
425 mcs_proto::DataMessageStanza message
;
426 message
.set_from(kAppName
);
427 message
.set_category(kCategoryName
);
428 EXPECT_TRUE(gcm_store
->AddOutgoingMessage(
429 base::IntToString(i
+ kNumMessagesPerApp
),
431 base::Bind(&GCMStoreImplTest::UpdateCallback
,
432 base::Unretained(this))));
437 // When the database is destroyed, all database updates should fail. At the
438 // same time, they per-app message counts should not go up, as failures should
439 // result in decrementing the counts.
440 TEST_F(GCMStoreImplTest
, AddMessageAfterDestroy
) {
441 scoped_ptr
<GCMStore
> gcm_store(BuildGCMStore());
442 scoped_ptr
<GCMStore::LoadResult
> load_result
;
443 gcm_store
->Load(base::Bind(&GCMStoreImplTest::LoadCallback
,
444 base::Unretained(this),
447 gcm_store
->Destroy(base::Bind(&GCMStoreImplTest::UpdateCallback
,
448 base::Unretained(this)));
451 expected_success_
= false;
452 for (int i
= 0; i
< kNumMessagesPerApp
* 2; ++i
) {
453 mcs_proto::DataMessageStanza message
;
454 message
.set_from(kAppName
);
455 message
.set_category(kCategoryName
);
456 // Because all adds are failing, none should hit the per-app message limits.
457 EXPECT_TRUE(gcm_store
->AddOutgoingMessage(
458 base::IntToString(i
),
460 base::Bind(&GCMStoreImplTest::UpdateCallback
,
461 base::Unretained(this))));