OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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 <algorithm> |
| 6 #include <string> |
| 7 |
| 8 #include "base/bind.h" |
| 9 #include "base/format_macros.h" |
| 10 #include "base/message_loop/message_loop.h" |
| 11 #include "base/strings/stringprintf.h" |
| 12 #include "media/base/media_log.h" |
| 13 #include "media/base/seekable_buffer.h" |
| 14 #include "media/blink/mock_webframeclient.h" |
| 15 #include "media/blink/mock_weburlloader.h" |
| 16 #include "media/blink/resource_multibuffer_data_provider.h" |
| 17 #include "media/blink/url_index.h" |
| 18 #include "net/base/net_errors.h" |
| 19 #include "net/http/http_request_headers.h" |
| 20 #include "net/http/http_util.h" |
| 21 #include "third_party/WebKit/public/platform/WebString.h" |
| 22 #include "third_party/WebKit/public/platform/WebURLError.h" |
| 23 #include "third_party/WebKit/public/platform/WebURLRequest.h" |
| 24 #include "third_party/WebKit/public/platform/WebURLResponse.h" |
| 25 #include "third_party/WebKit/public/web/WebLocalFrame.h" |
| 26 #include "third_party/WebKit/public/web/WebView.h" |
| 27 |
| 28 using ::testing::_; |
| 29 using ::testing::InSequence; |
| 30 using ::testing::Return; |
| 31 using ::testing::Truly; |
| 32 using ::testing::NiceMock; |
| 33 |
| 34 using blink::WebLocalFrame; |
| 35 using blink::WebString; |
| 36 using blink::WebURLError; |
| 37 using blink::WebURLResponse; |
| 38 using blink::WebView; |
| 39 |
| 40 namespace media { |
| 41 |
| 42 const char kHttpUrl[] = "http://test"; |
| 43 const char kHttpRedirect[] = "http://test/ing"; |
| 44 |
| 45 const int kDataSize = 1024; |
| 46 const int kHttpOK = 200; |
| 47 const int kHttpPartialContent = 206; |
| 48 |
| 49 enum NetworkState { NONE, LOADED, LOADING }; |
| 50 |
| 51 // Predicate that tests that request disallows compressed data. |
| 52 static bool CorrectAcceptEncoding(const blink::WebURLRequest& request) { |
| 53 std::string value = |
| 54 request.httpHeaderField( |
| 55 WebString::fromUTF8(net::HttpRequestHeaders::kAcceptEncoding)) |
| 56 .utf8(); |
| 57 return (value.find("identity;q=1") != std::string::npos) && |
| 58 (value.find("*;q=0") != std::string::npos); |
| 59 } |
| 60 |
| 61 class ResourceMultiBufferDataProviderTest : public testing::Test { |
| 62 public: |
| 63 ResourceMultiBufferDataProviderTest() |
| 64 : view_(WebView::create(nullptr)), |
| 65 frame_(WebLocalFrame::create(blink::WebTreeScopeType::Document, |
| 66 &client_)) { |
| 67 view_->setMainFrame(frame_); |
| 68 url_index_.reset(new UrlIndex(frame_, 0)); |
| 69 |
| 70 for (int i = 0; i < kDataSize; ++i) { |
| 71 data_[i] = i; |
| 72 } |
| 73 } |
| 74 |
| 75 virtual ~ResourceMultiBufferDataProviderTest() { |
| 76 view_->close(); |
| 77 frame_->close(); |
| 78 } |
| 79 |
| 80 void Initialize(const char* url, int first_position) { |
| 81 gurl_ = GURL(url); |
| 82 url_data_ = url_index_->GetByUrl(gurl_, UrlData::CORS_UNSPECIFIED); |
| 83 DCHECK(url_data_); |
| 84 DCHECK(url_data_->frame()); |
| 85 url_data_->OnRedirect( |
| 86 base::Bind(&ResourceMultiBufferDataProviderTest::RedirectCallback, |
| 87 base::Unretained(this))); |
| 88 |
| 89 first_position_ = first_position; |
| 90 |
| 91 scoped_ptr<ResourceMultiBufferDataProvider> loader( |
| 92 new ResourceMultiBufferDataProvider(url_data_.get(), first_position_)); |
| 93 loader_ = loader.get(); |
| 94 url_data_->multibuffer()->AddProvider(loader.Pass()); |
| 95 |
| 96 // |test_loader_| will be used when Start() is called. |
| 97 url_loader_ = new NiceMock<MockWebURLLoader>(); |
| 98 loader_->test_loader_ = scoped_ptr<blink::WebURLLoader>(url_loader_); |
| 99 } |
| 100 |
| 101 void Start() { |
| 102 InSequence s; |
| 103 EXPECT_CALL(*url_loader_, |
| 104 loadAsynchronously(Truly(CorrectAcceptEncoding), loader_)); |
| 105 |
| 106 loader_->Start(); |
| 107 } |
| 108 |
| 109 void FullResponse(int64 instance_size, bool ok = true) { |
| 110 WebURLResponse response(gurl_); |
| 111 response.setHTTPHeaderField( |
| 112 WebString::fromUTF8("Content-Length"), |
| 113 WebString::fromUTF8(base::StringPrintf("%" PRId64, instance_size))); |
| 114 response.setExpectedContentLength(instance_size); |
| 115 response.setHTTPStatusCode(kHttpOK); |
| 116 loader_->didReceiveResponse(url_loader_, response); |
| 117 |
| 118 if (ok) { |
| 119 EXPECT_EQ(instance_size, url_data_->length()); |
| 120 } |
| 121 |
| 122 EXPECT_FALSE(url_data_->range_supported()); |
| 123 } |
| 124 |
| 125 void PartialResponse(int64 first_position, |
| 126 int64 last_position, |
| 127 int64 instance_size) { |
| 128 PartialResponse(first_position, last_position, instance_size, false, true); |
| 129 } |
| 130 |
| 131 void PartialResponse(int64 first_position, |
| 132 int64 last_position, |
| 133 int64 instance_size, |
| 134 bool chunked, |
| 135 bool accept_ranges) { |
| 136 WebURLResponse response(gurl_); |
| 137 response.setHTTPHeaderField( |
| 138 WebString::fromUTF8("Content-Range"), |
| 139 WebString::fromUTF8( |
| 140 base::StringPrintf("bytes " |
| 141 "%" PRId64 "-%" PRId64 "/%" PRId64, |
| 142 first_position, last_position, instance_size))); |
| 143 |
| 144 // HTTP 1.1 doesn't permit Content-Length with Transfer-Encoding: chunked. |
| 145 int64 content_length = -1; |
| 146 if (chunked) { |
| 147 response.setHTTPHeaderField(WebString::fromUTF8("Transfer-Encoding"), |
| 148 WebString::fromUTF8("chunked")); |
| 149 } else { |
| 150 content_length = last_position - first_position + 1; |
| 151 } |
| 152 response.setExpectedContentLength(content_length); |
| 153 |
| 154 // A server isn't required to return Accept-Ranges even though it might. |
| 155 if (accept_ranges) { |
| 156 response.setHTTPHeaderField(WebString::fromUTF8("Accept-Ranges"), |
| 157 WebString::fromUTF8("bytes")); |
| 158 } |
| 159 |
| 160 response.setHTTPStatusCode(kHttpPartialContent); |
| 161 loader_->didReceiveResponse(url_loader_, response); |
| 162 |
| 163 EXPECT_EQ(instance_size, url_data_->length()); |
| 164 |
| 165 // A valid partial response should always result in this being true. |
| 166 EXPECT_TRUE(url_data_->range_supported()); |
| 167 } |
| 168 |
| 169 void Redirect(const char* url) { |
| 170 GURL redirectUrl(url); |
| 171 blink::WebURLRequest newRequest(redirectUrl); |
| 172 blink::WebURLResponse redirectResponse(gurl_); |
| 173 |
| 174 EXPECT_CALL(*this, RedirectCallback(_)) |
| 175 .WillOnce( |
| 176 Invoke(this, &ResourceMultiBufferDataProviderTest::SetUrlData)); |
| 177 |
| 178 loader_->willFollowRedirect(url_loader_, newRequest, redirectResponse); |
| 179 |
| 180 base::MessageLoop::current()->RunUntilIdle(); |
| 181 } |
| 182 |
| 183 void StopWhenLoad() { |
| 184 InSequence s; |
| 185 EXPECT_CALL(*url_loader_, cancel()); |
| 186 loader_ = nullptr; |
| 187 url_data_ = nullptr; |
| 188 } |
| 189 |
| 190 // Helper method to write to |loader_| from |data_|. |
| 191 void WriteLoader(int position, int size) { |
| 192 loader_->didReceiveData( |
| 193 url_loader_, reinterpret_cast<char*>(data_ + position), size, size); |
| 194 } |
| 195 |
| 196 void WriteData(int size) { |
| 197 scoped_ptr<char[]> data(new char[size]); |
| 198 loader_->didReceiveData(url_loader_, data.get(), size, size); |
| 199 } |
| 200 |
| 201 // Verifies that data in buffer[0...size] is equal to data_[pos...pos+size]. |
| 202 void VerifyBuffer(uint8* buffer, int pos, int size) { |
| 203 EXPECT_EQ(0, memcmp(buffer, data_ + pos, size)); |
| 204 } |
| 205 |
| 206 bool HasActiveLoader() { return loader_->active_loader_; } |
| 207 MOCK_METHOD1(RedirectCallback, void(const scoped_refptr<UrlData>&)); |
| 208 |
| 209 void SetUrlData(const scoped_refptr<UrlData>& new_url_data) { |
| 210 url_data_ = new_url_data; |
| 211 } |
| 212 |
| 213 protected: |
| 214 GURL gurl_; |
| 215 int64 first_position_; |
| 216 |
| 217 scoped_ptr<UrlIndex> url_index_; |
| 218 scoped_refptr<UrlData> url_data_; |
| 219 scoped_refptr<UrlData> redirected_to_; |
| 220 // The loader is owned by the UrlData above. |
| 221 ResourceMultiBufferDataProvider* loader_; |
| 222 NiceMock<MockWebURLLoader>* url_loader_; |
| 223 |
| 224 MockWebFrameClient client_; |
| 225 WebView* view_; |
| 226 WebLocalFrame* frame_; |
| 227 |
| 228 base::MessageLoop message_loop_; |
| 229 |
| 230 uint8 data_[kDataSize]; |
| 231 |
| 232 private: |
| 233 DISALLOW_COPY_AND_ASSIGN(ResourceMultiBufferDataProviderTest); |
| 234 }; |
| 235 |
| 236 TEST_F(ResourceMultiBufferDataProviderTest, StartStop) { |
| 237 Initialize(kHttpUrl, 0); |
| 238 Start(); |
| 239 StopWhenLoad(); |
| 240 } |
| 241 |
| 242 // Tests that a bad HTTP response is recived, e.g. file not found. |
| 243 TEST_F(ResourceMultiBufferDataProviderTest, BadHttpResponse) { |
| 244 Initialize(kHttpUrl, 0); |
| 245 Start(); |
| 246 |
| 247 EXPECT_CALL(*this, RedirectCallback(scoped_refptr<UrlData>(nullptr))); |
| 248 |
| 249 WebURLResponse response(gurl_); |
| 250 response.setHTTPStatusCode(404); |
| 251 response.setHTTPStatusText("Not Found\n"); |
| 252 loader_->didReceiveResponse(url_loader_, response); |
| 253 StopWhenLoad(); |
| 254 } |
| 255 |
| 256 // Tests that partial content is requested but not fulfilled. |
| 257 TEST_F(ResourceMultiBufferDataProviderTest, NotPartialResponse) { |
| 258 Initialize(kHttpUrl, 100); |
| 259 Start(); |
| 260 FullResponse(1024, false); |
| 261 StopWhenLoad(); |
| 262 } |
| 263 |
| 264 // Tests that a 200 response is received. |
| 265 TEST_F(ResourceMultiBufferDataProviderTest, FullResponse) { |
| 266 Initialize(kHttpUrl, 0); |
| 267 Start(); |
| 268 FullResponse(1024); |
| 269 StopWhenLoad(); |
| 270 } |
| 271 |
| 272 // Tests that a partial content response is received. |
| 273 TEST_F(ResourceMultiBufferDataProviderTest, PartialResponse) { |
| 274 Initialize(kHttpUrl, 100); |
| 275 Start(); |
| 276 PartialResponse(100, 200, 1024); |
| 277 StopWhenLoad(); |
| 278 } |
| 279 |
| 280 TEST_F(ResourceMultiBufferDataProviderTest, PartialResponse_Chunked) { |
| 281 Initialize(kHttpUrl, 100); |
| 282 Start(); |
| 283 PartialResponse(100, 200, 1024, true, true); |
| 284 StopWhenLoad(); |
| 285 } |
| 286 |
| 287 TEST_F(ResourceMultiBufferDataProviderTest, PartialResponse_NoAcceptRanges) { |
| 288 Initialize(kHttpUrl, 100); |
| 289 Start(); |
| 290 PartialResponse(100, 200, 1024, false, false); |
| 291 StopWhenLoad(); |
| 292 } |
| 293 |
| 294 TEST_F(ResourceMultiBufferDataProviderTest, |
| 295 PartialResponse_ChunkedNoAcceptRanges) { |
| 296 Initialize(kHttpUrl, 100); |
| 297 Start(); |
| 298 PartialResponse(100, 200, 1024, true, false); |
| 299 StopWhenLoad(); |
| 300 } |
| 301 |
| 302 // Tests that an invalid partial response is received. |
| 303 TEST_F(ResourceMultiBufferDataProviderTest, InvalidPartialResponse) { |
| 304 Initialize(kHttpUrl, 0); |
| 305 Start(); |
| 306 |
| 307 EXPECT_CALL(*this, RedirectCallback(scoped_refptr<UrlData>(nullptr))); |
| 308 |
| 309 WebURLResponse response(gurl_); |
| 310 response.setHTTPHeaderField( |
| 311 WebString::fromUTF8("Content-Range"), |
| 312 WebString::fromUTF8(base::StringPrintf("bytes " |
| 313 "%d-%d/%d", |
| 314 1, 10, 1024))); |
| 315 response.setExpectedContentLength(10); |
| 316 response.setHTTPStatusCode(kHttpPartialContent); |
| 317 loader_->didReceiveResponse(url_loader_, response); |
| 318 StopWhenLoad(); |
| 319 } |
| 320 |
| 321 TEST_F(ResourceMultiBufferDataProviderTest, TestRedirects) { |
| 322 // Test redirect. |
| 323 Initialize(kHttpUrl, 0); |
| 324 Start(); |
| 325 Redirect(kHttpRedirect); |
| 326 FullResponse(1024); |
| 327 StopWhenLoad(); |
| 328 } |
| 329 |
| 330 } // namespace media |
OLD | NEW |