chromium/chrome/browser/ash/printing/enterprise/bulk_printers_calculator_unittest.cc

// Copyright 2017 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/enterprise/bulk_printers_calculator.h"

#include <string>
#include <vector>

#include "base/functional/bind.h"
#include "base/test/task_environment.h"
#include "chromeos/printing/printer_configuration.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash {
namespace {

// The number of correct printers in BulkPolicyContentsJson.
constexpr size_t kNumValidPrinters = 3;

// An example bulk printer configuration file.
constexpr char kBulkPolicyContentsJson[] = R"json(
[
  {
    "guid": "First",
    "display_name": "LexaPrint",
    "description": "Laser on the test shelf",
    "uri": "ipp://192.168.1.5",
    "ppd_resource": {
      "effective_model": "MS610de"
    }
  }, {
    "guid": "Incorrect uri",
    "display_name": "aaa",
    "description": "bbbb",
    "uri":"ipp://:",
    "ppd_resource":{
      "effective_model": "fff"
    }
  }, {
    "guid": "Second",
    "display_name": "Color Laser",
    "uri":"ipps://print-server.intranet.example.com:443/ipp/cl2k4",
    "ppd_resource":{
      "effective_model": "ColorLaser2k4"
    }
  }, {
    "guid": "Third",
    "display_name": "YaLP",
    "description": "Fancy Fancy Fancy",
    "uri": "ipp://192.168.1.8",
    "ppd_resource": {
      "autoconf": true
    }
  }
])json";

// A different bulk printer configuration file.
constexpr char kMoreContentsJson[] = R"json(
[
  {
    "guid": "ThirdPrime",
    "display_name": "Printy McPrinter",
    "description": "Laser on the test shelf",
    "uri": "ipp://192.168.1.5",
    "ppd_resource": {
      "effective_model": "MS610de"
    }
  }
])json";

// Observer that counts the number of times it has been called.
class TestObserver : public BulkPrintersCalculator::Observer {
 public:
  void OnPrintersChanged(const BulkPrintersCalculator* sender) override {
    last_valid = sender->IsComplete();
    called++;
  }

  // Counts the number of times the observer is invoked.
  int called = 0;
  // Holds the most recent value of valid.
  bool last_valid = false;
};

class BulkPrintersCalculatorTest : public testing::Test {
 public:
  BulkPrintersCalculatorTest() : task_environment_() {
    external_printers_ = BulkPrintersCalculator::Create();
  }
  ~BulkPrintersCalculatorTest() override {
    // Delete the printer before the task environment.
    external_printers_.reset();
  }

 protected:
  std::unique_ptr<BulkPrintersCalculator> external_printers_;
  base::test::TaskEnvironment task_environment_;
};

// Verify that we're initiall unset and empty.
TEST_F(BulkPrintersCalculatorTest, InitialConditions) {
  EXPECT_FALSE(external_printers_->IsDataPolicySet());
  EXPECT_TRUE(external_printers_->GetPrinters().empty());
}

// Verify that the object can be destroyed while parsing is in progress.
TEST_F(BulkPrintersCalculatorTest, DestructionIsSafe) {
  {
    std::unique_ptr<BulkPrintersCalculator> printers =
        BulkPrintersCalculator::Create();
    printers->SetAccessMode(BulkPrintersCalculator::BLOCKLIST_ONLY);
    printers->SetBlocklist({"Third"});
    printers->SetData(std::make_unique<std::string>(kBulkPolicyContentsJson));
    // Data is valid.  Computation is proceeding.
  }
  // printers is out of scope.  Destructor has run.  Pump the message queue to
  // see if anything strange happens.
  task_environment_.RunUntilIdle();
}

// Verifies that IsDataPolicySet returns false until data is set.
TEST_F(BulkPrintersCalculatorTest, PolicyUnsetWithMissingData) {
  auto data = std::make_unique<std::string>(kBulkPolicyContentsJson);
  external_printers_->ClearData();
  EXPECT_FALSE(external_printers_->IsDataPolicySet());
  external_printers_->SetData(std::move(data));
  EXPECT_TRUE(external_printers_->IsDataPolicySet());
  task_environment_.RunUntilIdle();
  EXPECT_TRUE(external_printers_->IsComplete());
}

