1 // Copyright 2013 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. 4 5 #include "google_apis/gcm/engine/rmq_store.h" 6 7 #include <string> 8 #include <vector> 9 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 "components/webdata/encryptor/encryptor.h" 18 #include "google_apis/gcm/base/mcs_message.h" 19 #include "google_apis/gcm/base/mcs_util.h" 20 #include "google_apis/gcm/protocol/mcs.pb.h" 21 #include "testing/gtest/include/gtest/gtest.h" 22 23 namespace gcm { 24 25 namespace { 26 27 // Number of persistent ids to use in tests. 28 const int kNumPersistentIds = 10; 29 30 const uint64 kDeviceId = 22; 31 const uint64 kDeviceToken = 55; 32 33 class RMQStoreTest : public testing::Test { 34 public: 35 RMQStoreTest(); 36 virtual ~RMQStoreTest(); 37 38 scoped_ptr<RMQStore> BuildRMQStore(); 39 40 std::string GetNextPersistentId(); 41 42 void PumpLoop(); 43 44 void LoadCallback(RMQStore::LoadResult* result_dst, 45 const RMQStore::LoadResult& result); 46 void UpdateCallback(bool success); 47 48 private: 49 base::MessageLoop message_loop_; 50 base::ScopedTempDir temp_directory_; 51 scoped_ptr<base::RunLoop> run_loop_; 52 }; 53 54 RMQStoreTest::RMQStoreTest() { 55 EXPECT_TRUE(temp_directory_.CreateUniqueTempDir()); 56 run_loop_.reset(new base::RunLoop()); 57 58 // On OSX, prevent the Keychain permissions popup during unit tests. 59 #if defined(OS_MACOSX) 60 Encryptor::UseMockKeychain(true); 61 #endif 62 } 63 64 RMQStoreTest::~RMQStoreTest() { 65 } 66 67 scoped_ptr<RMQStore> RMQStoreTest::BuildRMQStore() { 68 return scoped_ptr<RMQStore>(new RMQStore(temp_directory_.path(), 69 message_loop_.message_loop_proxy())); 70 } 71 72 std::string RMQStoreTest::GetNextPersistentId() { 73 return base::Uint64ToString(base::Time::Now().ToInternalValue()); 74 } 75 76 void RMQStoreTest::PumpLoop() { 77 message_loop_.RunUntilIdle(); 78 } 79 80 void RMQStoreTest::LoadCallback(RMQStore::LoadResult* result_dst, 81 const RMQStore::LoadResult& result) { 82 ASSERT_TRUE(result.success); 83 *result_dst = result; 84 run_loop_->Quit(); 85 run_loop_.reset(new base::RunLoop()); 86 } 87 88 void RMQStoreTest::UpdateCallback(bool success) { 89 ASSERT_TRUE(success); 90 } 91 92 // Verify creating a new database and loading it. 93 TEST_F(RMQStoreTest, LoadNew) { 94 scoped_ptr<RMQStore> rmq_store(BuildRMQStore()); 95 RMQStore::LoadResult load_result; 96 rmq_store->Load(base::Bind(&RMQStoreTest::LoadCallback, 97 base::Unretained(this), 98 &load_result)); 99 PumpLoop(); 100 101 ASSERT_EQ(0U, load_result.device_android_id); 102 ASSERT_EQ(0U, load_result.device_security_token); 103 ASSERT_TRUE(load_result.incoming_messages.empty()); 104 ASSERT_TRUE(load_result.outgoing_messages.empty()); 105 } 106 107 TEST_F(RMQStoreTest, DeviceCredentials) { 108 scoped_ptr<RMQStore> rmq_store(BuildRMQStore()); 109 RMQStore::LoadResult load_result; 110 rmq_store->Load(base::Bind(&RMQStoreTest::LoadCallback, 111 base::Unretained(this), 112 &load_result)); 113 PumpLoop(); 114 115 rmq_store->SetDeviceCredentials(kDeviceId, 116 kDeviceToken, 117 base::Bind(&RMQStoreTest::UpdateCallback, 118 base::Unretained(this))); 119 PumpLoop(); 120 121 rmq_store = BuildRMQStore().Pass(); 122 rmq_store->Load(base::Bind(&RMQStoreTest::LoadCallback, 123 base::Unretained(this), 124 &load_result)); 125 PumpLoop(); 126 127 ASSERT_EQ(kDeviceId, load_result.device_android_id); 128 ASSERT_EQ(kDeviceToken, load_result.device_security_token); 129 } 130 131 // Verify saving some incoming messages, reopening the directory, and then 132 // removing those incoming messages. 133 TEST_F(RMQStoreTest, IncomingMessages) { 134 scoped_ptr<RMQStore> rmq_store(BuildRMQStore()); 135 RMQStore::LoadResult load_result; 136 rmq_store->Load(base::Bind(&RMQStoreTest::LoadCallback, 137 base::Unretained(this), 138 &load_result)); 139 PumpLoop(); 140 141 std::vector<std::string> persistent_ids; 142 for (int i = 0; i < kNumPersistentIds; ++i) { 143 persistent_ids.push_back(GetNextPersistentId()); 144 rmq_store->AddIncomingMessage(persistent_ids.back(), 145 base::Bind(&RMQStoreTest::UpdateCallback, 146 base::Unretained(this))); 147 PumpLoop(); 148 } 149 150 rmq_store = BuildRMQStore().Pass(); 151 rmq_store->Load(base::Bind(&RMQStoreTest::LoadCallback, 152 base::Unretained(this), 153 &load_result)); 154 PumpLoop(); 155 156 ASSERT_EQ(persistent_ids, load_result.incoming_messages); 157 ASSERT_TRUE(load_result.outgoing_messages.empty()); 158 159 rmq_store->RemoveIncomingMessages(persistent_ids, 160 base::Bind(&RMQStoreTest::UpdateCallback, 161 base::Unretained(this))); 162 PumpLoop(); 163 164 rmq_store = BuildRMQStore().Pass(); 165 load_result.incoming_messages.clear(); 166 rmq_store->Load(base::Bind(&RMQStoreTest::LoadCallback, 167 base::Unretained(this), 168 &load_result)); 169 PumpLoop(); 170 171 ASSERT_TRUE(load_result.incoming_messages.empty()); 172 ASSERT_TRUE(load_result.outgoing_messages.empty()); 173 } 174 175 // Verify saving some outgoing messages, reopening the directory, and then 176 // removing those outgoing messages. 177 TEST_F(RMQStoreTest, OutgoingMessages) { 178 scoped_ptr<RMQStore> rmq_store(BuildRMQStore()); 179 RMQStore::LoadResult load_result; 180 rmq_store->Load(base::Bind(&RMQStoreTest::LoadCallback, 181 base::Unretained(this), 182 &load_result)); 183 PumpLoop(); 184 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(persistent_ids.back()); 191 message.set_category(persistent_ids.back()); 192 rmq_store->AddOutgoingMessage(persistent_ids.back(), 193 MCSMessage(message), 194 base::Bind(&RMQStoreTest::UpdateCallback, 195 base::Unretained(this))); 196 PumpLoop(); 197 } 198 199 rmq_store = BuildRMQStore().Pass(); 200 rmq_store->Load(base::Bind(&RMQStoreTest::LoadCallback, 201 base::Unretained(this), 202 &load_result)); 203 PumpLoop(); 204 205 ASSERT_TRUE(load_result.incoming_messages.empty()); 206 ASSERT_EQ(load_result.outgoing_messages.size(), persistent_ids.size()); 207 for (int i =0 ; i < kNumPersistentIds; ++i) { 208 std::string id = persistent_ids[i]; 209 ASSERT_TRUE(load_result.outgoing_messages[id]); 210 const mcs_proto::DataMessageStanza* message = 211 reinterpret_cast<mcs_proto::DataMessageStanza *>( 212 load_result.outgoing_messages[id]); 213 ASSERT_EQ(message->from(), id); 214 ASSERT_EQ(message->category(), id); 215 } 216 217 rmq_store->RemoveOutgoingMessages(persistent_ids, 218 base::Bind(&RMQStoreTest::UpdateCallback, 219 base::Unretained(this))); 220 PumpLoop(); 221 222 rmq_store = BuildRMQStore().Pass(); 223 load_result.outgoing_messages.clear(); 224 rmq_store->Load(base::Bind(&RMQStoreTest::LoadCallback, 225 base::Unretained(this), 226 &load_result)); 227 PumpLoop(); 228 229 ASSERT_TRUE(load_result.incoming_messages.empty()); 230 ASSERT_TRUE(load_result.outgoing_messages.empty()); 231 } 232 233 // Verify incoming and outgoing messages don't conflict. 234 TEST_F(RMQStoreTest, IncomingAndOutgoingMessages) { 235 scoped_ptr<RMQStore> rmq_store(BuildRMQStore()); 236 RMQStore::LoadResult load_result; 237 rmq_store->Load(base::Bind(&RMQStoreTest::LoadCallback, 238 base::Unretained(this), 239 &load_result)); 240 PumpLoop(); 241 242 std::vector<std::string> persistent_ids; 243 const int kNumPersistentIds = 10; 244 for (int i = 0; i < kNumPersistentIds; ++i) { 245 persistent_ids.push_back(GetNextPersistentId()); 246 rmq_store->AddIncomingMessage(persistent_ids.back(), 247 base::Bind(&RMQStoreTest::UpdateCallback, 248 base::Unretained(this))); 249 PumpLoop(); 250 251 mcs_proto::DataMessageStanza message; 252 message.set_from(persistent_ids.back()); 253 message.set_category(persistent_ids.back()); 254 rmq_store->AddOutgoingMessage(persistent_ids.back(), 255 MCSMessage(message), 256 base::Bind(&RMQStoreTest::UpdateCallback, 257 base::Unretained(this))); 258 PumpLoop(); 259 } 260 261 262 rmq_store = BuildRMQStore().Pass(); 263 rmq_store->Load(base::Bind(&RMQStoreTest::LoadCallback, 264 base::Unretained(this), 265 &load_result)); 266 PumpLoop(); 267 268 ASSERT_EQ(persistent_ids, load_result.incoming_messages); 269 ASSERT_EQ(load_result.outgoing_messages.size(), persistent_ids.size()); 270 for (int i =0 ; i < kNumPersistentIds; ++i) { 271 std::string id = persistent_ids[i]; 272 ASSERT_TRUE(load_result.outgoing_messages[id]); 273 const mcs_proto::DataMessageStanza* message = 274 reinterpret_cast<mcs_proto::DataMessageStanza *>( 275 load_result.outgoing_messages[id]); 276 ASSERT_EQ(message->from(), id); 277 ASSERT_EQ(message->category(), id); 278 } 279 280 rmq_store->RemoveIncomingMessages(persistent_ids, 281 base::Bind(&RMQStoreTest::UpdateCallback, 282 base::Unretained(this))); 283 PumpLoop(); 284 rmq_store->RemoveOutgoingMessages(persistent_ids, 285 base::Bind(&RMQStoreTest::UpdateCallback, 286 base::Unretained(this))); 287 PumpLoop(); 288 289 rmq_store = BuildRMQStore().Pass(); 290 load_result.incoming_messages.clear(); 291 load_result.outgoing_messages.clear(); 292 rmq_store->Load(base::Bind(&RMQStoreTest::LoadCallback, 293 base::Unretained(this), 294 &load_result)); 295 PumpLoop(); 296 297 ASSERT_TRUE(load_result.incoming_messages.empty()); 298 ASSERT_TRUE(load_result.outgoing_messages.empty()); 299 } 300 301 } // namespace 302 303 } // namespace gcm 304