Merge branch '3.3.x' into 3.4.x

Closes gh-44697
This commit is contained in:
Andy Wilkinson
2025-03-12 15:48:36 +00:00
24 changed files with 210 additions and 408 deletions

View File

@@ -18,21 +18,26 @@ package org.springframework.boot.testsupport.classpath.resources;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.FileSystem;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.util.Assert;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.function.ThrowingConsumer;
/**
* A collection of resources.
@@ -50,27 +55,43 @@ class Resources {
}
Resources addPackage(Package root, String[] resourceNames) {
String packageName = root.getName();
Set<String> unmatchedNames = new HashSet<>(Arrays.asList(resourceNames));
withPathsForPackage(packageName, (packagePath) -> {
for (String resourceName : resourceNames) {
Path resource = packagePath.resolve(resourceName);
if (Files.exists(resource) && !Files.isDirectory(resource)) {
Path target = this.root.resolve(resourceName);
Path targetDirectory = target.getParent();
if (!Files.isDirectory(targetDirectory)) {
Files.createDirectories(targetDirectory);
}
Files.copy(resource, target);
register(resourceName, target, true);
unmatchedNames.remove(resourceName);
}
}
});
Assert.isTrue(unmatchedNames.isEmpty(),
"Package '" + packageName + "' did not contain resources: " + unmatchedNames);
return this;
}
private void withPathsForPackage(String packageName, ThrowingConsumer<Path> consumer) {
try {
Enumeration<URL> sources = getClass().getClassLoader().getResources(root.getName().replace(".", "/"));
for (URL source : Collections.list(sources)) {
Path sourceRoot = Paths.get(source.toURI());
for (String resourceName : resourceNames) {
Path resource = sourceRoot.resolve(resourceName);
if (Files.isRegularFile(resource)) {
Path target = this.root.resolve(resourceName);
Path targetDirectory = target.getParent();
if (!Files.isDirectory(targetDirectory)) {
Files.createDirectories(targetDirectory);
}
Files.copy(resource, target);
register(resourceName, target, true);
unmatchedNames.remove(resourceName);
List<URL> sources = Collections
.list(getClass().getClassLoader().getResources(packageName.replace(".", "/")));
for (URL source : sources) {
URI sourceUri = source.toURI();
try {
consumer.accept(Paths.get(sourceUri));
}
catch (FileSystemNotFoundException ex) {
try (FileSystem fileSystem = FileSystems.newFileSystem(sourceUri, Collections.emptyMap())) {
consumer.accept(Paths.get(sourceUri));
}
}
}
Assert.isTrue(unmatchedNames.isEmpty(),
"Package '" + root.getName() + "' did not contain resources: " + unmatchedNames);
}
catch (IOException ex) {
throw new UncheckedIOException(ex);
@@ -78,7 +99,6 @@ class Resources {
catch (URISyntaxException ex) {
throw new RuntimeException(ex);
}
return this;
}
Resources addResource(String name, String content, boolean additional) {

View File

@@ -1,196 +0,0 @@
/*
* Copyright 2012-2023 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.
* You may obtain a copy of the License at
*
* https://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.springframework.boot.testsupport.web.servlet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterRegistration;
import jakarta.servlet.Servlet;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRegistration;
import jakarta.servlet.SessionCookieConfig;
import org.springframework.mock.web.MockSessionCookieConfig;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
/**
* Base class for Mock {@code ServletWebServer} implementations. Reduces the amount of
* code that would otherwise be duplicated in {@code spring-boot},
* {@code spring-boot-autoconfigure} and {@code spring-boot-actuator}.
*
* @author Phillip Webb
* @author Andy Wilkinson
*/
public abstract class MockServletWebServer {
private ServletContext servletContext;
private final Initializer[] initializers;
private final List<RegisteredServlet> registeredServlets = new ArrayList<>();
private final List<RegisteredFilter> registeredFilters = new ArrayList<>();
private final int port;
public MockServletWebServer(Initializer[] initializers, int port) {
this.initializers = initializers;
this.port = port;
initialize();
}
private void initialize() {
try {
this.servletContext = mock(ServletContext.class);
lenient().doAnswer((invocation) -> {
RegisteredServlet registeredServlet = new RegisteredServlet(invocation.getArgument(1));
MockServletWebServer.this.registeredServlets.add(registeredServlet);
return registeredServlet.getRegistration();
}).when(this.servletContext).addServlet(anyString(), any(Servlet.class));
lenient().doAnswer((invocation) -> {
RegisteredFilter registeredFilter = new RegisteredFilter(invocation.getArgument(1));
MockServletWebServer.this.registeredFilters.add(registeredFilter);
return registeredFilter.getRegistration();
}).when(this.servletContext).addFilter(anyString(), any(Filter.class));
final SessionCookieConfig sessionCookieConfig = new MockSessionCookieConfig();
given(this.servletContext.getSessionCookieConfig()).willReturn(sessionCookieConfig);
final Map<String, String> initParameters = new HashMap<>();
lenient().doAnswer((invocation) -> {
initParameters.put(invocation.getArgument(0), invocation.getArgument(1));
return null;
}).when(this.servletContext).setInitParameter(anyString(), anyString());
given(this.servletContext.getInitParameterNames())
.willReturn(Collections.enumeration(initParameters.keySet()));
lenient().doAnswer((invocation) -> initParameters.get(invocation.getArgument(0)))
.when(this.servletContext)
.getInitParameter(anyString());
given(this.servletContext.getAttributeNames()).willReturn(Collections.emptyEnumeration());
for (Initializer initializer : this.initializers) {
initializer.onStartup(this.servletContext);
}
}
catch (ServletException ex) {
throw new RuntimeException(ex);
}
}
public void stop() {
this.servletContext = null;
this.registeredServlets.clear();
}
public ServletContext getServletContext() {
return this.servletContext;
}
public Servlet[] getServlets() {
Servlet[] servlets = new Servlet[this.registeredServlets.size()];
Arrays.setAll(servlets, (i) -> this.registeredServlets.get(i).getServlet());
return servlets;
}
public RegisteredServlet getRegisteredServlet(int index) {
return getRegisteredServlets().get(index);
}
public List<RegisteredServlet> getRegisteredServlets() {
return this.registeredServlets;
}
public RegisteredFilter getRegisteredFilters(int index) {
return getRegisteredFilters().get(index);
}
public List<RegisteredFilter> getRegisteredFilters() {
return this.registeredFilters;
}
public int getPort() {
return this.port;
}
/**
* A registered servlet.
*/
public static class RegisteredServlet {
private final Servlet servlet;
private final ServletRegistration.Dynamic registration;
public RegisteredServlet(Servlet servlet) {
this.servlet = servlet;
this.registration = mock(ServletRegistration.Dynamic.class);
}
public ServletRegistration.Dynamic getRegistration() {
return this.registration;
}
public Servlet getServlet() {
return this.servlet;
}
}
/**
* A registered filter.
*/
public static class RegisteredFilter {
private final Filter filter;
private final FilterRegistration.Dynamic registration;
public RegisteredFilter(Filter filter) {
this.filter = filter;
this.registration = mock(FilterRegistration.Dynamic.class);
}
public FilterRegistration.Dynamic getRegistration() {
return this.registration;
}
public Filter getFilter() {
return this.filter;
}
}
/**
* Initializer (usually implement by adapting {@code Initializer}).
*/
@FunctionalInterface
protected interface Initializer {
void onStartup(ServletContext context) throws ServletException;
}
}

View File

@@ -1,56 +0,0 @@
/*
* Copyright 2012-2023 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.
* You may obtain a copy of the License at
*
* https://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.springframework.boot.testsupport.web.servlet;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockSessionCookieConfig;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link MockServletWebServer}.
*
* @author Stephane Nicoll
*/
class MockServletWebServerTests {
@Test
void servletContextIsConfigured() {
MockServletWebServer server = TestMockServletWebServer.create();
assertThat(server.getServletContext()).isNotNull();
}
@Test
void servletContextHasSessionCookieConfigConfigured() {
MockServletWebServer server = TestMockServletWebServer.create();
assertThat(server.getServletContext().getSessionCookieConfig()).isInstanceOf(MockSessionCookieConfig.class);
}
private static final class TestMockServletWebServer extends MockServletWebServer {
private TestMockServletWebServer(Initializer[] initializers, int port) {
super(initializers, port);
}
static MockServletWebServer create(Initializer... initializers) {
return new TestMockServletWebServer(initializers, 8080);
}
}
}