// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/exo/data_source.h" #include #include "base/barrier_closure.h" #include "base/bind.h" #include "base/callback_helpers.h" #include "base/files/file_util.h" #include "base/test/task_environment.h" #include "components/exo/data_source_delegate.h" #include "components/exo/test/exo_test_base.h" #include "testing/gtest/include/gtest/gtest.h" namespace exo { namespace { constexpr char kTestData[] = "Test Data"; class DataSourceTest : public testing::Test { protected: base::test::TaskEnvironment task_environment_{ base::test::TaskEnvironment::MainThreadType::DEFAULT, base::test::TaskEnvironment::ThreadPoolExecutionMode::ASYNC}; }; class TestDataSourceDelegate : public DataSourceDelegate { public: TestDataSourceDelegate() {} ~TestDataSourceDelegate() override {} // Overridden from DataSourceDelegate: void OnDataSourceDestroying(DataSource* source) override {} void OnTarget(const absl::optional& mime_type) override {} void OnSend(const std::string& mime_type, base::ScopedFD fd) override { ASSERT_TRUE(base::WriteFileDescriptor(fd.get(), kTestData)); } void OnCancelled() override {} void OnDndDropPerformed() override {} void OnDndFinished() override {} void OnAction(DndAction dnd_action) override {} bool CanAcceptDataEventsForSurface(Surface* surface) const override { return true; } }; void CheckMimeType(const std::string& expected, base::OnceClosure counter, const std::string& mime_type, const std::vector& data) { EXPECT_FALSE(expected.empty()); EXPECT_EQ(expected, mime_type); std::move(counter).Run(); } void CheckTextMimeType(const std::string& expected, base::OnceClosure counter, const std::string& mime_type, std::u16string data) { EXPECT_FALSE(expected.empty()); EXPECT_EQ(expected, mime_type); std::move(counter).Run(); } struct FileContents { std::string mime_type; std::string parsed_filename; }; void CheckFileContentsMimeType(const FileContents& file_contents, base::OnceClosure counter, const std::string& mime_type, const base::FilePath& filename, const std::vector& data) { EXPECT_FALSE(file_contents.mime_type.empty()); EXPECT_EQ(file_contents.mime_type, mime_type); EXPECT_EQ(file_contents.parsed_filename, filename.value()); std::move(counter).Run(); } void CheckWebCustomDataMimeType(const std::string& expected, base::OnceClosure counter, const std::string& mime_type, const std::vector& data) { EXPECT_FALSE(mime_type.empty()); EXPECT_EQ(expected, mime_type); std::move(counter).Run(); } void IncrementFailureCounter(std::atomic_int* failure_count, base::RepeatingClosure counter) { ++(*failure_count); std::move(counter).Run(); } void CheckMimeTypesReceived( DataSource* data_source, const std::string& text_mime, const std::string& rtf_mime, const std::string& html_mime, const std::string& image_mime, const std::string& filenames_mime, const FileContents& file_contents, const std::string& web_custom_data_mime = std::string()) { base::RunLoop run_loop; base::RepeatingClosure counter = base::BarrierClosure(DataSource::kMaxDataTypes, run_loop.QuitClosure()); std::atomic_int failure_count; failure_count.store(0); data_source->GetDataForPreferredMimeTypes( base::BindOnce(&CheckTextMimeType, text_mime, counter), base::BindOnce(&CheckMimeType, rtf_mime, counter), base::BindOnce(&CheckTextMimeType, html_mime, counter), base::BindOnce(&CheckMimeType, image_mime, counter), base::BindOnce(&CheckMimeType, filenames_mime, counter), base::BindOnce(&CheckFileContentsMimeType, file_contents, counter), base::BindOnce(&CheckWebCustomDataMimeType, web_custom_data_mime, counter), base::BindRepeating(&IncrementFailureCounter, &failure_count, counter)); run_loop.Run(); int expected_failure_count = 0; for (const auto& mime_type : {text_mime, rtf_mime, html_mime, image_mime, filenames_mime, file_contents.mime_type, web_custom_data_mime}) { if (mime_type.empty()) ++expected_failure_count; } EXPECT_EQ(expected_failure_count, failure_count.load()); } TEST_F(DataSourceTest, ReadData) { TestDataSourceDelegate delegate; DataSource data_source(&delegate); std::string mime_type("text/plain;charset=utf-8"); data_source.Offer(mime_type.c_str()); data_source.ReadDataForTesting( mime_type, base::BindOnce([](const std::string& mime_type, const std::vector& data) { std::string string_data(data.begin(), data.end()); EXPECT_EQ(std::string(kTestData), string_data); })); task_environment_.RunUntilIdle(); } TEST_F(DataSourceTest, ReadDataArbitraryMimeType) { TestDataSourceDelegate delegate; DataSource data_source(&delegate); std::string mime_type("abc/def;key=value"); data_source.Offer(mime_type.c_str()); data_source.ReadDataForTesting( mime_type, base::BindOnce([](const std::string& mime_type, const std::vector& data) { std::string string_data(data.begin(), data.end()); EXPECT_EQ(std::string(kTestData), string_data); })); task_environment_.RunUntilIdle(); } TEST_F(DataSourceTest, ReadData_UnknownMimeType) { TestDataSourceDelegate delegate; DataSource data_source(&delegate); data_source.Offer("text/plain;charset=utf-8"); std::string unknown_type("text/unknown"); data_source.ReadDataForTesting( unknown_type, base::BindOnce([](const std::string& mime_type, const std::vector& data) { FAIL() << "Callback should not be invoked when known " "mimetype is not offerred"; })); task_environment_.RunUntilIdle(); } TEST_F(DataSourceTest, ReadData_Destroyed) { TestDataSourceDelegate delegate; { DataSource data_source(&delegate); std::string mime_type("text/plain;charset=utf-8"); data_source.Offer(mime_type); data_source.ReadDataForTesting( mime_type, base::BindOnce([](const std::string& mime_type, const std::vector& data) { FAIL() << "Callback should not be invoked after " "data source is destroyed"; })); } task_environment_.RunUntilIdle(); } TEST_F(DataSourceTest, ReadData_Cancelled) { TestDataSourceDelegate delegate; DataSource data_source(&delegate); std::string mime_type("text/plain;charset=utf-8"); data_source.Offer(mime_type); data_source.ReadDataForTesting( mime_type, base::BindOnce([](const std::string& mime_type, const std::vector& data) { FAIL() << "Callback should not be invoked after cancelled"; })); data_source.Cancelled(); task_environment_.RunUntilIdle(); } TEST_F(DataSourceTest, CheckDteMimeTypeReceived) { TestDataSourceDelegate delegate; DataSource data_source(&delegate); const std::string kDteMimeType("chromium/x-data-transfer-endpoint"); data_source.Offer(kDteMimeType); base::RunLoop run_loop; base::RepeatingClosure counter = base::BarrierClosure(1, run_loop.QuitClosure()); std::atomic_int failure_count{0}; data_source.ReadDataTransferEndpoint( base::BindOnce(&CheckTextMimeType, kDteMimeType, counter), base::BindRepeating(&IncrementFailureCounter, &failure_count, counter)); run_loop.Run(); EXPECT_EQ(0, failure_count.load()); } TEST_F(DataSourceTest, PreferredMimeTypeUTF16) { TestDataSourceDelegate delegate; DataSource data_source(&delegate); data_source.Offer("text/plain;charset=utf-16"); data_source.Offer("text/plain;charset=UTF-8"); data_source.Offer("text/html;charset=UTF-16"); data_source.Offer("text/html;charset=utf-8"); CheckMimeTypesReceived(&data_source, "text/plain;charset=utf-16", "", "text/html;charset=UTF-16", "", "", {}); } TEST_F(DataSourceTest, PreferredMimeTypeUTF16LE) { TestDataSourceDelegate delegate; DataSource data_source(&delegate); data_source.Offer("text/plain;charset=utf-16le"); data_source.Offer("text/plain;charset=utf8"); data_source.Offer("text/html;charset=utf16le"); data_source.Offer("text/html;charset=utf-8"); CheckMimeTypesReceived(&data_source, "text/plain;charset=utf-16le", "", "text/html;charset=utf16le", "", "", {}); } TEST_F(DataSourceTest, PreferredMimeTypeUTF16BE) { TestDataSourceDelegate delegate; DataSource data_source(&delegate); data_source.Offer("text/plain;charset=utf-16be"); data_source.Offer("text/plain;charset=UTF8"); data_source.Offer("text/html;charset=UTF16be"); data_source.Offer("text/html;charset=utf-8"); CheckMimeTypesReceived(&data_source, "text/plain;charset=utf-16be", "", "text/html;charset=UTF16be", "", "", {}); } TEST_F(DataSourceTest, PreferredMimeTypeUTFToOther) { TestDataSourceDelegate delegate; DataSource data_source(&delegate); data_source.Offer("text/plain;charset=utf-8"); data_source.Offer("text/plain;charset=iso-8859-1"); data_source.Offer("text/html;charset=utf-8"); data_source.Offer("text/html;charset=iso-8859-1"); CheckMimeTypesReceived(&data_source, "text/plain;charset=utf-8", "", "text/html;charset=utf-8", "", "", {}); } TEST_F(DataSourceTest, RecogniseUTF8Legaccy) { TestDataSourceDelegate delegate; DataSource data_source(&delegate); data_source.Offer("UTF8_STRING"); data_source.Offer("text/plain;charset=iso-8859-1"); CheckMimeTypesReceived(&data_source, "UTF8_STRING", "", "", "", "", {}); } TEST_F(DataSourceTest, PreferredMimeTypeOtherToAscii) { TestDataSourceDelegate delegate; DataSource data_source(&delegate); data_source.Offer("text/plain;charset=iso-8859-1"); data_source.Offer("text/plain;charset=ASCII"); data_source.Offer("text/html;charset=iso-8859-1"); data_source.Offer("text/html;charset=ascii"); CheckMimeTypesReceived(&data_source, "text/plain;charset=iso-8859-1", "", "text/html;charset=iso-8859-1", "", "", {}); } TEST_F(DataSourceTest, PreferredMimeTypeOtherToUnspecified) { TestDataSourceDelegate delegate; DataSource data_source(&delegate); data_source.Offer("text/plain;charset=iso-8859-1"); data_source.Offer("text/plain"); data_source.Offer("text/html;charset=iso-8859-1"); data_source.Offer("text/html"); CheckMimeTypesReceived(&data_source, "text/plain;charset=iso-8859-1", "", "text/html;charset=iso-8859-1", "", "", {}); } TEST_F(DataSourceTest, PreferredMimeTypeRTF) { TestDataSourceDelegate delegate; DataSource data_source(&delegate); data_source.Offer("text/rtf"); CheckMimeTypesReceived(&data_source, "", "text/rtf", "", "", "", {}); } TEST_F(DataSourceTest, PreferredMimeTypePNGtoBitmap) { TestDataSourceDelegate delegate; DataSource data_source(&delegate); data_source.Offer("image/bmp"); data_source.Offer("image/png"); CheckMimeTypesReceived(&data_source, "", "", "", "image/png", "", {}); } TEST_F(DataSourceTest, PreferredMimeTypePNGToJPEG) { TestDataSourceDelegate delegate; DataSource data_source(&delegate); data_source.Offer("image/png"); data_source.Offer("image/jpeg"); data_source.Offer("image/jpg"); CheckMimeTypesReceived(&data_source, "", "", "", "image/png", "", {}); } TEST_F(DataSourceTest, PreferredMimeTypeBitmaptoJPEG) { TestDataSourceDelegate delegate; DataSource data_source(&delegate); data_source.Offer("image/bmp"); data_source.Offer("image/jpeg"); data_source.Offer("image/jpg"); CheckMimeTypesReceived(&data_source, "", "", "", "image/bmp", "", {}); } TEST_F(DataSourceTest, PreferredMimeTypeTextUriList) { TestDataSourceDelegate delegate; DataSource data_source(&delegate); data_source.Offer("text/uri-list"); CheckMimeTypesReceived(&data_source, "", "", "", "", "text/uri-list", {}); } TEST_F(DataSourceTest, PreferredMimeTypeOctetStream) { TestDataSourceDelegate delegate; DataSource data_source(&delegate); data_source.Offer("application/octet-stream;name=test.jpg"); CheckMimeTypesReceived( &data_source, "", "", "", "", "", {"application/octet-stream;name=test.jpg", "test.jpg"}); } TEST_F(DataSourceTest, OctetStreamWithoutName) { TestDataSourceDelegate delegate; DataSource data_source(&delegate); data_source.Offer("application/octet-stream"); CheckMimeTypesReceived(&data_source, "", "", "", "", "", {}); } TEST_F(DataSourceTest, OctetStreamWithQuotedName) { TestDataSourceDelegate delegate; DataSource data_source(&delegate); data_source.Offer("application/octet-stream;name=\"t\\\\est\\\".jpg\""); CheckMimeTypesReceived( &data_source, "", "", "", "", "", {"application/octet-stream;name=\"t\\\\est\\\".jpg\"", "t\\est\".jpg"}); } TEST_F(DataSourceTest, WebCustomDataMime) { TestDataSourceDelegate delegate; DataSource data_source(&delegate); std::string web_custom_data_mime("chromium/x-web-custom-data"); data_source.Offer(web_custom_data_mime); CheckMimeTypesReceived(&data_source, "", "", "", "", "", {}, web_custom_data_mime); } } // namespace } // namespace exo