--- old/test/jdk/java/awt/print/RemotePrinterStatusRefresh/RemotePrinterStatusRefresh.java 2019-03-29 15:00:56.807886900 +0000 +++ new/test/jdk/java/awt/print/RemotePrinterStatusRefresh/RemotePrinterStatusRefresh.java 2019-03-29 15:00:55.451248200 +0000 @@ -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; -public class RemotePrinterStatusRefresh -{ - private static TestUI test = null; - public static void main(String args[]) throws Exception { - final CountDownLatch latch = new CountDownLatch(1); - - // Test UI creation - test = new TestUI(latch); - - SwingUtilities.invokeAndWait(new Runnable() { - @Override - public void run() { - try { - test.createUI(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - }); +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; + + + 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; - // RemotePrinterStatusRefresh creation - RemotePrinterStatusRefresh RemotePrinterStatusRefresh = new RemotePrinterStatusRefresh(); - SwingUtilities.invokeAndWait(() -> { - collectPrintersList(test.resultsTextArea, true); - }); + private JButton refreshButton; + private JButton passButton; + private JButton failButton; - // 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!!"); + 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; } - boolean status = latch.await(1, TimeUnit.MINUTES); - if (!status) { - dispose(); - throw new RuntimeException("Test timed out."); + @Override + public String toString() { + return name; } - if (test.testResult == false) { - dispose(); - throw new RuntimeException("Test Failed."); + @Override + public boolean equals(Object obj) { + return (obj instanceof ServiceItem) + && ((ServiceItem) obj).name.equals(name); } - dispose(); + @Override + public int hashCode() { + return name.hashCode(); + } } - public static void dispose() throws Exception { - SwingUtilities.invokeAndWait(() -> { - test.disposeUI(); - }); - } + private static class ServiceItemListModel extends AbstractListModel { + private final List list; - public static boolean waitForFlag (long maxTimeoutInMsec) throws Exception { - while(!test.isAdded && maxTimeoutInMsec > 0) { - maxTimeoutInMsec -= 100; - Thread.sleep(100); + private ServiceItemListModel(List list) { + this.list = list; } - if(maxTimeoutInMsec <= 0) { - return false; - } else { - return true; + @Override + public int getSize() { + return list.size(); + } + + @Override + public ServiceItem getElementAt(int index) { + return list.get(index); + } + + private void refreshList(List newList) { + list.clear(); + list.addAll(newList); + fireChanged(); + } + + private void fireChanged() { + fireContentsChanged(this, 0, list.size() - 1); } } - 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 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; } } -} -class TestUI { - private static JFrame mainFrame; - private static JPanel mainControlPanel; - - private static JTextArea instructionTextArea; - - private static JPanel resultButtonPanel; - private static JButton passButton; - private static JButton failButton; - private static JButton addedButton; - - private static JPanel testPanel; - private static JButton testButton; - private static JLabel buttonPressCountLabel; - - 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; - } - - 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")); - - 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); - - addedButton = new JButton("Printer Added/Removed"); - addedButton.setActionCommand("Added"); - addedButton.addActionListener((ActionEvent e) -> { - System.out.println("Added Button pressed!"); - isAdded = true; - }); + 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" : "")); + } + } + + 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 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 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); - // Create resultButtonPanel with Pass, Fail buttons 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); + 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 = 1; - gbc.gridy = 0; - resultButtonPanel.add(passButton, gbc); + private static List collectPrinterList() { + PrintService[] printServices = PrintServiceLookup.lookupPrintServices(null, null); + List list = new ArrayList<>(printServices.length); + for (PrintService service : printServices) { + list.add(new ServiceItem(service.getName())); + } + return list; + } - gbc.gridx = 2; - gbc.gridy = 0; - resultButtonPanel.add(failButton, gbc); + private static void logList(final String title, final List list) { + System.out.println(title); + for (ServiceItem item : list) { + System.out.println(item.name); + } + System.out.println(); + } - resultsTextArea = new JTextArea(); - resultsTextArea.setEditable(false); - resultsTextArea.setBorder(BorderFactory. - createTitledBorder("Results")); + 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); + } - gbc.gridx = 0; - gbc.gridy = 1; - gbc.fill = GridBagConstraints.HORIZONTAL; - mainControlPanel.add(resultsTextArea, gbc); + @SuppressWarnings("unused") + private void pass(ActionEvent e) { + System.out.println("Pass button pressed"); + testResult = true; + disposeUI(); + } - gbc.gridx = 0; - gbc.gridy = 2; - mainControlPanel.add(resultButtonPanel, gbc); + @SuppressWarnings("unused") + private void fail(ActionEvent e) { + System.out.println("Fail button pressed"); + testResult = false; + disposeUI(); + } - mainFrame.add(mainControlPanel); - mainFrame.pack(); - mainFrame.setVisible(true); + @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))); } - public void disposeUI() { - mainFrame.dispose(); + private static String formatTime(final long seconds) { + long minutes = seconds / 60; + return String.format("%d:%02d", minutes, seconds - minutes * 60); } + }