8221263: [TEST_BUG] RemotePrinterStatusRefresh test is hard to use
authoraivanov
Tue, 09 Apr 2019 08:50:08 +0100
changeset 54505 7f53d59593e2
parent 54504 55b0469425e1
child 54506 8b3b89320d03
8221263: [TEST_BUG] RemotePrinterStatusRefresh test is hard to use Reviewed-by: serb, prr
test/jdk/java/awt/print/RemotePrinterStatusRefresh/RemotePrinterStatusRefresh.java
--- 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);
+    }
+
 }