--- a/test/jdk/java/awt/print/RemotePrinterStatusRefresh/RemotePrinterStatusRefresh.java Mon Apr 08 14:48:35 2019 -0700
+++ b/test/jdk/java/awt/print/RemotePrinterStatusRefresh/RemotePrinterStatusRefresh.java Tue Apr 09 08:50:08 2019 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -21,244 +21,480 @@
* questions.
*/
-/**
+/*
* @test
- * @bug 8153732 8212202
+ * @bug 8153732 8212202 8221263 8221412
* @requires (os.family == "Windows")
* @summary Windows remote printer changes do not reflect in lookupPrintServices()
- * @ignore Requires a new network printer installation\removal
* @run main/manual RemotePrinterStatusRefresh
*/
-import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.GridLayout;
import java.awt.event.ActionEvent;
-import java.awt.print.PageFormat;
-import java.awt.print.Paper;
-import java.awt.print.PrinterException;
+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 java.util.concurrent.TimeUnit;
+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 java.awt.print.PrinterJob;
-import javax.print.PrintService;
+import javax.swing.Timer;
+
+import static javax.swing.BorderFactory.createTitledBorder;
+
+public class RemotePrinterStatusRefresh extends WindowAdapter {
+
+ private static final long refreshTime = getRefreshTime();
+
+ private static final long TIMEOUT = refreshTime * 4 + 60;
+
-public class RemotePrinterStatusRefresh
-{
- private static TestUI test = null;
- public static void main(String args[]) throws Exception {
- final CountDownLatch latch = new CountDownLatch(1);
+ 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;
- // Test UI creation
- test = new TestUI(latch);
+ 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;
+
- SwingUtilities.invokeAndWait(new Runnable() {
- @Override
- public void run() {
- try {
- test.createUI();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- });
+ 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;
+ }
- // RemotePrinterStatusRefresh creation
- RemotePrinterStatusRefresh RemotePrinterStatusRefresh = new RemotePrinterStatusRefresh();
- SwingUtilities.invokeAndWait(() -> {
- collectPrintersList(test.resultsTextArea, true);
- });
+ @Override
+ public boolean equals(Object obj) {
+ return (obj instanceof ServiceItem)
+ && ((ServiceItem) obj).name.equals(name);
+ }
- // 8 min = 480000 msec
- if(waitForFlag(480000)) {
- SwingUtilities.invokeAndWait(() -> {
- collectPrintersList(test.resultsTextArea, false);
- });
- } else {
- dispose();
- throw new RuntimeException("No new network printer got added/removed!! Test timed out!!");
+ @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();
}
- boolean status = latch.await(1, TimeUnit.MINUTES);
- if (!status) {
- dispose();
- throw new RuntimeException("Test timed out.");
+ @Override
+ public ServiceItem getElementAt(int index) {
+ return list.get(index);
+ }
+
+ private void refreshList(List<ServiceItem> newList) {
+ list.clear();
+ list.addAll(newList);
+ fireChanged();
}
- if (test.testResult == false) {
- dispose();
- throw new RuntimeException("Test Failed.");
+ private void fireChanged() {
+ fireContentsChanged(this, 0, list.size() - 1);
}
+ }
- dispose();
+ 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;
+ }
}
- public static void dispose() throws Exception {
- SwingUtilities.invokeAndWait(() -> {
- test.disposeUI();
- });
+ 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 4 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 "
+ + "4 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" : ""));
+ }
}
- public static boolean waitForFlag (long maxTimeoutInMsec) throws Exception {
- while(!test.isAdded && maxTimeoutInMsec > 0) {
- maxTimeoutInMsec -= 100;
- Thread.sleep(100);
- }
-
- if(maxTimeoutInMsec <= 0) {
- return false;
- } else {
- return true;
+ private static long getRefreshTime() {
+ String refreshTime =
+ System.getProperty("sun.java2d.print.minRefreshTime", "240");
+ try {
+ long value = Long.parseLong(refreshTime);
+ return value < 240L ? 240L : value;
+ } catch (NumberFormatException e) {
+ return 240L;
}
}
- private static void collectPrintersList(JTextArea textArea, boolean before) {
- if(before) {
- System.out.println("List of printers(before): ");
- textArea.setText("List of printers(before): \n");
- for (PrintService printServiceBefore : PrinterJob.lookupPrintServices()) {
- System.out.println(printServiceBefore);
- textArea.append(printServiceBefore.toString());
- textArea.append("\n");
- }
- } else {
- textArea.append("\n");
- System.out.println("List of printers(after): ");
- textArea.append("List of printers(after): \n");
- for (PrintService printServiceAfter : PrinterJob.lookupPrintServices()) {
- System.out.println(printServiceAfter);
- textArea.append(printServiceAfter.toString());
- textArea.append("\n");
- }
- }
+ 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);
-class TestUI {
- private static JFrame mainFrame;
- private static JPanel mainControlPanel;
+ 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);
- private static JTextArea instructionTextArea;
+ JLabel nextRefreshLabel = new JLabel("Next printer refresh in:");
+ nextRefresh = new JTextField();
+ nextRefresh.setEditable(false);
+ nextRefreshLabel.setLabelFor(nextRefresh);
- private static JPanel resultButtonPanel;
- private static JButton passButton;
- private static JButton failButton;
- private static JButton addedButton;
+ JLabel timeoutLabel = new JLabel("Time left:");
+ timeLeft = new JTextField();
+ timeLeft.setEditable(false);
+ timeoutLabel.setLabelFor(timeLeft);
- private static JPanel testPanel;
- private static JButton testButton;
- private static JLabel buttonPressCountLabel;
+ 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 static GridBagLayout layout;
- private final CountDownLatch latch;
- public boolean testResult = false;
- public volatile Boolean isAdded = false;
- public static JTextArea resultsTextArea;
-
- public TestUI(CountDownLatch latch) throws Exception {
- this.latch = latch;
+ 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;
}
- public final void createUI() {
- mainFrame = new JFrame("RemotePrinterStatusRefresh");
- layout = new GridBagLayout();
- mainControlPanel = new JPanel(layout);
- resultButtonPanel = new JPanel(layout);
- testPanel = new JPanel(layout);
- GridBagConstraints gbc = new GridBagConstraints();
-
- // Create Test instructions
- String instructions
- = "This test displays the current list of printers(before) attached to \n"
- + "this computer in the results panel.\n\n"
- + "Please follow the below steps for this manual test\n"
- + "--------------------------------------------------------------------\n"
- + "Step 1: Add/Remove a new network printer and Wait for 4 minutes after adding/removing\n"
- + "Step 2: Then click on 'Printer Added/Removed' button\n"
- + "Step 2: Once the new network printer is added/removed, see if it is \n"
- + " the same as displayed/not displayed in the results panel.\n"
- + "Step 3: If displayed/not displayed, then click 'Pass' else click on 'Fail' button";
-
- instructionTextArea = new JTextArea();
- instructionTextArea.setText(instructions);
- instructionTextArea.setEditable(false);
- instructionTextArea.setBorder(BorderFactory.
- createTitledBorder("Test Instructions"));
+ 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());
- gbc.gridx = 0;
- gbc.gridy = 0;
- gbc.fill = GridBagConstraints.HORIZONTAL;
- mainControlPanel.add(instructionTextArea, gbc);
-
- gbc.gridx = 0;
- gbc.gridy = 1;
- testPanel.add(Box.createVerticalStrut(50));
- mainControlPanel.add(testPanel);
+ 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());
- addedButton = new JButton("Printer Added/Removed");
- addedButton.setActionCommand("Added");
- addedButton.addActionListener((ActionEvent e) -> {
- System.out.println("Added Button pressed!");
- isAdded = true;
- });
+ panel.add(labelPanel);
+ panel.add(new JScrollPane(list));
+ return panel;
+ }
- // Create resultButtonPanel with Pass, Fail buttons
+ private JPanel createButtonPanel() {
+ refreshButton = new JButton("Refresh");
+ refreshButton.addActionListener(this::refresh);
+
passButton = new JButton("Pass");
- passButton.setActionCommand("Pass");
- passButton.addActionListener((ActionEvent e) -> {
- System.out.println("Pass Button pressed!");
- testResult = true;
- latch.countDown();
- disposeUI();
- });
+ passButton.addActionListener(this::pass);
+ passButton.setEnabled(false);
failButton = new JButton("Fail");
- failButton.setActionCommand("Fail");
- failButton.addActionListener((ActionEvent e) -> {
- System.out.println("Fail Button pressed!");
- testResult = false;
- latch.countDown();
- disposeUI();
- });
+ failButton.addActionListener(this::fail);
+ failButton.setEnabled(false);
- gbc.gridx = 0;
- gbc.gridy = 0;
- resultButtonPanel.add(addedButton, gbc);
-
- gbc.gridx = 1;
- gbc.gridy = 0;
- resultButtonPanel.add(passButton, gbc);
+ 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;
+ }
- gbc.gridx = 2;
- gbc.gridy = 0;
- resultButtonPanel.add(failButton, gbc);
-
- resultsTextArea = new JTextArea();
- resultsTextArea.setEditable(false);
- resultsTextArea.setBorder(BorderFactory.
- createTitledBorder("Results"));
+ 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;
+ }
- gbc.gridx = 0;
- gbc.gridy = 1;
- gbc.fill = GridBagConstraints.HORIZONTAL;
- mainControlPanel.add(resultsTextArea, gbc);
-
- gbc.gridx = 0;
- gbc.gridy = 2;
- mainControlPanel.add(resultButtonPanel, gbc);
-
- mainFrame.add(mainControlPanel);
- mainFrame.pack();
- mainFrame.setVisible(true);
+ 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();
}
- public void disposeUI() {
- mainFrame.dispose();
+ 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);
+ }
+
}