text:
// 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.

import {CupsPrintersBrowserProxyImpl, PrinterType} from 'chrome://os-settings/chromeos/lazy_load.js';
import {Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
import {OncMojo} from 'chrome://resources/cr_components/chromeos/network/onc_mojo.js';
import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {waitAfterNextRender} from 'chrome://test/test_util.js';

import {flushTasks} from '../../test_util.js';

import {createCupsPrinterInfo, createPrinterListEntry, getPrinterEntries} from './cups_printer_test_utils.js';
import {TestCupsPrintersBrowserProxy} from './test_cups_printers_browser_proxy.js';

const arrowUpEvent = new KeyboardEvent(
    'keydown', {cancelable: true, key: 'ArrowUp', keyCode: 38});

const arrowDownEvent = new KeyboardEvent(
    'keydown', {cancelable: true, key: 'ArrowDown', keyCode: 40});

const arrowLeftEvent = new KeyboardEvent(
    'keydown', {cancelable: true, key: 'ArrowLeft', keyCode: 37});

const arrowRightEvent = new KeyboardEvent(
    'keydown', {cancelable: true, key: 'ArrowRight', keyCode: 39});

/**
 * @param {!HTMLElement} button
 * @private
 */
function clickButton(button) {
  assertTrue(!!button);
  assertTrue(!button.disabled);

  button.click();
  flush();
}

/**
 * @param {!HTMLElement} page
 * @return {!HTMLElement}
 * @private
 */
function initializeEditDialog(page) {
  const editDialog = page.shadowRoot.querySelector('#editPrinterDialog');
  assertTrue(!!editDialog);

  // Edit dialog will reset |ppdModel| when |ppdManufacturer| is set. Must
  // set values separately.
  editDialog.pendingPrinter_.ppdManufacturer = 'make';
  editDialog.pendingPrinter_.ppdModel = 'model';

  // Initializing |activePrinter| in edit dialog will set
  // |needsReconfigured_| to true. Reset it so that any changes afterwards
  // mimics user input.
  editDialog.needsReconfigured_ = false;

  return editDialog;
}

/**
 * @param {string} expectedMessage
 * @param {!HTMLElement} toast
 * @private
 */
function verifyErrorToastMessage(expectedMessage, toast) {
  assertTrue(toast.open);
  assertEquals(expectedMessage, toast.textContent.trim());
}

/**
 * Helper function that verifies that |printerList| matches the printers in
 * |entryList|.
 * @param {!HTMLElement} entryList
 * @param {!Array<!CupsPrinterInfo>} printerList
 * @private
 */
function verifyPrintersList(entryList, printerList) {
  for (let index = 0; index < printerList.length; ++index) {
    const entryInfo = entryList[index].printerEntry.printerInfo;
    const printerInfo = printerList[index];

    assertEquals(printerInfo.printerName, entryInfo.printerName);
    assertEquals(printerInfo.printerAddress, entryInfo.printerAddress);
    assertEquals(printerInfo.printerId, entryInfo.printerId);
    assertEquals(entryList.length, printerList.length);
  }
}

/**
 * Helper function to verify that printers in |printerListEntries| that contain
 * |searchTerm| are not in |hiddenEntries|.
 * @param {!Element} printerEntryListTestElement
 * @param {string} searchTerm
 */
function verifyFilteredPrinters(printerEntryListTestElement, searchTerm) {
  const printerListEntries =
      Array.from(printerEntryListTestElement.querySelectorAll(
          'settings-cups-printers-entry'));
  const hiddenEntries = Array.from(printerEntryListTestElement.querySelectorAll(
      'settings-cups-printers-entry[hidden]'));

  for (let i = 0; i < printerListEntries.length; ++i) {
    const entry = printerListEntries[i];
    if (hiddenEntries.indexOf(entry) === -1) {
      assertTrue(
          entry.printerEntry.printerInfo.printerName.toLowerCase().includes(
              searchTerm.toLowerCase()));
    }
  }
}

/**
 * Helper function to verify that the actual visible printers match the
 * expected printer list.
 * @param {!Element} printerEntryListTestElement
 * @param {!Array<!PrinterListEntry>} expectedVisiblePrinters
 */
function verifyVisiblePrinters(
    printerEntryListTestElement, expectedVisiblePrinters) {
  const actualPrinterList =
      Array.from(printerEntryListTestElement.querySelectorAll(
          'settings-cups-printers-entry:not([hidden])'));

  assertEquals(expectedVisiblePrinters.length, actualPrinterList.length);
  for (let i = 0; i < expectedVisiblePrinters.length; i++) {
    const expectedPrinter = expectedVisiblePrinters[i].printerInfo;
    const actualPrinter = actualPrinterList[i].printerEntry.printerInfo;

    assertEquals(actualPrinter.printerName, expectedPrinter.printerName);
    assertEquals(actualPrinter.printerAddress, expectedPrinter.printerAddress);
    assertEquals(actualPrinter.printerId, expectedPrinter.printerId);
  }
}

/**
 * Helper function to verify that printers are hidden accordingly if they do not
 * match the search query. Also checks if the no search results section is shown
 * when appropriate.
 * @param {!Element} printersElement
 * @param {!Array<!PrinterListEntry>} expectedVisiblePrinters
 * @param {string} searchTerm
 */
function verifySearchQueryResults(
    printersElement, expectedVisiblePrinters, searchTerm) {
  const printerEntryListTestElement =
      printersElement.shadowRoot.querySelector('#printerEntryList');

  verifyVisiblePrinters(printerEntryListTestElement, expectedVisiblePrinters);
  verifyFilteredPrinters(printerEntryListTestElement, searchTerm);

  if (expectedVisiblePrinters.length) {
    assertTrue(
        printersElement.shadowRoot.querySelector('#no-search-results').hidden);
  } else {
    assertFalse(
        printersElement.shadowRoot.querySelector('#no-search-results').hidden);
  }
}

/**
 * Removes a saved printer located at |index|.
 * @param {!TestCupsPrintersBrowserProxy} cupsPrintersBrowserProxy
 * @param {!HTMLElement} savedPrintersElement
 * @param {number} index
 * @return {!Promise}
 */
function removePrinter(cupsPrintersBrowserProxy, savedPrintersElement, index) {
  const printerList = cupsPrintersBrowserProxy.printerList.printerList;
  const savedPrinterEntries = getPrinterEntries(savedPrintersElement);

  clickButton(
      savedPrinterEntries[index].shadowRoot.querySelector('.icon-more-vert'));
  clickButton(savedPrintersElement.shadowRoot.querySelector('#removeButton'));

  return cupsPrintersBrowserProxy.whenCalled('removeCupsPrinter')
      .then(function() {
        // Simulate removing the printer from |cupsPrintersBrowserProxy|.
        printerList.splice(index, 1);

        // Simuluate saved printer changes.
        webUIListenerCallback(
            'on-saved-printers-changed', cupsPrintersBrowserProxy.printerList);
        flush();
      });
}

/**
 * Removes all saved printers through recursion.
 * @param {!TestCupsPrintersBrowserProxy} cupsPrintersBrowserProxy
 * @param {!HTMLElement} savedPrintersElement
 * @return {!Promise}
 */
function removeAllPrinters(cupsPrintersBrowserProxy, savedPrintersElement) {
  const printerList = cupsPrintersBrowserProxy.printerList.printerList;
  const savedPrinterEntries = getPrinterEntries(savedPrintersElement);

  if (!printerList.length) {
    return Promise.resolve();
  }

  return removePrinter(
             cupsPrintersBrowserProxy, savedPrintersElement, 0 /* index */)
      .then(flushTasks)
      .then(removeAllPrinters.bind(
          this, cupsPrintersBrowserProxy, savedPrintersElement));
}

suite('CupsSavedPrintersTests', function() {
  let page = null;
  let savedPrintersElement = null;

  /** @type {?settings.TestCupsPrintersBrowserProxy} */
  let cupsPrintersBrowserProxy = null;

  /** @type {?Array<!CupsPrinterInfo>} */
  let printerList = null;

  setup(function() {
    cupsPrintersBrowserProxy = new TestCupsPrintersBrowserProxy();

    PolymerTest.clearBody();
    Router.getInstance().navigateTo(routes.CUPS_PRINTERS);
  });

  teardown(function() {
    cupsPrintersBrowserProxy.reset();
    page.remove();
    savedPrintersElement = null;
    printerList = null;
    page = null;
  });

  /** @param {!Array<!CupsPrinterInfo>} printerList */
  function createCupsPrinterPage(printers) {
    printerList = printers;
    // |cupsPrinterBrowserProxy| needs to have a list of saved printers before
    // initializing the landing page.
    cupsPrintersBrowserProxy.printerList = {printerList: printerList};
    CupsPrintersBrowserProxyImpl.setInstanceForTesting(
        cupsPrintersBrowserProxy);

    page = document.createElement('settings-cups-printers');
    document.body.appendChild(page);
    assertTrue(!!page);

    flush();
  }

  /** @param {!CupsPrinterInfo} printer*/
  function addNewSavedPrinter(printer) {
    printerList.push(printer);
    updateSavedPrinters();
  }

  /** @param {number} id*/
  function removeSavedPrinter(id) {
    const idx = printerList.findIndex(p => p.printerId === id);
    printerList.splice(idx, 1);
    updateSavedPrinters();
  }

  function updateSavedPrinters() {
    cupsPrintersBrowserProxy.printerList = {printerList: printerList};
    webUIListenerCallback(
        'on-saved-printers-changed', cupsPrintersBrowserProxy.printerList);
    flush();
  }

  test('SavedPrintersSuccessfullyPopulates', function() {
    createCupsPrinterPage([
      createCupsPrinterInfo('google', '4', 'id4'),
      createCupsPrinterInfo('test1', '1', 'id1'),
      createCupsPrinterInfo('test2', '2', 'id2'),
    ]);
    return cupsPrintersBrowserProxy.whenCalled('getCupsSavedPrintersList')
        .then(() => {
          // Wait for saved printers to populate.
          flush();

          savedPrintersElement =
              page.shadowRoot.querySelector('settings-cups-saved-printers');
          assertTrue(!!savedPrintersElement);

          // List component contained by CupsSavedPrinters.
          const savedPrintersList =
              savedPrintersElement.shadowRoot.querySelector(
                  'settings-cups-printers-entry-list');

          const printerListEntries = getPrinterEntries(savedPrintersElement);

          verifyPrintersList(printerListEntries, printerList);
        });
  });

  test('SuccessfullyRemoveMultipleSavedPrinters', function() {
    const savedPrinterEntries = [];

    createCupsPrinterPage([
      createCupsPrinterInfo('google', '4', 'id4'),
      createCupsPrinterInfo('test1', '1', 'id1'),
      createCupsPrinterInfo('test2', '2', 'id2'),
    ]);
    return cupsPrintersBrowserProxy.whenCalled('getCupsSavedPrintersList')
        .then(() => {
          // Wait for saved printers to populate.
          flush();

          savedPrintersElement =
              page.shadowRoot.querySelector('settings-cups-saved-printers');
          assertTrue(!!savedPrintersElement);

          return removeAllPrinters(
              cupsPrintersBrowserProxy, savedPrintersElement);
        })
        .then(() => {
          const entryList = getPrinterEntries(savedPrintersElement);
          verifyPrintersList(entryList, printerList);
        });
  });

  test('HideSavedPrintersWhenEmpty', function() {
    // List component contained by CupsSavedPrinters.
    let savedPrintersList = [];
    let savedPrinterEntries = [];

    createCupsPrinterPage([
      createCupsPrinterInfo('google', '4', 'id4'),
      createCupsPrinterInfo('test1', '1', 'id1'),
      createCupsPrinterInfo('test2', '2', 'id2'),
    ]);
    return cupsPrintersBrowserProxy.whenCalled('getCupsSavedPrintersList')
        .then(() => {
          // Wait for saved printers to populate.
          flush();

          savedPrintersElement =
              page.shadowRoot.querySelector('settings-cups-saved-printers');
          assertTrue(!!savedPrintersElement);

          savedPrintersList = savedPrintersElement.shadowRoot.querySelector(
              'settings-cups-printers-entry-list');
          savedPrinterEntries = getPrinterEntries(savedPrintersElement);

          verifyPrintersList(savedPrinterEntries, printerList);

          assertTrue(!!page.shadowRoot.querySelector('#savedPrinters'));

          return removeAllPrinters(
              cupsPrintersBrowserProxy, savedPrintersElement);
        })
        .then(() => {
          assertFalse(!!page.shadowRoot.querySelector('#savedPrinters'));
        });
  });

  test('UpdateSavedPrinter', function() {
    const expectedName = 'edited name';

    let editDialog = null;
    let savedPrinterEntries = null;

    createCupsPrinterPage([
      createCupsPrinterInfo('google', '4', 'id4'),
      createCupsPrinterInfo('test1', '1', 'id1'),
      createCupsPrinterInfo('test2', '2', 'id2'),
    ]);
    return cupsPrintersBrowserProxy.whenCalled('getCupsSavedPrintersList')
        .then(() => {
          // Wait for saved printers to populate.
          flush();

          savedPrintersElement =
              page.shadowRoot.querySelector('settings-cups-saved-printers');
          assertTrue(!!savedPrintersElement);

          savedPrinterEntries = getPrinterEntries(savedPrintersElement);

          // Update the printer name of the first entry.
          clickButton(savedPrinterEntries[0].shadowRoot.querySelector(
              '.icon-more-vert'));
          clickButton(
              savedPrintersElement.shadowRoot.querySelector('#editButton'));

          flush();

          editDialog = initializeEditDialog(page);

          // Change name of printer and save the change.
          const nameField =
              editDialog.shadowRoot.querySelector('.printer-name-input');
          assertTrue(!!nameField);
          nameField.value = expectedName;
          nameField.dispatchEvent(
              new CustomEvent('input', {bubbles: true, composed: true}));

          flush();

          clickButton(editDialog.shadowRoot.querySelector('.action-button'));

          return cupsPrintersBrowserProxy.whenCalled('updateCupsPrinter');
        })
        .then(() => {
          assertEquals(expectedName, editDialog.activePrinter.printerName);

          // Mimic changes to |cupsPrintersBrowserProxy.printerList|.
          printerList[0].printerName = expectedName;

          verifyPrintersList(savedPrinterEntries, printerList);
        });
  });

  test('ReconfigureSavedPrinter', function() {
    const expectedName = 'edited name';
    const expectedAddress = '1.1.1.1';

    let savedPrinterEntries = null;
    let editDialog = null;

    createCupsPrinterPage([
      createCupsPrinterInfo('google', '4', 'id4'),
      createCupsPrinterInfo('test1', '1', 'id1'),
      createCupsPrinterInfo('test2', '2', 'id2'),
    ]);
    return cupsPrintersBrowserProxy.whenCalled('getCupsSavedPrintersList')
        .then(() => {
          // Wait for saved printers to populate.
          flush();

          savedPrintersElement =
              page.shadowRoot.querySelector('settings-cups-saved-printers');
          assertTrue(!!savedPrintersElement);

          savedPrinterEntries = getPrinterEntries(savedPrintersElement);

          // Edit the first entry.
          clickButton(savedPrinterEntries[0].shadowRoot.querySelector(
              '.icon-more-vert'));
          clickButton(
              savedPrintersElement.shadowRoot.querySelector('#editButton'));

          flush();

          editDialog = initializeEditDialog(page);

          const nameField =
              editDialog.shadowRoot.querySelector('.printer-name-input');
          assertTrue(!!nameField);
          nameField.value = expectedName;
          nameField.dispatchEvent(
              new CustomEvent('input', {bubbles: true, composed: true}));

          const addressField =
              editDialog.shadowRoot.querySelector('#printerAddress');
          assertTrue(!!addressField);
          addressField.value = expectedAddress;
          addressField.dispatchEvent(
              new CustomEvent('input', {bubbles: true, composed: true}));

          assertFalse(
              editDialog.shadowRoot.querySelector('.cancel-button').hidden);
          assertFalse(
              editDialog.shadowRoot.querySelector('.action-button').hidden);

          flush();

          clickButton(editDialog.shadowRoot.querySelector('.action-button'));

          return cupsPrintersBrowserProxy.whenCalled('reconfigureCupsPrinter');
        })
        .then(() => {
          assertEquals(expectedName, editDialog.activePrinter.printerName);
          assertEquals(
              expectedAddress, editDialog.activePrinter.printerAddress);

          // Mimic changes to |cupsPrintersBrowserProxy.printerList|.
          printerList[0].printerName = expectedName;
          printerList[0].printerAddress = expectedAddress;

          verifyPrintersList(savedPrinterEntries, printerList);
        });
  });

  test('SavedPrintersSearchTermFiltersCorrectPrinters', function() {
    createCupsPrinterPage([
      createCupsPrinterInfo('google', '4', 'id4'),
      createCupsPrinterInfo('test1', '1', 'id1'),
      createCupsPrinterInfo('test2', '2', 'id2'),
    ]);
    return cupsPrintersBrowserProxy.whenCalled('getCupsSavedPrintersList')
        .then(() => {
          // Wait for saved printers to populate.
          flush();

          savedPrintersElement =
              page.shadowRoot.querySelector('settings-cups-saved-printers');
          assertTrue(!!savedPrintersElement);

          const printerListEntries = getPrinterEntries(savedPrintersElement);
          verifyPrintersList(printerListEntries, printerList);

          let searchTerm = 'google';
          savedPrintersElement.searchTerm = searchTerm;
          flush();

          // Filtering "google" should result in one visible entry and two
          // hidden entries.
          verifySearchQueryResults(
              savedPrintersElement,
              [createPrinterListEntry('google', '4', 'id4', PrinterType.SAVED)],
              searchTerm);

          // Change the search term and assert that entries are filtered
          // correctly. Filtering "test" should result in three visible entries
          // and one hidden entry.
          searchTerm = 'test';
          savedPrintersElement.searchTerm = searchTerm;
          flush();

          verifySearchQueryResults(
              savedPrintersElement,
              [
                createPrinterListEntry('test1', '1', 'id1', PrinterType.SAVED),
                createPrinterListEntry('test2', '2', 'id2', PrinterType.SAVED),
              ],
              searchTerm);

          // Add more printers and assert that they are correctly filtered.
          addNewSavedPrinter(createCupsPrinterInfo('test3', '3', 'id3'));
          addNewSavedPrinter(createCupsPrinterInfo('google2', '6', 'id6'));
          flush();

          verifySearchQueryResults(
              savedPrintersElement,
              [
                createPrinterListEntry('test3', '3', 'id3', PrinterType.SAVED),
                createPrinterListEntry('test1', '1', 'id1', PrinterType.SAVED),
                createPrinterListEntry('test2', '2', 'id2', PrinterType.SAVED),
              ],
              searchTerm);
        });
  });

  test('SavedPrintersNoSearchFound', function() {
    createCupsPrinterPage([
      createCupsPrinterInfo('google', '4', 'id4'),
      createCupsPrinterInfo('test1', '1', 'id1'),
      createCupsPrinterInfo('test2', '2', 'id2'),
    ]);
    return cupsPrintersBrowserProxy.whenCalled('getCupsSavedPrintersList')
        .then(() => {
          // Wait for saved printers to populate.
          flush();

          savedPrintersElement =
              page.shadowRoot.querySelector('settings-cups-saved-printers');
          assertTrue(!!savedPrintersElement);

          const printerListEntries = getPrinterEntries(savedPrintersElement);
          verifyPrintersList(printerListEntries, printerList);

          let searchTerm = 'google';
          savedPrintersElement.searchTerm = searchTerm;
          flush();

          // Filtering "google" should result in one visible entry and three
          // hidden entries.
          verifySearchQueryResults(
              savedPrintersElement,
              [createPrinterListEntry('google', '4', 'id4', PrinterType.SAVED)],
              searchTerm);

          // Change search term to something that has no matches.
          searchTerm = 'noSearchFound';
          savedPrintersElement.searchTerm = searchTerm;
          flush();

          verifySearchQueryResults(savedPrintersElement, [], searchTerm);

          // Change search term back to "google" and verify that the No search
          // found message is no longer there.
          searchTerm = 'google';
          savedPrintersElement.searchTerm = searchTerm;
          flush();

          verifySearchQueryResults(
              savedPrintersElement,
              [createPrinterListEntry('google', '4', 'id4', PrinterType.SAVED)],
              searchTerm);
        });
  });

  test('NavigateSavedPrintersList', function() {
    createCupsPrinterPage([
      createCupsPrinterInfo('google', '4', 'id4'),
      createCupsPrinterInfo('test1', '1', 'id1'),
      createCupsPrinterInfo('test2', '2', 'id2'),
    ]);
    return cupsPrintersBrowserProxy.whenCalled('getCupsSavedPrintersList')
        .then(async () => {
          // Wait for saved printers to populate.
          flush();
          savedPrintersElement =
              page.shadowRoot.querySelector('settings-cups-saved-printers');
          assertTrue(!!savedPrintersElement);
          const printerEntryList =
              savedPrintersElement.shadowRoot.querySelector(
                  '#printerEntryList');
          const printerListEntries = getPrinterEntries(savedPrintersElement);
          printerEntryList.focus();
          printerEntryList.dispatchEvent(arrowDownEvent);
          flush();
          assertEquals(printerListEntries[1], getDeepActiveElement());
          printerEntryList.dispatchEvent(arrowDownEvent);
          flush();
          assertEquals(printerListEntries[2], getDeepActiveElement());
          printerEntryList.dispatchEvent(arrowUpEvent);
          flush();
          assertEquals(printerListEntries[1], getDeepActiveElement());
          printerEntryList.dispatchEvent(arrowUpEvent);
          flush();
          assertEquals(printerListEntries[0], getDeepActiveElement());
        });
  });

  test('Deep link to saved printers', async () => {
    createCupsPrinterPage([
      createCupsPrinterInfo('google', '4', 'id4'),
      createCupsPrinterInfo('test1', '1', 'id1'),
      createCupsPrinterInfo('test2', '2', 'id2'),
      createCupsPrinterInfo('test3', '3', 'id3'),
    ]);

    await cupsPrintersBrowserProxy.whenCalled('getCupsSavedPrintersList');

    const params = new URLSearchParams();
    params.append('settingId', '1401');
    Router.getInstance().navigateTo(routes.CUPS_PRINTERS, params);

    flush();

    const savedPrinters =
        page.shadowRoot.querySelector('settings-cups-saved-printers');
    const printerEntry = savedPrinters &&
        savedPrinters.shadowRoot.querySelector('settings-cups-printers-entry');
    const deepLinkElement =
        printerEntry && printerEntry.shadowRoot.querySelector('#moreActions');
    await waitAfterNextRender(deepLinkElement);
    assertEquals(
        deepLinkElement, getDeepActiveElement(),
        'First saved printer menu button should be focused for settingId=1401.');
  });

  test('ShowMoreButtonIsInitiallyHiddenAndANewPrinterIsAdded', function() {
    createCupsPrinterPage([
      createCupsPrinterInfo('google', '4', 'id4'),
      createCupsPrinterInfo('test1', '1', 'id1'),
      createCupsPrinterInfo('test2', '2', 'id2'),
    ]);
    return cupsPrintersBrowserProxy.whenCalled('getCupsSavedPrintersList')
        .then(() => {
          // Wait for saved printers to populate.
          flush();

          savedPrintersElement =
              page.shadowRoot.querySelector('settings-cups-saved-printers');
          assertTrue(!!savedPrintersElement);

          const printerEntryListTestElement =
              savedPrintersElement.shadowRoot.querySelector(
                  '#printerEntryList');

          verifyVisiblePrinters(printerEntryListTestElement, [
            createPrinterListEntry('google', '4', 'id4', PrinterType.SAVED),
            createPrinterListEntry('test1', '1', 'id1', PrinterType.SAVED),
            createPrinterListEntry('test2', '2', 'id2', PrinterType.SAVED),
          ]);
          // Assert that the Show more button is hidden because printer list
          // length is <= 3.
          assertFalse(!!savedPrintersElement.shadowRoot.querySelector(
              '#show-more-container'));

          // Newly added printers will always be visible and inserted to the
          // top of the list.
          addNewSavedPrinter(createCupsPrinterInfo('test3', '3', 'id3'));
          const expectedVisiblePrinters = [
            createPrinterListEntry('test3', '3', 'id3', PrinterType.SAVED),
            createPrinterListEntry('google', '4', 'id4', PrinterType.SAVED),
            createPrinterListEntry('test1', '1', 'id1', PrinterType.SAVED),
            createPrinterListEntry('test2', '2', 'id2', PrinterType.SAVED),
          ];
          verifyVisiblePrinters(
              printerEntryListTestElement, expectedVisiblePrinters);
          // Assert that the Show more button is still hidden because all newly
          // added printers are visible.
          assertFalse(!!savedPrintersElement.shadowRoot.querySelector(
              '#show-more-container'));
        });
  });

  test('PressShowMoreButton', function() {
    createCupsPrinterPage([
      createCupsPrinterInfo('google', '4', 'id4'),
      createCupsPrinterInfo('test1', '1', 'id1'),
      createCupsPrinterInfo('test2', '2', 'id2'),
      createCupsPrinterInfo('test3', '3', 'id3'),
    ]);
    return cupsPrintersBrowserProxy.whenCalled('getCupsSavedPrintersList')
        .then(() => {
          // Wait for saved printers to populate.
          flush();

          savedPrintersElement =
              page.shadowRoot.querySelector('settings-cups-saved-printers');
          assertTrue(!!savedPrintersElement);

          const printerEntryListTestElement =
              savedPrintersElement.shadowRoot.querySelector(
                  '#printerEntryList');

          // There are 4 total printers but only 3 printers are visible and 1 is
          // hidden underneath the Show more section.
          verifyVisiblePrinters(printerEntryListTestElement, [
            createPrinterListEntry('google', '4', 'id4', PrinterType.SAVED),
            createPrinterListEntry('test1', '1', 'id1', PrinterType.SAVED),
            createPrinterListEntry('test2', '2', 'id2', PrinterType.SAVED),
          ]);
          // Assert that the Show more button is shown since printer list length
          // is > 3.
          assertTrue(!!savedPrintersElement.shadowRoot.querySelector(
              '#show-more-container'));

          // Click on the Show more button.
          clickButton(
              savedPrintersElement.shadowRoot.querySelector('#show-more-icon'));
          assertFalse(!!savedPrintersElement.shadowRoot.querySelector(
              '#show-more-container'));
          // Clicking on the Show more button reveals all hidden printers.
          verifyVisiblePrinters(printerEntryListTestElement, [
            createPrinterListEntry('google', '4', 'id4', PrinterType.SAVED),
            createPrinterListEntry('test1', '1', 'id1', PrinterType.SAVED),
            createPrinterListEntry('test2', '2', 'id2', PrinterType.SAVED),
            createPrinterListEntry('test3', '3', 'id3', PrinterType.SAVED),
          ]);
        });
  });

  test('ShowMoreButtonIsInitiallyShownAndWithANewPrinterAdded', function() {
    createCupsPrinterPage([
      createCupsPrinterInfo('google', '4', 'id4'),
      createCupsPrinterInfo('test1', '1', 'id1'),
      createCupsPrinterInfo('test2', '2', 'id2'),
      createCupsPrinterInfo('test3', '3', 'id3'),
    ]);
    return cupsPrintersBrowserProxy.whenCalled('getCupsSavedPrintersList')
        .then(() => {
          // Wait for saved printers to populate.
          flush();

          savedPrintersElement =
              page.shadowRoot.querySelector('settings-cups-saved-printers');
          assertTrue(!!savedPrintersElement);

          const printerEntryListTestElement =
              savedPrintersElement.shadowRoot.querySelector(
                  '#printerEntryList');

          // There are 4 total printers but only 3 printers are visible and 1 is
          // hidden underneath the Show more section.
          verifyVisiblePrinters(printerEntryListTestElement, [
            createPrinterListEntry('google', '4', 'id4', PrinterType.SAVED),
            createPrinterListEntry('test1', '1', 'id1', PrinterType.SAVED),
            createPrinterListEntry('test2', '2', 'id2', PrinterType.SAVED),
          ]);
          // Assert that the Show more button is shown since printer list length
          // is > 3.
          assertTrue(!!savedPrintersElement.shadowRoot.querySelector(
              '#show-more-container'));

          // Newly added printers will always be visible.
          addNewSavedPrinter(createCupsPrinterInfo('test5', '5', 'id5'));
          verifyVisiblePrinters(printerEntryListTestElement, [
            createPrinterListEntry('test5', '5', 'id5', PrinterType.SAVED),
            createPrinterListEntry('google', '4', 'id4', PrinterType.SAVED),
            createPrinterListEntry('test1', '1', 'id1', PrinterType.SAVED),
            createPrinterListEntry('test2', '2', 'id2', PrinterType.SAVED),
          ]);
          // Assert that the Show more button is still shown.
          assertTrue(!!savedPrintersElement.shadowRoot.querySelector(
              '#show-more-container'));
        });
  });

  test('ShowMoreButtonIsShownAndRemovePrinters', function() {
    createCupsPrinterPage([
      createCupsPrinterInfo('google', '3', 'id3'),
      createCupsPrinterInfo('google2', '4', 'id4'),
      createCupsPrinterInfo('google3', '5', 'id5'),
      createCupsPrinterInfo('test1', '1', 'id1'),
      createCupsPrinterInfo('test2', '2', 'id2'),
    ]);
    return cupsPrintersBrowserProxy.whenCalled('getCupsSavedPrintersList')
        .then(() => {
          // Wait for saved printers to populate.
          flush();

          savedPrintersElement =
              page.shadowRoot.querySelector('settings-cups-saved-printers');
          assertTrue(!!savedPrintersElement);

          const printerEntryListTestElement =
              savedPrintersElement.shadowRoot.querySelector(
                  '#printerEntryList');

          // There are 5 total printers but only 3 printers are visible and 2
          // are hidden underneath the Show more section.
          verifyVisiblePrinters(printerEntryListTestElement, [
            createPrinterListEntry('google', '3', 'id3', PrinterType.SAVED),
            createPrinterListEntry('google2', '4', 'id4', PrinterType.SAVED),
            createPrinterListEntry('google3', '5', 'id5', PrinterType.SAVED),
          ]);
          // Assert that the Show more button is shown since printer list length
          // is > 3.
          assertTrue(!!savedPrintersElement.shadowRoot.querySelector(
              '#show-more-container'));

          // Simulate removing 'google' printer.
          removeSavedPrinter('id3');
          // Printer list has 4 elements now, but since the list is still
          // collapsed we should still expect only 3 elements to be visible.
          // Since printers were initially alphabetically sorted, we should
          // expect 'test1' to be the next visible printer.
          verifyVisiblePrinters(printerEntryListTestElement, [
            createPrinterListEntry('google2', '4', 'id4', PrinterType.SAVED),
            createPrinterListEntry('google3', '5', 'id5', PrinterType.SAVED),
            createPrinterListEntry('test1', '1', 'id1', PrinterType.SAVED),
          ]);
          assertTrue(!!savedPrintersElement.shadowRoot.querySelector(
              '#show-more-container'));

          // Simulate removing 'google2' printer.
          removeSavedPrinter('id4');
          // Printer list has 3 elements now, the Show more button should be
          // hidden.
          verifyVisiblePrinters(printerEntryListTestElement, [
            createPrinterListEntry('google3', '5', 'id5', PrinterType.SAVED),
            createPrinterListEntry('test1', '1', 'id1', PrinterType.SAVED),
            createPrinterListEntry('test2', '2', 'id2', PrinterType.SAVED),
          ]);
          assertFalse(!!savedPrintersElement.shadowRoot.querySelector(
              '#show-more-container'));
        });
  });

  test('ShowMoreButtonIsShownAndSearchQueryFiltersCorrectly', function() {
    createCupsPrinterPage([
      createCupsPrinterInfo('google', '3', 'id3'),
      createCupsPrinterInfo('google2', '4', 'id4'),
      createCupsPrinterInfo('google3', '5', 'id5'),
      createCupsPrinterInfo('google4', '6', 'id6'),
      createCupsPrinterInfo('test1', '1', 'id1'),
      createCupsPrinterInfo('test2', '2', 'id2'),
    ]);
    return cupsPrintersBrowserProxy.whenCalled('getCupsSavedPrintersList')
        .then(() => {
          // Wait for saved printers to populate.
          flush();

          savedPrintersElement =
              page.shadowRoot.querySelector('settings-cups-saved-printers');
          assertTrue(!!savedPrintersElement);

          const printerEntryListTestElement =
              savedPrintersElement.shadowRoot.querySelector(
                  '#printerEntryList');

          // There are 6 total printers but only 3 printers are visible and 3
          // are hidden underneath the Show more section.
          verifyVisiblePrinters(printerEntryListTestElement, [
            createPrinterListEntry('google', '3', 'id3', PrinterType.SAVED),
            createPrinterListEntry('google2', '4', 'id4', PrinterType.SAVED),
            createPrinterListEntry('google3', '5', 'id5', PrinterType.SAVED),
          ]);
          // Assert that the Show more button is shown since printer list length
          // is > 3.
          assertTrue(!!savedPrintersElement.shadowRoot.querySelector(
              '#show-more-container'));

          // Set search term to 'google' and expect 4 visible printers.
          let searchTerm = 'google';
          savedPrintersElement.searchTerm = searchTerm;
          flush();
          verifySearchQueryResults(
              savedPrintersElement,
              [
                createPrinterListEntry('google', '3', 'id3', PrinterType.SAVED),
                createPrinterListEntry(
                    'google2', '4', 'id4', PrinterType.SAVED),
                createPrinterListEntry(
                    'google3', '5', 'id5', PrinterType.SAVED),
                createPrinterListEntry(
                    'google4', '6', 'id6', PrinterType.SAVED),
              ],
              searchTerm);
          // Having a search term should hide the Show more button.
          assertFalse(!!savedPrintersElement.shadowRoot.querySelector(
              '#show-more-container'));

          // Search for a term with no matching printers. Expect Show more
          // button to still be hidden.
          searchTerm = 'noSearchFound';
          savedPrintersElement.searchTerm = searchTerm;
          flush();
          verifySearchQueryResults(savedPrintersElement, [], searchTerm);

          assertFalse(!!savedPrintersElement.shadowRoot.querySelector(
              '#show-more-container'));

          // Change search term and expect new set of visible printers.
          searchTerm = 'test';
          savedPrintersElement.searchTerm = searchTerm;
          flush();
          verifySearchQueryResults(
              savedPrintersElement,
              [
                createPrinterListEntry('test1', '1', 'id1', PrinterType.SAVED),
                createPrinterListEntry('test2', '2', 'id2', PrinterType.SAVED),
              ],
              searchTerm);
          assertFalse(!!savedPrintersElement.shadowRoot.querySelector(
              '#show-more-container'));

          // Remove the search term and expect the collapsed list to appear
          // again.
          searchTerm = '';
          savedPrintersElement.searchTerm = searchTerm;
          flush();
          const expectedVisiblePrinters = [
            createPrinterListEntry('google', '3', 'id3', PrinterType.SAVED),
            createPrinterListEntry('google2', '4', 'id4', PrinterType.SAVED),
            createPrinterListEntry('google3', '5', 'id5', PrinterType.SAVED),
          ];
          verifySearchQueryResults(
              savedPrintersElement, expectedVisiblePrinters, searchTerm);
          verifyVisiblePrinters(
              printerEntryListTestElement, expectedVisiblePrinters);
          assertTrue(!!savedPrintersElement.shadowRoot.querySelector(
              '#show-more-container'));
        });
  });

  test('ShowMoreButtonAddAndRemovePrinters', function() {
    createCupsPrinterPage([
      createCupsPrinterInfo('google', '3', 'id3'),
      createCupsPrinterInfo('google2', '4', 'id4'),
      createCupsPrinterInfo('test1', '1', 'id1'),
      createCupsPrinterInfo('test2', '2', 'id2'),
    ]);
    return cupsPrintersBrowserProxy.whenCalled('getCupsSavedPrintersList')
        .then(() => {
          // Wait for saved printers to populate.
          flush();

          savedPrintersElement =
              page.shadowRoot.querySelector('settings-cups-saved-printers');
          assertTrue(!!savedPrintersElement);

          const printerEntryListTestElement =
              savedPrintersElement.shadowRoot.querySelector(
                  '#printerEntryList');

          // There are 4 total printers but only 3 printers are visible and 1 is
          // hidden underneath the Show more section.
          verifyVisiblePrinters(printerEntryListTestElement, [
            createPrinterListEntry('google', '3', 'id3', PrinterType.SAVED),
            createPrinterListEntry('google2', '4', 'id4', PrinterType.SAVED),
            createPrinterListEntry('test1', '1', 'id1', PrinterType.SAVED),
          ]);
          // Assert that the Show more button is shown since printer list length
          // is > 3.
          assertTrue(!!savedPrintersElement.shadowRoot.querySelector(
              '#show-more-container'));

          // Add a new printer and expect it to be at the top of the list.
          addNewSavedPrinter(createCupsPrinterInfo('newPrinter', '5', 'id5'));
          verifyVisiblePrinters(printerEntryListTestElement, [
            createPrinterListEntry('newPrinter', '5', 'id5', PrinterType.SAVED),
            createPrinterListEntry('google', '3', 'id3', PrinterType.SAVED),
            createPrinterListEntry('google2', '4', 'id4', PrinterType.SAVED),
            createPrinterListEntry('test1', '1', 'id1', PrinterType.SAVED),
          ]);
          assertTrue(!!savedPrintersElement.shadowRoot.querySelector(
              '#show-more-container'));

          // Now simulate removing printer 'test1'.
          removeSavedPrinter('id1');
          // If the number of visible printers is > 3, removing printers will
          // decrease the number of visible printers until there are only 3
          // visible printers. In this case, we remove 'test1' and now only
          // have 3 visible printers and 1 hidden printer: 'test2'.
          verifyVisiblePrinters(printerEntryListTestElement, [
            createPrinterListEntry('newPrinter', '5', 'id5', PrinterType.SAVED),
            createPrinterListEntry('google', '3', 'id3', PrinterType.SAVED),
            createPrinterListEntry('google2', '4', 'id4', PrinterType.SAVED),
          ]);
          assertTrue(!!savedPrintersElement.shadowRoot.querySelector(
              '#show-more-container'));

          // Remove another printer and assert that we still have 3 visible
          // printers but now 'test2' is our third visible printer.
          removeSavedPrinter('id4');
          verifyVisiblePrinters(printerEntryListTestElement, [
            createPrinterListEntry('newPrinter', '5', 'id5', PrinterType.SAVED),
            createPrinterListEntry('google', '3', 'id3', PrinterType.SAVED),
            createPrinterListEntry('test2', '2', 'id2', PrinterType.SAVED),
          ]);
          // Printer list length is <= 3, Show more button should be hidden.
          assertFalse(!!savedPrintersElement.shadowRoot.querySelector(
              '#show-more-container'));
        });
  });
});

suite('CupsNearbyPrintersTests', function() {
  let page = null;
  let nearbyPrintersElement = null;

  /** @type {?settings.TestCupsPrintersBrowserProxy} */
  let cupsPrintersBrowserProxy = null;

  /** @type{!HtmlElement} */
  let printerEntryListTestElement = null;

  /** @type {!chromeos.networkConfig.mojom.NetworkStateProperties|undefined} */
  let wifi1;

  setup(function() {
    const mojom = chromeos.networkConfig.mojom;
    cupsPrintersBrowserProxy = new TestCupsPrintersBrowserProxy();

    CupsPrintersBrowserProxyImpl.setInstanceForTesting(
        cupsPrintersBrowserProxy);

    // Simulate internet connection.
    wifi1 = OncMojo.getDefaultNetworkState(mojom.NetworkType.kWiFi, 'wifi1');
    wifi1.connectionState = mojom.ConnectionStateType.kOnline;

    PolymerTest.clearBody();
    Router.getInstance().navigateTo(routes.CUPS_PRINTERS);

    page = document.createElement('settings-cups-printers');
    document.body.appendChild(page);
    assertTrue(!!page);
    page.onActiveNetworksChanged([wifi1]);

    flush();
  });

  teardown(function() {
    cupsPrintersBrowserProxy.reset();
    page.remove();
    nearbyPrintersElement = null;
    page = null;
  });

  test('nearbyPrintersSuccessfullyPopulates', function() {
    const automaticPrinterList = [
      createCupsPrinterInfo('test1', '1', 'id1'),
      createCupsPrinterInfo('test2', '2', 'id2'),
    ];
    const discoveredPrinterList = [
      createCupsPrinterInfo('test3', '3', 'id3'),
      createCupsPrinterInfo('test4', '4', 'id4'),
    ];

    return flushTasks().then(() => {
      nearbyPrintersElement =
          page.shadowRoot.querySelector('settings-cups-nearby-printers');
      assertTrue(!!nearbyPrintersElement);

      // Assert that no printers have been detected.
      let nearbyPrinterEntries = getPrinterEntries(nearbyPrintersElement);
      assertEquals(0, nearbyPrinterEntries.length);

      // Simuluate finding nearby printers.
      webUIListenerCallback(
          'on-nearby-printers-changed', automaticPrinterList,
          discoveredPrinterList);

      flush();

      nearbyPrinterEntries = getPrinterEntries(nearbyPrintersElement);

      const expectedPrinterList =
          automaticPrinterList.concat(discoveredPrinterList);
      verifyPrintersList(nearbyPrinterEntries, expectedPrinterList);
    });
  });

  test('nearbyPrintersSortOrderAutoFirstThenDiscovered', function() {
    const discoveredPrinterA =
        createCupsPrinterInfo('printerNameA', 'printerAddress1', 'printerId1');
    const discoveredPrinterB =
        createCupsPrinterInfo('printerNameB', 'printerAddress2', 'printerId2');
    const discoveredPrinterC =
        createCupsPrinterInfo('printerNameC', 'printerAddress3', 'printerId3');
    const autoPrinterD =
        createCupsPrinterInfo('printerNameD', 'printerAddress4', 'printerId4');
    const autoPrinterE =
        createCupsPrinterInfo('printerNameE', 'printerAddress5', 'printerId5');
    const autoPrinterF =
        createCupsPrinterInfo('printerNameF', 'printerAddress6', 'printerId6');

    // Add printers in a non-alphabetical order to test sorting.
    const automaticPrinterList = [autoPrinterF, autoPrinterD, autoPrinterE];
    const discoveredPrinterList =
        [discoveredPrinterC, discoveredPrinterA, discoveredPrinterB];

    // Expected sort order is to sort automatic printers first then
    // sort discovered printers
    const expectedPrinterList = [
      autoPrinterD,
      autoPrinterE,
      autoPrinterF,
      discoveredPrinterA,
      discoveredPrinterB,
      discoveredPrinterC,
    ];

    return flushTasks().then(() => {
      nearbyPrintersElement =
          page.shadowRoot.querySelector('settings-cups-nearby-printers');
      assertTrue(!!nearbyPrintersElement);

      // Simuluate finding nearby printers.
      webUIListenerCallback(
          'on-nearby-printers-changed', automaticPrinterList,
          discoveredPrinterList);

      flush();

      const nearbyPrinterEntries = getPrinterEntries(nearbyPrintersElement);

      verifyPrintersList(nearbyPrinterEntries, expectedPrinterList);
    });
  });

  test('addingAutomaticPrinterIsSuccessful', function() {
    const automaticPrinterList = [createCupsPrinterInfo('test1', '1', 'id1')];
    const discoveredPrinterList = [];

    let addButton = null;

    return flushTasks()
        .then(() => {
          nearbyPrintersElement =
              page.shadowRoot.querySelector('settings-cups-nearby-printers');
          assertTrue(!!nearbyPrintersElement);

          // Simuluate finding nearby printers.
          webUIListenerCallback(
              'on-nearby-printers-changed', automaticPrinterList,
              discoveredPrinterList);

          flush();

          // Requery and assert that the newly detected printer automatic
          // printer has the correct button.
          const nearbyPrinterEntries = getPrinterEntries(nearbyPrintersElement);
          assertEquals(1, nearbyPrinterEntries.length);
          assertTrue(!!nearbyPrinterEntries[0].shadowRoot.querySelector(
              '.save-printer-button'));

          // Add an automatic printer and assert that that the toast
          // notification is shown.
          addButton = nearbyPrinterEntries[0].shadowRoot.querySelector(
              '.save-printer-button');
          clickButton(addButton);
          // Add button should be disabled during setup.
          assertTrue(addButton.disabled);

          return cupsPrintersBrowserProxy.whenCalled('addDiscoveredPrinter');
        })
        .then(() => {
          assertFalse(addButton.disabled);
          const expectedToastMessage =
              'Added ' + automaticPrinterList[0].printerName;
          verifyErrorToastMessage(
              expectedToastMessage,
              page.shadowRoot.querySelector('#errorToast'));
        });
  });

  test('NavigateNearbyPrinterList', function() {
    const discoveredPrinterList = [
      createCupsPrinterInfo('first', '3', 'id3'),
      createCupsPrinterInfo('second', '4', 'id4'),
      createCupsPrinterInfo('third', '2', 'id5'),
    ];
    return flushTasks().then(async () => {
      nearbyPrintersElement =
          page.shadowRoot.querySelector('settings-cups-nearby-printers');

      // Block so that FocusRowBehavior.attached can run.
      await waitAfterNextRender(nearbyPrintersElement);

      assertTrue(!!nearbyPrintersElement);
      // Simuluate finding nearby printers.
      webUIListenerCallback(
          'on-nearby-printers-changed', [], discoveredPrinterList);
      flush();

      // Wait one more time to ensure that async setup in FocusRowBehavior has
      // executed.
      await waitAfterNextRender(nearbyPrintersElement);
      const nearbyPrinterEntries = getPrinterEntries(nearbyPrintersElement);
      const printerEntryList =
          nearbyPrintersElement.shadowRoot.querySelector('#printerEntryList');

      nearbyPrinterEntries[0].shadowRoot.querySelector('#entry').focus();
      assertEquals(
          nearbyPrinterEntries[0].shadowRoot.querySelector('#entry'),
          getDeepActiveElement());
      // Ensure that we can navigate through items in a row
      getDeepActiveElement().dispatchEvent(arrowRightEvent);
      assertEquals(
          nearbyPrinterEntries[0].shadowRoot.querySelector(
              '#setupPrinterButton'),
          getDeepActiveElement());
      getDeepActiveElement().dispatchEvent(arrowLeftEvent);
      assertEquals(
          nearbyPrinterEntries[0].shadowRoot.querySelector('#entry'),
          getDeepActiveElement());

      // Ensure that we can navigate through printer rows
      printerEntryList.dispatchEvent(arrowDownEvent);
      assertEquals(
          nearbyPrinterEntries[1].shadowRoot.querySelector('#entry'),
          getDeepActiveElement());
      printerEntryList.dispatchEvent(arrowDownEvent);
      assertEquals(
          nearbyPrinterEntries[2].shadowRoot.querySelector('#entry'),
          getDeepActiveElement());
      printerEntryList.dispatchEvent(arrowUpEvent);
      assertEquals(
          nearbyPrinterEntries[1].shadowRoot.querySelector('#entry'),
          getDeepActiveElement());
      printerEntryList.dispatchEvent(arrowUpEvent);
      assertEquals(
          nearbyPrinterEntries[0].shadowRoot.querySelector('#entry'),
          getDeepActiveElement());
    });
  });

  test('addingDiscoveredPrinterIsSuccessful', function() {
    const automaticPrinterList = [];
    const discoveredPrinterList = [createCupsPrinterInfo('test3', '3', 'id3')];

    let manufacturerDialog = null;
    let setupButton = null;

    return flushTasks()
        .then(() => {
          nearbyPrintersElement =
              page.shadowRoot.querySelector('settings-cups-nearby-printers');
          assertTrue(!!nearbyPrintersElement);

          // Simuluate finding nearby printers.
          webUIListenerCallback(
              'on-nearby-printers-changed', automaticPrinterList,
              discoveredPrinterList);

          flush();

          // Requery and assert that a newly detected discovered printer has
          // the correct icon button.
          const nearbyPrinterEntries = getPrinterEntries(nearbyPrintersElement);
          assertEquals(1, nearbyPrinterEntries.length);
          assertTrue(!!nearbyPrinterEntries[0].shadowRoot.querySelector(
              '#setupPrinterButton'));

          // Force a failure with adding a discovered printer.
          cupsPrintersBrowserProxy.setAddDiscoveredPrinterFailure(
              discoveredPrinterList[0]);

          // Assert that clicking on the setup button shows the advanced
          // configuration dialog.
          setupButton = nearbyPrinterEntries[0].shadowRoot.querySelector(
              '#setupPrinterButton');
          clickButton(setupButton);
          // Setup button should be disabled during setup.
          assertTrue(setupButton.disabled);

          return cupsPrintersBrowserProxy.whenCalled('addDiscoveredPrinter');
        })
        .then(() => {
          assertFalse(setupButton.disabled);

          flush();
          const addDialog = page.shadowRoot.querySelector('#addPrinterDialog');
          manufacturerDialog = addDialog.shadowRoot.querySelector(
              'add-printer-manufacturer-model-dialog');
          assertTrue(!!manufacturerDialog);

          return cupsPrintersBrowserProxy.whenCalled(
              'getCupsPrinterManufacturersList');
        })
        .then(() => {
          const addButton =
              manufacturerDialog.shadowRoot.querySelector('#addPrinterButton');
          assertTrue(addButton.disabled);

          // Populate the manufacturer and model fields to enable the Add
          // button.
          manufacturerDialog.shadowRoot.querySelector('#manufacturerDropdown')
              .value = 'make';
          const modelDropdown =
              manufacturerDialog.shadowRoot.querySelector('#modelDropdown');
          modelDropdown.value = 'model';

          clickButton(addButton);
          return cupsPrintersBrowserProxy.whenCalled('addCupsPrinter');
        })
        .then(() => {
          // Assert that the toast notification is shown and has the expected
          // message when adding a discovered printer.
          const expectedToastMessage =
              'Added ' + discoveredPrinterList[0].printerName;
          verifyErrorToastMessage(
              expectedToastMessage,
              page.shadowRoot.querySelector('#errorToast'));
        });
  });

  test('NetworkConnectedButNoInternet', function() {
    // Simulate connecting to a network with no internet connection.
    wifi1.connectionState =
        chromeos.networkConfig.mojom.ConnectionStateType.kConnected;
    page.onActiveNetworksChanged([wifi1]);
    flush();

    return flushTasks().then(() => {
      // We require internet to be able to add a new printer. Connecting to
      // a network without connectivity should be equivalent to not being
      // connected to a network.
      assertTrue(!!page.shadowRoot.querySelector('#cloudOffIcon'));
      assertTrue(!!page.shadowRoot.querySelector('#connectionMessage'));
      assertTrue(
          !!page.shadowRoot.querySelector('#addManualPrinterIcon').disabled);
    });
  });

  test('checkNetworkConnection', function() {
    // Simulate disconnecting from a network.
    wifi1.connectionState =
        chromeos.networkConfig.mojom.ConnectionStateType.kNotConnected;
    page.onActiveNetworksChanged([wifi1]);
    flush();

    return flushTasks()
        .then(() => {
          // Expect offline text to show up when no internet is
          // connected.
          assertTrue(!!page.shadowRoot.querySelector('#cloudOffIcon'));
          assertTrue(!!page.shadowRoot.querySelector('#connectionMessage'));
          assertTrue(!!page.shadowRoot.querySelector('#addManualPrinterIcon')
                           .disabled);

          // Simulate connecting to a network with connectivity.
          wifi1.connectionState =
              chromeos.networkConfig.mojom.ConnectionStateType.kOnline;
          page.onActiveNetworksChanged([wifi1]);
          flush();

          return flushTasks();
        })
        .then(() => {
          const automaticPrinterList = [
            createCupsPrinterInfo('test1', '1', 'id1'),
            createCupsPrinterInfo('test2', '2', 'id2'),
          ];
          const discoveredPrinterList = [
            createCupsPrinterInfo('test3', '3', 'id3'),
            createCupsPrinterInfo('test4', '4', 'id4'),
          ];

          // Simuluate finding nearby printers.
          webUIListenerCallback(
              'on-nearby-printers-changed', automaticPrinterList,
              discoveredPrinterList);

          flush();

          nearbyPrintersElement =
              page.shadowRoot.querySelector('settings-cups-nearby-printers');
          assertTrue(!!nearbyPrintersElement);

          printerEntryListTestElement =
              nearbyPrintersElement.shadowRoot.querySelector(
                  '#printerEntryList');
          assertTrue(!!printerEntryListTestElement);

          const nearbyPrinterEntries = getPrinterEntries(nearbyPrintersElement);

          const expectedPrinterList =
              automaticPrinterList.concat(discoveredPrinterList);
          verifyPrintersList(nearbyPrinterEntries, expectedPrinterList);
        });
  });

  test('NearbyPrintersSearchTermFiltersCorrectPrinters', function() {
    const discoveredPrinterList = [
      createCupsPrinterInfo('test1', 'printerAddress1', 'printerId1'),
      createCupsPrinterInfo('test2', 'printerAddress2', 'printerId2'),
      createCupsPrinterInfo('google', 'printerAddress3', 'printerId3'),
    ];

    return flushTasks().then(() => {
      nearbyPrintersElement =
          page.shadowRoot.querySelector('settings-cups-nearby-printers');
      assertTrue(!!nearbyPrintersElement);

      printerEntryListTestElement =
          nearbyPrintersElement.shadowRoot.querySelector('#printerEntryList');
      assertTrue(!!printerEntryListTestElement);

      // Simuluate finding nearby printers.
      webUIListenerCallback(
          'on-nearby-printers-changed', [], discoveredPrinterList);

      flush();

      verifyVisiblePrinters(printerEntryListTestElement, [
        createPrinterListEntry(
            'google', 'printerAddress3', 'printerId3', PrinterType.DISCOVERD),
        createPrinterListEntry(
            'test1', 'printerAddress1', 'printerId1', PrinterType.DISCOVERD),
        createPrinterListEntry(
            'test2', 'printerAddress2', 'printerId2', PrinterType.DISCOVERD),
      ]);

      let searchTerm = 'google';
      nearbyPrintersElement.searchTerm = searchTerm;
      flush();

      // Filtering "google" should result in one visible entry and two hidden
      // entries.
      verifySearchQueryResults(
          nearbyPrintersElement, [createPrinterListEntry(
                                     'google', 'printerAddress3', 'printerId3',
                                     PrinterType.DISCOVERD)],
          searchTerm);

      // Filtering "test" should result in two visible entries and one hidden
      // entry.
      searchTerm = 'test';
      nearbyPrintersElement.searchTerm = searchTerm;
      flush();

      verifySearchQueryResults(
          nearbyPrintersElement,
          [
            createPrinterListEntry(
                'test1', 'printerAddress1', 'printerId1',
                PrinterType.DISCOVERD),
            createPrinterListEntry(
                'test2', 'printerAddress2', 'printerId2',
                PrinterType.DISCOVERD),
          ],
          searchTerm);

      // Add more printers and assert that they are correctly filtered.
      discoveredPrinterList.push(
          createCupsPrinterInfo('test3', 'printerAddress4', 'printerId4'));
      discoveredPrinterList.push(
          createCupsPrinterInfo('google2', 'printerAddress5', 'printerId5'));

      // Simuluate finding nearby printers.
      webUIListenerCallback(
          'on-nearby-printers-changed', [], discoveredPrinterList);

      flush();

      verifySearchQueryResults(
          nearbyPrintersElement,
          [
            createPrinterListEntry(
                'test1', 'printerAddress1', 'printerId1',
                PrinterType.DISCOVERD),
            createPrinterListEntry(
                'test2', 'printerAddress2', 'printerId2',
                PrinterType.DISCOVERD),
            createPrinterListEntry(
                'test3', 'printerAddress4', 'printerId4',
                PrinterType.DISCOVERD),
          ],
          searchTerm);
    });
  });

  test('NearbyPrintersNoSearchFound', function() {
    const discoveredPrinterList = [
      createCupsPrinterInfo('test1', 'printerAddress1', 'printerId1'),
      createCupsPrinterInfo('google', 'printerAddress2', 'printerId2'),
    ];

    return flushTasks().then(() => {
      nearbyPrintersElement =
          page.shadowRoot.querySelector('settings-cups-nearby-printers');
      assertTrue(!!nearbyPrintersElement);

      printerEntryListTestElement =
          nearbyPrintersElement.shadowRoot.querySelector('#printerEntryList');
      assertTrue(!!printerEntryListTestElement);

      // Simuluate finding nearby printers.
      webUIListenerCallback(
          'on-nearby-printers-changed', [], discoveredPrinterList);

      flush();

      let searchTerm = 'google';
      nearbyPrintersElement.searchTerm = searchTerm;
      flush();

      // Set the search term and filter out the printers. Filtering "google"
      // should result in one visible entry and one hidden entries.
      verifySearchQueryResults(
          nearbyPrintersElement, [createPrinterListEntry(
                                     'google', 'printerAddress2', 'printerId2',
                                     PrinterType.DISCOVERED)],
          searchTerm);

      // Change search term to something that has no matches.
      searchTerm = 'noSearchFound';
      nearbyPrintersElement.searchTerm = searchTerm;
      flush();

      verifySearchQueryResults(nearbyPrintersElement, [], searchTerm);

      // Change search term back to "google" and verify that the No search found
      // message is no longer there.
      searchTerm = 'google';
      nearbyPrintersElement.searchTerm = searchTerm;
      flush();

      verifySearchQueryResults(
          nearbyPrintersElement, [createPrinterListEntry(
                                     'google', 'printerAddress2', 'printerId2',
                                     PrinterType.DISCOVERD)],
          searchTerm);
    });
  });
});

suite('CupsEnterprisePrintersTests', function() {
  let page = null;
  let enterprisePrintersElement = null;

  /** @type{!HtmlElement} */
  let printerEntryListTestElement = null;

  /** @type {?settings.TestCupsPrintersBrowserProxy} */
  let cupsPrintersBrowserProxy = null;

  /** @type {?Array<!CupsPrinterInfo>} */
  let printerList = null;

  setup(function() {
    cupsPrintersBrowserProxy = new TestCupsPrintersBrowserProxy();

    PolymerTest.clearBody();
    Router.getInstance().navigateTo(routes.CUPS_PRINTERS);
  });

  teardown(function() {
    cupsPrintersBrowserProxy.reset();
    page.remove();
    enterprisePrintersElement = null;
    printerList = null;
    page = null;
  });

  /** @param {!Array<!CupsPrinterInfo>} printerList */
  function createCupsPrinterPage(printers) {
    printerList = printers;
    // |cupsPrinterBrowserProxy| needs to have a list of printers before
    // initializing the landing page.
    cupsPrintersBrowserProxy.printerList = {printerList: printerList};
    CupsPrintersBrowserProxyImpl.setInstanceForTesting(
        cupsPrintersBrowserProxy);

    page = document.createElement('settings-cups-printers');
    document.body.appendChild(page);
    assertTrue(!!page);

    flush();
  }

  // Verifies that enterprise printers are correctly shown on the OS settings
  // page.
  test('EnterprisePrinters', () => {
    createCupsPrinterPage([
      createCupsPrinterInfo('test1', '1', 'id1', /*isManaged=*/ true),
      createCupsPrinterInfo('test2', '2', 'id2', /*isManaged=*/ true),
    ]);
    return cupsPrintersBrowserProxy.whenCalled('getCupsEnterprisePrintersList')
        .then(() => {
          // Wait for saved printers to populate.
          flush();

          enterprisePrintersElement = page.shadowRoot.querySelector(
              'settings-cups-enterprise-printers');
          printerEntryListTestElement =
              enterprisePrintersElement.shadowRoot.querySelector(
                  '#printerEntryList');
          verifyVisiblePrinters(printerEntryListTestElement, [
            createPrinterListEntry('test1', '1', 'id1', PrinterType.ENTERPRISE),
            createPrinterListEntry('test2', '2', 'id2', PrinterType.ENTERPRISE),
          ]);
        });
  });

  // Verifies that enterprise printers are not editable.
  test('EnterprisePrinterDialog', function() {
    const expectedName = 'edited name';
    let editDialog = null;

    createCupsPrinterPage([
      createCupsPrinterInfo('test1', '1', 'id1', true),
    ]);
    return cupsPrintersBrowserProxy.whenCalled('getCupsEnterprisePrintersList')
        .then(() => {
          // Wait for enterprise printers to populate.
          flush();

          enterprisePrintersElement = page.shadowRoot.querySelector(
              'settings-cups-enterprise-printers');
          assertTrue(!!enterprisePrintersElement);

          const enterprisePrinterEntries =
              getPrinterEntries(enterprisePrintersElement);

          // Users are not allowed to remove enterprise printers.
          const removeButton =
              enterprisePrintersElement.shadowRoot.querySelector(
                  '#removeButton');
          assertTrue(removeButton.disabled);

          clickButton(enterprisePrinterEntries[0].shadowRoot.querySelector(
              '.icon-more-vert'));
          clickButton(enterprisePrintersElement.shadowRoot.querySelector(
              '#viewButton'));

          flush();

          editDialog = initializeEditDialog(page);

          const nameField =
              editDialog.shadowRoot.querySelector('.printer-name-input');
          assertTrue(!!nameField);
          assertEquals('test1', nameField.value);
          assertTrue(nameField.readonly);

          assertTrue(
              editDialog.shadowRoot.querySelector('#printerAddress').readonly);
          assertTrue(
              editDialog.shadowRoot.querySelector('.md-select').disabled);
          assertTrue(
              editDialog.shadowRoot.querySelector('#printerQueue').readonly);
          assertTrue(
              editDialog.shadowRoot.querySelector('#printerPPDManufacturer')
                  .readonly);

          // The "specify PDD" section should be hidden.
          assertTrue(editDialog.shadowRoot.querySelector('.browse-button')
                         .parentElement.hidden);
          assertTrue(editDialog.shadowRoot.querySelector('#ppdLabel').hidden);

          // Save and Cancel buttons should be hidden. Close button should be
          // visible.
          assertTrue(
              editDialog.shadowRoot.querySelector('.cancel-button').hidden);
          assertTrue(
              editDialog.shadowRoot.querySelector('.action-button').hidden);
          assertFalse(
              editDialog.shadowRoot.querySelector('.close-button').hidden);
        });
  });

  test('PressShowMoreButton', function() {
    createCupsPrinterPage([
      createCupsPrinterInfo('test1', '1', 'id1', true),
      createCupsPrinterInfo('test2', '2', 'id2', true),
      createCupsPrinterInfo('test3', '3', 'id3', true),
      createCupsPrinterInfo('test4', '4', 'id4', true),
    ]);
    return cupsPrintersBrowserProxy.whenCalled('getCupsEnterprisePrintersList')
        .then(() => {
          // Wait for enterprise printers to populate.
          flush();

          enterprisePrintersElement = page.shadowRoot.querySelector(
              'settings-cups-enterprise-printers');
          assertTrue(!!enterprisePrintersElement);

          const printerEntryListTestElement =
              enterprisePrintersElement.shadowRoot.querySelector(
                  '#printerEntryList');

          // There are 4 total printers but only 3 printers are visible and 1 is
          // hidden underneath the Show more section.
          verifyVisiblePrinters(printerEntryListTestElement, [
            createPrinterListEntry('test1', '1', 'id1', PrinterType.ENTERPRISE),
            createPrinterListEntry('test2', '2', 'id2', PrinterType.ENTERPRISE),
            createPrinterListEntry('test3', '3', 'id3', PrinterType.ENTERPRISE),
          ]);
          // Assert that the Show more button is shown since printer list length
          // is > 3.
          assertTrue(!!enterprisePrintersElement.shadowRoot.querySelector(
              '#show-more-container'));

          // Click on the Show more button.
          clickButton(enterprisePrintersElement.shadowRoot.querySelector(
              '#show-more-icon'));
          assertFalse(!!enterprisePrintersElement.shadowRoot.querySelector(
              '#show-more-container'));
          // Clicking on the Show more button reveals all hidden printers.
          verifyVisiblePrinters(printerEntryListTestElement, [
            createPrinterListEntry('test1', '1', 'id1', PrinterType.ENTERPRISE),
            createPrinterListEntry('test2', '2', 'id2', PrinterType.ENTERPRISE),
            createPrinterListEntry('test3', '3', 'id3', PrinterType.ENTERPRISE),
            createPrinterListEntry('test4', '4', 'id4', PrinterType.ENTERPRISE),
          ]);
        });
  });
});


text:
package cn.echo.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import cn.echo.model.User;

/**
 * Servlet implementation class SessionServlet
 */
@WebServlet("/SessionServlet.do")
public class SessionServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	/**
	 * @see HttpServlet#HttpServlet()
	 */
	public SessionServlet() {
		super();
		// TODO Auto-generated constructor stub
	}

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub

	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		HttpSession session = request.getSession();
		session.setAttribute("msg", "ÄãºÃÎÒÊÇsession");
		String username = request.getParameter("username");
		session.setAttribute("username", username);
		request.setAttribute("msg","ÄãºÃÎÒÊÇrequestÀïÃæµÄmsg");
//		request.getRequestDispatcher("SessionSucess.jsp").forward(request, response);;
	    String url = response.encodeURL("SessionSucess.jsp;jsessionid="+session.getId());
		response.sendRedirect(url);
		//ĬÈÏʹÓÃϵͳcookie»úÖÆ
		//cookieÈç¹û±»½ûÓà --->ÖØдURLµØÖ·ºóÃæ¼ÓÉÏ;jesssionid= »ñÈ¡µ½µÄsessionid
	}
}

