public static void updateDetailMessage(
            @Nullable Throwable root, @Nullable Function<Throwable, String> throwableToMessage) {
        if (throwableToMessage == null) {
            return;
        }

        Throwable it = root;
        while (it != null) {
            String newMessage = throwableToMessage.apply(it);
            if (newMessage != null) {
                updateDetailMessageOfThrowable(it, newMessage);
            }

            it = it.getCause();
        }
    }
@Test
    void testUpdateDetailMessageOfRelevantThrowableAsCause() {
        Throwable oomCause =
                new IllegalArgumentException("another message deep down in the cause tree");

        Throwable oom = new OutOfMemoryError("old message").initCause(oomCause);
        oom.setStackTrace(
                new StackTraceElement[] {new StackTraceElement("class", "method", "file", 1)});
        oom.addSuppressed(new NullPointerException());

        Throwable rootThrowable = new IllegalStateException("another message", oom);
        ExceptionUtils.updateDetailMessage(
                rootThrowable,
                t -> t.getClass().equals(OutOfMemoryError.class) ? "new message" : null);

        assertThat(rootThrowable.getCause()).isSameAs(oom);
        assertThat(rootThrowable.getCause().getMessage()).isEqualTo("new message");
        assertThat(rootThrowable.getCause().getStackTrace()).isEqualTo(oom.getStackTrace());
        assertThat(rootThrowable.getCause().getSuppressed()).isEqualTo(oom.getSuppressed());

        assertThat(rootThrowable.getCause().getCause()).isSameAs(oomCause);
    }