// Verify printer list after all attributes have been set.
TEST_F(BulkPrintersCalculatorTest, AllPoliciesResultInPrinters) {
  auto data = std::make_unique<std::string>(kBulkPolicyContentsJson);
  external_printers_->SetAccessMode(
      BulkPrintersCalculator::AccessMode::ALL_ACCESS);
  external_printers_->SetData(std::move(data));
  EXPECT_TRUE(external_printers_->IsDataPolicySet());

  task_environment_.RunUntilIdle();
  EXPECT_TRUE(external_printers_->IsComplete());
  const auto& printers = external_printers_->GetPrinters();
  EXPECT_EQ(kNumValidPrinters, printers.size());
  EXPECT_EQ("LexaPrint", printers.at("First").display_name());
  EXPECT_EQ("Color Laser", printers.at("Second").display_name());
  EXPECT_EQ("YaLP", printers.at("Third").display_name());
}

// The external policy was cleared, results should be invalidated.
TEST_F(BulkPrintersCalculatorTest, PolicyClearedNowUnset) {
  auto data = std::make_unique<std::string>(kBulkPolicyContentsJson);
  external_printers_->SetAccessMode(
      BulkPrintersCalculator::AccessMode::ALL_ACCESS);
  external_printers_->ClearData();
  external_printers_->SetData(std::move(data));

  task_environment_.RunUntilIdle();
  ASSERT_TRUE(external_printers_->IsDataPolicySet());

  external_printers_->ClearData();
  task_environment_.RunUntilIdle();
  EXPECT_FALSE(external_printers_->IsDataPolicySet());
  EXPECT_TRUE(external_printers_->GetPrinters().empty());
}

// Verify that the blocklist policy is applied correctly.  Printers in the
// blocklist policy should not be available.  Printers not in the blocklist
// should be available.
TEST_F(BulkPrintersCalculatorTest, BlocklistPolicySet) {
  auto data = std::make_unique<std::string>(kBulkPolicyContentsJson);
  external_printers_->ClearData();
  external_printers_->SetData(std::move(data));
  external_printers_->SetAccessMode(BulkPrintersCalculator::BLOCKLIST_ONLY);
  task_environment_.RunUntilIdle();
  external_printers_->SetBlocklist({"Second", "Third"});
  task_environment_.RunUntilIdle();
  EXPECT_TRUE(external_printers_->IsComplete());

  task_environment_.RunUntilIdle();
  const auto& printers = external_printers_->GetPrinters();
  EXPECT_EQ(1U, printers.size());
  EXPECT_EQ("LexaPrint", printers.at("First").display_name());
}

// Verify that the allowlist policy is correctly applied.  Only printers
// available in the allowlist are available.
TEST_F(BulkPrintersCalculatorTest, AllowlistPolicySet) {
  auto data = std::make_unique<std::string>(kBulkPolicyContentsJson);
  external_printers_->ClearData();
  external_printers_->SetData(std::move(data));
  external_printers_->SetAccessMode(BulkPrintersCalculator::ALLOWLIST_ONLY);
  task_environment_.RunUntilIdle();
  external_printers_->SetAllowlist({"First"});

  task_environment_.RunUntilIdle();
  EXPECT_TRUE(external_printers_->IsComplete());
  const auto& printers = external_printers_->GetPrinters();
  EXPECT_EQ(1U, printers.size());
  EXPECT_EQ("LexaPrint", printers.at("First").display_name());
}

// Verify that an empty blocklist results in no printer limits.
TEST_F(BulkPrintersCalculatorTest, EmptyBlocklistAllPrinters) {
  auto data = std::make_unique<std::string>(kBulkPolicyContentsJson);
  external_printers_->ClearData();
  external_printers_->SetData(std::move(data));
  external_printers_->SetAccessMode(BulkPrintersCalculator::BLOCKLIST_ONLY);
  task_environment_.RunUntilIdle();
  external_printers_->SetBlocklist({});

  task_environment_.RunUntilIdle();
  EXPECT_TRUE(external_printers_->IsComplete());
  const auto& printers = external_printers_->GetPrinters();
  EXPECT_EQ(kNumValidPrinters, printers.size());
}

