blob: 6ddd64e025ee2684e9d5cc9beb73ce0fc969a617 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/web_package/web_bundle_parser_factory.h"
#include <algorithm>
#include <optional>
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace web_package {
namespace {
using testing::UnorderedElementsAreArray;
base::FilePath GetTestFilePath(const base::FilePath& path) {
base::FilePath test_path;
base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &test_path);
test_path = test_path.Append(
base::FilePath(FILE_PATH_LITERAL("components/test/data/web_package")));
return test_path.Append(path);
}
} // namespace
class WebBundleParserFactoryTest : public testing::Test {
public:
WebBundleParserFactoryTest()
: factory_(std::make_unique<WebBundleParserFactory>()) {}
std::unique_ptr<mojom::BundleDataSource> CreateFileDataSource(
base::File file) {
return factory_->CreateFileDataSourceForTesting(std::move(file));
}
void GetParserForFile(mojo::PendingReceiver<mojom::WebBundleParser> receiver,
base::File file,
const std::optional<GURL>& base_url) {
mojom::WebBundleParserFactory* factory = factory_.get();
mojo::PendingRemote<mojom::BundleDataSource>
file_data_source_pending_remote;
auto file_data_source_pending_receiver =
file_data_source_pending_remote.InitWithNewPipeAndPassReceiver();
factory->BindFileDataSource(std::move(file_data_source_pending_receiver),
std::move(file));
return factory->GetParserForDataSource(
std::move(receiver), base_url,
std::move(file_data_source_pending_remote));
}
private:
std::unique_ptr<WebBundleParserFactory> factory_;
base::test::TaskEnvironment task_environment_;
};
TEST_F(WebBundleParserFactoryTest, FileDataSource) {
base::FilePath test_file =
GetTestFilePath(base::FilePath(FILE_PATH_LITERAL("hello_b2.wbn")));
base::File file(test_file, base::File::FLAG_OPEN | base::File::FLAG_READ);
ASSERT_TRUE(file.IsValid());
int64_t file_length = file.GetLength();
constexpr int64_t test_length = 16;
ASSERT_LE(test_length, file_length);
std::vector<uint8_t> first16b(test_length);
ASSERT_TRUE(file.ReadAndCheck(0, first16b));
std::vector<uint8_t> last16b(test_length);
ASSERT_TRUE(file.ReadAndCheck(file_length - test_length, last16b));
auto data_source = CreateFileDataSource(std::move(file));
{
base::test::TestFuture<const std::optional<std::vector<uint8_t>>&> future;
data_source->Read(/*offset=*/0, test_length, future.GetCallback());
ASSERT_TRUE(future.Get());
EXPECT_EQ(first16b, *future.Get());
}
{
base::test::TestFuture<const std::optional<std::vector<uint8_t>>&> future;
data_source->Read(file_length - test_length, test_length,
future.GetCallback());
ASSERT_TRUE(future.Get());
EXPECT_EQ(last16b, *future.Get());
}
{
base::test::TestFuture<const std::optional<std::vector<uint8_t>>&> future;
data_source->Read(file_length - test_length, test_length + 1,
future.GetCallback());
ASSERT_TRUE(future.Get());
EXPECT_EQ(last16b, *future.Get());
}
{
base::test::TestFuture<const std::optional<std::vector<uint8_t>>&> future;
data_source->Read(file_length + 1, test_length, future.GetCallback());
ASSERT_FALSE(future.Get());
}
{
base::test::TestFuture<int64_t> future;
data_source->Length(future.GetCallback());
EXPECT_EQ(file_length, future.Get());
}
{
base::test::TestFuture<bool> future;
data_source->IsRandomAccessContext(future.GetCallback());
EXPECT_TRUE(future.Get());
}
// Close the file should just work
{
base::test::TestFuture<void> future;
data_source->Close(future.GetCallback());
future.Get();
}
}
TEST_F(WebBundleParserFactoryTest, GetParserForFile) {
base::File file(
GetTestFilePath(base::FilePath(FILE_PATH_LITERAL("hello_b2.wbn"))),
base::File::FLAG_OPEN | base::File::FLAG_READ);
ASSERT_TRUE(file.IsValid());
mojo::Remote<mojom::WebBundleParser> parser;
GetParserForFile(parser.BindNewPipeAndPassReceiver(), std::move(file),
std::nullopt);
mojom::BundleMetadataPtr metadata;
{
base::test::TestFuture<mojom::BundleMetadataPtr,
mojom::BundleMetadataParseErrorPtr>
future;
parser->ParseMetadata(/*offset=*/std::nullopt, future.GetCallback());
metadata = std::get<0>(future.Take());
}
ASSERT_TRUE(metadata);
ASSERT_EQ(metadata->requests.size(), 4u);
std::map<std::string, mojom::BundleResponsePtr> responses;
for (const auto& item : metadata->requests) {
base::test::TestFuture<mojom::BundleResponsePtr,
mojom::BundleResponseParseErrorPtr>
future;
parser->ParseResponse(item.second->offset, item.second->length,
future.GetCallback());
auto [response, error] = future.Take();
ASSERT_TRUE(response);
ASSERT_FALSE(error);
responses[item.first.spec()] = std::move(response);
}
ASSERT_TRUE(responses["https://test.example.org/"]);
EXPECT_EQ(responses["https://test.example.org/"]->response_code, 200);
EXPECT_EQ(
responses["https://test.example.org/"]->response_headers["content-type"],
"text/html; charset=utf-8");
EXPECT_TRUE(responses["https://test.example.org/index.html"]);
EXPECT_TRUE(responses["https://test.example.org/manifest.webmanifest"]);
EXPECT_TRUE(responses["https://test.example.org/script.js"]);
}
TEST_F(WebBundleParserFactoryTest, GetParserForFileWithRelativeUrls) {
base::File file(GetTestFilePath(base::FilePath(
FILE_PATH_LITERAL("mixed_absolute_relative_urls.wbn"))),
base::File::FLAG_OPEN | base::File::FLAG_READ);
ASSERT_TRUE(file.IsValid());
mojo::Remote<mojom::WebBundleParser> parser;
GetParserForFile(parser.BindNewPipeAndPassReceiver(), std::move(file),
GURL("https://example.com/foo/"));
mojom::BundleMetadataPtr metadata;
{
base::test::TestFuture<mojom::BundleMetadataPtr,
mojom::BundleMetadataParseErrorPtr>
future;
parser->ParseMetadata(/*offset=*/std::nullopt, future.GetCallback());
metadata = std::get<0>(future.Take());
}
ASSERT_TRUE(metadata);
std::vector<GURL> requests;
requests.reserve(metadata->requests.size());
std::ranges::transform(metadata->requests, std::back_inserter(requests),
[](const auto& entry) { return entry.first; });
EXPECT_THAT(requests, UnorderedElementsAreArray(
{GURL("https://test.example.org/absolute-url"),
GURL("https://example.com/relative-url-1"),
GURL("https://example.com/foo/relative-url-2")}));
}
TEST_F(WebBundleParserFactoryTest, DeleteFile) {
base::ScopedTempDir tmp_dir;
ASSERT_TRUE(tmp_dir.CreateUniqueTempDir());
base::FilePath file_path = tmp_dir.GetPath();
base::CreateTemporaryFile(&file_path);
base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
ASSERT_TRUE(file.IsValid());
mojo::Remote<mojom::WebBundleParser> parser;
GetParserForFile(parser.BindNewPipeAndPassReceiver(), std::move(file),
std::nullopt);
base::test::TestFuture<void> future;
parser->Close(future.GetCallback());
future.Get();
EXPECT_TRUE(base::DeleteFile(file_path));
}
} // namespace web_package