test/jdk/java/awt/print/RemotePrinterStatusRefresh/RemotePrinterStatusRefresh.java
author phh
Sat, 30 Nov 2019 14:33:05 -0800
changeset 59330 5b96c12f909d
parent 57930 020f8fab32e2
permissions -rw-r--r--
8234541: C1 emits an empty message when it inlines successfully Summary: Use "inline" as the message when successfull Reviewed-by: thartmann, mdoerr Contributed-by: navy.xliu@gmail.com

/*
 * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 * @test
 * @bug 8153732 8212202 8221263 8221412 8222108
 * @requires (os.family == "Windows")
 * @summary Windows remote printer changes do not reflect in lookupPrintServices()
 * @run main/manual/othervm -Dsun.java2d.print.minRefreshTime=120 RemotePrinterStatusRefresh
 */

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.swing.AbstractListModel;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultListCellRenderer;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

import static javax.swing.BorderFactory.createTitledBorder;

public class RemotePrinterStatusRefresh extends WindowAdapter {

    private static final long DEFAULT_REFRESH_TIME = 240L;
    private static final long MINIMAL_REFRESH_TIME = 120L;

    private static final long refreshTime = getRefreshTime();

    private static final long TIMEOUT = refreshTime * 4 + 60;


    private static final CountDownLatch latch = new CountDownLatch(1);
    private static volatile RemotePrinterStatusRefresh test;

    private volatile boolean testResult;
    private volatile boolean testTimedOut;

    private final JFrame frame;

    private JButton refreshButton;
    private JButton passButton;
    private JButton failButton;

    private final ServiceItemListModel beforeList;
    private final ServiceItemListModel afterList;

    private JTextField nextRefresh;
    private JTextField timeLeft;

    private final Timer timer;
    private final long startTime;


    private static class ServiceItem {
        private enum State {
            REMOVED, UNCHANGED, ADDED
        }

        final String name;
        State state;

        private ServiceItem(final String name) {
            this.name = name;
            state = State.UNCHANGED;
        }

        @Override
        public String toString() {
            return name;
        }

        @Override
        public boolean equals(Object obj) {
            return (obj instanceof ServiceItem)
                    && ((ServiceItem) obj).name.equals(name);
        }

        @Override
        public int hashCode() {
            return name.hashCode();
        }
    }

    private static class ServiceItemListModel extends AbstractListModel<ServiceItem> {
        private final List<ServiceItem> list;

        private ServiceItemListModel(List<ServiceItem> list) {
            this.list = list;
        }

        @Override
        public int getSize() {
            return list.size();
        }

        @Override
        public ServiceItem getElementAt(int index) {
            return list.get(index);
        }

        private void refreshList(List<ServiceItem> newList) {
            list.clear();
            list.addAll(newList);
            fireChanged();
        }

        private void fireChanged() {
            fireContentsChanged(this, 0, list.size() - 1);
        }
    }

    private static class ServiceItemListRenderer extends DefaultListCellRenderer {
        @Override
        public Component getListCellRendererComponent(JList<?> list,
                                                      Object value,
                                                      int index,
                                                      boolean isSelected,
                                                      boolean cellHasFocus) {
            Component component =
                    super.getListCellRendererComponent(list, value, index,
                                                       isSelected, cellHasFocus);
            switch (((ServiceItem) value).state) {
                case REMOVED:
                    component.setBackground(Color.RED);
                    component.setForeground(Color.WHITE);
                    break;
                case ADDED:
                    component.setBackground(Color.GREEN);
                    component.setForeground(Color.BLACK);
                    break;
                case UNCHANGED:
                default:
                    break;
            }
            return component;
        }
    }

    private static final String INSTRUCTIONS_TEXT =
            "Please follow the steps for this manual test:\n"
                    + "Step 0: \"Before\" list is populated with currently "
                    +          "configured printers.\n"
                    + "Step 1: Add or Remove a network printer using "
                    +          "Windows Control Panel.\n"
                    + "Step 2: Wait for 2\u20134 minutes after adding or removing.\n"
                    + "             \"Next printer refresh in\" gives you a "
                    +          "rough estimation on when update will happen.\n"
                    + "Step 3: Click Refresh."
                    +          "\"After\" list is populated with updated list "
                    +          "of printers.\n"
                    + "Step 4: Compare the list of printers in \"Before\" and "
                    +          "\"After\" lists.\n"
                    + "              Added printers are highlighted with "
                    +               "green color, removed ones \u2014 with "
                    +               "red color.\n"
                    + "Step 5: Click Pass if the list of printers is correctly "
                    +          "updated.\n"
                    + "Step 6: If the list is not updated, wait for another "
                    +          "2\u20134 minutes, and then click Refresh again.\n"
                    + "Step 7: If the list does not update, click Fail.\n"
                    + "\n"
                    + "You have to click Refresh to enable Pass and Fail buttons. "
                    + "If no button is pressed,\n"
                    + "the test will time out. "
                    + "Closing the window also fails the test.";

