diff --git a/spring-core/src/test/java/org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.java b/spring-core/src/test/java/org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.java index 41db4dcff58..31b3d6938cb 100644 --- a/spring-core/src/test/java/org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.java +++ b/spring-core/src/test/java/org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2022 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. @@ -16,14 +16,16 @@ package org.springframework.core.io.support; -import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; -import java.util.ArrayList; +import java.io.UncheckedIOException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; -import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.core.io.Resource; @@ -33,8 +35,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; /** - * If this test case fails, uncomment diagnostics in the - * {@link #assertProtocolAndFilenames} method. + * Tests for {@link PathMatchingResourcePatternResolver}. + * + *

If tests fail, uncomment the diagnostics in {@link #assertFilenames(String, boolean, String...)}. * * @author Oliver Hutchison * @author Juergen Hoeller @@ -44,124 +47,138 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; */ class PathMatchingResourcePatternResolverTests { - private static final String[] CLASSES_IN_CORE_IO_SUPPORT = - new String[] {"EncodedResource.class", "LocalizedResourceHelper.class", - "PathMatchingResourcePatternResolver.class", "PropertiesLoaderSupport.class", - "PropertiesLoaderUtils.class", "ResourceArrayPropertyEditor.class", - "ResourcePatternResolver.class", "ResourcePatternUtils.class"}; + private static final String[] CLASSES_IN_CORE_IO_SUPPORT = { "EncodedResource.class", + "LocalizedResourceHelper.class", "PathMatchingResourcePatternResolver.class", "PropertiesLoaderSupport.class", + "PropertiesLoaderUtils.class", "ResourceArrayPropertyEditor.class", "ResourcePatternResolver.class", + "ResourcePatternUtils.class", "SpringFactoriesLoader.class" }; - private static final String[] TEST_CLASSES_IN_CORE_IO_SUPPORT = - new String[] {"PathMatchingResourcePatternResolverTests.class"}; + private static final String[] TEST_CLASSES_IN_CORE_IO_SUPPORT = { "PathMatchingResourcePatternResolverTests.class" }; - private static final String[] CLASSES_IN_REACTOR_UTIL_ANNOTATIONS = - new String[] {"NonNull.class", "NonNullApi.class", "Nullable.class"}; - - private PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + private static final String[] CLASSES_IN_REACTOR_UTIL_ANNOTATION = { "NonNull.class", "NonNullApi.class", "Nullable.class" }; - @Test - void invalidPrefixWithPatternElementInIt() throws IOException { - assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() -> - resolver.getResources("xx**:**/*.xy")); + private final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + + + @Nested + class InvalidPatterns { + + @Test + void invalidPrefixWithPatternElementInItThrowsException() { + assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() -> resolver.getResources("xx**:**/*.xy")); + } + } - @Test - void singleResourceOnFileSystem() throws IOException { - Resource[] resources = - resolver.getResources("org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.class"); - assertThat(resources.length).isEqualTo(1); - assertProtocolAndFilenames(resources, "file", "PathMatchingResourcePatternResolverTests.class"); + + @Nested + class FileSystemResources { + + @Test + void singleResourceOnFileSystem() { + String pattern = "org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.class"; + assertExactFilenames(pattern, "PathMatchingResourcePatternResolverTests.class"); + } + + @Test + void classpathStarWithPatternOnFileSystem() { + String pattern = "classpath*:org/springframework/core/io/sup*/*.class"; + String[] expectedFilenames = StringUtils.concatenateStringArrays(CLASSES_IN_CORE_IO_SUPPORT, TEST_CLASSES_IN_CORE_IO_SUPPORT); + assertFilenames(pattern, expectedFilenames); + } + + @Nested + class WithHashtagsInTheirFileNames { + + @Test + void usingClasspathStarProtocol() { + String pattern = "classpath*:org/springframework/core/io/**/resource#test*.txt"; + assertExactFilenames(pattern, "resource#test1.txt", "resource#test2.txt"); + } + + @Test + void usingFilePrototol() { + Path testResourcesDir = Paths.get("src/test/resources").toAbsolutePath(); + String pattern = String.format("file:%s/scanned-resources/**", testResourcesDir); + assertExactFilenames(pattern, "resource#test1.txt", "resource#test2.txt"); + } + + } + } - @Test - void singleResourceInJar() throws IOException { - Resource[] resources = resolver.getResources("org/reactivestreams/Publisher.class"); - assertThat(resources.length).isEqualTo(1); - assertProtocolAndFilenames(resources, "jar", "Publisher.class"); + + @Nested + class JarResources { + + @Test + void singleResourceInJar() { + String pattern = "org/reactivestreams/Publisher.class"; + assertExactFilenames(pattern, "Publisher.class"); + } + + @Test + void singleResourceInRootOfJar() { + String pattern = "aspectj_1_5_0.dtd"; + assertExactFilenames(pattern, "aspectj_1_5_0.dtd"); + } + + @Test + void classpathWithPatternInJar() { + String pattern = "classpath:reactor/util/annotation/*.class"; + assertExactFilenames(pattern, CLASSES_IN_REACTOR_UTIL_ANNOTATION); + } + + @Test + void classpathStarWithPatternInJar() { + String pattern = "classpath*:reactor/util/annotation/*.class"; + assertExactFilenames(pattern, CLASSES_IN_REACTOR_UTIL_ANNOTATION); + } + + // Fails in a native image -- https://github.com/oracle/graal/issues/5020 + @Test + void rootPatternRetrievalInJarFiles() throws IOException { + assertThat(resolver.getResources("classpath*:aspectj*.dtd")).extracting(Resource::getFilename) + .as("Could not find aspectj_1_5_0.dtd in the root of the aspectjweaver jar") + .containsExactly("aspectj_1_5_0.dtd"); + } + } - @Disabled - @Test - void classpathStarWithPatternOnFileSystem() throws IOException { - Resource[] resources = resolver.getResources("classpath*:org/springframework/core/io/sup*/*.class"); - // Have to exclude Clover-generated class files here, - // as we might be running as part of a Clover test run. - List noCloverResources = new ArrayList<>(); - for (Resource resource : resources) { - if (!resource.getFilename().contains("$__CLOVER_")) { - noCloverResources.add(resource); + + private void assertFilenames(String pattern, String... filenames) { + assertFilenames(pattern, false, filenames); + } + + private void assertExactFilenames(String pattern, String... filenames) { + assertFilenames(pattern, true, filenames); + } + + private void assertFilenames(String pattern, boolean exactly, String... filenames) { + try { + Resource[] resources = resolver.getResources(pattern); + List actualNames = Arrays.stream(resources) + .map(Resource::getFilename) + .sorted() + .collect(Collectors.toList()); + + // Uncomment the following if you encounter problems with matching against the file system. + // List expectedNames = Arrays.stream(filenames).sorted().toList(); + // System.out.println("----------------------------------------------------------------------"); + // System.out.println("Expected: " + expectedNames); + // System.out.println("Actual: " + actualNames); + // Arrays.stream(resources).forEach(System.out::println); + + if (exactly) { + assertThat(actualNames).as("subset of files found").containsExactlyInAnyOrder(filenames); + } + else { + assertThat(actualNames).as("subset of files found").contains(filenames); } } - resources = noCloverResources.toArray(new Resource[0]); - assertProtocolAndFilenames(resources, "file", - StringUtils.concatenateStringArrays(CLASSES_IN_CORE_IO_SUPPORT, TEST_CLASSES_IN_CORE_IO_SUPPORT)); - } - - @Test - void getResourcesOnFileSystemContainingHashtagsInTheirFileNames() throws IOException { - Resource[] resources = resolver.getResources("classpath*:org/springframework/core/io/**/resource#test*.txt"); - assertThat(resources).extracting(Resource::getFile).extracting(File::getName) - .containsExactlyInAnyOrder("resource#test1.txt", "resource#test2.txt"); - } - - @Test - void classpathWithPatternInJar() throws IOException { - Resource[] resources = resolver.getResources("classpath:reactor/util/annotation/*.class"); - assertProtocolAndFilenames(resources, "jar", CLASSES_IN_REACTOR_UTIL_ANNOTATIONS); - } - - @Test - void classpathStarWithPatternInJar() throws IOException { - Resource[] resources = resolver.getResources("classpath*:reactor/util/annotation/*.class"); - assertProtocolAndFilenames(resources, "jar", CLASSES_IN_REACTOR_UTIL_ANNOTATIONS); - } - - @Test - void rootPatternRetrievalInJarFiles() throws IOException { - Resource[] resources = resolver.getResources("classpath*:*.dtd"); - boolean found = false; - for (Resource resource : resources) { - if (resource.getFilename().equals("aspectj_1_5_0.dtd")) { - found = true; - break; - } + catch (IOException ex) { + throw new UncheckedIOException(ex); } - assertThat(found).as("Could not find aspectj_1_5_0.dtd in the root of the aspectjweaver jar").isTrue(); - } - - - private void assertProtocolAndFilenames(Resource[] resources, String protocol, String... filenames) - throws IOException { - - // Uncomment the following if you encounter problems with matching against the file system - // It shows file locations. -// String[] actualNames = new String[resources.length]; -// for (int i = 0; i < resources.length; i++) { -// actualNames[i] = resources[i].getFilename(); -// } -// List sortedActualNames = new LinkedList(Arrays.asList(actualNames)); -// List expectedNames = new LinkedList(Arrays.asList(fileNames)); -// Collections.sort(sortedActualNames); -// Collections.sort(expectedNames); -// -// System.out.println("-----------"); -// System.out.println("Expected: " + StringUtils.collectionToCommaDelimitedString(expectedNames)); -// System.out.println("Actual: " + StringUtils.collectionToCommaDelimitedString(sortedActualNames)); -// for (int i = 0; i < resources.length; i++) { -// System.out.println(resources[i]); -// } - - assertThat(resources.length).as("Correct number of files found").isEqualTo(filenames.length); - for (Resource resource : resources) { - String actualProtocol = resource.getURL().getProtocol(); - assertThat(actualProtocol).isEqualTo(protocol); - assertFilenameIn(resource, filenames); - } - } - - private void assertFilenameIn(Resource resource, String... filenames) { - String filename = resource.getFilename(); - assertThat(Arrays.stream(filenames).anyMatch(filename::endsWith)).as(resource + " does not have a filename that matches any of the specified names").isTrue(); } } diff --git a/spring-core/src/test/resources/scanned-resources/resource#test1.txt b/spring-core/src/test/resources/scanned-resources/resource#test1.txt new file mode 100644 index 00000000000..4f67a836c51 --- /dev/null +++ b/spring-core/src/test/resources/scanned-resources/resource#test1.txt @@ -0,0 +1 @@ +test 1 \ No newline at end of file diff --git a/spring-core/src/test/resources/scanned-resources/resource#test2.txt b/spring-core/src/test/resources/scanned-resources/resource#test2.txt new file mode 100644 index 00000000000..81403e41bd3 --- /dev/null +++ b/spring-core/src/test/resources/scanned-resources/resource#test2.txt @@ -0,0 +1 @@ +test 2 \ No newline at end of file