You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2019/02/19 14:45:58 UTC

[mina-sshd] branch SSHD-897 created (now b47195e)

This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a change to branch SSHD-897
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git.


      at b47195e  [SSHD-897] Moved some shell stream related classes to sshd-common

This branch includes the following new commits:

     new 995970a  [SSHD-897] Provide channel instance in SignalListener#signal invocation
     new b47195e  [SSHD-897] Moved some shell stream related classes to sshd-common

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[mina-sshd] 01/02: [SSHD-897] Provide channel instance in SignalListener#signal invocation

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch SSHD-897
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit 995970a964b60446c999d7f33c8a93bc1813200f
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Tue Feb 19 13:58:50 2019 +0200

    [SSHD-897] Provide channel instance in SignalListener#signal invocation
---
 CHANGES.md                                         |  2 ++
 .../org/apache/sshd/common/channel/PtyMode.java    |  0
 .../org/apache/sshd/common/util/GenericUtils.java  | 16 +++++++++++
 .../java/org/apache/sshd/server/ExitCallback.java  |  0
 .../main/java/org/apache/sshd/server/Signal.java   |  0
 .../apache/sshd/common/channel/PtyModeTest.java    |  4 +--
 .../java/org/apache/sshd/server/Environment.java   | 19 ++++++++-----
 .../org/apache/sshd/server/SignalListener.java     |  5 ++--
 .../apache/sshd/server/StandardEnvironment.java    | 33 +++++++++-------------
 .../apache/sshd/server/channel/ChannelSession.java |  8 ++++--
 .../sshd/server/StandardEnvironmentTest.java       |  2 +-
 11 files changed, 54 insertions(+), 35 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 07b4430..aba6cfa 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -18,6 +18,8 @@ current sesssion - client/server proposals and what has been negotiated.
 
 * The `Session` object provides a `KexExtensionHandler` for usage with [KEX extension negotiation](https://tools.wordtothewise.com/rfc/rfc8308)
 
+* The `SignalListener` accepts a `Channel` argument indicating the channel instance through which the signal was received
+
 ## Behavioral changes and enhancements
 
 * [SSHD-882](https://issues.apache.org/jira/browse/SSHD-882) - Provide hooks to allow users to register a consumer
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/channel/PtyMode.java b/sshd-common/src/main/java/org/apache/sshd/common/channel/PtyMode.java
similarity index 100%
rename from sshd-core/src/main/java/org/apache/sshd/common/channel/PtyMode.java
rename to sshd-common/src/main/java/org/apache/sshd/common/channel/PtyMode.java
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java
index 294148d..ea75c08 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java
@@ -595,6 +595,22 @@ public final class GenericUtils {
         return set;
     }
 
+    @SafeVarargs
+    public static <E extends Enum<E>> Set<E> asEnumSet(E... values) {
+        if (isEmpty(values)) {
+            return Collections.emptySet();
+        }
+
+        Set<E> s = EnumSet.of(values[0]);
+        for (int index = 1 /* we used [0] to populate the initial set */; index < values.length; index++) {
+            if (!s.add(values[index])) {
+                continue;   // debug breakpoint
+            }
+        }
+
+        return s;
+    }
+
     /**
      * @param <V> Type of mapped value
      * @return A {@link Supplier} that returns a <U>new</U> {@link NavigableMap}
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/ExitCallback.java b/sshd-common/src/main/java/org/apache/sshd/server/ExitCallback.java
similarity index 100%
rename from sshd-core/src/main/java/org/apache/sshd/server/ExitCallback.java
rename to sshd-common/src/main/java/org/apache/sshd/server/ExitCallback.java
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/Signal.java b/sshd-common/src/main/java/org/apache/sshd/server/Signal.java
similarity index 100%
rename from sshd-core/src/main/java/org/apache/sshd/server/Signal.java
rename to sshd-common/src/main/java/org/apache/sshd/server/Signal.java
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/channel/PtyModeTest.java b/sshd-common/src/test/java/org/apache/sshd/common/channel/PtyModeTest.java
similarity index 95%
rename from sshd-core/src/test/java/org/apache/sshd/common/channel/PtyModeTest.java
rename to sshd-common/src/test/java/org/apache/sshd/common/channel/PtyModeTest.java
index 819bc3a..3234948 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/channel/PtyModeTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/channel/PtyModeTest.java
@@ -24,7 +24,7 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.JUnitTestSupport;
 import org.apache.sshd.util.test.NoIoTestCase;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -36,7 +36,7 @@ import org.junit.runners.MethodSorters;
  */
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Category({ NoIoTestCase.class })
-public class PtyModeTest extends BaseTestSupport {
+public class PtyModeTest extends JUnitTestSupport {
     public PtyModeTest() {
         super();
     }
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/Environment.java b/sshd-core/src/main/java/org/apache/sshd/server/Environment.java
index 192cb86..ea3d769 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/Environment.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/Environment.java
@@ -22,6 +22,7 @@ import java.util.Collection;
 import java.util.Map;
 
 import org.apache.sshd.common.channel.PtyMode;
+import org.apache.sshd.common.util.GenericUtils;
 
 /**
  * Interface providing access to the environment map and allowing the registration
@@ -73,24 +74,28 @@ public interface Environment {
      * Add a qualified listener for the specific signals
      *
      * @param listener the {@link SignalListener} to register
-     * @param signal   the {@link Signal}s  the listener is interested in
+     * @param signals The (never {@code null}/empty) {@link Signal}s the listener is interested in
      */
-    void addSignalListener(SignalListener listener, Signal... signal);
+    default void addSignalListener(SignalListener listener, Signal... signals) {
+        addSignalListener(listener, GenericUtils.asEnumSet(signals));
+    }
 
     /**
-     * Add a qualified listener for the specific signals
+     * Add a global listener for all signals
      *
      * @param listener the {@link SignalListener} to register
-     * @param signals  the {@link Signal}s the listener is interested in
      */
-    void addSignalListener(SignalListener listener, Collection<Signal> signals);
+    default void addSignalListener(SignalListener listener) {
+        addSignalListener(listener, Signal.SIGNALS);
+    }
 
     /**
-     * Add a global listener for all signals
+     * Add a qualified listener for the specific signals
      *
      * @param listener the {@link SignalListener} to register
+     * @param signals  the {@link Signal}s the listener is interested in
      */
-    void addSignalListener(SignalListener listener);
+    void addSignalListener(SignalListener listener, Collection<Signal> signals);
 
     /**
      * Remove a previously registered listener for all the signals it was registered
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/SignalListener.java b/sshd-core/src/main/java/org/apache/sshd/server/SignalListener.java
index 2675e5c..ef1b7b7 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/SignalListener.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/SignalListener.java
@@ -18,6 +18,7 @@
  */
 package org.apache.sshd.server;
 
+import org.apache.sshd.common.channel.Channel;
 import org.apache.sshd.common.util.SshdEventListener;
 
 /**
@@ -27,11 +28,11 @@ import org.apache.sshd.common.util.SshdEventListener;
  */
 @FunctionalInterface
 public interface SignalListener extends SshdEventListener {
-
     /**
+     * @param channel The {@link Channel} through which the signal was received
      * @param signal The received {@link Signal}
      */
-    void signal(Signal signal);
+    void signal(Channel channel, Signal signal);
 
     static <L extends SignalListener> L validateListener(L listener) {
         return SshdEventListener.validateListener(listener, SignalListener.class.getSimpleName());
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/StandardEnvironment.java b/sshd-core/src/main/java/org/apache/sshd/server/StandardEnvironment.java
index 8c67a43..1e16a3a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/StandardEnvironment.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/StandardEnvironment.java
@@ -18,12 +18,12 @@
  */
 package org.apache.sshd.server;
 
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArraySet;
 
+import org.apache.sshd.common.channel.Channel;
 import org.apache.sshd.common.channel.PtyMode;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
@@ -43,16 +43,6 @@ public class StandardEnvironment extends AbstractLoggingBean implements Environm
         ptyModes = new ConcurrentHashMap<>();
     }
 
-    @Override
-    public void addSignalListener(SignalListener listener, Signal... signals) {
-        addSignalListener(listener, Arrays.asList(ValidateUtils.checkNotNullAndNotEmpty(signals, "No signals")));
-    }
-
-    @Override
-    public void addSignalListener(SignalListener listener) {
-        addSignalListener(listener, Signal.SIGNALS);
-    }
-
     /*
      * NOTE: we don't care if the collection is a Set or not - after all,
      * we hold the listeners inside a Set, so even if we add several times
@@ -64,7 +54,8 @@ public class StandardEnvironment extends AbstractLoggingBean implements Environm
         ValidateUtils.checkNotNullAndNotEmpty(signals, "No signals");
 
         for (Signal s : signals) {
-            getSignalListeners(s, true).add(listener);
+            Collection<SignalListener> list = getSignalListeners(s, true);
+            list.add(listener);
         }
     }
 
@@ -93,26 +84,27 @@ public class StandardEnvironment extends AbstractLoggingBean implements Environm
         }
     }
 
-    public void signal(Signal signal) {
+    public void signal(Channel channel, Signal signal) {
         Collection<SignalListener> ls = getSignalListeners(signal, false);
         if (log.isDebugEnabled()) {
-            log.debug("signal({}) - listeners={}", signal, ls);
+            log.debug("signal({})[{}] - listeners={}", channel, signal, ls);
         }
 
         if (GenericUtils.isEmpty(ls)) {
             return;
         }
 
+        boolean traceEnabled = log.isTraceEnabled();
         for (SignalListener l : ls) {
             try {
-                l.signal(signal);
+                l.signal(channel, signal);
 
-                if (log.isTraceEnabled()) {
-                    log.trace("Signal {} to {}", signal, l);
+                if (traceEnabled) {
+                    log.trace("signal({}) Signal {} to {}", channel, signal, l);
                 }
             } catch (RuntimeException e) {
-                log.warn("Failed ({}) to signal {} to listener={}: {}",
-                         e.getClass().getSimpleName(), signal, l, e.getMessage());
+                log.warn("signal({}) Failed ({}) to signal {} to listener={}: {}",
+                     channel, e.getClass().getSimpleName(), signal, l, e.getMessage());
             }
         }
     }
@@ -126,7 +118,8 @@ public class StandardEnvironment extends AbstractLoggingBean implements Environm
      */
     public void set(String key, String value) {
         // TODO: listening for property changes would be nice too.
-        getEnv().put(ValidateUtils.checkNotNullAndNotEmpty(key, "Empty environment variable name"), value);
+        Map<String, String> environ = getEnv();
+        environ.put(ValidateUtils.checkNotNullAndNotEmpty(key, "Empty environment variable name"), value);
     }
 
     /**
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
index ab57cb1..2c2f6bd 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
@@ -471,7 +471,7 @@ public class ChannelSession extends AbstractServerChannel {
         StandardEnvironment e = getEnvironment();
         e.set(Environment.ENV_COLUMNS, Integer.toString(tColumns));
         e.set(Environment.ENV_LINES, Integer.toString(tRows));
-        e.signal(Signal.WINCH);
+        e.signal(this, Signal.WINCH);
         return RequestHandler.Result.ReplySuccess;
     }
 
@@ -484,7 +484,8 @@ public class ChannelSession extends AbstractServerChannel {
 
         Signal signal = Signal.get(name);
         if (signal != null) {
-            getEnvironment().signal(signal);
+            StandardEnvironment environ = getEnvironment();
+            environ.signal(this, signal);
         } else {
             log.warn("handleSignal({}) unknown signal received: {}", this, name);
         }
@@ -498,7 +499,8 @@ public class ChannelSession extends AbstractServerChannel {
             log.debug("handleBreak({}) length={}", this, breakLength);
         }
 
-        getEnvironment().signal(Signal.INT);
+        StandardEnvironment environ = getEnvironment();
+        environ.signal(this, Signal.INT);
         return RequestHandler.Result.ReplySuccess;
     }
 
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/StandardEnvironmentTest.java b/sshd-core/src/test/java/org/apache/sshd/server/StandardEnvironmentTest.java
index 2e36d57..7b18a77 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/StandardEnvironmentTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/StandardEnvironmentTest.java
@@ -42,7 +42,7 @@ public class StandardEnvironmentTest extends BaseTestSupport {
     @Test
     public void testAddSignalListenerOnDuplicateSignals() {
         StandardEnvironment environ = new StandardEnvironment();
-        SignalListener listener = signal -> {
+        SignalListener listener = (channel, signal) -> {
             // ignored
         };
 


[mina-sshd] 02/02: [SSHD-897] Moved some shell stream related classes to sshd-common

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch SSHD-897
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit b47195e4f156e475639de2fe3ceae232376b0d1d
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Tue Feb 19 16:45:47 2019 +0200

    [SSHD-897] Moved some shell stream related classes to sshd-common
---
 .../org/apache/sshd/common/channel/PtyMode.java    | 108 ++++++++++++++++++++-
 .../sshd/server/shell/TtyFilterInputStream.java    |   3 +-
 .../sshd/server/shell/TtyFilterOutputStream.java   |   0
 .../apache/sshd/common/channel/PtyModeTest.java    |   8 +-
 .../sshd/common/channel/PtyModeValueTest.java      |  97 ++++++++++++++++++
 .../AuthorizedKeyEntryLoginOptionsParseTest.java   |   6 +-
 .../server/shell/TtyFilterInputStreamTest.java     |   4 +-
 .../server/shell/TtyFilterOutputStreamTest.java    |   8 +-
 .../sshd/common/util/io/LineOutputStreamTest.java  |   3 +
 9 files changed, 222 insertions(+), 15 deletions(-)

diff --git a/sshd-common/src/main/java/org/apache/sshd/common/channel/PtyMode.java b/sshd-common/src/main/java/org/apache/sshd/common/channel/PtyMode.java
index 21af35e..a4745e5 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/channel/PtyMode.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/channel/PtyMode.java
@@ -27,6 +27,7 @@ import java.util.Map;
 import java.util.NavigableMap;
 import java.util.Set;
 import java.util.function.Function;
+import java.util.function.ToIntFunction;
 
 import org.apache.sshd.common.util.GenericUtils;
 
@@ -295,10 +296,29 @@ public enum PtyMode {
     public static final Set<PtyMode> MODES =
         Collections.unmodifiableSet(EnumSet.allOf(PtyMode.class));
 
-    private static final NavigableMap<Integer, PtyMode> COMMANDS =
+    public static final NavigableMap<Integer, PtyMode> COMMANDS =
         Collections.unmodifiableNavigableMap(
             GenericUtils.toSortedMap(MODES, PtyMode::toInt, Function.identity(), Comparator.naturalOrder()));
 
+    /**
+     * A {@code null}-safe {@link ToIntFunction} that returns the {@link PtyMode#toInt()} value and (-1) for {@code null}
+     */
+    public static final ToIntFunction<PtyMode> OPCODE_EXTRACTOR = v -> (v == null) ? -1 : v.toInt();
+
+    /**
+     * A {@code null}-safe {@link Comparator} of {@link PtyMode} values
+     * according to their {@link PtyMode#toInt()} value
+     * @see #OPCODE_EXTRACTOR
+     */
+    public static final Comparator<PtyMode> BY_OPCODE = new Comparator<PtyMode>() {
+            @Override
+            public int compare(PtyMode o1, PtyMode o2) {
+                int v1 = OPCODE_EXTRACTOR.applyAsInt(o1);
+                int v2 = OPCODE_EXTRACTOR.applyAsInt(o2);
+                return Integer.compare(v1, v2);
+            }
+        };
+
     private final int v;
 
     PtyMode(int v) {
@@ -318,6 +338,20 @@ public enum PtyMode {
         return COMMANDS.get(0x00FF & b);
     }
 
+    public static PtyMode fromName(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+
+        for (PtyMode m : MODES) {
+            if (name.equalsIgnoreCase(m.name())) {
+                return m;
+            }
+        }
+
+        return null;
+    }
+
     /**
      * @param options The options to enable - ignored if {@code null}/empty
      * @return A {@link Map} where all the specified {@link PtyMode}s have {@link #TRUE_SETTING}
@@ -391,6 +425,63 @@ public enum PtyMode {
     }
 
     /**
+     * @param modes The {@link Map} of {@link PtyMode}s resolved by the &quot;pty-req&quot; message.
+     * @param enablers A {@link Collection} of enabler settings to be consulted
+     * @param defaultValue The default value to be used if no definite setting could be deduced
+     * @return {@code true} if the CR mode is enabled:</BR>
+     * <UL>
+     *      <LI>
+     *      If<tt>modes</tt> or <tt>enablers</tt> are {@code null}/empty
+     *      then <tt>defaultValue</tt> is used
+     *      </LI>
+     *
+     *      <LI>
+     *      If <U>any</U> of the <tt>enablers</tt> modes are enabled
+     *      then the CR mode is enabled.
+     *      </LI>
+     *
+     *      <LI>
+     *      If <U>none</U> of the <tt>enablers</tt> modes were specified
+     *      then use  <tt>defaultValue</tt>
+     *      </LI>
+     *
+     *      <LI>
+     *      Otherwise (i.e., at least one or more of the <tt>enablers</tt>
+     *      modes were specified, but <U>all</U> of them said {@code no})
+     *      then {@code false}.
+     *      </LI>
+     * </UL>
+     */
+    public static boolean getBooleanSettingValue(
+            Map<PtyMode, ?> modes, Collection<PtyMode> enablers, boolean defaultValue) {
+        if (GenericUtils.isEmpty(modes) || GenericUtils.isEmpty(enablers)) {
+            return defaultValue;
+        }
+
+        int settingsCount = 0;
+        for (PtyMode m : enablers) {
+            Object v = modes.get(m);
+            if (v == null) {
+                continue;
+            }
+
+            settingsCount++;
+
+            // if any setting says yes then use it
+            if (getBooleanSettingValue(v)) {
+                return true;
+            }
+        }
+
+        // ALL (!) settings have said NO
+        if (settingsCount > 0) {
+            return false;
+        } else {
+            return defaultValue;    // none of the settings has been found - assume default
+        }
+    }
+
+    /**
      * @param v The value to be tested
      * @return {@code true} if <U>all</U> of these conditions hold:</BR>
      * <UL>
@@ -410,4 +501,19 @@ public enum PtyMode {
     public static boolean getBooleanSettingValue(int v) {
         return v != 0;
     }
+
+    /**
+     * @param m The {@link PtyMode}
+     * @return {@code true} if not {@code null} and one of the settings that
+     * refers to a character value - name usually starts with {@code Vxxx}
+     */
+    public static boolean isCharSetting(PtyMode m) {
+        if (m == null) {
+            return false;
+        }
+
+        String name = m.name();
+        char ch = name.charAt(0);
+        return (ch == 'v') || (ch == 'V');
+    }
 }
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/shell/TtyFilterInputStream.java b/sshd-common/src/main/java/org/apache/sshd/server/shell/TtyFilterInputStream.java
similarity index 98%
rename from sshd-core/src/main/java/org/apache/sshd/server/shell/TtyFilterInputStream.java
rename to sshd-common/src/main/java/org/apache/sshd/server/shell/TtyFilterInputStream.java
index b894805..5760310 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/shell/TtyFilterInputStream.java
+++ b/sshd-common/src/main/java/org/apache/sshd/server/shell/TtyFilterInputStream.java
@@ -25,6 +25,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 import org.apache.sshd.common.channel.PtyMode;
@@ -51,7 +52,7 @@ public class TtyFilterInputStream extends FilterInputStream {
     }
 
     public TtyFilterInputStream(InputStream in, Collection<PtyMode> ttyOptions) {
-        super(in);
+        super(Objects.requireNonNull(in, "No input stream provided"));
         // we create a copy of the options so as to avoid concurrent modifications
         this.ttyOptions = GenericUtils.of(ttyOptions);  // TODO validate non-conflicting options
     }
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/shell/TtyFilterOutputStream.java b/sshd-common/src/main/java/org/apache/sshd/server/shell/TtyFilterOutputStream.java
similarity index 100%
rename from sshd-core/src/main/java/org/apache/sshd/server/shell/TtyFilterOutputStream.java
rename to sshd-common/src/main/java/org/apache/sshd/server/shell/TtyFilterOutputStream.java
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/channel/PtyModeTest.java b/sshd-common/src/test/java/org/apache/sshd/common/channel/PtyModeTest.java
index 3234948..69ae5e8 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/channel/PtyModeTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/channel/PtyModeTest.java
@@ -42,12 +42,8 @@ public class PtyModeTest extends JUnitTestSupport {
     }
 
     @Test
-    public void testFromInt() {
-        for (PtyMode expected : PtyMode.MODES) {
-            int num = expected.toInt();
-            PtyMode actual = PtyMode.fromInt(num);
-            assertSame("Mismatched result for value=" + num, expected, actual);
-        }
+    public void testOpcodeExtractorOnNull() {
+        assertEquals(-1, PtyMode.OPCODE_EXTRACTOR.applyAsInt(null));
     }
 
     @Test
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/channel/PtyModeValueTest.java b/sshd-common/src/test/java/org/apache/sshd/common/channel/PtyModeValueTest.java
new file mode 100644
index 0000000..98ab7ce
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/channel/PtyModeValueTest.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.channel;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class PtyModeValueTest extends JUnitTestSupport {
+    private final PtyMode expected;
+
+    public PtyModeValueTest(PtyMode expected) {
+        this.expected = expected;
+    }
+
+    @Parameters(name = "{0}")
+    public static List<Object[]> parameters() {
+        return parameterize(PtyMode.MODES);
+    }
+
+    @Test
+    public void testOpcodeExtractor() {
+        assertEquals(expected.toInt(), PtyMode.OPCODE_EXTRACTOR.applyAsInt(expected));
+    }
+
+    @Test
+    public void testByOpcodeComparator() {
+        int v1 = expected.toInt();
+        for (PtyMode actual : PtyMode.MODES) {
+            int v2 = actual.toInt();
+            int cmpExpected = Integer.signum(Integer.compare(v1, v2));
+            int cmpActual = Integer.signum(PtyMode.BY_OPCODE.compare(expected, actual));
+            assertEquals(expected + " vs. " + actual, cmpExpected, cmpActual);
+        }
+    }
+
+    @Test
+    public void testFromName() {
+        String name = expected.name();
+        for (int index = 0; index < Byte.SIZE; index++) {
+            PtyMode actual = PtyMode.fromName(name);
+            assertSame(name, expected, actual);
+            name = shuffleCase(name);
+        }
+    }
+
+    @Test
+    public void testGetBooleanSettingValueOnNullOrEmptyValues() {
+        for (@SuppressWarnings("unchecked") Map<PtyMode, ?> modes : new Map[] {null, Collections.emptyMap()}) {
+            String s = (modes == null) ? "null" : "empty";
+            assertFalse("Map is " + s, PtyMode.getBooleanSettingValue(modes, expected));
+        }
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "[" + expected + "]";
+    }
+}
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java
index d912998..c37aa28 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java
@@ -26,8 +26,10 @@ import java.util.Map;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
 import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
+import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
 import org.junit.runners.MethodSorters;
 import org.junit.runners.Parameterized;
@@ -40,13 +42,15 @@ import org.junit.runners.Parameterized.UseParametersRunnerFactory;
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
 @UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
 public class AuthorizedKeyEntryLoginOptionsParseTest extends JUnitTestSupport {
     private final String value;
     private final String loginPart;
     private final String keyPart;
     private final Map<String, String> options;
 
-    public AuthorizedKeyEntryLoginOptionsParseTest(String value, String loginPart, String keyPart, Map<String, String> options) {
+    public AuthorizedKeyEntryLoginOptionsParseTest(
+            String value, String loginPart, String keyPart, Map<String, String> options) {
         this.value = value;
         this.loginPart = loginPart;
         this.keyPart = keyPart;
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/shell/TtyFilterInputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/server/shell/TtyFilterInputStreamTest.java
similarity index 98%
rename from sshd-core/src/test/java/org/apache/sshd/server/shell/TtyFilterInputStreamTest.java
rename to sshd-common/src/test/java/org/apache/sshd/server/shell/TtyFilterInputStreamTest.java
index bd4913a..a17fa01 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/shell/TtyFilterInputStreamTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/server/shell/TtyFilterInputStreamTest.java
@@ -40,8 +40,8 @@ import org.apache.sshd.common.channel.PtyMode;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
 import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
 import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
 import org.apache.sshd.util.test.NoIoTestCase;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -59,7 +59,7 @@ import org.junit.runners.Parameterized.UseParametersRunnerFactory;
 @RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
 @UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
 @Category({ NoIoTestCase.class })
-public class TtyFilterInputStreamTest extends BaseTestSupport {
+public class TtyFilterInputStreamTest extends JUnitTestSupport {
     private static final List<PtyMode> MODES =
         Collections.unmodifiableList(
             Stream.concat(Stream.of(PtyMode.ECHO), TtyFilterInputStream.INPUT_OPTIONS.stream())
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/shell/TtyFilterOutputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/server/shell/TtyFilterOutputStreamTest.java
similarity index 95%
rename from sshd-core/src/test/java/org/apache/sshd/server/shell/TtyFilterOutputStreamTest.java
rename to sshd-common/src/test/java/org/apache/sshd/server/shell/TtyFilterOutputStreamTest.java
index 5844836..be72b32 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/shell/TtyFilterOutputStreamTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/server/shell/TtyFilterOutputStreamTest.java
@@ -34,8 +34,8 @@ import java.util.Objects;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.sshd.common.channel.PtyMode;
-import org.apache.sshd.util.test.BaseTestSupport;
 import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
 import org.apache.sshd.util.test.NoIoTestCase;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -53,7 +53,7 @@ import org.junit.runners.Parameterized.UseParametersRunnerFactory;
 @RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
 @UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
 @Category({ NoIoTestCase.class })
-public class TtyFilterOutputStreamTest extends BaseTestSupport {
+public class TtyFilterOutputStreamTest extends JUnitTestSupport {
     private final PtyMode mode;
 
     public TtyFilterOutputStreamTest(PtyMode mode) {
@@ -71,8 +71,8 @@ public class TtyFilterOutputStreamTest extends BaseTestSupport {
                 getClass().getSimpleName(), getCurrentTestName(),
                 "(" + mode + ")", new Date(System.currentTimeMillis()).toString());
 
-        final AtomicInteger crCount = new AtomicInteger(0);
-        final AtomicInteger lfCount = new AtomicInteger(0);
+        AtomicInteger crCount = new AtomicInteger(0);
+        AtomicInteger lfCount = new AtomicInteger(0);
         try (OutputStream output = new OutputStream() {
                 @Override
                 public void write(int b) throws IOException {
diff --git a/sshd-contrib/src/test/java/org/apache/sshd/common/util/io/LineOutputStreamTest.java b/sshd-contrib/src/test/java/org/apache/sshd/common/util/io/LineOutputStreamTest.java
index c49f608..8a449ec 100644
--- a/sshd-contrib/src/test/java/org/apache/sshd/common/util/io/LineOutputStreamTest.java
+++ b/sshd-contrib/src/test/java/org/apache/sshd/common/util/io/LineOutputStreamTest.java
@@ -32,8 +32,10 @@ import java.util.List;
 
 import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
 import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
+import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
 import org.junit.runners.MethodSorters;
 import org.junit.runners.Parameterized;
@@ -46,6 +48,7 @@ import org.junit.runners.Parameterized.UseParametersRunnerFactory;
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
 @UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
 public class LineOutputStreamTest extends JUnitTestSupport {
     private final boolean withCR;