    public static void main(String[] args) throws Exception {
        SwingUtilities.invokeAndWait(RemotePrinterStatusRefresh::createUI);

        latch.await();
        if (!test.testResult) {
            throw new RuntimeException("Test failed"
                + (test.testTimedOut ? " because of time out" : ""));
        }
    }

    private static long getRefreshTime() {
        String refreshTime =
                System.getProperty("sun.java2d.print.minRefreshTime",
                                   Long.toString(DEFAULT_REFRESH_TIME));
        try {
            long value = Long.parseLong(refreshTime);
            return value < MINIMAL_REFRESH_TIME ? MINIMAL_REFRESH_TIME : value;
        } catch (NumberFormatException e) {
            return DEFAULT_REFRESH_TIME;
        }
    }

    private static void createUI() {
        test = new RemotePrinterStatusRefresh();
    }

    private RemotePrinterStatusRefresh() {
        frame = new JFrame("RemotePrinterStatusRefresh");
        frame.addWindowListener(this);


        JPanel northPanel = new JPanel(new BorderLayout());
        northPanel.add(createInfoPanel(), BorderLayout.NORTH);
        northPanel.add(createInstructionsPanel(), BorderLayout.SOUTH);


        beforeList = new ServiceItemListModel(
                Collections.unmodifiableList(collectPrinterList()));
        afterList = new ServiceItemListModel(new ArrayList<>());
        logList("Before:", beforeList.list);

        JPanel listPanel = new JPanel(new GridLayout(1, 2));
        listPanel.setBorder(createTitledBorder("Print Services"));
        listPanel.add(createListPanel(beforeList, "Before:", 'b'));
        listPanel.add(createListPanel(afterList, "After:", 'a'));


        JPanel mainPanel = new JPanel(new BorderLayout());
        mainPanel.add(northPanel, BorderLayout.NORTH);
        mainPanel.add(listPanel, BorderLayout.CENTER);
        mainPanel.add(createButtonPanel(), BorderLayout.SOUTH);


        frame.add(mainPanel);
        frame.pack();
        refreshButton.requestFocusInWindow();
        frame.setVisible(true);


        timer = new Timer(1000, this::updateTimeLeft);
        timer.start();
        startTime = System.currentTimeMillis();
        updateTimeLeft(null);
    }

