diff --git a/pom.xml b/pom.xml
index 7f3a5be5a4e8..885e54b8fd57 100644
--- a/pom.xml
+++ b/pom.xml
@@ -153,6 +153,7 @@
documentation
jetty-keystore
jetty-unixdomain-server
+ test-websocket-core
diff --git a/tests/pom.xml b/tests/pom.xml
index 36993d7cc35b..a8140e9c0bdf 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -56,5 +56,6 @@
test-http-client-transport
test-distribution
test-cdi
+ test-jpms
diff --git a/tests/test-jpms/pom.xml b/tests/test-jpms/pom.xml
new file mode 100644
index 000000000000..abe7e8d2b0c5
--- /dev/null
+++ b/tests/test-jpms/pom.xml
@@ -0,0 +1,33 @@
+
+
+
+ org.eclipse.jetty.tests
+ tests-parent
+ 10.0.7-SNAPSHOT
+
+ 4.0.0
+ test-jpms
+ pom
+ Jetty Tests :: JPMS Parent
+
+ test-websocket-core
+
+
+
+
+ org.eclipse.jetty
+ jetty-slf4j-impl
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ org.eclipse.jetty.toolchain
+ jetty-test-helper
+ test
+
+
+
+
diff --git a/tests/test-jpms/test-websocket-core/pom.xml b/tests/test-jpms/test-websocket-core/pom.xml
new file mode 100644
index 000000000000..903d38e64fd7
--- /dev/null
+++ b/tests/test-jpms/test-websocket-core/pom.xml
@@ -0,0 +1,25 @@
+
+
+
+ test-jpms
+ org.eclipse.jetty.tests
+ 10.0.7-SNAPSHOT
+
+ 4.0.0
+ test-websocket-core
+ jar
+ Jetty Tests :: JPMS :: WebSocket Core Tests
+
+
+
+ org.eclipse.jetty.websocket
+ websocket-core-server
+ ${project.version}
+
+
+ org.eclipse.jetty.websocket
+ websocket-core-client
+ ${project.version}
+
+
+
\ No newline at end of file
diff --git a/tests/test-jpms/test-websocket-core/src/main/java/module-info.java b/tests/test-jpms/test-websocket-core/src/main/java/module-info.java
new file mode 100644
index 000000000000..33471bc1ce48
--- /dev/null
+++ b/tests/test-jpms/test-websocket-core/src/main/java/module-info.java
@@ -0,0 +1,7 @@
+module org.eclipse.jetty.websocket.core.tests
+{
+ exports org.example.websocket;
+
+ requires org.eclipse.jetty.websocket.core.server;
+ requires org.eclipse.jetty.websocket.core.client;
+}
\ No newline at end of file
diff --git a/tests/test-jpms/test-websocket-core/src/main/java/org/example/websocket/MyFrameHandler.java b/tests/test-jpms/test-websocket-core/src/main/java/org/example/websocket/MyFrameHandler.java
new file mode 100644
index 000000000000..d02b7e593bd1
--- /dev/null
+++ b/tests/test-jpms/test-websocket-core/src/main/java/org/example/websocket/MyFrameHandler.java
@@ -0,0 +1,62 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Eclipse Public License v. 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+// which is available at https://www.apache.org/licenses/LICENSE-2.0.
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.example.websocket;
+
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.websocket.core.CloseStatus;
+import org.eclipse.jetty.websocket.core.CoreSession;
+import org.eclipse.jetty.websocket.core.Frame;
+import org.eclipse.jetty.websocket.core.FrameHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MyFrameHandler implements FrameHandler
+{
+ private static final Logger LOG = LoggerFactory.getLogger(MyFrameHandler.class);
+
+ private final String _id;
+
+ public MyFrameHandler(String id)
+ {
+ _id = id;
+ }
+
+ @Override
+ public void onOpen(CoreSession coreSession, Callback callback)
+ {
+ LOG.info(_id + " onOpen");
+ callback.succeeded();
+ }
+
+ @Override
+ public void onFrame(Frame frame, Callback callback)
+ {
+ LOG.info(_id + " onFrame");
+ callback.succeeded();
+ }
+
+ @Override
+ public void onError(Throwable cause, Callback callback)
+ {
+ LOG.info(_id + " onError");
+ callback.succeeded();
+ }
+
+ @Override
+ public void onClosed(CloseStatus closeStatus, Callback callback)
+ {
+ LOG.info(_id + " onClosed");
+ callback.succeeded();
+ }
+}
diff --git a/tests/test-jpms/test-websocket-core/src/test/java/WebSocketCoreJpmsTest.java b/tests/test-jpms/test-websocket-core/src/test/java/WebSocketCoreJpmsTest.java
new file mode 100644
index 000000000000..c38841542466
--- /dev/null
+++ b/tests/test-jpms/test-websocket-core/src/test/java/WebSocketCoreJpmsTest.java
@@ -0,0 +1,72 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Eclipse Public License v. 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+// which is available at https://www.apache.org/licenses/LICENSE-2.0.
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+import java.net.URI;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.websocket.core.CoreSession;
+import org.eclipse.jetty.websocket.core.FrameHandler;
+import org.eclipse.jetty.websocket.core.client.CoreClientUpgradeRequest;
+import org.eclipse.jetty.websocket.core.client.WebSocketCoreClient;
+import org.eclipse.jetty.websocket.core.server.WebSocketNegotiator;
+import org.eclipse.jetty.websocket.core.server.WebSocketUpgradeHandler;
+import org.example.websocket.MyFrameHandler;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class WebSocketCoreJpmsTest
+{
+ private Server _server;
+ private ServerConnector _serverConnector;
+ private WebSocketCoreClient _client;
+
+ @BeforeEach
+ public void before() throws Exception
+ {
+ _server = new Server();
+ _serverConnector = new ServerConnector(_server);
+ _server.addConnector(_serverConnector);
+
+ WebSocketUpgradeHandler webSocketUpgradeHandler = new WebSocketUpgradeHandler();
+ FrameHandler myFrameHandler = new MyFrameHandler("Server");
+ webSocketUpgradeHandler.addMapping("/ws", WebSocketNegotiator.from(negotiation -> myFrameHandler));
+
+ _server.setHandler(webSocketUpgradeHandler);
+ _server.start();
+
+ _client = new WebSocketCoreClient();
+ _client.start();
+ }
+
+ @AfterEach
+ public void after() throws Exception
+ {
+ _client.stop();
+ _server.stop();
+ }
+
+ @Test
+ public void testSimpleEcho() throws Exception
+ {
+ MyFrameHandler frameHandler = new MyFrameHandler("Client");
+ URI uri = URI.create("ws://localhost:" + _serverConnector.getLocalPort() + "/ws");
+ CoreClientUpgradeRequest upgradeRequest = CoreClientUpgradeRequest.from(_client, uri, frameHandler);
+ upgradeRequest.addExtensions("permessage-deflate");
+ CoreSession coreSession = _client.connect(upgradeRequest).get(5, TimeUnit.SECONDS);
+ coreSession.close(Callback.NOOP);
+ }
+}