redpajama_set_name: RedPajamaGithub


text:
[clappr](../../index.md) / [io.clappr.player.plugin.control](../index.md) / [PlayButton](index.md) / [resourceLayout](./resource-layout.md)

# resourceLayout

`open val resourceLayout: `[`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html)

Overrides [ButtonPlugin.resourceLayout](../-button-plugin/resource-layout.md)


text:
require (File.expand_path('./../../../spec_helper', __FILE__))
# For Ruby > 1.9.3, use this instead of require
# require_relative '../../spec_helper'

describe Dhl::Shipment do

  let(:shipment) { Dhl::Shipment.new }

  describe '#to_hash' do
    it "should return the expected hash structure" do
      Dhl.config.account = 123456789
      shipment.pickup_time = Time.parse("31/12/2013 16:45:10 GMT")
      shipment.pieces = 2
      shipment.description = 'General goods'
      shipment.domestic = true

      hash = shipment.to_hash
      hash.should == {
        shipment_info: {
          drop_off_type: "REGULAR_PICKUP",
          service_type: 'N',
          currency: 'EUR',
          unit_of_measurement: 'SI', # Or SU, UK, US
          account: 123456789
        },
        ship_timestamp: '2013-12-31T17:45:10GMT+01:00', # When is the shipment going to be ready for pickup?
        payment_info: 'DAP',
        international_detail: {
          commodities: {
            number_of_pieces: 2,
            description: 'General goods',
            country_of_manufacture: "IT"
          },
          content: "NON_DOCUMENTS"
        }
      }
    end

    # it "should raise an exception if not all required fields are present"
  end

end


text:
package ch.fhnw.ether.audio;

public class NullAudioTarget extends AbstractAudioTarget {
	private final int   numChannels;
	private final float sRate;
	private double      sTime;

	public NullAudioTarget(int numChannels, float sampleRate) {
		super(Thread.NORM_PRIORITY, false);
		this.numChannels = numChannels;
		this.sRate       = sampleRate;
	}

	@Override
	public void render() {
		sTime += getFrame().samples.length;
	}

	@Override
	public double getTime() {
		if(timebase != null) return timebase.getTime();
		return sTime / (getSampleRate() * getNumChannels());
	}

	@Override
	public int getNumChannels() {
		return numChannels;
	}

	@Override
	public float getSampleRate() {
		return sRate;
	}
}


text:
package com.goodworkalan.go.go.missing;

import com.goodworkalan.go.go.Command;
import com.goodworkalan.go.go.Commandable;
import com.goodworkalan.go.go.Environment;
import com.goodworkalan.go.go.commands.Snap;

/**
 * Test a missing message bundle.
 *
 * @author Alan Gutierrez
 */
@Command(parent = Snap.class)
public class NoBundleCommand implements Commandable {
    /**
     * Send a message to stderr.
     *
     * @param env
     *            The environment.
     */
    public void execute(Environment env) {
        env.verbose("a");
    }
}


text:
module Azure::StorageCache::Mgmt::V2019_11_01
  module Models
    #
    # Result of the request to list Caches. It contains a list of Caches and a
    # URL link to get the next set of results.
    #
    class CachesListResult

      include MsRestAzure

      include MsRest::JSONable
      # @return [String] URL to get the next set of Cache list results, if
      # there are any.
      attr_accessor :next_link

      # @return [Array<Cache>] List of Caches.
      attr_accessor :value

      # return [Proc] with next page method call.
      attr_accessor :next_method

      #
      # Gets the rest of the items for the request, enabling auto-pagination.
      #
      # @return [Array<Cache>] operation results.
      #
      def get_all_items
        items = @value
        page = self
        while page.next_link != nil && !page.next_link.strip.empty? do
          page = page.get_next_page
          items.concat(page.value)
        end
        items
      end

      #
      # Gets the next page of results.
      #
      # @return [CachesListResult] with next page content.
      #
      def get_next_page
        response = @next_method.call(@next_link).value! unless @next_method.nil?
        unless response.nil?
          @next_link = response.body.next_link
          @value = response.body.value
          self
        end
      end

      #
      # Mapper for CachesListResult class as Ruby Hash.
      # This will be used for serialization/deserialization.
      #
      def self.mapper()
        {
          client_side_validation: true,
          required: false,
          serialized_name: 'CachesListResult',
          type: {
            name: 'Composite',
            class_name: 'CachesListResult',
            model_properties: {
              next_link: {
                client_side_validation: true,
                required: false,
                serialized_name: 'nextLink',
                type: {
                  name: 'String'
                }
              },
              value: {
                client_side_validation: true,
                required: false,
                serialized_name: 'value',
                type: {
                  name: 'Sequence',
                  element: {
                      client_side_validation: true,
                      required: false,
                      serialized_name: 'CacheElementType',
                      type: {
                        name: 'Composite',
                        class_name: 'Cache'
                      }
                  }
                }
              }
            }
          }
        }
      end
    end
  end
end


text:
package com.fengjx.commons.system.security.shiro.cache;

import com.fengjx.commons.plugin.jedis.JedisUtils;
import com.fengjx.commons.utils.WebUtil;
import com.google.common.collect.Sets;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;

import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;

/**
 * 自定义授权缓存管理类
 *
 * @author fengjx
 * @version fengjx
 */
public class JedisCacheManager implements CacheManager {

    private String cacheKeyPrefix = "shiro_cache_";

    @Override
    public <K, V> Cache<K, V> getCache(String name) throws CacheException {
        return new JedisCache<K, V>(cacheKeyPrefix + name);
    }

    public String getCacheKeyPrefix() {
        return cacheKeyPrefix;
    }

    public void setCacheKeyPrefix(String cacheKeyPrefix) {
        this.cacheKeyPrefix = cacheKeyPrefix;
    }

    /**
     * 自定义授权缓存管理类
     *
     * @author ThinkGem
     * @version 2014-7-20
     */
    public class JedisCache<K, V> implements Cache<K, V> {

        private Logger logger = LoggerFactory.getLogger(getClass());

        private String cacheKeyName = null;

        public JedisCache(String cacheKeyName) {
            this.cacheKeyName = cacheKeyName;
            // if (!JedisUtils.exists(cacheKeyName)){
            // Map<String, Object> map = Maps.newHashMap();
            // JedisUtils.setObjectMap(cacheKeyName, map, 60 * 60 * 24);
            // }
            // logger.debug("Init: cacheKeyName {} ", cacheKeyName);
        }

        @SuppressWarnings("unchecked")
        @Override
        public V get(K key) throws CacheException {
            if (key == null) {
                return null;
            }
            V v;
            HttpServletRequest request = WebUtil.getRequest();
            if (request != null) {
                v = (V) request.getAttribute(cacheKeyName);
                if (v != null) {
                    return v;
                }
            }

            V value = null;
            Jedis jedis = null;
            try {
                jedis = JedisUtils.getResource();
                value = (V) JedisUtils.toObject(jedis.hget(JedisUtils.getBytesKey(cacheKeyName),
                        JedisUtils.getBytesKey(key)));
                logger.debug("get {} {} {}", cacheKeyName, key,
                        request != null ? request.getRequestURI() : "");
            } catch (Exception e) {
                logger.error("get {} {} {}", cacheKeyName, key,
                        request != null ? request.getRequestURI() : "", e);
            } finally {
                JedisUtils.returnResource(jedis);
            }

            if (request != null && value != null) {
                request.setAttribute(cacheKeyName, value);
            }

            return value;
        }

        @Override
        public V put(K key, V value) throws CacheException {
            if (key == null) {
                return null;
            }
            Jedis jedis = null;
            try {
                jedis = JedisUtils.getResource();
                jedis.hset(JedisUtils.getBytesKey(cacheKeyName), JedisUtils.getBytesKey(key),
                        JedisUtils.toBytes(value));
                logger.debug("put {} {} = {}", cacheKeyName, key, value);
            } catch (Exception e) {
                logger.error("put {} {}", cacheKeyName, key, e);
            } finally {
                JedisUtils.returnResource(jedis);
            }
            return value;
        }

        @SuppressWarnings("unchecked")
        @Override
        public V remove(K key) throws CacheException {
            V value = null;
            Jedis jedis = null;
            try {
                jedis = JedisUtils.getResource();
                value = (V) JedisUtils.toObject(jedis.hget(JedisUtils.getBytesKey(cacheKeyName),
                        JedisUtils.getBytesKey(key)));
                jedis.hdel(JedisUtils.getBytesKey(cacheKeyName), JedisUtils.getBytesKey(key));
                logger.debug("remove {} {}", cacheKeyName, key);
            } catch (Exception e) {
                logger.warn("remove {} {}", cacheKeyName, key, e);
            } finally {
                JedisUtils.returnResource(jedis);
            }
            return value;
        }

        @Override
        public void clear() throws CacheException {
            Jedis jedis = null;
            try {
                jedis = JedisUtils.getResource();
                jedis.hdel(JedisUtils.getBytesKey(cacheKeyName));
                logger.debug("clear {}", cacheKeyName);
            } catch (Exception e) {
                logger.error("clear {}", cacheKeyName, e);
            } finally {
                JedisUtils.returnResource(jedis);
            }
        }

        @Override
        public int size() {
            int size = 0;
            Jedis jedis = null;
            try {
                jedis = JedisUtils.getResource();
                size = jedis.hlen(JedisUtils.getBytesKey(cacheKeyName)).intValue();
                logger.debug("size {} {} ", cacheKeyName, size);
                return size;
            } catch (Exception e) {
                logger.error("clear {}", cacheKeyName, e);
            } finally {
                JedisUtils.returnResource(jedis);
            }
            return size;
        }

        @SuppressWarnings("unchecked")
        @Override
        public Set<K> keys() {
            Set<K> keys = Sets.newHashSet();
            Jedis jedis = null;
            try {
                jedis = JedisUtils.getResource();
                Set<byte[]> set = jedis.hkeys(JedisUtils.getBytesKey(cacheKeyName));
                for (byte[] key : set) {
                    keys.add((K) key);
                }
                logger.debug("keys {} {} ", cacheKeyName, keys);
                return keys;
            } catch (Exception e) {
                logger.error("keys {}", cacheKeyName, e);
            } finally {
                JedisUtils.returnResource(jedis);
            }
            return keys;
        }

        @SuppressWarnings("unchecked")
        @Override
        public Collection<V> values() {
            Collection<V> vals = Collections.emptyList();
            ;
            Jedis jedis = null;
            try {
                jedis = JedisUtils.getResource();
                Collection<byte[]> col = jedis.hvals(JedisUtils.getBytesKey(cacheKeyName));
                for (byte[] val : col) {
                    vals.add((V) val);
                }
                logger.debug("values {} {} ", cacheKeyName, vals);
                return vals;
            } catch (Exception e) {
                logger.error("values {}", cacheKeyName, e);
            } finally {
                JedisUtils.returnResource(jedis);
            }
            return vals;
        }
    }
}


text:
//
//  SubquestManager.cs
//
//  Author:
//       Benito Palacios <benito356@gmail.com>
//
//  Copyright (c) 2015 Benito Palacios
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
using System;
using Libgame;
using Libgame.IO;
using System.IO;
using System.Collections.Generic;

namespace Downlitor
{
	public class SubquestManager
	{
		const string FormatName = "SubQuest{0:D4}.sq";
        const string Folder = "subquests";
		static SubquestManager instance;

        private string[] entries;

		private SubquestManager()
		{
            Read();
		}

        public int NumEntries {
            get { return entries.Length; }
        }

        public int LastSubQuest {
            get { return 2030; }
        }

		public static SubquestManager Instance {
			get {
				if (instance == null)
					instance = new SubquestManager();

				return instance;
			}
		}

        public string GetEntry(int index) {
            if (index < 0 || index >= NumEntries)
                return null;

            return this.entries[index];
        }

        public int GetNumber(int index)
        {
            if (index < 0 || index >= NumEntries)
                return -1;

            string num = this.entries[index];
            return Convert.ToUInt16(num.Substring(0, 4));
        }

        public int GetIndex(int number)
        {
            for (int i = 0; i < NumEntries; i++)
                if (GetNumber(i) == number)
                    return i;

            return -1;
        }

        private void Read()
        {
            List<string> entries = new List<string>();
            for (int i = 0; i <= LastSubQuest; i++) {
                string title = GetTitle(i);
                if (!string.IsNullOrEmpty(title))
                    entries.Add(i.ToString("D4") + " - " + title);
            }

            this.entries = entries.ToArray();
        }

        public string GetTitle(int index)
		{
			if (index < 0)
				return null;

            var filename = Path.Combine(Folder, string.Format(FormatName, index));
            if (!File.Exists(filename))
                return null;

            var stream = new DataStream(filename, FileMode.Open, FileAccess.Read);
            var reader = new DataReader(stream,
                EndiannessMode.LittleEndian, System.Text.Encoding.GetEncoding("shift_jis"));
			stream.Seek(4, SeekMode.Origin);

			ushort nameLen = reader.ReadUInt16();
			string name = reader.ReadString(nameLen, "replace", false);

			stream.Dispose();
			return name;
		}
	}
}


text:
'use strict';

const cp = require('child_process');
const _ = require('underscore');

const commander = require('commander');

const batch = require('abacus-batch');
const throttle = require('abacus-throttle');
const request = require('abacus-request');
const router = require('abacus-router');
const express = require('abacus-express');
const dbclient = require('abacus-dbclient');

const map = _.map;
const range = _.range;
const clone = _.clone;
const omit = _.omit;

// Batch the requests
const brequest = batch(request);

// Setup the debug log
const debug = require('abacus-debug')('abacus-usage-meter-itest');

// Parse command line options
const argv = clone(process.argv);
argv.splice(1, 1, 'usage-meter-itest');
commander
  .option('-o, --orgs <n>', 'number of organizations', parseInt)
  .option('-i, --instances <n>', 'number of resource instances', parseInt)
  .option('-u, --usagedocs <n>', 'number of usage docs', parseInt)
  .option('-d, --day <d>',
    'usage time shift using number of days', parseInt)
  .option('-t, --start-timeout <n>',
    'external processes start timeout in milliseconds', parseInt)
  .option('-x, --total-timeout <n>',
    'test timeout in milliseconds', parseInt)
  .allowUnknownOption(true)
  .parse(argv);

// Number of organizations
const orgs = commander.orgs || 1;

// Number of resource instances
const resourceInstances = commander.instances || 1;

// Number of usage docs
const usage = commander.usagedocs || 1;

// Usage time shift by number of days in milli-seconds
const tshift = commander.day * 24 * 60 * 60 * 1000 || 0;

// External Abacus processes start timeout
const startTimeout = commander.startTimeout || 30000;

// This test timeout
const totalTimeout = commander.totalTimeout || 60000;

// Module directory
const moduleDir = (module) => {
  const path = require.resolve(module);
  return path.substr(0, path.indexOf(module + '/') + module.length);
};

describe('abacus-usage-meter-itest', () => {
  before((done) => {
    const start = (module) => {
      const c = cp.spawn('npm', ['run', 'start'],
        { cwd: moduleDir(module), env: clone(process.env) });

      // Add listeners to stdout, stderr and exit messsage and forward the
      // messages to debug logs
      c.stdout.on('data', (d) => process.stdout.write(d));
      c.stderr.on('data', (d) => process.stderr.write(d));
      c.on('exit', (c) => debug('Application exited with code %d', c));
    };

    const services = () => {
      // Start usage meter
      start('abacus-usage-meter');

      done();
    };

    // Start local database server
    if (!process.env.DB) {
      start('abacus-pouchserver');
      services();
    }
    else
      // Delete test dbs on the configured db server
      dbclient.drop(process.env.DB, /^abacus-meter-/, () => {
        services();
      });
  });

  after((done) => {
    let counter = process.env.DB ? 1 : 2;
    const finishCb = (module, code) => {
      counter--;
      debug('Module %s exited with code %d. Left %d modules',
        module, code, counter);
      if (counter === 0) {
        debug('All modules stopped. Exiting test');
        done();
      }
    };

    const stop = (module, cb) => {
      debug('Stopping %s in directory %s', module, moduleDir(module));
      const c = cp.spawn('npm', ['run', 'stop'],
        { cwd: moduleDir(module), env: clone(process.env) });

      // Add listeners to stdout, stderr and exit message and forward the
      // messages to debug logs
      c.stdout.on('data', (data) => process.stdout.write(data));
      c.stderr.on('data', (data) => process.stderr.write(data));
      c.on('exit', (code) => cb(module, code));
    };

    // Stop usage meter
    stop('abacus-usage-meter', finishCb);

    // Stop local database server
    if (!process.env.DB)
      stop('abacus-pouchserver', finishCb);
  });

  it('meter normalized usage submissions', function(done) {
    // Configure the test timeout based on the number of usage docs or
    // a predefined timeout
    const timeout = Math.max(totalTimeout,
      100 * orgs * resourceInstances * usage);
    this.timeout(timeout + 2000);
    const processingDeadline = Date.now() + timeout;

    // Setup accumulator spy
    const accumulate = spy((req, res, next) => {
      res.status(201).send();
    });

    // Start usage accumulator stub with the accumulate spy
    const app = express();
    const routes = router();
    routes.post('/v1/metering/metered/usage', accumulate);
    app.use(routes);
    app.use(router.batch(routes));
    app.listen(9200);

    // Initialize usage doc properties with unique values
    const start = 1435629365220 + tshift;
    const end = 1435629465220 + tshift;

    const oid = (o) => ['a3d7fe4d-3cb1-4cc3-a831-ffe98e20cf27',
      o + 1].join('-');
    const sid = (o, ri) => ['aaeae239-f3f8-483c-9dd0-de5d41c38b6a',
      o + 1].join('-');
    const cid = (o, ri) => ['bbeae239-f3f8-483c-9dd0-de6781c38bab',
      o + 1].join('-');
    const pid = (ri, u) => 'basic';
    const mpid = () => 'test-metering-plan';
    const rpid = () => 'test-rating-plan';
    const ppid = () => 'test-pricing-basic';

    const riid = (o, ri) => ['0b39fa70-a65f-4183-bae8-385633ca5c87',
      o + 1, ri + 1].join('-');

    const bid = (u) => [start, u + 1].join('-');

    // Measured usage for a given org, resource instance and usage #s
    const normalizedTemplate = (o, ri, u) => ({
      collected_usage_id: bid(u),
      start: start + u,
      end: end + u,
      organization_id: oid(o),
      space_id: sid(o, ri),
      resource_id: 'test-resource',
      resource_instance_id: riid(o, ri),
      plan_id: pid(ri, u),
      consumer_id: cid(o, ri),
      resource_type: 'test-resource',
      account_id: '1234',
      pricing_country: 'USA',
      metering_plan_id: mpid(),
      rating_plan_id: rpid(),
      pricing_plan_id: ppid(),
      prices: {
        metrics: [
          { name: 'storage',
            price: pid() === 'basic' ? 1 : 0.5 },
          { name: 'thousand_light_api_calls',
            price: pid() === 'basic' ? 0.03 : 0.04 },
          { name: 'heavy_api_calls',
            price: pid() === 'basic' ? 0.15 : 0.18 },
          { name: 'memory',
            price: pid() === 'basic' ? 0.00014 : 0.00028 }
        ]
      },
      measured_usage: [
        { measure: 'storage', quantity: 1073741824 },
        { measure: 'light_api_calls', quantity: 1000 },
        { measure: 'heavy_api_calls', quantity: 100 },
        { measure: 'current_instance_memory', quantity: 3221225472 },
        { measure: 'current_running_instances', quantity: 2 }
      ]
    });

    // Metered usage for given org, resource instance and usage #s
    // TODO check the values of the metered usage
    /*
    const meteredTemplate = (o, ri, u) => extend(normalizedTemplate(o, ri, u), {
      metered_usage: [
        { metric: 'storage', quantity: 1 },
        { metric: 'thousand_light_api_calls', quantity: 1 },
        { metric: 'heavy_api_calls', quantity: 100 },
        { metric: 'memory', quantity: { consuming: 6, since: start + u }}
      ]
    });
    */

    // Post a normalized usage doc, throttled to default concurrent requests
    const post = throttle((o, ri, u, cb) => {
      debug('Submit normalized usage for org%d instance%d usage%d',
        o + 1, ri + 1, u + 1);

      brequest.post('http://localhost::p/v1/metering/normalized/usage',
        { p: 9100, body: normalizedTemplate(o, ri, u) }, (err, val) => {
          expect(err).to.equal(undefined);
          expect(val.statusCode).to.equal(201);
          expect(val.headers.location).to.not.equal(undefined);

          debug('Normalized usage for org%d instance%d' +
            'usage%d, verifying it...', o + 1, ri + 1, u + 1);

          brequest.get(val.headers.location, undefined, (err, val) => {
            debug('Verify usage for org%d instance%d usage%d',
              o + 1, ri + 1, u + 1);

            expect(err).to.equal(undefined);
            expect(val.statusCode).to.equal(200);

            expect(omit(val.body, 'id', 'processed', 'processed_id'))
              .to.deep.equal(normalizedTemplate(o, ri, u));

            debug('Verified usage for org%d instance%d usage%d',
              o + 1, ri + 1, u + 1);

            cb();
          });
        });
    });

    // Post the requested number of normalized usage docs
    const submit = (done) => {
      let posts = 0;
      const cb = () => {
        if(++posts === orgs * resourceInstances * usage) done();
      };

      // Submit usage for all orgs and resource instances
      map(range(usage), (u) => map(range(resourceInstances),
        (ri) => map(range(orgs), (o) => post(o, ri, u, cb))));
    };

    const verifyAccumulator = (done) => {
      try {
        debug('Verifying accumulator calls %d to equal to %d',
          accumulate.callCount, orgs * resourceInstances * usage);

        // TODO check the values of the metered usage
        expect(accumulate.callCount).to.equal(orgs * resourceInstances * usage);
        done();
      }
      catch (e) {
        // If the comparison fails we'll be called again to retry
        // after 250 msec, but give up after deadline
        if(Date.now() >= processingDeadline) throw e;

        debug('Retry#%d', retries);
      }
    };

    // Wait for usage meter to start
    request.waitFor('http://localhost::p/batch',
      { p: 9100 }, startTimeout, (err, value) => {
        // Failed to ping usage meter before timing out
        if (err) throw err;

        // Submit normalized usage and verify
        submit(() => {
          const i = setInterval(() =>
            verifyAccumulator(() => done(clearInterval(i))), 250);
        });
      });
  });
});