    private JPanel createInfoPanel() {
        JLabel javaLabel = new JLabel("Java version:");
        JTextField javaVersion =
                new JTextField(System.getProperty("java.runtime.version"));
        javaVersion.setEditable(false);
        javaLabel.setLabelFor(javaVersion);

        JLabel refreshTimeLabel = new JLabel("Refresh interval:");
        long minutes = refreshTime / 60;
        long seconds = refreshTime % 60;
        String interval = String.format("%1$d seconds%2$s",
                refreshTime,
                minutes > 0
                    ? String.format(" (%1$d %2$s%3$s)",
                        minutes,
                        minutes > 1 ? "minutes" : "minute",
                        seconds > 0
                            ? String.format(" %1$d %2$s",
                                seconds,
                                seconds > 1 ? "seconds" : "second")
                            : "")
                    : ""
        );
        JTextField refreshInterval = new JTextField(interval);
        refreshInterval.setEditable(false);
        refreshTimeLabel.setLabelFor(refreshInterval);

        JLabel nextRefreshLabel = new JLabel("Next printer refresh in:");
        nextRefresh = new JTextField();
        nextRefresh.setEditable(false);
        nextRefreshLabel.setLabelFor(nextRefresh);

        JLabel timeoutLabel = new JLabel("Time left:");
        timeLeft = new JTextField();
        timeLeft.setEditable(false);
        timeoutLabel.setLabelFor(timeLeft);

        JPanel infoPanel = new JPanel();
        GroupLayout layout = new GroupLayout(infoPanel);
        infoPanel.setLayout(layout);
        infoPanel.setBorder(BorderFactory.createTitledBorder("Info"));
        layout.setAutoCreateGaps(true);
        layout.setHorizontalGroup(
            layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
                    .addComponent(javaLabel)
                    .addComponent(refreshTimeLabel)
                    .addComponent(nextRefreshLabel)
                    .addComponent(timeoutLabel)
                )
                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING, true)
                    .addComponent(javaVersion)
                    .addComponent(refreshInterval)
                    .addComponent(nextRefresh)
                    .addComponent(timeLeft)
                )
        );
        layout.setVerticalGroup(
            layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(javaLabel)
                    .addComponent(javaVersion)
                )
                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(refreshTimeLabel)
                    .addComponent(refreshInterval))
                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(nextRefreshLabel)
                    .addComponent(nextRefresh))
                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(timeoutLabel)
                    .addComponent(timeLeft))
        );
        return infoPanel;
    }

    private JPanel createInstructionsPanel() {
        JPanel instructionsPanel = new JPanel(new BorderLayout());
        JTextArea instructionText = new JTextArea(INSTRUCTIONS_TEXT);
        instructionText.setEditable(false);
        instructionsPanel.setBorder(createTitledBorder("Test Instructions"));
        instructionsPanel.add(new JScrollPane(instructionText));
        return  instructionsPanel;
    }

    private JPanel createListPanel(final ServiceItemListModel model,
                                   final String title,
                                   final char mnemonic) {
        JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
        JList<ServiceItem> list = new JList<>(model);
        list.setCellRenderer(new ServiceItemListRenderer());

        JLabel label = new JLabel(title);
        label.setLabelFor(list);
        label.setDisplayedMnemonic(mnemonic);
        JPanel labelPanel = new JPanel();
        labelPanel.setLayout(new BoxLayout(labelPanel, BoxLayout.X_AXIS));
        labelPanel.add(label, BorderLayout.EAST);
        labelPanel.add(Box.createHorizontalGlue());

        panel.add(labelPanel);
        panel.add(new JScrollPane(list));
        return panel;
    }

    private JPanel createButtonPanel() {
        refreshButton = new JButton("Refresh");
        refreshButton.addActionListener(this::refresh);

        passButton = new JButton("Pass");
        passButton.addActionListener(this::pass);
        passButton.setEnabled(false);

        failButton = new JButton("Fail");
        failButton.addActionListener(this::fail);
        failButton.setEnabled(false);

        JPanel buttonPanel = new JPanel();
        buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
        buttonPanel.add(Box.createHorizontalGlue());
        buttonPanel.add(refreshButton);
        buttonPanel.add(passButton);
        buttonPanel.add(failButton);
        buttonPanel.add(Box.createHorizontalGlue());
        return buttonPanel;
    }

    private static List<ServiceItem> collectPrinterList() {
        PrintService[] printServices = PrintServiceLookup.lookupPrintServices(null, null);
        List<ServiceItem> list = new ArrayList<>(printServices.length);
        for (PrintService service : printServices) {
            list.add(new ServiceItem(service.getName()));
        }
        return list;
    }

    private static void logList(final String title, final List<ServiceItem> list) {
        System.out.println(title);
        for (ServiceItem item : list) {
            System.out.println(item.name);
        }
        System.out.println();
    }

    private static void compareLists(final ServiceItemListModel before, final ServiceItemListModel after) {
        boolean beforeUpdated = false;
        boolean afterUpdated = false;

        for (ServiceItem item : before.list) {
            if (!after.list.contains(item)) {
                item.state = ServiceItem.State.REMOVED;
                beforeUpdated = true;
            } else if (item.state != ServiceItem.State.UNCHANGED) {
                item.state = ServiceItem.State.UNCHANGED;
                beforeUpdated = true;
            }
        }

        for (ServiceItem item : after.list) {
            if (!before.list.contains(item)) {
                item.state = ServiceItem.State.ADDED;
                afterUpdated = true;
            } else if (item.state != ServiceItem.State.UNCHANGED) {
                item.state = ServiceItem.State.UNCHANGED;
                afterUpdated = true;
            }
        }

        if (beforeUpdated) {
            before.fireChanged();
        }
        if (afterUpdated) {
            after.fireChanged();
        }
    }

    @Override
    public void windowClosing(WindowEvent e) {
        System.out.println("The window closed");
        disposeUI();
    }

    private void disposeUI() {
        timer.stop();
        latch.countDown();
        frame.dispose();
    }

    @SuppressWarnings("unused")
    private void refresh(ActionEvent e) {
        System.out.println("Refresh button pressed");
        afterList.refreshList(collectPrinterList());
        compareLists(beforeList, afterList);
        passButton.setEnabled(true);
        failButton.setEnabled(true);
        logList("After:", afterList.list);
    }

    @SuppressWarnings("unused")
    private void pass(ActionEvent e) {
        System.out.println("Pass button pressed");
        testResult = true;
        disposeUI();
    }

    @SuppressWarnings("unused")
    private void fail(ActionEvent e) {
        System.out.println("Fail button pressed");
        testResult = false;
        disposeUI();
    }

    @SuppressWarnings("unused")
    private void updateTimeLeft(ActionEvent e) {
        long elapsed = (System.currentTimeMillis() - startTime) / 1000;
        long left = TIMEOUT - elapsed;
        if (left < 0) {
            testTimedOut = true;
            disposeUI();
        }
        timeLeft.setText(formatTime(left));
        nextRefresh.setText(formatTime(refreshTime - (elapsed % refreshTime)));
    }

    private static String formatTime(final long seconds) {
        long minutes = seconds / 60;
        return String.format("%d:%02d", minutes, seconds - minutes * 60);
    }

}