| // 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 "chrome/browser/ash/printing/print_servers_provider.h" |
| |
| #include <string> |
| #include <vector> |
| |
| #include "chrome/browser/ash/printing/print_server.h" |
| #include "chrome/common/pref_names.h" |
| #include "components/sync_preferences/testing_pref_service_syncable.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| |
| namespace ash { |
| namespace { |
| |
| // An example of configuration file with print servers. |
| constexpr char kPrintServersPolicyJson1[] = R"json( |
| [ |
| { |
| "id": "id1", |
| "display_name": "MyPrintServer", |
| "url": "ipp://192.168.1.5" |
| }, { |
| "id": "id2", |
| "display_name": "Server API", |
| "url":"ipps://print-server.intra.example.com:444/ipp/cl2k4" |
| }, { |
| "id": "id3", |
| "display_name": "YaLP", |
| "url": "http://192.168.1.8/bleble/print" |
| } |
| ])json"; |
| |
| PrintServer Server1() { |
| return {"id1", GURL("http://192.168.1.5:631"), "MyPrintServer"}; |
| } |
| |
| PrintServer Server3() { |
| return {"id3", GURL("http://192.168.1.8/bleble/print"), "YaLP"}; |
| } |
| |
| // Corresponding vector with PrintServers. |
| std::vector<PrintServer> PrintServersPolicyData1() { |
| return {{Server1(), |
| {"id2", GURL("https://print-server.intra.example.com:444/ipp/cl2k4"), |
| "Server API"}, |
| Server3()}}; |
| } |
| |
| // Observer that stores all its calls. |
| class TestObserver : public PrintServersProvider::Observer { |
| public: |
| struct ObserverCall { |
| bool complete; |
| std::vector<PrintServer> servers; |
| ObserverCall(bool complete, std::vector<PrintServer> servers) |
| : complete(complete), servers(std::move(servers)) {} |
| }; |
| |
| ~TestObserver() override = default; |
| |
| // Callback from PrintServersProvider::Observer. |
| void OnServersChanged(bool complete, |
| const std::vector<PrintServer>& servers) override { |
| calls_.emplace_back(complete, servers); |
| } |
| |
| // Returns history of all calls to OnServersChanged(...). |
| const std::vector<ObserverCall>& GetCalls() const { return calls_; } |
| |
| private: |
| // history of all callbacks |
| std::vector<ObserverCall> calls_; |
| }; |
| |
| class PrintServersProviderTest : public testing::Test { |
| public: |
| PrintServersProviderTest() = default; |
| |
| protected: |
| void SetUp() override { |
| pref_service_.registry()->RegisterListPref(kAllowlistPrefName); |
| external_servers_->SetAllowlistPref(&pref_service_, kAllowlistPrefName); |
| } |
| |
| static constexpr char kAllowlistPrefName[] = "test"; |
| |
| // Everything must be called on Chrome_UIThread. |
| content::BrowserTaskEnvironment task_environment_; |
| // Test prefs. |
| sync_preferences::TestingPrefServiceSyncable pref_service_; |
| // Tested object. |
| std::unique_ptr<PrintServersProvider> external_servers_ = |
| PrintServersProvider::Create(); |
| }; |
| |
| // static |
| constexpr char PrintServersProviderTest::kAllowlistPrefName[]; |
| |
| // Verify that the object can be destroyed while parsing is in progress. |
| TEST_F(PrintServersProviderTest, DestructionIsSafe) { |
| { |
| std::unique_ptr<PrintServersProvider> servers = |
| PrintServersProvider::Create(); |
| servers->SetData(std::make_unique<std::string>(kPrintServersPolicyJson1)); |
| // Data is valid. Computation is proceeding. |
| } |
| // servers is out of scope. Destructor has run. Pump the message queue to |
| // see if anything strange happens. |
| task_environment_.RunUntilIdle(); |
| } |
| |
| // Verify that we're initially unset and empty. |
| // After initialization "complete" flags = false. |
| TEST_F(PrintServersProviderTest, InitialConditions) { |
| TestObserver obs; |
| external_servers_->AddObserver(&obs); |
| ASSERT_EQ(obs.GetCalls().size(), 1u); |
| EXPECT_EQ(obs.GetCalls().back().complete, false); |
| EXPECT_TRUE(obs.GetCalls().back().servers.empty()); |
| external_servers_->RemoveObserver(&obs); |
| } |
| |
| // Verify two ClearData() calls. |
| // ClearData() sets empty list and "complete" flag = true. |
| TEST_F(PrintServersProviderTest, ClearData2) { |
| TestObserver obs; |
| external_servers_->AddObserver(&obs); |
| external_servers_->ClearData(); |
| ASSERT_EQ(obs.GetCalls().size(), 2u); |
| EXPECT_EQ(obs.GetCalls().back().complete, true); |
| EXPECT_TRUE(obs.GetCalls().back().servers.empty()); |
| external_servers_->ClearData(); |
| // no changes, because observed object's state is the same |
| ASSERT_EQ(obs.GetCalls().size(), 2u); |
| external_servers_->RemoveObserver(&obs); |
| } |
| |
| // Verifies SetData(). |
| // SetData(...) sets "complete" flag = false, then parse given data in the |
| // background and sets resultant list with "complete" flag = true. |
| TEST_F(PrintServersProviderTest, SetData) { |
| TestObserver obs; |
| external_servers_->AddObserver(&obs); |
| external_servers_->SetData( |
| std::make_unique<std::string>(kPrintServersPolicyJson1)); |
| // single call from AddObserver, since SetData(...) is not processed yet |
| ASSERT_EQ(obs.GetCalls().size(), 1u); |
| // make sure that SetData(...) is processed |
| task_environment_.RunUntilIdle(); |
| // now the call from SetData(...) is there also |
| ASSERT_EQ(obs.GetCalls().size(), 2u); |
| EXPECT_EQ(obs.GetCalls().back().complete, true); |
| EXPECT_EQ(obs.GetCalls().back().servers, PrintServersPolicyData1()); |
| external_servers_->RemoveObserver(&obs); |
| } |
| |
| // Verify two SetData() calls. |
| TEST_F(PrintServersProviderTest, SetData2) { |
| TestObserver obs; |
| external_servers_->AddObserver(&obs); |
| external_servers_->SetData( |
| std::make_unique<std::string>(kPrintServersPolicyJson1)); |
| // single call from AddObserver, since SetData(...) is not processed yet |
| ASSERT_EQ(obs.GetCalls().size(), 1u); |
| external_servers_->SetData(std::make_unique<std::string>(R"json( |
| [ |
| { |
| "id": "id1", |
| "display_name": "CUPS", |
| "url": "ipp://192.168.1.15" |
| } |
| ])json")); |
| // no changes, because nothing was processed yet |
| ASSERT_EQ(obs.GetCalls().size(), 1u); |
| task_environment_.RunUntilIdle(); |
| // both calls from SetData(...) should be reported |
| ASSERT_EQ(obs.GetCalls().size(), 3u); |
| EXPECT_EQ(obs.GetCalls()[1].complete, false); |
| EXPECT_EQ(obs.GetCalls()[1].servers, PrintServersPolicyData1()); |
| EXPECT_EQ(obs.GetCalls()[2].complete, true); |
| EXPECT_EQ(obs.GetCalls()[2].servers, |
| std::vector<PrintServer>( |
| {{"id1", GURL("http://192.168.1.15:631"), "CUPS"}})); |
| external_servers_->RemoveObserver(&obs); |
| } |
| |
| // Verifies SetData() + ClearData() before SetData() completes. |
| TEST_F(PrintServersProviderTest, SetDataClearData) { |
| TestObserver obs; |
| external_servers_->AddObserver(&obs); |
| external_servers_->SetData( |
| std::make_unique<std::string>(kPrintServersPolicyJson1)); |
| // single call from AddObserver, since SetData(...) is not processed yet |
| ASSERT_EQ(obs.GetCalls().size(), 1u); |
| external_servers_->ClearData(); |
| // a call from ClearData() was added, SetData(...) is not processed yet |
| ASSERT_EQ(obs.GetCalls().size(), 2u); |
| EXPECT_EQ(obs.GetCalls().back().complete, true); |
| EXPECT_TRUE(obs.GetCalls().back().servers.empty()); |
| // process SetData(...) |
| task_environment_.RunUntilIdle(); |
| // no changes, effects of SetData(...) were already replaced by ClearData() |
| ASSERT_EQ(obs.GetCalls().size(), 2u); |
| external_servers_->RemoveObserver(&obs); |
| } |
| |
| // Verifies ClearData() before AddObserver() + SetData() after. |
| TEST_F(PrintServersProviderTest, ClearDataSetData) { |
| TestObserver obs; |
| external_servers_->ClearData(); |
| external_servers_->AddObserver(&obs); |
| // single call from AddObserver, but with effects of ClearData() |
| ASSERT_EQ(obs.GetCalls().size(), 1u); |
| EXPECT_EQ(obs.GetCalls().back().complete, true); |
| EXPECT_TRUE(obs.GetCalls().back().servers.empty()); |
| external_servers_->SetData( |
| std::make_unique<std::string>(kPrintServersPolicyJson1)); |
| // SetData(...) is not completed, but generates a call switching "complete" |
| // flag to false |
| ASSERT_EQ(obs.GetCalls().size(), 2u); |
| EXPECT_EQ(obs.GetCalls().back().complete, false); |
| EXPECT_TRUE(obs.GetCalls().back().servers.empty()); |
| // process SetData(...) |
| task_environment_.RunUntilIdle(); |
| // next call with results from processed SetData(...) |
| ASSERT_EQ(obs.GetCalls().size(), 3u); |
| EXPECT_EQ(obs.GetCalls().back().complete, true); |
| EXPECT_EQ(obs.GetCalls().back().servers, PrintServersPolicyData1()); |
| external_servers_->RemoveObserver(&obs); |
| } |
| |
| // Verify that invalid URLs are filtered out. |
| TEST_F(PrintServersProviderTest, InvalidURLs) { |
| TestObserver obs; |
| external_servers_->AddObserver(&obs); |
| external_servers_->SetData(std::make_unique<std::string>(R"json( |
| [ |
| { |
| "id": "1", |
| "display_name": "server_1", |
| "url": "ipp://aaa.bbb.ccc:666/xx" |
| }, { |
| "id": "2", |
| "display_name": "server_2", |
| "url":"ipps:/print.server.intra.example.com:443z/ipp" |
| }, { |
| "id": "3", |
| "display_name": "server_3", |
| "url": "file://192.168.1.8/bleble/print" |
| }, { |
| "id": "4", |
| "display_name": "server_4", |
| "url": "http://aaa.bbb.ccc:666/xx" |
| }, { |
| "id": "5", |
| "display_name": "server_5", |
| "url": "\n \t ipps://aaa.bbb.ccc:666/yy" |
| }, { |
| "id": "6", |
| "display_name": "server_6", |
| "url":"ipps:/print.server^.intra.example.com/ipp" |
| }, { |
| "id": "3", |
| "display_name": "server_7", |
| "url": "file://194.169.2.18/bleble2/print" |
| }, { |
| "display_name": "server_8", |
| "url": "file://195.161.3.28/bleble/print" |
| } |
| ])json")); |
| task_environment_.RunUntilIdle(); |
| ASSERT_EQ(obs.GetCalls().size(), 2u); |
| EXPECT_EQ(obs.GetCalls().back().complete, true); |
| // Only two records are valid: |
| // server_1 - OK |
| // server_2 - invalid URL - invalid port number |
| // server_3 - unsupported scheme |
| // server_4 - duplicate of server_1 |
| // server_5 - leading whitespaces, but OK |
| // server_6 - invalid URL - forbidden character |
| // server_7 - duplicate id |
| // server_8 - missing id |
| EXPECT_EQ(obs.GetCalls().back().servers, |
| std::vector<PrintServer>( |
| {{"1", GURL("http://aaa.bbb.ccc:666/xx"), "server_1"}, |
| {"5", GURL("https://aaa.bbb.ccc:666/yy"), "server_5"}})); |
| external_servers_->RemoveObserver(&obs); |
| } |
| |
| // Verify that allowlist works as expected. |
| TEST_F(PrintServersProviderTest, Allowlist) { |
| // The sequence from SetData test. |
| TestObserver obs; |
| external_servers_->AddObserver(&obs); |
| external_servers_->SetData( |
| std::make_unique<std::string>(kPrintServersPolicyJson1)); |
| // Apply an empty allowlist on the top. |
| pref_service_.SetManagedPref(kAllowlistPrefName, |
| base::Value(base::Value::Type::LIST)); |
| // Check the resultant list - is is supposed to be empty. |
| task_environment_.RunUntilIdle(); |
| ASSERT_FALSE(obs.GetCalls().empty()); |
| EXPECT_TRUE(obs.GetCalls().back().complete); |
| EXPECT_TRUE(obs.GetCalls().back().servers.empty()); |
| // Apply allowlist. |
| base::Value::List value; |
| for (std::string id : {"id3", "idX", "id1"}) |
| value.Append(std::move(id)); |
| pref_service_.SetManagedPref(kAllowlistPrefName, |
| base::Value(std::move(value))); |
| // Check the resultant list. |
| task_environment_.RunUntilIdle(); |
| EXPECT_TRUE(obs.GetCalls().back().complete); |
| EXPECT_EQ(obs.GetCalls().back().servers, |
| std::vector<PrintServer>({Server1(), Server3()})); |
| // The end. |
| external_servers_->RemoveObserver(&obs); |
| } |
| |
| } // namespace |
| } // namespace ash |