// Copyright 2024 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/common/web_ui_loading_util.h" #include <cstdint> #include <optional> #include <vector> #include "base/containers/span.h" #include "base/memory/ref_counted_memory.h" #include "base/memory/scoped_refptr.h" #include "base/test/task_environment.h" #include "base/types/expected.h" #include "mojo/public/c/system/data_pipe.h" #include "mojo/public/c/system/types.h" #include "net/base/net_errors.h" #include "net/http/http_byte_range.h" #include "net/http/http_request_headers.h" #include "services/network/public/mojom/url_response_head.mojom.h" #include "services/network/test/test_url_loader_client.h" #include "testing/gtest/include/gtest/gtest.h" namespace content { namespace webui { namespace { struct RangeTestCase { std::optional<std::string> range; std::optional<GetRequestedRangeError> error; }; struct SendDataTestCase { std::optional<net::HttpByteRange> range; std::string data; base::expected<std::string, int> result; }; class WebUILoadingUtilGetRequestedRangeTest : public ::testing::TestWithParam<RangeTestCase> {}; class WebUILoadingUtilSendDataTest : public ::testing::TestWithParam<SendDataTestCase> { private: // For TestURLLoaderClient. base::test::TaskEnvironment task_environment_; }; std::optional<std::string> ReadAllData(network::TestURLLoaderClient& client) { size_t response_size; MojoResult result; result = client.response_body().ReadData( MOJO_READ_DATA_FLAG_QUERY, base::span<uint8_t>(), response_size); if (result != MOJO_RESULT_OK) { return std::nullopt; } std::vector<uint8_t> response_bytes(response_size); result = client.response_body().ReadData(MOJO_READ_DATA_FLAG_ALL_OR_NONE, response_bytes, response_size); if (result != MOJO_RESULT_OK) { return std::nullopt; } std::string response_body(response_bytes.begin(), response_bytes.end()); return std::make_optional(response_body); } } // namespace TEST_P(WebUILoadingUtilGetRequestedRangeTest, GetRequestedRange) { net::HttpRequestHeaders headers; if (GetParam().range) { headers.SetHeader(net::HttpRequestHeaders::kRange, *GetParam().range); } base::expected<net::HttpByteRange, GetRequestedRangeError> result = GetRequestedRange(headers); if (GetParam().error) { EXPECT_FALSE(result.has_value()); EXPECT_EQ(result.error(), *GetParam().error); } else { EXPECT_TRUE(result.has_value()); } } INSTANTIATE_TEST_SUITE_P( WebUILoadingUtilGetRequestedRangeTest, WebUILoadingUtilGetRequestedRangeTest, ::testing::Values( RangeTestCase(std::nullopt, std::make_optional(GetRequestedRangeError::kNoRanges)), RangeTestCase(std::make_optional("bytes=1-9"), std::nullopt), RangeTestCase( std::make_optional("bytes=1-9,11-19"), std::make_optional(GetRequestedRangeError::kMultipleRanges)), RangeTestCase(std::make_optional("bytes=3-2"), std::make_optional(GetRequestedRangeError::kParseFailed)), RangeTestCase( std::make_optional("byt"), std::make_optional(GetRequestedRangeError::kParseFailed)))); TEST_P(WebUILoadingUtilSendDataTest, SendData) { network::TestURLLoaderClient client; auto url_response_head = network::mojom::URLResponseHead::New(); const scoped_refptr<base::RefCountedString> data = base::MakeRefCounted<base::RefCountedString>(GetParam().data); const bool success = SendData(std::move(url_response_head), client.CreateRemote(), GetParam().range, data); client.RunUntilComplete(); // If an error is expected, verify the status error code is correct and // terminate the test early. if (!GetParam().result.has_value()) { EXPECT_FALSE(success); EXPECT_EQ(GetParam().result.error(), client.completion_status().error_code); return; } // No error expected. Verify response body is correct. EXPECT_TRUE(success); EXPECT_EQ(net::OK, client.completion_status().error_code); ASSERT_TRUE(client.response_body().is_valid()); std::optional<std::string> response_body = ReadAllData(client); ASSERT_TRUE(response_body); EXPECT_EQ(GetParam().result.value(), *response_body); } INSTANTIATE_TEST_SUITE_P( WebUILoadingUtilSendDataTest, WebUILoadingUtilSendDataTest, ::testing::Values( SendDataTestCase(std::nullopt, "this is some data", "this is some data"), SendDataTestCase(std::make_optional(net::HttpByteRange::Bounded(3, 8)), "this is some data", "s is s"), SendDataTestCase( std::make_optional(net::HttpByteRange::Bounded(3, 2)), "this is some data", base::unexpected(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)))); } // namespace webui } // namespace content