Home | History | Annotate | Download | only in common
      1 //
      2 // Copyright (C) 2009 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 "update_engine/common/mock_http_fetcher.h"
     18 
     19 #include <algorithm>
     20 
     21 #include <base/bind.h>
     22 #include <base/logging.h>
     23 #include <base/strings/string_util.h>
     24 #include <base/time/time.h>
     25 #include <gtest/gtest.h>
     26 
     27 // This is a mock implementation of HttpFetcher which is useful for testing.
     28 
     29 using brillo::MessageLoop;
     30 using std::min;
     31 
     32 namespace chromeos_update_engine {
     33 
     34 MockHttpFetcher::~MockHttpFetcher() {
     35   CHECK(timeout_id_ == MessageLoop::kTaskIdNull) <<
     36       "Call TerminateTransfer() before dtor.";
     37 }
     38 
     39 void MockHttpFetcher::BeginTransfer(const std::string& url) {
     40   EXPECT_FALSE(never_use_);
     41   if (fail_transfer_ || data_.empty()) {
     42     // No data to send, just notify of completion..
     43     SignalTransferComplete();
     44     return;
     45   }
     46   if (sent_size_ < data_.size())
     47     SendData(true);
     48 }
     49 
     50 // Returns false on one condition: If timeout_id_ was already set
     51 // and it needs to be deleted by the caller. If timeout_id_ is null
     52 // when this function is called, this function will always return true.
     53 bool MockHttpFetcher::SendData(bool skip_delivery) {
     54   if (fail_transfer_) {
     55     SignalTransferComplete();
     56     return timeout_id_ != MessageLoop::kTaskIdNull;
     57   }
     58 
     59   CHECK_LT(sent_size_, data_.size());
     60   if (!skip_delivery) {
     61     const size_t chunk_size = min(kMockHttpFetcherChunkSize,
     62                                   data_.size() - sent_size_);
     63     CHECK(delegate_);
     64     delegate_->ReceivedBytes(this, &data_[sent_size_], chunk_size);
     65     // We may get terminated in the callback.
     66     if (sent_size_ == data_.size()) {
     67       LOG(INFO) << "Terminated in the ReceivedBytes callback.";
     68       return timeout_id_ != MessageLoop::kTaskIdNull;
     69     }
     70     sent_size_ += chunk_size;
     71     CHECK_LE(sent_size_, data_.size());
     72     if (sent_size_ == data_.size()) {
     73       // We've sent all the data. Notify of success.
     74       SignalTransferComplete();
     75     }
     76   }
     77 
     78   if (paused_) {
     79     // If we're paused, we should return true if timeout_id_ is set,
     80     // since we need the caller to delete it.
     81     return timeout_id_ != MessageLoop::kTaskIdNull;
     82   }
     83 
     84   if (timeout_id_ != MessageLoop::kTaskIdNull) {
     85     // we still need a timeout if there's more data to send
     86     return sent_size_ < data_.size();
     87   } else if (sent_size_ < data_.size()) {
     88     // we don't have a timeout source and we need one
     89     timeout_id_ = MessageLoop::current()->PostDelayedTask(
     90         FROM_HERE,
     91         base::Bind(&MockHttpFetcher::TimeoutCallback, base::Unretained(this)),
     92         base::TimeDelta::FromMilliseconds(10));
     93   }
     94   return true;
     95 }
     96 
     97 void MockHttpFetcher::TimeoutCallback() {
     98   CHECK(!paused_);
     99   if (SendData(false)) {
    100     // We need to re-schedule the timeout.
    101     timeout_id_ = MessageLoop::current()->PostDelayedTask(
    102         FROM_HERE,
    103         base::Bind(&MockHttpFetcher::TimeoutCallback, base::Unretained(this)),
    104         base::TimeDelta::FromMilliseconds(10));
    105   } else {
    106     timeout_id_ = MessageLoop::kTaskIdNull;
    107   }
    108 }
    109 
    110 // If the transfer is in progress, aborts the transfer early.
    111 // The transfer cannot be resumed.
    112 void MockHttpFetcher::TerminateTransfer() {
    113   LOG(INFO) << "Terminating transfer.";
    114   sent_size_ = data_.size();
    115   // Kill any timeout, it is ok to call with kTaskIdNull.
    116   MessageLoop::current()->CancelTask(timeout_id_);
    117   timeout_id_ = MessageLoop::kTaskIdNull;
    118   delegate_->TransferTerminated(this);
    119 }
    120 
    121 void MockHttpFetcher::SetHeader(const std::string& header_name,
    122                                 const std::string& header_value) {
    123   extra_headers_[base::ToLowerASCII(header_name)] = header_value;
    124 }
    125 
    126 std::string MockHttpFetcher::GetHeader(const std::string& header_name) const {
    127   const auto it = extra_headers_.find(base::ToLowerASCII(header_name));
    128   if (it == extra_headers_.end())
    129     return "";
    130   return it->second;
    131 }
    132 
    133 void MockHttpFetcher::Pause() {
    134   CHECK(!paused_);
    135   paused_ = true;
    136   MessageLoop::current()->CancelTask(timeout_id_);
    137   timeout_id_ = MessageLoop::kTaskIdNull;
    138 }
    139 
    140 void MockHttpFetcher::Unpause() {
    141   CHECK(paused_) << "You must pause before unpause.";
    142   paused_ = false;
    143   if (sent_size_ < data_.size()) {
    144     SendData(false);
    145   }
    146 }
    147 
    148 void MockHttpFetcher::FailTransfer(int http_response_code) {
    149   fail_transfer_ = true;
    150   http_response_code_ = http_response_code;
    151 }
    152 
    153 void MockHttpFetcher::SignalTransferComplete() {
    154   // If the transfer has been failed, the HTTP response code should be set
    155   // already.
    156   if (!fail_transfer_) {
    157     http_response_code_ = 200;
    158   }
    159   delegate_->TransferComplete(this, !fail_transfer_);
    160 }
    161 
    162 }  // namespace chromeos_update_engine
    163