// Verify that an empty allowlist results in no printers.
TEST_F(BulkPrintersCalculatorTest, EmptyAllowlistNoPrinters) {
  auto data = std::make_unique<std::string>(kBulkPolicyContentsJson);
  external_printers_->ClearData();
  external_printers_->SetData(std::move(data));
  external_printers_->SetAccessMode(BulkPrintersCalculator::ALLOWLIST_ONLY);
  task_environment_.RunUntilIdle();
  external_printers_->SetAllowlist({});

  task_environment_.RunUntilIdle();
  EXPECT_TRUE(external_printers_->IsComplete());
  const auto& printers = external_printers_->GetPrinters();
  EXPECT_EQ(0U, printers.size());
}

// Verify that switching from allowlist to blocklist behaves correctly.
TEST_F(BulkPrintersCalculatorTest, BlocklistToAllowlistSwap) {
  auto data = std::make_unique<std::string>(kBulkPolicyContentsJson);
  external_printers_->ClearData();
  external_printers_->SetData(std::move(data));
  external_printers_->SetAccessMode(BulkPrintersCalculator::BLOCKLIST_ONLY);
  external_printers_->SetAllowlist({"First"});
  external_printers_->SetBlocklist({"First"});

  // This should result in 2 printers.  But we're switching the mode anyway.

  external_printers_->SetAccessMode(BulkPrintersCalculator::ALLOWLIST_ONLY);
  task_environment_.RunUntilIdle();
  EXPECT_TRUE(external_printers_->IsComplete());
  const auto& printers = external_printers_->GetPrinters();
  EXPECT_EQ(1U, printers.size());
  EXPECT_EQ("LexaPrint", printers.at("First").display_name());
}

// Verify that updated configurations are handled properly.
TEST_F(BulkPrintersCalculatorTest, MultipleUpdates) {
  auto data = std::make_unique<std::string>(kBulkPolicyContentsJson);
  external_printers_->ClearData();
  external_printers_->SetData(std::move(data));
  external_printers_->SetAccessMode(BulkPrintersCalculator::ALL_ACCESS);
  // There will be 3 printers here.  But we don't want to wait for computation
  // to complete to verify the final value gets used.

  auto new_data = std::make_unique<std::string>(kMoreContentsJson);
  external_printers_->SetData(std::move(new_data));
  task_environment_.RunUntilIdle();
  const auto& printers = external_printers_->GetPrinters();
  ASSERT_EQ(1U, printers.size());
  EXPECT_EQ("ThirdPrime", printers.at("ThirdPrime").id());
}

// Verifies that the observer is called at the expected times.
TEST_F(BulkPrintersCalculatorTest, ObserverTest) {
  TestObserver obs;
  external_printers_->AddObserver(&obs);

  external_printers_->SetAccessMode(BulkPrintersCalculator::ALL_ACCESS);
  external_printers_->SetAllowlist(std::vector<std::string>());
  external_printers_->SetBlocklist(std::vector<std::string>());
  external_printers_->ClearData();
  task_environment_.RunUntilIdle();
  EXPECT_EQ(1, obs.called);

  external_printers_->SetData(
      std::make_unique<std::string>(kBulkPolicyContentsJson));

  task_environment_.RunUntilIdle();
  EXPECT_TRUE(external_printers_->IsDataPolicySet());
  EXPECT_EQ(2, obs.called);
  EXPECT_TRUE(obs.last_valid);  // ready now
  // Printer list is correct after notification.
  EXPECT_EQ(kNumValidPrinters, external_printers_->GetPrinters().size());

  external_printers_->SetAccessMode(BulkPrintersCalculator::ALLOWLIST_ONLY);
  task_environment_.RunUntilIdle();
  EXPECT_EQ(3, obs.called);  // effective list changed.  Notified.
  EXPECT_TRUE(obs.last_valid);

  external_printers_->SetAccessMode(BulkPrintersCalculator::BLOCKLIST_ONLY);
  task_environment_.RunUntilIdle();
  EXPECT_EQ(4, obs.called);  // effective list changed.  Notified.
  EXPECT_TRUE(obs.last_valid);

  external_printers_->ClearData();
  task_environment_.RunUntilIdle();
  EXPECT_EQ(5, obs.called);  // Called for transition to invalid policy.
  EXPECT_TRUE(obs.last_valid);
  EXPECT_TRUE(external_printers_->GetPrinters().empty());

  // cleanup
  external_printers_->RemoveObserver(&obs);
}

}  // namespace
}  // namespace ash