1 /* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package jdk.incubator.http.internal.websocket; 24 25 import org.testng.SkipException; 26 import org.testng.annotations.Test; 27 import jdk.incubator.http.internal.websocket.Frame.Opcode; 28 29 import java.io.IOException; 30 import java.net.ProtocolException; 31 import jdk.incubator.http.WebSocket; 32 import java.nio.ByteBuffer; 33 import java.util.concurrent.CompletableFuture; 34 import java.util.concurrent.ExecutionException; 35 import java.util.concurrent.Executors; 36 import java.util.concurrent.TimeUnit; 37 38 import static jdk.incubator.http.internal.websocket.TestSupport.Expectation.ifExpect; 39 import static jdk.incubator.http.internal.websocket.TestSupport.assertCompletesExceptionally; 40 import static jdk.incubator.http.internal.websocket.TestSupport.checkExpectations; 41 import static org.testng.Assert.assertSame; 42 43 /* 44 * Examines sendPing/onPing contracts 45 */ 46 public final class PingTest { 47 48 /* 49 * sendPing(message) is invoked. If the `message` argument is illegal, then 50 * the method must throw an appropriate exception. Otherwise no exception 51 * must be thrown. 52 */ 53 // @Test(dataProvider = "outgoingData", dataProviderClass = DataProviders.class) 54 public void testSendPingArguments(ByteBuffer message) { 55 WebSocket ws = newWebSocket(); 56 ifExpect( 57 message == null, 58 NullPointerException.class::isInstance) 59 .orExpect( 60 message != null && message.remaining() > 125, 61 IllegalArgumentException.class::isInstance) 62 .assertThrows( 63 () -> ws.sendPing(message) 64 ); 65 } 66 67 /* 68 * sendPing(message) with a legal argument has been invoked, then: 69 * 70 * 1. A Ping message with the same payload appears on the wire 71 * 2. The CF returned from the method completes normally with the same 72 * WebSocket that sendPing has been called on 73 */ 74 @Test(dataProvider = "outgoingData", dataProviderClass = DataProviders.class) 75 public void testSendPingWysiwyg(ByteBuffer message) throws ExecutionException, InterruptedException { 76 if (message == null || message.remaining() > 125) { 77 return; 78 } 79 ByteBuffer snapshot = copy(message); 80 MockChannel channel = new MockChannel.Builder() 81 .expectPing(snapshot::equals) 82 .build(); 83 WebSocket ws = newWebSocket(channel); 84 CompletableFuture<WebSocket> cf = ws.sendPing(message); 85 WebSocket ws1 = cf.join(); 86 assertSame(ws1, ws); // (2) 87 checkExpectations(channel); // (1) 88 } 89 90 /* 91 * If an I/O error occurs while Ping messages is being sent, then: 92 * 93 * 1. The CF returned from sendPing completes exceptionally with this I/O 94 * error as the cause 95 */ 96 // @Test 97 public void testSendPingIOException() { 98 MockChannel ch = new MockChannel.Builder() 99 // .provideWriteException(IOException::new) 100 .build(); 101 WebSocket ws = newWebSocket(ch); 102 CompletableFuture<WebSocket> cf = ws.sendPing(ByteBuffer.allocate(16)); 103 assertCompletesExceptionally(IOException.class, cf); 104 } 105 106 /* 107 * If an incorrect Ping frame appears on the wire, then: 108 * 109 * 1. onError with the java.net.ProtocolException is invoked 110 * 1. A Close frame with status code 1002 appears on the wire 111 */ 112 // @Test(dataProvider = "incorrectFrame", dataProviderClass = DataProviders.class) 113 public void testOnPingIncorrect(boolean fin, boolean rsv1, boolean rsv2, 114 boolean rsv3, ByteBuffer data) { 115 if (fin && !rsv1 && !rsv2 && !rsv3 && data.remaining() <= 125) { 116 throw new SkipException("Correct frame"); 117 } 118 CompletableFuture<WebSocket> webSocket = new CompletableFuture<>(); 119 MockChannel channel = new MockChannel.Builder() 120 .provideFrame(fin, rsv1, rsv2, rsv3, Opcode.PING, data) 121 .expectClose((code, reason) -> 122 Integer.valueOf(1002).equals(code) && "".equals(reason)) 123 .build(); 124 MockListener listener = new MockListener.Builder() 125 .expectOnOpen((ws) -> true) 126 .expectOnError((ws, error) -> error instanceof ProtocolException) 127 .build(); 128 webSocket.complete(newWebSocket(channel, listener)); 129 checkExpectations(500, TimeUnit.MILLISECONDS, channel, listener); 130 } 131 132 /* 133 * If a Ping message has been read off the wire, then: 134 * 135 * 1. onPing is invoked with the data and the WebSocket the listener has 136 * been attached to 137 * 2. A Pong message with the same contents will be sent in reply 138 */ 139 @Test(dataProvider = "incomingData", dataProviderClass = DataProviders.class) 140 public void testOnPingReply(ByteBuffer data) { 141 CompletableFuture<WebSocket> webSocket = new CompletableFuture<>(); 142 MockChannel channel = new MockChannel.Builder() 143 .provideFrame(true, false, false, false, Opcode.PING, data) 144 .expectPong(data::equals) 145 .build(); 146 MockListener listener = new MockListener.Builder() 147 .expectOnOpen((ws) -> true) // maybe should capture with a CF? 148 .expectOnPing((ws, bb) -> data.equals(bb)) 149 .build(); 150 webSocket.complete(newWebSocket(channel, listener)); 151 checkExpectations(500, TimeUnit.MILLISECONDS, channel, listener); 152 } 153 154 /* 155 * If onPing throws an exception or CS returned from it completes 156 * exceptionally, then: 157 * 158 * 1. onError is invoked with this particular exception as the cause and the 159 * WebSocket the listener has been attached to 160 */ 161 public void testOnPingExceptions() { 162 } 163 164 /* 165 * If a Ping message has been read off the wire and an I/O error occurs 166 * while WebSocket sends a Pong reply to it, then: 167 * 168 * 1. onError is invoked with this error as the cause and the WebSocket this 169 * listener has been attached to 170 */ 171 public void testOnPingReplyIOException() { 172 } 173 174 private WebSocket newWebSocket() { 175 return newWebSocket(new MockChannel.Builder().build()); 176 } 177 178 private WebSocket newWebSocket(RawChannel ch) { 179 return newWebSocket(ch, new WebSocket.Listener() { }); 180 } 181 182 private WebSocket newWebSocket(RawChannel ch, WebSocket.Listener l) { 183 // WebSocketImpl ws = new WebSocketImpl("", ch, l, Executors.newCachedThreadPool()); 184 // ws.(); 185 // ws.request(Long.MAX_VALUE); 186 return null; // FIXME 187 } 188 189 public static ByteBuffer copy(ByteBuffer src) { 190 int pos = src.position(); 191 ByteBuffer b = ByteBuffer.allocate(src.remaining()).put(src).flip(); 192 src.position(pos); 193 return b; 194 } 195 }