Home | History | Annotate | Download | only in shill
      1 //
      2 // Copyright (C) 2012 The Android Open Source Project
      3 //
      4 // Licensed under the Apache License, Version 2.0 (the "License");
      5 // you may not use this file except in compliance with the License.
      6 // You may obtain a copy of the License at
      7 //
      8 //      http://www.apache.org/licenses/LICENSE-2.0
      9 //
     10 // Unless required by applicable law or agreed to in writing, software
     11 // distributed under the License is distributed on an "AS IS" BASIS,
     12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 // See the License for the specific language governing permissions and
     14 // limitations under the License.
     15 //
     16 
     17 #include "shill/hook_table.h"
     18 
     19 #include <memory>
     20 #include <string>
     21 
     22 #include <base/bind.h>
     23 #include <base/message_loop/message_loop.h>
     24 
     25 #include "shill/error.h"
     26 #include "shill/test_event_dispatcher.h"
     27 #include "shill/testing.h"
     28 
     29 using base::Bind;
     30 using base::Closure;
     31 using base::Unretained;
     32 using std::string;
     33 using ::testing::_;
     34 using ::testing::InSequence;
     35 using ::testing::Return;
     36 using ::testing::SaveArg;
     37 
     38 namespace shill {
     39 
     40 namespace {
     41 
     42 const char kName[] = "test";
     43 const char kName1[] = "test1";
     44 const char kName2[] = "test2";
     45 const char kName3[] = "test3";
     46 
     47 }  // namespace
     48 
     49 class HookTableTest : public testing::Test {
     50  public:
     51   MOCK_METHOD0(StartAction, void());
     52   MOCK_METHOD0(StartAction2, void());
     53   MOCK_METHOD1(DoneAction, void(const Error&));
     54 
     55  protected:
     56   HookTableTest()
     57       : hook_table_(&event_dispatcher_) {}
     58 
     59   ResultCallback* GetDoneCallback() { return &hook_table_.done_callback_; }
     60 
     61   EventDispatcherForTest event_dispatcher_;
     62   HookTable hook_table_;
     63 };
     64 
     65 TEST_F(HookTableTest, ActionCompletes) {
     66   EXPECT_CALL(*this, StartAction());
     67   EXPECT_CALL(*this, DoneAction(IsSuccess()));
     68   Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
     69   ResultCallback done_callback =
     70       Bind(&HookTableTest::DoneAction, Unretained(this));
     71   hook_table_.Add(kName, start_callback);
     72   hook_table_.Run(0, done_callback);
     73   hook_table_.ActionComplete(kName);
     74 
     75   // Ensure that the timeout callback got cancelled.  If it did not get
     76   // cancelled, done_callback will be run twice and make this test fail.
     77   event_dispatcher_.DispatchPendingEvents();
     78 }
     79 
     80 ACTION_P2(CompleteAction, hook_table, name) {
     81   hook_table->ActionComplete(name);
     82 }
     83 
     84 ACTION_P2(CompleteActionAndRemoveAction, hook_table, name) {
     85   hook_table->ActionComplete(name);
     86   hook_table->Remove(name);
     87 }
     88 
     89 TEST_F(HookTableTest, ActionCompletesAndRemovesActionInDoneCallback) {
     90   EXPECT_CALL(*this, StartAction())
     91       .WillOnce(CompleteActionAndRemoveAction(&hook_table_, kName));
     92   EXPECT_CALL(*this, StartAction2())
     93       .WillOnce(CompleteAction(&hook_table_, kName2));
     94   EXPECT_CALL(*this, DoneAction(IsSuccess()));
     95   Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
     96   Closure start2_callback =
     97       Bind(&HookTableTest::StartAction2, Unretained(this));
     98   ResultCallback done_callback =
     99       Bind(&HookTableTest::DoneAction, Unretained(this));
    100   hook_table_.Add(kName, start_callback);
    101   hook_table_.Add(kName2, start2_callback);
    102   hook_table_.Run(0, done_callback);
    103 
    104   // Ensure that the timeout callback got cancelled.  If it did not get
    105   // cancelled, done_callback will be run twice and make this test fail.
    106   event_dispatcher_.DispatchPendingEvents();
    107 }
    108 
    109 TEST_F(HookTableTest, ActionCompletesInline) {
    110   // StartAction completes immediately before HookTable::Run() returns.
    111   EXPECT_CALL(*this, StartAction())
    112       .WillOnce(CompleteAction(&hook_table_, kName));
    113   EXPECT_CALL(*this, DoneAction(IsSuccess()));
    114   Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
    115   ResultCallback done_callback =
    116       Bind(&HookTableTest::DoneAction, Unretained(this));
    117   hook_table_.Add(kName, start_callback);
    118   hook_table_.Run(0, done_callback);
    119 
    120   // Ensure that the timeout callback got cancelled.  If it did not get
    121   // cancelled, done_callback will be run twice and make this test fail.
    122   event_dispatcher_.DispatchPendingEvents();
    123 }
    124 
    125 TEST_F(HookTableTest, ActionTimesOut) {
    126   const int kTimeout = 1;
    127   EXPECT_CALL(*this, StartAction());
    128   EXPECT_CALL(*this, DoneAction(IsFailure()));
    129 
    130   Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
    131   ResultCallback done_callback =
    132       Bind(&HookTableTest::DoneAction, Unretained(this));
    133 
    134   hook_table_.Add(kName, start_callback);
    135   hook_table_.Run(kTimeout, done_callback);
    136 
    137   // Cause the event dispatcher to exit after kTimeout + 1 ms.
    138   event_dispatcher_.PostDelayedTask(base::MessageLoop::QuitWhenIdleClosure(),
    139                                     kTimeout + 1);
    140   event_dispatcher_.DispatchForever();
    141   EXPECT_TRUE(GetDoneCallback()->is_null());
    142 }
    143 
    144 TEST_F(HookTableTest, MultipleActionsAllSucceed) {
    145   Closure pending_callback;
    146   const int kTimeout = 10;
    147   EXPECT_CALL(*this, StartAction()).Times(2);
    148 
    149   // StartAction2 completes immediately before HookTable::Run() returns.
    150   EXPECT_CALL(*this, StartAction2())
    151       .WillOnce(CompleteAction(&hook_table_, kName1));
    152   EXPECT_CALL(*this, DoneAction(IsSuccess()));
    153 
    154   Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
    155   Closure start2_callback =
    156       Bind(&HookTableTest::StartAction2, Unretained(this));
    157   ResultCallback done_callback =
    158       Bind(&HookTableTest::DoneAction, Unretained(this));
    159 
    160   hook_table_.Add(kName1, start2_callback);
    161   hook_table_.Add(kName2, start_callback);
    162   hook_table_.Add(kName3, start_callback);
    163   hook_table_.Run(kTimeout, done_callback);
    164   hook_table_.ActionComplete(kName2);
    165   hook_table_.ActionComplete(kName3);
    166 }
    167 
    168 TEST_F(HookTableTest, MultipleActionsAndOneTimesOut) {
    169   Closure pending_callback;
    170   const int kTimeout = 1;
    171   EXPECT_CALL(*this, StartAction()).Times(3);
    172   EXPECT_CALL(*this, DoneAction(IsFailure()));
    173 
    174   Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
    175   ResultCallback done_callback =
    176       Bind(&HookTableTest::DoneAction, Unretained(this));
    177 
    178   hook_table_.Add(kName1, start_callback);
    179   hook_table_.Add(kName2, start_callback);
    180   hook_table_.Add(kName3, start_callback);
    181   hook_table_.Run(kTimeout, done_callback);
    182   hook_table_.ActionComplete(kName1);
    183   hook_table_.ActionComplete(kName3);
    184   // Cause the event dispatcher to exit after kTimeout + 1 ms.
    185   event_dispatcher_.PostDelayedTask(base::MessageLoop::QuitWhenIdleClosure(),
    186                                     kTimeout + 1);
    187   event_dispatcher_.DispatchForever();
    188 }
    189 
    190 TEST_F(HookTableTest, AddActionsWithSameName) {
    191   EXPECT_CALL(*this, StartAction()).Times(0);
    192   EXPECT_CALL(*this, StartAction2());
    193   EXPECT_CALL(*this, DoneAction(IsSuccess()));
    194   Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
    195   Closure start2_callback =
    196       Bind(&HookTableTest::StartAction2, Unretained(this));
    197   ResultCallback done_callback =
    198       Bind(&HookTableTest::DoneAction, Unretained(this));
    199   hook_table_.Add(kName, start_callback);
    200 
    201   // Adding an action with the same name kName.  New callbacks should replace
    202   // old ones.
    203   hook_table_.Add(kName, start2_callback);
    204   hook_table_.Run(0, done_callback);
    205   hook_table_.ActionComplete(kName);
    206 
    207   // Ensure that the timeout callback got cancelled.  If it did not get
    208   // cancelled, done_callback will be run twice and make this test fail.
    209   event_dispatcher_.DispatchPendingEvents();
    210 }
    211 
    212 TEST_F(HookTableTest, RemoveAction) {
    213   EXPECT_CALL(*this, StartAction()).Times(0);
    214   EXPECT_CALL(*this, DoneAction(IsSuccess()));
    215   Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
    216   ResultCallback done_callback =
    217       Bind(&HookTableTest::DoneAction, Unretained(this));
    218   hook_table_.Add(kName, start_callback);
    219   hook_table_.Remove(kName);
    220   hook_table_.Run(0, done_callback);
    221 }
    222 
    223 TEST_F(HookTableTest, ActionCompleteFollowedByRemove) {
    224   EXPECT_CALL(*this, StartAction()).Times(0);
    225   Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
    226   hook_table_.Add(kName, start_callback);
    227   hook_table_.ActionComplete(kName);
    228   hook_table_.Remove(kName);
    229 }
    230 
    231 TEST_F(HookTableTest, IsEmpty) {
    232   EXPECT_TRUE(hook_table_.IsEmpty());
    233   hook_table_.Add(kName, Closure());
    234   EXPECT_FALSE(hook_table_.IsEmpty());
    235   hook_table_.Remove(kName);
    236   EXPECT_TRUE(hook_table_.IsEmpty());
    237 }
    238 
    239 class SomeClass : public base::RefCounted<SomeClass> {
    240  public:
    241   SomeClass() {}
    242   void StartAction() {}
    243 
    244  private:
    245   DISALLOW_COPY_AND_ASSIGN(SomeClass);
    246 };
    247 
    248 // This test verifies that a class that removes itself from a hook table upon
    249 // destruction does not crash if the hook table is destroyed first.
    250 TEST_F(HookTableTest, RefcountedObject) {
    251   std::unique_ptr<HookTable> ht(new HookTable(&event_dispatcher_));
    252   {
    253     scoped_refptr<SomeClass> ref_counted_object = new SomeClass();
    254     Closure start_callback = Bind(&SomeClass::StartAction, ref_counted_object);
    255     ht->Add(kName, start_callback);
    256   }
    257 }
    258 
    259 TEST_F(HookTableTest, ActionAddedBeforePreviousActionCompletes) {
    260   EXPECT_CALL(*this, StartAction());
    261   EXPECT_CALL(*this, StartAction2()).Times(0);
    262   EXPECT_CALL(*this, DoneAction(IsSuccess()));
    263   Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
    264   Closure start2_callback =
    265       Bind(&HookTableTest::StartAction2, Unretained(this));
    266   ResultCallback done_callback =
    267       Bind(&HookTableTest::DoneAction, Unretained(this));
    268   hook_table_.Add(kName, start_callback);
    269   hook_table_.Run(0, done_callback);
    270 
    271   // An action with the same name is added before the previous actions complete.
    272   // It should not be run.
    273   hook_table_.Add(kName, start2_callback);
    274   hook_table_.ActionComplete(kName);
    275 }
    276 
    277 }  // namespace shill
    278