/* * Copyright (c) 2016, 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. */ package jdk.incubator.http.internal.websocket; import org.testng.SkipException; import org.testng.annotations.Test; import jdk.incubator.http.internal.websocket.Frame.Opcode; import java.io.IOException; import java.net.ProtocolException; import jdk.incubator.http.WebSocket; import java.nio.ByteBuffer; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import static jdk.incubator.http.internal.websocket.TestSupport.Expectation.ifExpect; import static jdk.incubator.http.internal.websocket.TestSupport.assertCompletesExceptionally; import static jdk.incubator.http.internal.websocket.TestSupport.checkExpectations; import static org.testng.Assert.assertSame; /* * Examines sendPing/onPing contracts */ public final class PingTest { /* * sendPing(message) is invoked. If the `message` argument is illegal, then * the method must throw an appropriate exception. Otherwise no exception * must be thrown. */ // @Test(dataProvider = "outgoingData", dataProviderClass = DataProviders.class) public void testSendPingArguments(ByteBuffer message) { WebSocket ws = newWebSocket(); ifExpect( message == null, NullPointerException.class::isInstance) .orExpect( message != null && message.remaining() > 125, IllegalArgumentException.class::isInstance) .assertThrows( () -> ws.sendPing(message) ); } /* * sendPing(message) with a legal argument has been invoked, then: * * 1. A Ping message with the same payload appears on the wire * 2. The CF returned from the method completes normally with the same * WebSocket that sendPing has been called on */ @Test(dataProvider = "outgoingData", dataProviderClass = DataProviders.class) public void testSendPingWysiwyg(ByteBuffer message) throws ExecutionException, InterruptedException { if (message == null || message.remaining() > 125) { return; } ByteBuffer snapshot = copy(message); MockChannel channel = new MockChannel.Builder() .expectPing(snapshot::equals) .build(); WebSocket ws = newWebSocket(channel); CompletableFuture<WebSocket> cf = ws.sendPing(message); WebSocket ws1 = cf.join(); assertSame(ws1, ws); // (2) checkExpectations(channel); // (1) } /* * If an I/O error occurs while Ping messages is being sent, then: * * 1. The CF returned from sendPing completes exceptionally with this I/O * error as the cause */ // @Test public void testSendPingIOException() { MockChannel ch = new MockChannel.Builder() // .provideWriteException(IOException::new) .build(); WebSocket ws = newWebSocket(ch); CompletableFuture<WebSocket> cf = ws.sendPing(ByteBuffer.allocate(16)); assertCompletesExceptionally(IOException.class, cf); } /* * If an incorrect Ping frame appears on the wire, then: * * 1. onError with the java.net.ProtocolException is invoked * 1. A Close frame with status code 1002 appears on the wire */ // @Test(dataProvider = "incorrectFrame", dataProviderClass = DataProviders.class) public void testOnPingIncorrect(boolean fin, boolean rsv1, boolean rsv2, boolean rsv3, ByteBuffer data) { if (fin && !rsv1 && !rsv2 && !rsv3 && data.remaining() <= 125) { throw new SkipException("Correct frame"); } CompletableFuture<WebSocket> webSocket = new CompletableFuture<>(); MockChannel channel = new MockChannel.Builder() .provideFrame(fin, rsv1, rsv2, rsv3, Opcode.PING, data) .expectClose((code, reason) -> Integer.valueOf(1002).equals(code) && "".equals(reason)) .build(); MockListener listener = new MockListener.Builder() .expectOnOpen((ws) -> true) .expectOnError((ws, error) -> error instanceof ProtocolException) .build(); webSocket.complete(newWebSocket(channel, listener)); checkExpectations(500, TimeUnit.MILLISECONDS, channel, listener); } /* * If a Ping message has been read off the wire, then: * * 1. onPing is invoked with the data and the WebSocket the listener has * been attached to * 2. A Pong message with the same contents will be sent in reply */ @Test(dataProvider = "incomingData", dataProviderClass = DataProviders.class) public void testOnPingReply(ByteBuffer data) { CompletableFuture<WebSocket> webSocket = new CompletableFuture<>(); MockChannel channel = new MockChannel.Builder() .provideFrame(true, false, false, false, Opcode.PING, data) .expectPong(data::equals) .build(); MockListener listener = new MockListener.Builder() .expectOnOpen((ws) -> true) // maybe should capture with a CF? .expectOnPing((ws, bb) -> data.equals(bb)) .build(); webSocket.complete(newWebSocket(channel, listener)); checkExpectations(500, TimeUnit.MILLISECONDS, channel, listener); } /* * If onPing throws an exception or CS returned from it completes * exceptionally, then: * * 1. onError is invoked with this particular exception as the cause and the * WebSocket the listener has been attached to */ public void testOnPingExceptions() { } /* * If a Ping message has been read off the wire and an I/O error occurs * while WebSocket sends a Pong reply to it, then: * * 1. onError is invoked with this error as the cause and the WebSocket this * listener has been attached to */ public void testOnPingReplyIOException() { } private WebSocket newWebSocket() { return newWebSocket(new MockChannel.Builder().build()); } private WebSocket newWebSocket(RawChannel ch) { return newWebSocket(ch, new WebSocket.Listener() { }); } private WebSocket newWebSocket(RawChannel ch, WebSocket.Listener l) { // WebSocketImpl ws = new WebSocketImpl("", ch, l, Executors.newCachedThreadPool()); // ws.(); // ws.request(Long.MAX_VALUE); return null; // FIXME } public static ByteBuffer copy(ByteBuffer src) { int pos = src.position(); ByteBuffer b = ByteBuffer.allocate(src.remaining()).put(src).flip(); src.position(pos); return b; } }