Home | History | Annotate | Download | only in engine
      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