@VisibleForTesting
    protected static String stringifyThreadInfo(
            java.lang.management.ThreadInfo threadInfo, int maxDepth) {
        StringBuilder sb =
                new StringBuilder(
                        "\""
                                + threadInfo.getThreadName()
                                + "\""
                                + " Id="
                                + threadInfo.getThreadId()
                                + " "
                                + threadInfo.getThreadState());
        if (threadInfo.getLockName() != null) {
            sb.append(" on " + threadInfo.getLockName());
        }
        if (threadInfo.getLockOwnerName() != null) {
            sb.append(
                    " owned by \""
                            + threadInfo.getLockOwnerName()
                            + "\" Id="
                            + threadInfo.getLockOwnerId());
        }
        if (threadInfo.isSuspended()) {
            sb.append(" (suspended)");
        }
        if (threadInfo.isInNative()) {
            sb.append(" (in native)");
        }
        sb.append('\n');
        int i = 0;
        StackTraceElement[] stackTraceElements = threadInfo.getStackTrace();
        for (; i < stackTraceElements.length && i < maxDepth; i++) {
            StackTraceElement ste = stackTraceElements[i];
            sb.append("\tat " + ste.toString());
            sb.append('\n');
            if (i == 0 && threadInfo.getLockInfo() != null) {
                Thread.State ts = threadInfo.getThreadState();
                switch (ts) {
                    case BLOCKED:
                        sb.append("\t-  blocked on " + threadInfo.getLockInfo());
                        sb.append('\n');
                        break;
                    case WAITING:
                    case TIMED_WAITING:
                        sb.append("\t-  waiting on " + threadInfo.getLockInfo());
                        sb.append('\n');
                        break;
                    default:
                }
            }

            for (MonitorInfo mi : threadInfo.getLockedMonitors()) {
                if (mi.getLockedStackDepth() == i) {
                    sb.append("\t-  locked " + mi);
                    sb.append('\n');
                }
            }
        }
        if (i < threadInfo.getStackTrace().length) {
            sb.append("\t...");
            sb.append('\n');
        }

        LockInfo[] locks = threadInfo.getLockedSynchronizers();
        if (locks.length > 0) {
            sb.append("\n\tNumber of locked synchronizers = " + locks.length);
            sb.append('\n');
            for (LockInfo li : locks) {
                sb.append("\t- " + li);
                sb.append('\n');
            }
        }
        sb.append('\n');
        return sb.toString();
    }
@Test
    void testComparedWithDefaultJDKImplemetation() {
        ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean();
        ThreadInfo threadInfo =
                threadMxBean.getThreadInfo(Thread.currentThread().getId(), Integer.MAX_VALUE);

        // JDK11 has increased the output info of threadInfo.daemon and threadInfo.priority compared
        // to JDK8, hence only compare the output of stacktrace content for compatibility.
        String[] threadInfoLines = threadInfo.toString().split("\n");
        String[] expected = Arrays.copyOfRange(threadInfoLines, 1, threadInfoLines.length);

        String stringifyThreadInfo = ThreadDumpInfo.stringifyThreadInfo(threadInfo, 8);
        String[] stringifyThreadInfoLines = stringifyThreadInfo.split("\n");
        String[] stringified =
                Arrays.copyOfRange(stringifyThreadInfoLines, 1, stringifyThreadInfoLines.length);

        assertThat(stringified).isEqualTo(expected);
    }