xref: /aosp_15_r20/external/cronet/net/test/url_request/url_request_test_job_backed_by_file_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2014 The Chromium Authors
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 "net/test/url_request/url_request_test_job_backed_by_file.h"
6 #include "base/memory/raw_ptr.h"
7 
8 #include <memory>
9 
10 #include "base/check.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/run_loop.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/task/single_thread_task_runner.h"
17 #include "net/base/filename_util.h"
18 #include "net/base/net_errors.h"
19 #include "net/test/test_with_task_environment.h"
20 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
21 #include "net/url_request/url_request.h"
22 #include "net/url_request/url_request_context.h"
23 #include "net/url_request/url_request_context_builder.h"
24 #include "net/url_request/url_request_test_util.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "url/url_constants.h"
27 
28 namespace net {
29 
30 namespace {
31 
32 // A URLRequestTestJobBackedByFile for testing values passed to OnSeekComplete
33 // and OnReadComplete.
34 class TestURLRequestTestJobBackedByFile : public URLRequestTestJobBackedByFile {
35  public:
36   // |seek_position| will be set to the value passed in to OnSeekComplete.
37   // |observed_content| will be set to the concatenated data from all calls to
38   // OnReadComplete.
TestURLRequestTestJobBackedByFile(URLRequest * request,const base::FilePath & file_path,const scoped_refptr<base::TaskRunner> & file_task_runner,int * open_result,int64_t * seek_position,bool * done_reading,std::string * observed_content)39   TestURLRequestTestJobBackedByFile(
40       URLRequest* request,
41       const base::FilePath& file_path,
42       const scoped_refptr<base::TaskRunner>& file_task_runner,
43       int* open_result,
44       int64_t* seek_position,
45       bool* done_reading,
46       std::string* observed_content)
47       : URLRequestTestJobBackedByFile(request,
48                                       file_path,
49                                       file_task_runner),
50         open_result_(open_result),
51         seek_position_(seek_position),
52         done_reading_(done_reading),
53         observed_content_(observed_content) {
54     *open_result_ = ERR_IO_PENDING;
55     *seek_position_ = ERR_IO_PENDING;
56     *done_reading_ = false;
57     observed_content_->clear();
58   }
59 
60   ~TestURLRequestTestJobBackedByFile() override = default;
61 
62  protected:
OnOpenComplete(int result)63   void OnOpenComplete(int result) override {
64     // Should only be called once.
65     ASSERT_EQ(ERR_IO_PENDING, *open_result_);
66     *open_result_ = result;
67   }
68 
OnSeekComplete(int64_t result)69   void OnSeekComplete(int64_t result) override {
70     // Should only call this if open succeeded.
71     EXPECT_EQ(OK, *open_result_);
72     // Should only be called once.
73     ASSERT_EQ(ERR_IO_PENDING, *seek_position_);
74     *seek_position_ = result;
75   }
76 
OnReadComplete(IOBuffer * buf,int result)77   void OnReadComplete(IOBuffer* buf, int result) override {
78     // Should only call this if seek succeeded.
79     EXPECT_GE(*seek_position_, 0);
80     observed_content_->append(std::string(buf->data(), result));
81   }
82 
DoneReading()83   void DoneReading() override { *done_reading_ = true; }
84 
85   const raw_ptr<int> open_result_;
86   const raw_ptr<int64_t> seek_position_;
87   raw_ptr<bool> done_reading_;
88   const raw_ptr<std::string> observed_content_;
89 };
90 
91 // A simple holder for start/end used in http range requests.
92 struct Range {
93   int start;
94   int end;
95 
Rangenet::__anonaaf106690111::Range96   Range() {
97     start = 0;
98     end = 0;
99   }
100 
Rangenet::__anonaaf106690111::Range101   Range(int start, int end) {
102     this->start = start;
103     this->end = end;
104   }
105 };
106 
107 // A superclass for tests of the OnReadComplete / OnSeekComplete /
108 // OnReadComplete functions of URLRequestTestJobBackedByFile.
109 class URLRequestTestJobBackedByFileEventsTest : public TestWithTaskEnvironment {
110  public:
111   URLRequestTestJobBackedByFileEventsTest();
112 
113  protected:
114   void TearDown() override;
115 
116   // This creates a file with |content| as the contents, and then creates and
117   // runs a TestURLRequestTestJobBackedByFile job to get the contents out of it,
118   // and makes sure that the callbacks observed the correct bytes. If a Range
119   // is provided, this function will add the appropriate Range http header to
120   // the request and verify that only the bytes in that range (inclusive) were
121   // observed.
122   void RunSuccessfulRequestWithString(const std::string& content,
123                                       const Range* range);
124 
125   // This is the same as the method above it, except that it will make sure
126   // the content matches |expected_content| and allow caller to specify the
127   // extension of the filename in |file_extension|.
128   void RunSuccessfulRequestWithString(
129       const std::string& content,
130       const std::string& expected_content,
131       const base::FilePath::StringPieceType& file_extension,
132       const Range* range);
133 
134   // Creates and runs a TestURLRequestTestJobBackedByFile job to read from file
135   // provided by |path|. If |range| value is provided, it will be passed in the
136   // range header.
137   void RunRequestWithPath(const base::FilePath& path,
138                           const std::string& range,
139                           int* open_result,
140                           int64_t* seek_position,
141                           bool* done_reading,
142                           std::string* observed_content);
143 
144   base::ScopedTempDir directory_;
145   std::unique_ptr<URLRequestContext> context_;
146   TestDelegate delegate_;
147 };
148 
149 URLRequestTestJobBackedByFileEventsTest::
URLRequestTestJobBackedByFileEventsTest()150     URLRequestTestJobBackedByFileEventsTest()
151     : context_(CreateTestURLRequestContextBuilder()->Build()) {}
152 
TearDown()153 void URLRequestTestJobBackedByFileEventsTest::TearDown() {
154   // Gives a chance to close the opening file.
155   base::RunLoop().RunUntilIdle();
156   ASSERT_TRUE(!directory_.IsValid() || directory_.Delete());
157   TestWithTaskEnvironment::TearDown();
158 }
159 
RunSuccessfulRequestWithString(const std::string & content,const Range * range)160 void URLRequestTestJobBackedByFileEventsTest::RunSuccessfulRequestWithString(
161     const std::string& content,
162     const Range* range) {
163   RunSuccessfulRequestWithString(content, content, FILE_PATH_LITERAL(""),
164                                  range);
165 }
166 
RunSuccessfulRequestWithString(const std::string & raw_content,const std::string & expected_content,const base::FilePath::StringPieceType & file_extension,const Range * range)167 void URLRequestTestJobBackedByFileEventsTest::RunSuccessfulRequestWithString(
168     const std::string& raw_content,
169     const std::string& expected_content,
170     const base::FilePath::StringPieceType& file_extension,
171     const Range* range) {
172   ASSERT_TRUE(directory_.CreateUniqueTempDir());
173   base::FilePath path = directory_.GetPath().Append(FILE_PATH_LITERAL("test"));
174   if (!file_extension.empty())
175     path = path.AddExtension(file_extension);
176   ASSERT_TRUE(base::WriteFile(path, raw_content));
177 
178   std::string range_value;
179   if (range) {
180     ASSERT_GE(range->start, 0);
181     ASSERT_GE(range->end, 0);
182     ASSERT_LE(range->start, range->end);
183     ASSERT_LT(static_cast<unsigned int>(range->end), expected_content.length());
184     range_value = base::StringPrintf("bytes=%d-%d", range->start, range->end);
185   }
186 
187   {
188     int open_result;
189     int64_t seek_position;
190     bool done_reading;
191     std::string observed_content;
192     RunRequestWithPath(path, range_value, &open_result, &seek_position,
193                        &done_reading, &observed_content);
194 
195     EXPECT_EQ(OK, open_result);
196     EXPECT_FALSE(delegate_.request_failed());
197     int expected_length =
198         range ? (range->end - range->start + 1) : expected_content.length();
199     EXPECT_EQ(delegate_.bytes_received(), expected_length);
200 
201     std::string expected_data_received;
202     if (range) {
203       expected_data_received.insert(0, expected_content, range->start,
204                                     expected_length);
205       EXPECT_EQ(expected_data_received, observed_content);
206     } else {
207       expected_data_received = expected_content;
208       EXPECT_EQ(raw_content, observed_content);
209     }
210 
211     EXPECT_EQ(expected_data_received, delegate_.data_received());
212     EXPECT_EQ(seek_position, range ? range->start : 0);
213     EXPECT_TRUE(done_reading);
214   }
215 }
216 
RunRequestWithPath(const base::FilePath & path,const std::string & range,int * open_result,int64_t * seek_position,bool * done_reading,std::string * observed_content)217 void URLRequestTestJobBackedByFileEventsTest::RunRequestWithPath(
218     const base::FilePath& path,
219     const std::string& range,
220     int* open_result,
221     int64_t* seek_position,
222     bool* done_reading,
223     std::string* observed_content) {
224   const GURL kUrl("http://intercepted-url/");
225 
226   std::unique_ptr<URLRequest> request(context_->CreateRequest(
227       kUrl, DEFAULT_PRIORITY, &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS));
228   TestScopedURLInterceptor interceptor(
229       kUrl, std::make_unique<TestURLRequestTestJobBackedByFile>(
230                 request.get(), path,
231                 base::SingleThreadTaskRunner::GetCurrentDefault(), open_result,
232                 seek_position, done_reading, observed_content));
233   if (!range.empty()) {
234     request->SetExtraRequestHeaderByName(HttpRequestHeaders::kRange, range,
235                                          true /*overwrite*/);
236   }
237   request->Start();
238   delegate_.RunUntilComplete();
239 }
240 
241 // Helper function to make a character array filled with |size| bytes of
242 // test content.
MakeContentOfSize(int size)243 std::string MakeContentOfSize(int size) {
244   EXPECT_GE(size, 0);
245   std::string result;
246   result.reserve(size);
247   for (int i = 0; i < size; i++) {
248     result.append(1, static_cast<char>(i % 256));
249   }
250   return result;
251 }
252 
TEST_F(URLRequestTestJobBackedByFileEventsTest,ZeroByteFile)253 TEST_F(URLRequestTestJobBackedByFileEventsTest, ZeroByteFile) {
254   RunSuccessfulRequestWithString(std::string(""), nullptr);
255 }
256 
TEST_F(URLRequestTestJobBackedByFileEventsTest,TinyFile)257 TEST_F(URLRequestTestJobBackedByFileEventsTest, TinyFile) {
258   RunSuccessfulRequestWithString(std::string("hello world"), nullptr);
259 }
260 
TEST_F(URLRequestTestJobBackedByFileEventsTest,SmallFile)261 TEST_F(URLRequestTestJobBackedByFileEventsTest, SmallFile) {
262   RunSuccessfulRequestWithString(MakeContentOfSize(17 * 1024), nullptr);
263 }
264 
TEST_F(URLRequestTestJobBackedByFileEventsTest,BigFile)265 TEST_F(URLRequestTestJobBackedByFileEventsTest, BigFile) {
266   RunSuccessfulRequestWithString(MakeContentOfSize(3 * 1024 * 1024), nullptr);
267 }
268 
TEST_F(URLRequestTestJobBackedByFileEventsTest,Range)269 TEST_F(URLRequestTestJobBackedByFileEventsTest, Range) {
270   // Use a 15KB content file and read a range chosen somewhat arbitrarily but
271   // not aligned on any likely page boundaries.
272   int size = 15 * 1024;
273   Range range(1701, (6 * 1024) + 3);
274   RunSuccessfulRequestWithString(MakeContentOfSize(size), &range);
275 }
276 
TEST_F(URLRequestTestJobBackedByFileEventsTest,DecodeSvgzFile)277 TEST_F(URLRequestTestJobBackedByFileEventsTest, DecodeSvgzFile) {
278   std::string expected_content("Hello, World!");
279   unsigned char gzip_data[] = {
280       // From:
281       //   echo -n 'Hello, World!' | gzip | xxd -i | sed -e 's/^/  /'
282       0x1f, 0x8b, 0x08, 0x00, 0x2b, 0x02, 0x84, 0x55, 0x00, 0x03, 0xf3,
283       0x48, 0xcd, 0xc9, 0xc9, 0xd7, 0x51, 0x08, 0xcf, 0x2f, 0xca, 0x49,
284       0x51, 0x04, 0x00, 0xd0, 0xc3, 0x4a, 0xec, 0x0d, 0x00, 0x00, 0x00};
285   RunSuccessfulRequestWithString(
286       std::string(reinterpret_cast<char*>(gzip_data), sizeof(gzip_data)),
287       expected_content, FILE_PATH_LITERAL("svgz"), nullptr);
288 }
289 
TEST_F(URLRequestTestJobBackedByFileEventsTest,OpenNonExistentFile)290 TEST_F(URLRequestTestJobBackedByFileEventsTest, OpenNonExistentFile) {
291   base::FilePath path;
292   base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &path);
293   path = path.Append(
294       FILE_PATH_LITERAL("net/data/url_request_unittest/non-existent.txt"));
295 
296   int open_result;
297   int64_t seek_position;
298   bool done_reading;
299   std::string observed_content;
300   RunRequestWithPath(path, std::string(), &open_result, &seek_position,
301                      &done_reading, &observed_content);
302 
303   EXPECT_EQ(ERR_FILE_NOT_FOUND, open_result);
304   EXPECT_FALSE(done_reading);
305   EXPECT_TRUE(delegate_.request_failed());
306 }
307 
TEST_F(URLRequestTestJobBackedByFileEventsTest,MultiRangeRequestNotSupported)308 TEST_F(URLRequestTestJobBackedByFileEventsTest, MultiRangeRequestNotSupported) {
309   base::FilePath path;
310   base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &path);
311   path = path.Append(
312       FILE_PATH_LITERAL("net/data/url_request_unittest/BullRunSpeech.txt"));
313 
314   int open_result;
315   int64_t seek_position;
316   bool done_reading;
317   std::string observed_content;
318   RunRequestWithPath(path, "bytes=1-5,20-30", &open_result, &seek_position,
319                      &done_reading, &observed_content);
320 
321   EXPECT_EQ(OK, open_result);
322   EXPECT_EQ(ERR_REQUEST_RANGE_NOT_SATISFIABLE, seek_position);
323   EXPECT_FALSE(done_reading);
324   EXPECT_TRUE(delegate_.request_failed());
325 }
326 
TEST_F(URLRequestTestJobBackedByFileEventsTest,RangeExceedingFileSize)327 TEST_F(URLRequestTestJobBackedByFileEventsTest, RangeExceedingFileSize) {
328   base::FilePath path;
329   base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &path);
330   path = path.Append(
331       FILE_PATH_LITERAL("net/data/url_request_unittest/BullRunSpeech.txt"));
332 
333   int open_result;
334   int64_t seek_position;
335   bool done_reading;
336   std::string observed_content;
337   RunRequestWithPath(path, "bytes=50000-", &open_result, &seek_position,
338                      &done_reading, &observed_content);
339 
340   EXPECT_EQ(OK, open_result);
341   EXPECT_EQ(ERR_REQUEST_RANGE_NOT_SATISFIABLE, seek_position);
342   EXPECT_FALSE(done_reading);
343   EXPECT_TRUE(delegate_.request_failed());
344 }
345 
TEST_F(URLRequestTestJobBackedByFileEventsTest,IgnoreRangeParsingError)346 TEST_F(URLRequestTestJobBackedByFileEventsTest, IgnoreRangeParsingError) {
347   base::FilePath path;
348   base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &path);
349   path = path.Append(
350       FILE_PATH_LITERAL("net/data/url_request_unittest/simple.html"));
351 
352   int open_result;
353   int64_t seek_position;
354   bool done_reading;
355   std::string observed_content;
356   RunRequestWithPath(path, "bytes=3-z", &open_result, &seek_position,
357                      &done_reading, &observed_content);
358 
359   EXPECT_EQ(OK, open_result);
360   EXPECT_EQ(0, seek_position);
361   EXPECT_EQ("hello\n", observed_content);
362   EXPECT_TRUE(done_reading);
363   EXPECT_FALSE(delegate_.request_failed());
364 }
365 
366 }  // namespace
367 
368 }  // namespace net
369