/* * Copyright (c) 2019, 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. */ import java.io.IOException; import java.net.DatagramSocket; import java.net.MulticastSocket; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketOption; import java.nio.channels.DatagramChannel; import java.nio.channels.NetworkChannel; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Set; import java.util.stream.Stream; import org.testng.annotations.Test; import org.testng.annotations.DataProvider; import static java.net.StandardSocketOptions.*; /* * @test * @bug 8235141 * @summary verifies that our implementation supports the set * of SocketOptions that are required by the API documentation. * @run testng/othervm RequiredOptions */ public class RequiredOptions { static final Set> DATAGRAM_OPTIONS = Set.of(SO_BROADCAST, SO_SNDBUF, SO_RCVBUF, SO_REUSEADDR, IP_TOS); static final Set> MULTICAST_OPTIONS = concat(DATAGRAM_OPTIONS, Set.of(IP_MULTICAST_IF, IP_MULTICAST_LOOP, IP_MULTICAST_TTL)); static final Set> SOCKET_OPTIONS = Set.of(SO_KEEPALIVE, SO_LINGER, SO_SNDBUF, SO_RCVBUF, SO_REUSEADDR, TCP_NODELAY); static final Set> SERVER_OPTIONS = Set.of(SO_RCVBUF, SO_REUSEADDR); static Set> concat(Set> ...options) { return Set.of(Stream.of(options).flatMap(Set::stream).distinct().toArray(SocketOption[]::new)); } @DataProvider(name = "sockets") static Object[][] provider() throws IOException { return new Object[][] { // UDP { Configurable.of(new DatagramSocket(null)), DATAGRAM_OPTIONS }, { Configurable.of(new MulticastSocket(null)), MULTICAST_OPTIONS }, // TCP { Configurable.of(new Socket()), SOCKET_OPTIONS }, { Configurable.of(new ServerSocket()), SERVER_OPTIONS }, // Adaptors { Configurable.of(DatagramChannel.open().socket()), MULTICAST_OPTIONS }, { Configurable.of(SocketChannel.open().socket()), SOCKET_OPTIONS }, { Configurable.of(ServerSocketChannel.open().socket()), SERVER_OPTIONS }, }; } @Test(dataProvider = "sockets") public void test(Configurable socket, Set> options) throws E { try (var s = socket) { var impl = socket.socket().getClass(); System.out.println("Testing " + impl + " with " + options); Set> supported = socket.supportedOptions(); if (!supported.containsAll(options)) { for (var option : options) { if (!supported.contains(option)) { System.err.println("Option " + option + " not supported by " + impl); } } throw new AssertionError("Not all documented options are supported by " + impl); } } } static interface Configurable extends AutoCloseable { R setOption(SocketOption name, T value) throws E; T getOption(SocketOption name) throws E; Set> supportedOptions() throws E; R socket(); void close() throws E; static Configurable of(DatagramSocket socket) { return new ConfigurableImpl<>(socket, socket::setOption, socket::getOption, socket::supportedOptions, socket::close); } static Configurable of(Socket socket) { return new ConfigurableImpl<>(socket, socket::setOption, socket::getOption, socket::supportedOptions, socket::close); } static Configurable of(ServerSocket socket) { return new ConfigurableImpl<>(socket, socket::setOption, socket::getOption, socket::supportedOptions, socket::close); } } static final class ConfigurableImpl implements Configurable { @FunctionalInterface interface SetOption { R setOption(SocketOption name, T value) throws E; } @FunctionalInterface interface GetOption { T getOption(SocketOption name) throws E; } @FunctionalInterface interface SupportedOption { Set> supportedOptions() throws E; } @FunctionalInterface interface Closer { void close() throws E; } private final R socket; private final SetOption setter; private final GetOption getter; private final SupportedOption support; private final Closer closer; public ConfigurableImpl(R socket, SetOption setter, GetOption getter, SupportedOption support, Closer closer) { this.socket = socket; this.setter = setter; this.getter = getter; this.support = support; this.closer = closer; } @Override public R setOption(SocketOption name, T value) throws E { return setter.setOption(name, value); } @Override public T getOption(SocketOption name) throws E { return getter.getOption(name); } @Override public Set> supportedOptions() throws E { return support.supportedOptions(); } @Override public R socket() { return socket; } @Override public void close() throws E { closer.close(); } } }