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/external_task.h"
     18 
     19 #include <map>
     20 #include <memory>
     21 #include <set>
     22 #include <string>
     23 #include <vector>
     24 
     25 #include <base/bind.h>
     26 #include <base/files/file_path.h>
     27 #include <base/memory/weak_ptr.h>
     28 #include <base/strings/string_util.h>
     29 #include <gmock/gmock.h>
     30 #include <gtest/gtest.h>
     31 
     32 #include "shill/mock_adaptors.h"
     33 #include "shill/mock_process_manager.h"
     34 #include "shill/nice_mock_control.h"
     35 #include "shill/test_event_dispatcher.h"
     36 
     37 using std::map;
     38 using std::set;
     39 using std::string;
     40 using std::vector;
     41 using testing::_;
     42 using testing::Matcher;
     43 using testing::MatchesRegex;
     44 using testing::Mock;
     45 using testing::NiceMock;
     46 using testing::Return;
     47 using testing::SetArgumentPointee;
     48 using testing::StrEq;
     49 
     50 namespace shill {
     51 
     52 class ExternalTaskTest : public testing::Test,
     53                          public RPCTaskDelegate {
     54  public:
     55   ExternalTaskTest()
     56       : weak_ptr_factory_(this),
     57         death_callback_(
     58           base::Bind(&ExternalTaskTest::TaskDiedCallback,
     59                      weak_ptr_factory_.GetWeakPtr())),
     60         external_task_(
     61             new ExternalTask(&control_, &process_manager_,
     62                              weak_ptr_factory_.GetWeakPtr(),
     63                              death_callback_)),
     64         test_rpc_task_destroyed_(false) {}
     65 
     66   virtual ~ExternalTaskTest() {}
     67 
     68   virtual void TearDown() {
     69     if (!external_task_) {
     70       return;
     71     }
     72 
     73     if (external_task_->pid_) {
     74       EXPECT_CALL(process_manager_, StopProcess(external_task_->pid_));
     75     }
     76   }
     77 
     78   void set_test_rpc_task_destroyed(bool destroyed) {
     79     test_rpc_task_destroyed_ = destroyed;
     80   }
     81 
     82   // Defined out-of-line, due to dependency on TestRPCTask.
     83   void FakeUpRunningProcess(unsigned int tag, int pid);
     84 
     85   void ExpectStop(unsigned int tag, int pid) {
     86     EXPECT_CALL(process_manager_, StopProcess(pid));
     87   }
     88 
     89   void VerifyStop() {
     90     if (external_task_) {
     91       EXPECT_EQ(0, external_task_->pid_);
     92       EXPECT_FALSE(external_task_->rpc_task_);
     93     }
     94     EXPECT_TRUE(test_rpc_task_destroyed_);
     95     // Make sure EXPECTations were met before the fixture's dtor.
     96     Mock::VerifyAndClearExpectations(&process_manager_);
     97   }
     98 
     99  protected:
    100   // Implements RPCTaskDelegate interface.
    101   MOCK_METHOD2(GetLogin, void(string* user, string* password));
    102   MOCK_METHOD2(Notify, void(const string& reason,
    103                             const map<string, string>& dict));
    104 
    105   MOCK_METHOD2(TaskDiedCallback, void(pid_t pid, int exit_status));
    106 
    107   NiceMockControl control_;
    108   EventDispatcherForTest dispatcher_;
    109   MockProcessManager process_manager_;
    110   base::WeakPtrFactory<ExternalTaskTest> weak_ptr_factory_;
    111   base::Callback<void(pid_t, int)> death_callback_;
    112   std::unique_ptr<ExternalTask> external_task_;
    113   bool test_rpc_task_destroyed_;
    114 };
    115 
    116 namespace {
    117 
    118 class TestRPCTask : public RPCTask {
    119  public:
    120   TestRPCTask(ControlInterface* control, ExternalTaskTest* test);
    121   virtual ~TestRPCTask();
    122 
    123  private:
    124   ExternalTaskTest* test_;
    125 };
    126 
    127 TestRPCTask::TestRPCTask(ControlInterface* control, ExternalTaskTest* test)
    128     : RPCTask(control, test),
    129       test_(test) {
    130   test_->set_test_rpc_task_destroyed(false);
    131 }
    132 
    133 TestRPCTask::~TestRPCTask() {
    134   test_->set_test_rpc_task_destroyed(true);
    135   test_ = nullptr;
    136 }
    137 
    138 }  // namespace
    139 
    140 void ExternalTaskTest::FakeUpRunningProcess(unsigned int tag, int pid) {
    141   external_task_->pid_ = pid;
    142   external_task_->rpc_task_.reset(new TestRPCTask(&control_, this));
    143 }
    144 
    145 TEST_F(ExternalTaskTest, Destructor) {
    146   const unsigned int kTag = 123;
    147   const int kPID = 123456;
    148   FakeUpRunningProcess(kTag, kPID);
    149   ExpectStop(kTag, kPID);
    150   external_task_.reset();
    151   VerifyStop();
    152 }
    153 
    154 TEST_F(ExternalTaskTest, DestroyLater) {
    155   const unsigned int kTag = 123;
    156   const int kPID = 123456;
    157   FakeUpRunningProcess(kTag, kPID);
    158   ExpectStop(kTag, kPID);
    159   external_task_.release()->DestroyLater(&dispatcher_);
    160   dispatcher_.DispatchPendingEvents();
    161   VerifyStop();
    162 }
    163 
    164 namespace {
    165 
    166 // Returns true iff. there is at least one anchored match in |arg|,
    167 // for each item in |expected_values|. Order of items does not matter.
    168 //
    169 // |arg| is a NULL-terminated array of C-strings.
    170 // |expected_values| is a container of regular expressions (as strings).
    171 MATCHER_P(HasElementsMatching, expected_values, "") {
    172   for (const auto& expected_value : expected_values) {
    173     auto regex_matcher(MatchesRegex(expected_value).impl());
    174     char** arg_local = arg;
    175     while (*arg_local) {
    176       if (regex_matcher.MatchAndExplain(*arg_local, result_listener)) {
    177         break;
    178       }
    179       ++arg_local;
    180     }
    181     if (*arg_local == nullptr) {
    182       *result_listener << "missing value " << expected_value << "\n";
    183       arg_local = arg;
    184       while (*arg_local) {
    185         *result_listener << "received: " << *arg_local << "\n";
    186         ++arg_local;
    187       }
    188       return false;
    189     }
    190   }
    191   return true;
    192 }
    193 
    194 }  // namespace
    195 
    196 TEST_F(ExternalTaskTest, Start) {
    197   const string kCommand = "/run/me";
    198   const vector<string> kCommandOptions{"arg1", "arg2"};
    199   const map<string, string> kCommandEnv{{"env1", "val1"}, {"env2", "val2"}};
    200   map<string, string> expected_env;
    201   expected_env.emplace(kRPCTaskServiceVariable, RPCTaskMockAdaptor::kRpcConnId);
    202   expected_env.emplace(kRPCTaskPathVariable, RPCTaskMockAdaptor::kRpcId);
    203   expected_env.insert(kCommandEnv.begin(), kCommandEnv.end());
    204   const int kPID = 234678;
    205   EXPECT_CALL(process_manager_,
    206               StartProcess(_, base::FilePath(kCommand), kCommandOptions,
    207                            expected_env, false, _))
    208       .WillOnce(Return(-1))
    209       .WillOnce(Return(kPID));
    210   Error error;
    211   EXPECT_FALSE(external_task_->Start(
    212       base::FilePath(kCommand), kCommandOptions, kCommandEnv, false, &error));
    213   EXPECT_EQ(Error::kInternalError, error.type());
    214   EXPECT_FALSE(external_task_->rpc_task_);
    215 
    216   error.Reset();
    217   EXPECT_TRUE(external_task_->Start(
    218       base::FilePath(kCommand), kCommandOptions, kCommandEnv, false, &error));
    219   EXPECT_TRUE(error.IsSuccess());
    220   EXPECT_EQ(kPID, external_task_->pid_);
    221   EXPECT_NE(nullptr, external_task_->rpc_task_);
    222 }
    223 
    224 TEST_F(ExternalTaskTest, Stop) {
    225   const unsigned int kTag = 123;
    226   const int kPID = 123456;
    227   FakeUpRunningProcess(kTag, kPID);
    228   ExpectStop(kTag, kPID);
    229   external_task_->Stop();
    230   ASSERT_NE(nullptr, external_task_);
    231   VerifyStop();
    232 }
    233 
    234 TEST_F(ExternalTaskTest, StopNotStarted) {
    235   EXPECT_CALL(process_manager_, StopProcess(_)).Times(0);
    236   external_task_->Stop();
    237   EXPECT_FALSE(test_rpc_task_destroyed_);
    238 }
    239 
    240 TEST_F(ExternalTaskTest, GetLogin) {
    241   string username;
    242   string password;
    243   EXPECT_CALL(*this, GetLogin(&username, &password));
    244   EXPECT_CALL(*this, Notify(_, _)).Times(0);
    245   external_task_->GetLogin(&username, &password);
    246 }
    247 
    248 TEST_F(ExternalTaskTest, Notify) {
    249   const string kReason("you may already have won!");
    250   const map<string, string>& kArgs{
    251     {"arg1", "val1"},
    252     {"arg2", "val2"}};
    253   EXPECT_CALL(*this, GetLogin(_, _)).Times(0);
    254   EXPECT_CALL(*this, Notify(kReason, kArgs));
    255   external_task_->Notify(kReason, kArgs);
    256 }
    257 
    258 TEST_F(ExternalTaskTest, OnTaskDied) {
    259   const int kPID = 99999;
    260   const int kExitStatus = 1;
    261   external_task_->pid_ = kPID;
    262   EXPECT_CALL(process_manager_, StopProcess(_)).Times(0);
    263   EXPECT_CALL(*this, TaskDiedCallback(kPID, kExitStatus));
    264   external_task_->OnTaskDied(kExitStatus);
    265   EXPECT_EQ(0, external_task_->pid_);
    266 }
    267 
    268 }  // namespace shill
    269