| Index: chrome/browser/chromeos/printing/combining_printer_detector_unittest.cc
 | 
| diff --git a/chrome/browser/chromeos/printing/combining_printer_detector_unittest.cc b/chrome/browser/chromeos/printing/combining_printer_detector_unittest.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..478b923e8a1d6f0adfee56a67926f97d8e1fb951
 | 
| --- /dev/null
 | 
| +++ b/chrome/browser/chromeos/printing/combining_printer_detector_unittest.cc
 | 
| @@ -0,0 +1,220 @@
 | 
| +// 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 "chrome/browser/chromeos/printing/combining_printer_detector.h"
 | 
| +
 | 
| +#include <algorithm>
 | 
| +#include <memory>
 | 
| +#include <set>
 | 
| +#include <string>
 | 
| +#include <utility>
 | 
| +#include <vector>
 | 
| +
 | 
| +#include "base/memory/ptr_util.h"
 | 
| +#include "base/observer_list.h"
 | 
| +#include "base/strings/string_util.h"
 | 
| +#include "chromeos/printing/printer_configuration.h"
 | 
| +#include "testing/gtest/include/gtest/gtest.h"
 | 
| +
 | 
| +namespace chromeos {
 | 
| +
 | 
| +namespace {
 | 
| +// Fake printer detectors used as the source of detections to be combined.
 | 
| +class FakePrinterDetector : public PrinterDetector {
 | 
| + public:
 | 
| +  explicit FakePrinterDetector(
 | 
| +      const std::vector<std::string>& starting_printers)
 | 
| +      : printer_ids_(starting_printers.begin(), starting_printers.end()) {}
 | 
| +
 | 
| +  ~FakePrinterDetector() override = default;
 | 
| +
 | 
| +  void Start() override {}
 | 
| +
 | 
| +  // Add these printers to the list of detected printers, and notify
 | 
| +  // downstream observers.
 | 
| +  void AddPrinters(const std::vector<std::string>& ids) {
 | 
| +    for (const std::string& id : ids) {
 | 
| +      CHECK(printer_ids_.insert(id).second)
 | 
| +          << "Cowardly refusing to add printer with duplicate id " << id;
 | 
| +    }
 | 
| +    Notify();
 | 
| +  }
 | 
| +
 | 
| +  // Remove these printers from the list of detected printers, and notify
 | 
| +  // downstream observers.
 | 
| +  void RemovePrinters(const std::vector<std::string>& ids) {
 | 
| +    for (const std::string id : ids) {
 | 
| +      CHECK_EQ(printer_ids_.erase(id), static_cast<size_t>(1))
 | 
| +          << "Can't remove printer with nonexistant id " << id;
 | 
| +    }
 | 
| +    Notify();
 | 
| +  }
 | 
| +
 | 
| +  // Tell downstream observers that we're done finding printers.
 | 
| +  void PrinterScanComplete() {
 | 
| +    for (PrinterDetector::Observer& observer : observer_list_) {
 | 
| +      observer.OnPrinterScanComplete();
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  // PrinterDetector implementations.
 | 
| +  void AddObserver(PrinterDetector::Observer* observer) override {
 | 
| +    observer_list_.AddObserver(observer);
 | 
| +  }
 | 
| +
 | 
| +  void RemoveObserver(PrinterDetector::Observer* observer) override {
 | 
| +    observer_list_.RemoveObserver(observer);
 | 
| +  }
 | 
| +
 | 
| +  std::vector<Printer> GetPrinters() override {
 | 
| +    std::vector<Printer> printers;
 | 
| +    for (const std::string& id : printer_ids_) {
 | 
| +      printers.push_back(Printer(id));
 | 
| +    }
 | 
| +    return printers;
 | 
| +  }
 | 
| +
 | 
| + private:
 | 
| +  void Notify() {
 | 
| +    std::vector<Printer> printers = GetPrinters();
 | 
| +    for (PrinterDetector::Observer& observer : observer_list_) {
 | 
| +      observer.OnPrintersFound(printers);
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  base::ObserverList<PrinterDetector::Observer> observer_list_;
 | 
| +
 | 
| +  // In the fake, we only use printer ids to figure out if the right printers
 | 
| +  // are being propagated.  So internally we just track ids and construct
 | 
| +  // printers on the fly when asked for them.
 | 
| +  std::set<std::string> printer_ids_;
 | 
| +};
 | 
| +
 | 
| +class PrintingCombiningPrinterDetectorTest : public testing::Test,
 | 
| +                                             public PrinterDetector::Observer {
 | 
| + public:
 | 
| +  void Init(const std::vector<std::vector<std::string>>& id_lists) {
 | 
| +    combining_detector_ = CombiningPrinterDetector::Create();
 | 
| +
 | 
| +    bool test_owned = false;
 | 
| +    for (const std::vector<std::string>& id_list : id_lists) {
 | 
| +      // Divide up fake detectors between owned and unowned to exercise both
 | 
| +      // paths.  It shouldn't really matter in the test below where ownership of
 | 
| +      // a specific fake detector lies.
 | 
| +      auto fake_detector = base::MakeUnique<FakePrinterDetector>(id_list);
 | 
| +      fake_detectors_.push_back(fake_detector.get());
 | 
| +      if (test_owned) {
 | 
| +        combining_detector_->AddDetector(fake_detector.get());
 | 
| +        owned_fake_detectors_.emplace_back(std::move(fake_detector));
 | 
| +      } else {
 | 
| +        combining_detector_->AddOwnedDetector(std::move(fake_detector));
 | 
| +      }
 | 
| +      test_owned = !test_owned;
 | 
| +    }
 | 
| +    combining_detector_->AddObserver(this);
 | 
| +    combining_detector_->Start();
 | 
| +    printers_ = combining_detector_->GetPrinters();
 | 
| +  }
 | 
| +
 | 
| +  void OnPrintersFound(const std::vector<Printer>& printers) override {
 | 
| +    printers_ = printers;
 | 
| +  }
 | 
| +
 | 
| +  void OnPrinterScanComplete() override { scan_complete_ = true; }
 | 
| +
 | 
| + protected:
 | 
| +  // Testing utility function -- return true if the printers we got in the most
 | 
| +  // recent OnPrintersFound call are the same as printers (without considering
 | 
| +  // order).
 | 
| +  void ExpectFoundPrintersAre(const std::vector<std::string>& expected_ids) {
 | 
| +    std::vector<std::string> sorted_expected(expected_ids.begin(),
 | 
| +                                             expected_ids.end());
 | 
| +    std::vector<std::string> sorted_actual;
 | 
| +    for (const Printer& printer : printers_) {
 | 
| +      sorted_actual.push_back(printer.id());
 | 
| +    }
 | 
| +    std::sort(sorted_expected.begin(), sorted_expected.end());
 | 
| +    std::sort(sorted_actual.begin(), sorted_actual.end());
 | 
| +    if (sorted_expected != sorted_actual) {
 | 
| +      ADD_FAILURE() << "Printer ids mismatch.  Expected: {"
 | 
| +                    << base::JoinString(sorted_expected, ", ") << "}; Found: {"
 | 
| +                    << base::JoinString(sorted_actual, ", ") << "}";
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  // Have we been notified that the scan is complete?
 | 
| +  bool scan_complete_ = false;
 | 
| +
 | 
| +  // The printers in the most recent OnPrintersFound call.
 | 
| +  std::vector<Printer> printers_;
 | 
| +
 | 
| +  // The fake detectors plugged into the combining detector.
 | 
| +  std::vector<FakePrinterDetector*> fake_detectors_;
 | 
| +
 | 
| +  // Cleanup pointers for fake detectors owned by the test.  (The
 | 
| +  // other fake detectors are owned by the combining_detector.
 | 
| +  std::vector<std::unique_ptr<FakePrinterDetector>> owned_fake_detectors_;
 | 
| +
 | 
| +  std::unique_ptr<CombiningPrinterDetector> combining_detector_;
 | 
| +};
 | 
| +
 | 
| +TEST_F(PrintingCombiningPrinterDetectorTest, BasicUsage) {
 | 
| +  Init({{"1a"}, std::vector<std::string>(), {"3a"}});
 | 
| +  ExpectFoundPrintersAre({"1a", "3a"});
 | 
| +  EXPECT_FALSE(scan_complete_);
 | 
| +
 | 
| +  // Find some printers in the second detector.
 | 
| +  fake_detectors_[1]->AddPrinters({"2a", "2b", "2c"});
 | 
| +  ExpectFoundPrintersAre({"1a", "2a", "2b", "2c", "3a"});
 | 
| +  EXPECT_FALSE(scan_complete_);
 | 
| +
 | 
| +  // Find some printers on the first detector.
 | 
| +  fake_detectors_[0]->AddPrinters({"1b"});
 | 
| +  ExpectFoundPrintersAre({"1a", "1b", "2a", "2b", "2c", "3a"});
 | 
| +  EXPECT_FALSE(scan_complete_);
 | 
| +
 | 
| +  // Have the first detector signal completion, the combined detector should
 | 
| +  // not be complete until all 3 fake detectors have completed.
 | 
| +  fake_detectors_[0]->PrinterScanComplete();
 | 
| +  EXPECT_FALSE(scan_complete_);
 | 
| +  ExpectFoundPrintersAre({"1a", "1b", "2a", "2b", "2c", "3a"});
 | 
| +
 | 
| +  // Exercise removal of a printer.
 | 
| +  fake_detectors_[2]->RemovePrinters({"3a"});
 | 
| +  EXPECT_FALSE(scan_complete_);
 | 
| +  ExpectFoundPrintersAre({"1a", "1b", "2a", "2b", "2c"});
 | 
| +
 | 
| +  // Mark the 3rd detector as complete.  The 2nd detector has still not declared
 | 
| +  // itself complete.
 | 
| +  fake_detectors_[2]->PrinterScanComplete();
 | 
| +  EXPECT_FALSE(scan_complete_);
 | 
| +  ExpectFoundPrintersAre({"1a", "1b", "2a", "2b", "2c"});
 | 
| +
 | 
| +  // Multiple scan complete calls from the same detector shouldn't happen, but
 | 
| +  // if they do happen, it should be a nop.
 | 
| +  fake_detectors_[2]->PrinterScanComplete();
 | 
| +  EXPECT_FALSE(scan_complete_);
 | 
| +  ExpectFoundPrintersAre({"1a", "1b", "2a", "2b", "2c"});
 | 
| +
 | 
| +  // We can still add printers from a detector that has declared itself
 | 
| +  // complete.
 | 
| +  fake_detectors_[0]->AddPrinters({"1c"});
 | 
| +  ExpectFoundPrintersAre({"1a", "1b", "1c", "2a", "2b", "2c"});
 | 
| +  EXPECT_FALSE(scan_complete_);
 | 
| +
 | 
| +  // Finally the 2nd detector declares itself complete, which should mean the
 | 
| +  // combined detector is now complete.
 | 
| +  fake_detectors_[1]->PrinterScanComplete();
 | 
| +  EXPECT_TRUE(scan_complete_);
 | 
| +  ExpectFoundPrintersAre({"1a", "1b", "1c", "2a", "2b", "2c"});
 | 
| +
 | 
| +  // Should still be able to add printers even after completion.
 | 
| +  fake_detectors_[2]->AddPrinters({"3g", "3h", "3i"});
 | 
| +  EXPECT_TRUE(scan_complete_);
 | 
| +  ExpectFoundPrintersAre(
 | 
| +      {"1a", "1b", "1c", "2a", "2b", "2c", "3g", "3h", "3i"});
 | 
| +}
 | 
| +
 | 
| +}  // namespace
 | 
| +}  // namespace chromeos
 | 
| 
 |