blob: 3bfedb463a7fbe083a5dcc8daab583208b0fb3e0 [file] [log] [blame]
/*
* Copyright (c) 2018, 2021, 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 8263311
* @requires (os.family == "Windows")
* @summary Windows remote printer changes do not reflect in lookupPrintServices()
* @run main/manual 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 TIMEOUT = 15L * 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 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: Click Refresh."
+ "\"After\" list is populated with updated list "
+ "of printers.\n"
+ "Step 3: 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 4: Click Pass if the list of printers is correctly "
+ "updated.\n"
+ "Step 5: If the list is not updated, click Refresh again.\n"
+ "Step 6: 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 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 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(timeoutLabel)
)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING, true)
.addComponent(javaVersion)
.addComponent(timeLeft)
)
);
layout.setVerticalGroup(
layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(javaLabel)
.addComponent(javaVersion)
)
.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));
}
private static String formatTime(final long seconds) {
long minutes = seconds / 60;
return String.format("%d:%02d", minutes, seconds - minutes * 60);
}
}