From 2e906c472036d3ba92bd2474356319d985be9b73 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Thu, 25 Apr 2024 10:40:54 +0200 Subject: [PATCH] Catch exceptions thrown during String format when collecting diagnostics Registers the LoggingLinesWriter only if debug logging is actually enabled. Closes gh-40500 --- .../SpringBootMockMvcBuilderCustomizer.java | 14 ++++++++++++-- .../web/servlet/mockmvc/ExampleController1.java | 17 ++++++++++++++++- .../MockMvcSpringBootTestIntegrationTests.java | 10 +++++++++- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/SpringBootMockMvcBuilderCustomizer.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/SpringBootMockMvcBuilderCustomizer.java index 24922c81f21..2c1a84c8be9 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/SpringBootMockMvcBuilderCustomizer.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/SpringBootMockMvcBuilderCustomizer.java @@ -52,6 +52,7 @@ import org.springframework.web.context.WebApplicationContext; * * @author Phillip Webb * @author Andy Wilkinson + * @author Moritz Halbritter * @since 1.4.0 */ public class SpringBootMockMvcBuilderCustomizer implements MockMvcBuilderCustomizer { @@ -100,7 +101,7 @@ public class SpringBootMockMvcBuilderCustomizer implements MockMvcBuilderCustomi return null; } if (this.print == MockMvcPrint.LOG_DEBUG) { - return new LoggingLinesWriter(); + return (LoggingLinesWriter.isDebugEnabled()) ? new LoggingLinesWriter() : null; } return new SystemLinesWriter(this.print); } @@ -192,7 +193,12 @@ public class SpringBootMockMvcBuilderCustomizer implements MockMvcBuilderCustomi if (value != null && value.getClass().isArray()) { value = CollectionUtils.arrayToList(value); } - this.lines.add(String.format("%17s = %s", label, value)); + try { + this.lines.add("%17s = %s".formatted(label, value)); + } + catch (RuntimeException ex) { + this.lines.add("%17s = << Exception '%s' occurred while formatting >>".formatted(label, ex)); + } } List getLines() { @@ -277,6 +283,10 @@ public class SpringBootMockMvcBuilderCustomizer implements MockMvcBuilderCustomi } } + static boolean isDebugEnabled() { + return logger.isDebugEnabled(); + } + } /** diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/servlet/mockmvc/ExampleController1.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/servlet/mockmvc/ExampleController1.java index 3e8aabfb424..3fbd7e093e3 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/servlet/mockmvc/ExampleController1.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/servlet/mockmvc/ExampleController1.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,11 +20,14 @@ import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.WebRequest; /** * Example {@link Controller @Controller} used with {@link WebMvcTest @WebMvcTest} tests. * * @author Phillip Webb + * @author Moritz Halbritter */ @RestController public class ExampleController1 { @@ -44,4 +47,16 @@ public class ExampleController1 { return "Hello"; } + @GetMapping("/formatting") + public String formatting(WebRequest request) { + Object formattingFails = new Object() { + @Override + public String toString() { + throw new IllegalStateException("Formatting failed"); + } + }; + request.setAttribute("attribute-1", formattingFails, RequestAttributes.SCOPE_SESSION); + return "formatting"; + } + } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/servlet/mockmvc/MockMvcSpringBootTestIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/servlet/mockmvc/MockMvcSpringBootTestIntegrationTests.java index 1ac7af71400..b267b7391af 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/servlet/mockmvc/MockMvcSpringBootTestIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/servlet/mockmvc/MockMvcSpringBootTestIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,6 +41,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. * {@link AutoConfigureMockMvc @AutoConfigureMockMvc} (i.e. full integration test). * * @author Phillip Webb + * @author Moritz Halbritter */ @SpringBootTest @AutoConfigureMockMvc(print = MockMvcPrint.SYSTEM_ERR, printOnlyOnFailure = false) @@ -83,4 +84,11 @@ class MockMvcSpringBootTestIntegrationTests { webTestClient.get().uri("/one").exchange().expectStatus().isOk().expectBody(String.class).isEqualTo("one"); } + @Test + void shouldNotFailIfFormattingValueThrowsException(CapturedOutput output) throws Exception { + this.mvc.perform(get("/formatting")).andExpect(content().string("formatting")).andExpect(status().isOk()); + assertThat(output).contains( + "Session Attrs = << Exception 'java.lang.IllegalStateException: Formatting failed' occurred while formatting >>"); + } + }