mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0a471c95c1 |
@@ -36,4 +36,4 @@ runtime:
|
||||
failure_level: warn
|
||||
ui:
|
||||
bundle:
|
||||
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.20/ui-bundle.zip
|
||||
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.18/ui-bundle.zip
|
||||
|
||||
@@ -8,9 +8,9 @@ use https://www.gebish.org/[Geb] to make our tests even Groovy-er.
|
||||
== Why Geb and MockMvc?
|
||||
|
||||
Geb is backed by WebDriver, so it offers many of the
|
||||
xref:testing/mockmvc/htmlunit/webdriver.adoc#mockmvc-server-htmlunit-webdriver-why[same benefits]
|
||||
that we get from WebDriver. However, Geb makes things even easier by taking care of some
|
||||
of the boilerplate code for us.
|
||||
xref:testing/mockmvc/htmlunit/webdriver.adoc#spring-mvc-test-server-htmlunit-webdriver-why[same benefits] that we get from
|
||||
WebDriver. However, Geb makes things even easier by taking care of some of the
|
||||
boilerplate code for us.
|
||||
|
||||
[[mockmvc-server-htmlunit-geb-setup]]
|
||||
== MockMvc and Geb Setup
|
||||
@@ -28,8 +28,7 @@ def setup() {
|
||||
----
|
||||
|
||||
NOTE: This is a simple example of using `MockMvcHtmlUnitDriverBuilder`. For more advanced
|
||||
usage, see
|
||||
xref:testing/mockmvc/htmlunit/webdriver.adoc#mockmvc-server-htmlunit-webdriver-advanced-builder[Advanced `MockMvcHtmlUnitDriverBuilder`].
|
||||
usage, see xref:testing/mockmvc/htmlunit/webdriver.adoc#spring-mvc-test-server-htmlunit-webdriver-advanced-builder[Advanced `MockMvcHtmlUnitDriverBuilder`].
|
||||
|
||||
This ensures that any URL referencing `localhost` as the server is directed to our
|
||||
`MockMvc` instance without the need for a real HTTP connection. Any other URL is
|
||||
@@ -63,10 +62,10 @@ forwarded to the current page object. This removes a lot of the boilerplate code
|
||||
needed when using WebDriver directly.
|
||||
|
||||
As with direct WebDriver usage, this improves on the design of our
|
||||
xref:testing/mockmvc/htmlunit/mah.adoc#mockmvc-server-htmlunit-mah-usage[HtmlUnit test]
|
||||
by using the Page Object Pattern. As mentioned previously, we can use the Page Object
|
||||
Pattern with HtmlUnit and WebDriver, but it is even easier with Geb. Consider our new
|
||||
Groovy-based `CreateMessagePage` implementation:
|
||||
xref:testing/mockmvc/htmlunit/mah.adoc#spring-mvc-test-server-htmlunit-mah-usage[HtmlUnit test] by using the Page Object
|
||||
Pattern. As mentioned previously, we can use the Page Object Pattern with HtmlUnit and
|
||||
WebDriver, but it is even easier with Geb. Consider our new Groovy-based
|
||||
`CreateMessagePage` implementation:
|
||||
|
||||
[source,groovy]
|
||||
----
|
||||
|
||||
@@ -7,7 +7,8 @@ to use the raw HtmlUnit libraries.
|
||||
[[mockmvc-server-htmlunit-mah-setup]]
|
||||
== MockMvc and HtmlUnit Setup
|
||||
|
||||
First, make sure that you have included a test dependency on `org.htmlunit:htmlunit`.
|
||||
First, make sure that you have included a test dependency on
|
||||
`org.htmlunit:htmlunit`.
|
||||
|
||||
We can easily create an HtmlUnit `WebClient` that integrates with MockMvc by using the
|
||||
`MockMvcWebClientBuilder`, as follows:
|
||||
@@ -44,7 +45,7 @@ Kotlin::
|
||||
======
|
||||
|
||||
NOTE: This is a simple example of using `MockMvcWebClientBuilder`. For advanced usage,
|
||||
see <<mockmvc-server-htmlunit-mah-advanced-builder>>.
|
||||
see xref:testing/mockmvc/htmlunit/mah.adoc#spring-mvc-test-server-htmlunit-mah-advanced-builder[Advanced `MockMvcWebClientBuilder`].
|
||||
|
||||
This ensures that any URL that references `localhost` as the server is directed to our
|
||||
`MockMvc` instance without the need for a real HTTP connection. Any other URL is
|
||||
@@ -76,7 +77,7 @@ Kotlin::
|
||||
======
|
||||
|
||||
NOTE: The default context path is `""`. Alternatively, we can specify the context path,
|
||||
as described in <<mockmvc-server-htmlunit-mah-advanced-builder>>.
|
||||
as described in xref:testing/mockmvc/htmlunit/mah.adoc#spring-mvc-test-server-htmlunit-mah-advanced-builder[Advanced `MockMvcWebClientBuilder`].
|
||||
|
||||
Once we have a reference to the `HtmlPage`, we can then fill out the form and submit it
|
||||
to create a message, as the following example shows:
|
||||
@@ -143,10 +144,10 @@ Kotlin::
|
||||
======
|
||||
|
||||
The preceding code improves on our
|
||||
xref:testing/mockmvc/htmlunit/why.adoc#mockmvc-server-htmlunit-why[MockMvc test] in a
|
||||
number of ways. First, we no longer have to explicitly verify our form and then create a
|
||||
request that looks like the form. Instead, we request the form, fill it out, and submit
|
||||
it, thereby significantly reducing the overhead.
|
||||
xref:testing/mockmvc/htmlunit/why.adoc#spring-mvc-test-server-htmlunit-mock-mvc-test[MockMvc test] in a number of ways.
|
||||
First, we no longer have to explicitly verify our form and then create a request that
|
||||
looks like the form. Instead, we request the form, fill it out, and submit it, thereby
|
||||
significantly reducing the overhead.
|
||||
|
||||
Another important factor is that https://htmlunit.sourceforge.io/javascript.html[HtmlUnit
|
||||
uses the Mozilla Rhino engine] to evaluate JavaScript. This means that we can also test
|
||||
|
||||
@@ -203,7 +203,7 @@ Kotlin::
|
||||
======
|
||||
|
||||
NOTE: This is a simple example of using `MockMvcHtmlUnitDriverBuilder`. For more advanced
|
||||
usage, see <<mockmvc-server-htmlunit-webdriver-advanced-builder>>.
|
||||
usage, see xref:testing/mockmvc/htmlunit/webdriver.adoc#spring-mvc-test-server-htmlunit-webdriver-advanced-builder[Advanced `MockMvcHtmlUnitDriverBuilder`].
|
||||
|
||||
The preceding example ensures that any URL that references `localhost` as the server is
|
||||
directed to our `MockMvc` instance without the need for a real HTTP connection. Any other
|
||||
@@ -259,11 +259,10 @@ Kotlin::
|
||||
======
|
||||
--
|
||||
|
||||
This improves on the design of our
|
||||
xref:testing/mockmvc/htmlunit/mah.adoc#mockmvc-server-htmlunit-mah-usage[HtmlUnit test]
|
||||
This improves on the design of our xref:testing/mockmvc/htmlunit/mah.adoc#spring-mvc-test-server-htmlunit-mah-usage[HtmlUnit test]
|
||||
by leveraging the Page Object Pattern. As we mentioned in
|
||||
<<mockmvc-server-htmlunit-webdriver-why>>, we can use the Page Object Pattern with
|
||||
HtmlUnit, but it is much easier with WebDriver. Consider the following
|
||||
xref:testing/mockmvc/htmlunit/webdriver.adoc#mockmvc-server-htmlunit-webdriver-why[Why WebDriver and MockMvc?], we can use the Page Object Pattern
|
||||
with HtmlUnit, but it is much easier with WebDriver. Consider the following
|
||||
`CreateMessagePage` implementation:
|
||||
|
||||
--
|
||||
|
||||
@@ -60,7 +60,7 @@ assume our form looks like the following snippet:
|
||||
</form>
|
||||
----
|
||||
|
||||
How do we ensure that our form produces the correct request to create a new message? A
|
||||
How do we ensure that our form produce the correct request to create a new message? A
|
||||
naive attempt might resemble the following:
|
||||
|
||||
[tabs]
|
||||
@@ -154,7 +154,7 @@ validation.
|
||||
[[mockmvc-server-htmlunit-why-integration]]
|
||||
== Integration Testing to the Rescue?
|
||||
|
||||
To resolve the issues mentioned above, we could perform end-to-end integration testing,
|
||||
To resolve the issues mentioned earlier, we could perform end-to-end integration testing,
|
||||
but this has some drawbacks. Consider testing the view that lets us page through the
|
||||
messages. We might need the following tests:
|
||||
|
||||
@@ -171,7 +171,7 @@ leads to a number of additional challenges:
|
||||
* Testing can become slow, since each test would need to ensure that the database is in
|
||||
the correct state.
|
||||
* Since our database needs to be in a specific state, we cannot run tests in parallel.
|
||||
* Performing assertions on items such as auto-generated IDs, timestamps, and others can
|
||||
* Performing assertions on such items as auto-generated IDs, timestamps, and others can
|
||||
be difficult.
|
||||
|
||||
These challenges do not mean that we should abandon end-to-end integration testing
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=6.2.14
|
||||
version=6.2.13
|
||||
|
||||
org.gradle.caching=true
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
|
||||
+1
@@ -32,6 +32,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Juergen Hoeller
|
||||
* @author Rob Harrop
|
||||
* @since 1.1
|
||||
* @see PropertiesBeanDefinitionReader
|
||||
* @see org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader
|
||||
*/
|
||||
public abstract class BeanDefinitionReaderUtils {
|
||||
|
||||
+1
@@ -43,6 +43,7 @@ import org.springframework.core.AliasRegistry;
|
||||
* @see DefaultListableBeanFactory
|
||||
* @see org.springframework.context.support.GenericApplicationContext
|
||||
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
|
||||
* @see PropertiesBeanDefinitionReader
|
||||
*/
|
||||
public interface BeanDefinitionRegistry extends AliasRegistry {
|
||||
|
||||
|
||||
+3
-3
@@ -74,10 +74,10 @@ import org.springframework.util.StringUtils;
|
||||
* @author Rob Harrop
|
||||
* @since 26.11.2003
|
||||
* @see DefaultListableBeanFactory
|
||||
* @deprecated in favor of Spring's common bean definition formats and/or
|
||||
* custom BeanDefinitionReader implementations
|
||||
* @deprecated as of 5.3, in favor of Spring's common bean definition formats
|
||||
* and/or custom reader implementations
|
||||
*/
|
||||
@Deprecated(since = "5.3")
|
||||
@Deprecated
|
||||
public class PropertiesBeanDefinitionReader extends AbstractBeanDefinitionReader {
|
||||
|
||||
/**
|
||||
|
||||
Vendored
+20
-37
@@ -77,7 +77,7 @@ public class CaffeineCacheManager implements CacheManager {
|
||||
|
||||
private boolean allowNullValues = true;
|
||||
|
||||
private volatile boolean dynamic = true;
|
||||
private boolean dynamic = true;
|
||||
|
||||
private final Map<String, Cache> cacheMap = new ConcurrentHashMap<>(16);
|
||||
|
||||
@@ -102,15 +102,10 @@ public class CaffeineCacheManager implements CacheManager {
|
||||
|
||||
/**
|
||||
* Specify the set of cache names for this CacheManager's 'static' mode.
|
||||
* <p>The number of caches and their names will be fixed after a call
|
||||
* to this method, with no creation of further cache regions at runtime.
|
||||
* <p>Note that this method replaces existing caches of the given names
|
||||
* and prevents the creation of further cache regions from here on - but
|
||||
* does <i>not</i> remove unrelated existing caches. For a full reset,
|
||||
* consider calling {@link #resetCaches()} before calling this method.
|
||||
* <p>Calling this method with a {@code null} collection argument resets
|
||||
* the mode to 'dynamic', allowing for further creation of caches again.
|
||||
* @see #resetCaches()
|
||||
* <p>The number of caches and their names will be fixed after a call to this method,
|
||||
* with no creation of further cache regions at runtime.
|
||||
* <p>Calling this with a {@code null} collection argument resets the
|
||||
* mode to 'dynamic', allowing for further creation of caches again.
|
||||
*/
|
||||
public void setCacheNames(@Nullable Collection<String> cacheNames) {
|
||||
if (cacheNames != null) {
|
||||
@@ -250,6 +245,11 @@ public class CaffeineCacheManager implements CacheManager {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<String> getCacheNames() {
|
||||
return Collections.unmodifiableSet(this.cacheMap.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Cache getCache(String name) {
|
||||
@@ -260,33 +260,6 @@ public class CaffeineCacheManager implements CacheManager {
|
||||
return cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getCacheNames() {
|
||||
return Collections.unmodifiableSet(this.cacheMap.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset this cache manager's caches, removing them completely for on-demand
|
||||
* re-creation in 'dynamic' mode, or simply clearing their entries otherwise.
|
||||
* @since 6.2.14
|
||||
*/
|
||||
public void resetCaches() {
|
||||
this.cacheMap.values().forEach(Cache::clear);
|
||||
if (this.dynamic) {
|
||||
this.cacheMap.keySet().retainAll(this.customCacheNames);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified cache from this cache manager, applying to
|
||||
* custom caches as well as dynamically registered caches at runtime.
|
||||
* @param name the name of the cache
|
||||
* @since 6.1.15
|
||||
*/
|
||||
public void removeCache(String name) {
|
||||
this.customCacheNames.remove(name);
|
||||
this.cacheMap.remove(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given native Caffeine Cache instance with this cache manager,
|
||||
@@ -330,6 +303,16 @@ public class CaffeineCacheManager implements CacheManager {
|
||||
this.cacheMap.put(name, adaptCaffeineCache(name, cache));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified cache from this cache manager, applying to
|
||||
* custom caches as well as dynamically registered caches at runtime.
|
||||
* @param name the name of the cache
|
||||
* @since 6.1.15
|
||||
*/
|
||||
public void removeCache(String name) {
|
||||
this.customCacheNames.remove(name);
|
||||
this.cacheMap.remove(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapt the given new native Caffeine Cache instance to Spring's {@link Cache}
|
||||
|
||||
+3
-3
@@ -25,7 +25,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.Aware;
|
||||
import org.springframework.core.MethodClassKey;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
@@ -98,8 +98,8 @@ public abstract class AbstractFallbackJCacheOperationSource implements JCacheOpe
|
||||
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
|
||||
return null;
|
||||
}
|
||||
// Skip setBeanFactory method on BeanFactoryAware.
|
||||
if (method.getDeclaringClass() == BeanFactoryAware.class) {
|
||||
// Skip methods declared on BeanFactoryAware and co.
|
||||
if (method.getDeclaringClass().isInterface() && Aware.class.isAssignableFrom(method.getDeclaringClass())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+2
-22
@@ -24,6 +24,7 @@ import com.github.benmanes.caffeine.cache.CaffeineSpec;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.support.SimpleValueWrapper;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -41,7 +42,7 @@ class CaffeineCacheManagerTests {
|
||||
@Test
|
||||
@SuppressWarnings("cast")
|
||||
void dynamicMode() {
|
||||
CaffeineCacheManager cm = new CaffeineCacheManager();
|
||||
CacheManager cm = new CaffeineCacheManager();
|
||||
|
||||
Cache cache1 = cm.getCache("c1");
|
||||
assertThat(cache1).isInstanceOf(CaffeineCache.class);
|
||||
@@ -75,14 +76,6 @@ class CaffeineCacheManagerTests {
|
||||
cache1.evict("key3");
|
||||
assertThat(cache1.get("key3", () -> (String) null)).isNull();
|
||||
assertThat(cache1.get("key3", () -> (String) null)).isNull();
|
||||
|
||||
cm.removeCache("c1");
|
||||
assertThat(cm.getCache("c1")).isNotSameAs(cache1);
|
||||
assertThat(cm.getCache("c2")).isSameAs(cache2);
|
||||
|
||||
cm.resetCaches();
|
||||
assertThat(cm.getCache("c1")).isNotSameAs(cache1);
|
||||
assertThat(cm.getCache("c2")).isNotSameAs(cache2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -138,24 +131,11 @@ class CaffeineCacheManagerTests {
|
||||
|
||||
cm.setAllowNullValues(true);
|
||||
Cache cache1y = cm.getCache("c1");
|
||||
Cache cache2y = cm.getCache("c2");
|
||||
|
||||
cache1y.put("key3", null);
|
||||
assertThat(cache1y.get("key3").get()).isNull();
|
||||
cache1y.evict("key3");
|
||||
assertThat(cache1y.get("key3")).isNull();
|
||||
cache2y.put("key4", "value4");
|
||||
assertThat(cache2y.get("key4").get()).isEqualTo("value4");
|
||||
|
||||
cm.removeCache("c1");
|
||||
assertThat(cm.getCache("c1")).isNull();
|
||||
assertThat(cm.getCache("c2")).isSameAs(cache2y);
|
||||
assertThat(cache2y.get("key4").get()).isEqualTo("value4");
|
||||
|
||||
cm.resetCaches();
|
||||
assertThat(cm.getCache("c1")).isNull();
|
||||
assertThat(cm.getCache("c2")).isSameAs(cache2y);
|
||||
assertThat(cache2y.get("key4")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Vendored
+10
-27
@@ -54,7 +54,7 @@ public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderA
|
||||
|
||||
private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16);
|
||||
|
||||
private volatile boolean dynamic = true;
|
||||
private boolean dynamic = true;
|
||||
|
||||
private boolean allowNullValues = true;
|
||||
|
||||
@@ -82,15 +82,10 @@ public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderA
|
||||
|
||||
/**
|
||||
* Specify the set of cache names for this CacheManager's 'static' mode.
|
||||
* <p>The number of caches and their names will be fixed after a call
|
||||
* to this method, with no creation of further cache regions at runtime.
|
||||
* <p>Note that this method replaces existing caches of the given names
|
||||
* and prevents the creation of further cache regions from here on - but
|
||||
* does <i>not</i> remove unrelated existing caches. For a full reset,
|
||||
* consider calling {@link #resetCaches()} before calling this method.
|
||||
* <p>Calling this method with a {@code null} collection argument resets
|
||||
* the mode to 'dynamic', allowing for further creation of caches again.
|
||||
* @see #resetCaches()
|
||||
* <p>The number of caches and their names will be fixed after a call to this method,
|
||||
* with no creation of further cache regions at runtime.
|
||||
* <p>Calling this with a {@code null} collection argument resets the
|
||||
* mode to 'dynamic', allowing for further creation of caches again.
|
||||
*/
|
||||
public void setCacheNames(@Nullable Collection<String> cacheNames) {
|
||||
if (cacheNames != null) {
|
||||
@@ -165,6 +160,11 @@ public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderA
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<String> getCacheNames() {
|
||||
return Collections.unmodifiableSet(this.cacheMap.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Cache getCache(String name) {
|
||||
@@ -175,23 +175,6 @@ public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderA
|
||||
return cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getCacheNames() {
|
||||
return Collections.unmodifiableSet(this.cacheMap.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset this cache manager's caches, removing them completely for on-demand
|
||||
* re-creation in 'dynamic' mode, or simply clearing their entries otherwise.
|
||||
* @since 6.2.14
|
||||
*/
|
||||
public void resetCaches() {
|
||||
this.cacheMap.values().forEach(Cache::clear);
|
||||
if (this.dynamic) {
|
||||
this.cacheMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified cache from this cache manager.
|
||||
* @param name the name of the cache
|
||||
|
||||
+3
-3
@@ -27,7 +27,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.Aware;
|
||||
import org.springframework.core.MethodClassKey;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -140,8 +140,8 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
|
||||
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
|
||||
return null;
|
||||
}
|
||||
// Skip setBeanFactory method on BeanFactoryAware.
|
||||
if (method.getDeclaringClass() == BeanFactoryAware.class) {
|
||||
// Skip methods declared on BeanFactoryAware and co.
|
||||
if (method.getDeclaringClass().isInterface() && Aware.class.isAssignableFrom(method.getDeclaringClass())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+24
-6
@@ -18,6 +18,8 @@ package org.springframework.cache.support;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
@@ -30,29 +32,45 @@ import org.springframework.lang.Nullable;
|
||||
* for disabling caching, typically used for backing cache declarations
|
||||
* without an actual backing store.
|
||||
*
|
||||
* <p>This implementation will simply accept any items into the cache,
|
||||
* not actually storing them.
|
||||
* <p>Will simply accept any items into the cache not actually storing them.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Stephane Nicoll
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see NoOpCache
|
||||
*/
|
||||
public class NoOpCacheManager implements CacheManager {
|
||||
|
||||
private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16);
|
||||
private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<>(16);
|
||||
|
||||
private final Set<String> cacheNames = new LinkedHashSet<>(16);
|
||||
|
||||
|
||||
/**
|
||||
* This implementation always returns a {@link Cache} implementation that will not store items.
|
||||
* Additionally, the request cache will be remembered by the manager for consistency.
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public Cache getCache(String name) {
|
||||
return this.cacheMap.computeIfAbsent(name, NoOpCache::new);
|
||||
Cache cache = this.caches.get(name);
|
||||
if (cache == null) {
|
||||
this.caches.computeIfAbsent(name, NoOpCache::new);
|
||||
synchronized (this.cacheNames) {
|
||||
this.cacheNames.add(name);
|
||||
}
|
||||
}
|
||||
return this.caches.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation returns the name of the caches previously requested.
|
||||
*/
|
||||
@Override
|
||||
public Collection<String> getCacheNames() {
|
||||
return Collections.unmodifiableSet(this.cacheMap.keySet());
|
||||
synchronized (this.cacheNames) {
|
||||
return Collections.unmodifiableSet(this.cacheNames);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
@@ -227,6 +227,7 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl
|
||||
* @param beanFactory the bean factory to load bean definitions into
|
||||
* @throws BeansException if parsing of the bean definitions failed
|
||||
* @throws IOException if loading of bean definition files failed
|
||||
* @see org.springframework.beans.factory.support.PropertiesBeanDefinitionReader
|
||||
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
|
||||
*/
|
||||
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
|
||||
|
||||
+3
@@ -78,6 +78,8 @@ import org.springframework.util.Assert;
|
||||
* GenericApplicationContext ctx = new GenericApplicationContext();
|
||||
* XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx);
|
||||
* xmlReader.loadBeanDefinitions(new ClassPathResource("applicationContext.xml"));
|
||||
* PropertiesBeanDefinitionReader propReader = new PropertiesBeanDefinitionReader(ctx);
|
||||
* propReader.loadBeanDefinitions(new ClassPathResource("otherBeans.properties"));
|
||||
* ctx.refresh();
|
||||
*
|
||||
* MyBean myBean = (MyBean) ctx.getBean("myBean");
|
||||
@@ -99,6 +101,7 @@ import org.springframework.util.Assert;
|
||||
* @see #registerBeanDefinition
|
||||
* @see #refresh()
|
||||
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
|
||||
* @see org.springframework.beans.factory.support.PropertiesBeanDefinitionReader
|
||||
*/
|
||||
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
|
||||
|
||||
|
||||
+2
-22
@@ -19,6 +19,7 @@ package org.springframework.cache.concurrent;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -30,7 +31,7 @@ class ConcurrentMapCacheManagerTests {
|
||||
|
||||
@Test
|
||||
void testDynamicMode() {
|
||||
ConcurrentMapCacheManager cm = new ConcurrentMapCacheManager();
|
||||
CacheManager cm = new ConcurrentMapCacheManager();
|
||||
Cache cache1 = cm.getCache("c1");
|
||||
assertThat(cache1).isInstanceOf(ConcurrentMapCache.class);
|
||||
Cache cache1again = cm.getCache("c1");
|
||||
@@ -64,14 +65,6 @@ class ConcurrentMapCacheManagerTests {
|
||||
assertThat(cache1.get("key3").get()).isNull();
|
||||
cache1.evict("key3");
|
||||
assertThat(cache1.get("key3")).isNull();
|
||||
|
||||
cm.removeCache("c1");
|
||||
assertThat(cm.getCache("c1")).isNotSameAs(cache1);
|
||||
assertThat(cm.getCache("c2")).isSameAs(cache2);
|
||||
|
||||
cm.resetCaches();
|
||||
assertThat(cm.getCache("c1")).isNotSameAs(cache1);
|
||||
assertThat(cm.getCache("c2")).isNotSameAs(cache2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -114,24 +107,11 @@ class ConcurrentMapCacheManagerTests {
|
||||
|
||||
cm.setAllowNullValues(true);
|
||||
Cache cache1y = cm.getCache("c1");
|
||||
Cache cache2y = cm.getCache("c2");
|
||||
|
||||
cache1y.put("key3", null);
|
||||
assertThat(cache1y.get("key3").get()).isNull();
|
||||
cache1y.evict("key3");
|
||||
assertThat(cache1y.get("key3")).isNull();
|
||||
cache2y.put("key4", "value4");
|
||||
assertThat(cache2y.get("key4").get()).isEqualTo("value4");
|
||||
|
||||
cm.removeCache("c1");
|
||||
assertThat(cm.getCache("c1")).isNull();
|
||||
assertThat(cm.getCache("c2")).isSameAs(cache2y);
|
||||
assertThat(cache2y.get("key4").get()).isEqualTo("value4");
|
||||
|
||||
cm.resetCaches();
|
||||
assertThat(cm.getCache("c1")).isNull();
|
||||
assertThat(cm.getCache("c2")).isSameAs(cache2y);
|
||||
assertThat(cache2y.get("key4")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+2
-165
@@ -20,10 +20,8 @@ import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.AbstractCollection;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
@@ -31,8 +29,6 @@ import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.Spliterator;
|
||||
import java.util.Spliterators;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -108,18 +104,6 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
|
||||
@Nullable
|
||||
private volatile Set<Map.Entry<K, V>> entrySet;
|
||||
|
||||
/**
|
||||
* Late binding key set.
|
||||
*/
|
||||
@Nullable
|
||||
private Set<K> keySet;
|
||||
|
||||
/**
|
||||
* Late binding values collection.
|
||||
*/
|
||||
@Nullable
|
||||
private Collection<V> values;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code ConcurrentReferenceHashMap} instance.
|
||||
@@ -544,26 +528,6 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
|
||||
return entrySet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
Set<K> keySet = this.keySet;
|
||||
if (keySet == null) {
|
||||
keySet = new KeySet();
|
||||
this.keySet = keySet;
|
||||
}
|
||||
return keySet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
Collection<V> values = this.values;
|
||||
if (values == null) {
|
||||
values = new Values();
|
||||
this.values = values;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private <T> T doTask(@Nullable Object key, Task<T> task) {
|
||||
int hash = getHash(key);
|
||||
@@ -1005,7 +969,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
|
||||
/**
|
||||
* Internal entry-set implementation.
|
||||
*/
|
||||
private final class EntrySet extends AbstractSet<Map.Entry<K, V>> {
|
||||
private class EntrySet extends AbstractSet<Map.Entry<K, V>> {
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<K, V>> iterator() {
|
||||
@@ -1041,140 +1005,13 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
|
||||
public void clear() {
|
||||
ConcurrentReferenceHashMap.this.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<Map.Entry<K, V>> spliterator() {
|
||||
return Spliterators.spliterator(this, Spliterator.DISTINCT | Spliterator.CONCURRENT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal key-set implementation.
|
||||
*/
|
||||
private final class KeySet extends AbstractSet<K> {
|
||||
|
||||
@Override
|
||||
public Iterator<K> iterator() {
|
||||
return new KeyIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return ConcurrentReferenceHashMap.this.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return ConcurrentReferenceHashMap.this.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
ConcurrentReferenceHashMap.this.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object k) {
|
||||
return ConcurrentReferenceHashMap.this.containsKey(k);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<K> spliterator() {
|
||||
return Spliterators.spliterator(this, Spliterator.DISTINCT | Spliterator.CONCURRENT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal key iterator implementation.
|
||||
*/
|
||||
private final class KeyIterator implements Iterator<K> {
|
||||
|
||||
private final Iterator<Map.Entry<K, V>> iterator = entrySet().iterator();
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
this.iterator.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K next() {
|
||||
return this.iterator.next().getKey();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal values collection implementation.
|
||||
*/
|
||||
private final class Values extends AbstractCollection<V> {
|
||||
|
||||
@Override
|
||||
public Iterator<V> iterator() {
|
||||
return new ValueIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return ConcurrentReferenceHashMap.this.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return ConcurrentReferenceHashMap.this.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
ConcurrentReferenceHashMap.this.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object v) {
|
||||
return ConcurrentReferenceHashMap.this.containsValue(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<V> spliterator() {
|
||||
return Spliterators.spliterator(this, Spliterator.CONCURRENT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal value iterator implementation.
|
||||
*/
|
||||
private final class ValueIterator implements Iterator<V> {
|
||||
|
||||
private final Iterator<Map.Entry<K, V>> iterator = entrySet().iterator();
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
this.iterator.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V next() {
|
||||
return this.iterator.next().getValue();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal entry iterator implementation.
|
||||
*/
|
||||
private final class EntryIterator implements Iterator<Map.Entry<K, V>> {
|
||||
private class EntryIterator implements Iterator<Map.Entry<K, V>> {
|
||||
|
||||
private int segmentIndex;
|
||||
|
||||
|
||||
@@ -145,32 +145,26 @@ final class UnmodifiableMultiValueMap<K,V> implements MultiValueMap<K,V>, Serial
|
||||
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
Set<K> keySet = this.keySet;
|
||||
if (keySet == null) {
|
||||
keySet = Collections.unmodifiableSet(this.delegate.keySet());
|
||||
this.keySet = keySet;
|
||||
if (this.keySet == null) {
|
||||
this.keySet = Collections.unmodifiableSet(this.delegate.keySet());
|
||||
}
|
||||
return keySet;
|
||||
return this.keySet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<K, List<V>>> entrySet() {
|
||||
Set<Entry<K, List<V>>> entrySet = this.entrySet;
|
||||
if (entrySet == null) {
|
||||
entrySet = new UnmodifiableEntrySet<>(this.delegate.entrySet());
|
||||
this.entrySet = entrySet;
|
||||
if (this.entrySet == null) {
|
||||
this.entrySet = new UnmodifiableEntrySet<>(this.delegate.entrySet());
|
||||
}
|
||||
return entrySet;
|
||||
return this.entrySet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<List<V>> values() {
|
||||
Collection<List<V>> values = this.values;
|
||||
if (values == null) {
|
||||
values = new UnmodifiableValueCollection<>(this.delegate.values());
|
||||
this.values = values;
|
||||
if (this.values == null) {
|
||||
this.values = new UnmodifiableValueCollection<>(this.delegate.values());
|
||||
}
|
||||
return values;
|
||||
return this.values;
|
||||
}
|
||||
|
||||
// unsupported
|
||||
|
||||
+13
-179
@@ -16,7 +16,8 @@
|
||||
|
||||
package org.springframework.util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@@ -24,8 +25,6 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Spliterator;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -33,13 +32,12 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap.Entry;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap.Reference;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap.Restructure;
|
||||
import org.springframework.util.comparator.Comparators;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
|
||||
/**
|
||||
* Tests for {@link ConcurrentReferenceHashMap}.
|
||||
@@ -49,6 +47,8 @@ import static org.assertj.core.api.Assertions.entry;
|
||||
*/
|
||||
class ConcurrentReferenceHashMapTests {
|
||||
|
||||
private static final Comparator<? super String> NULL_SAFE_STRING_SORT = Comparators.nullsLow();
|
||||
|
||||
private TestWeakConcurrentCache<Integer, String> map = new TestWeakConcurrentCache<>();
|
||||
|
||||
|
||||
@@ -450,174 +450,19 @@ class ConcurrentReferenceHashMapTests {
|
||||
assertThat(this.map.keySet()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test // gh-35817
|
||||
void keySetContains() {
|
||||
this.map.put(123, "123");
|
||||
this.map.put(456, null);
|
||||
this.map.put(null, "789");
|
||||
assertThat(this.map.keySet()).containsExactlyInAnyOrder(123, 456, null);
|
||||
}
|
||||
|
||||
@Test // gh-35817
|
||||
void keySetRemove() {
|
||||
this.map.put(123, "123");
|
||||
this.map.put(456, null);
|
||||
this.map.put(null, "789");
|
||||
assertThat(this.map.keySet().remove(123)).isTrue();
|
||||
assertThat(this.map).doesNotContainKey(123);
|
||||
assertThat(this.map.keySet().remove(123)).isFalse();
|
||||
}
|
||||
|
||||
@Test // gh-35817
|
||||
void keySetIterator() {
|
||||
this.map.put(123, "123");
|
||||
this.map.put(456, null);
|
||||
this.map.put(null, "789");
|
||||
Iterator<Integer> it = this.map.keySet().iterator();
|
||||
assertThat(it).toIterable().containsExactlyInAnyOrder(123, 456, null);
|
||||
assertThat(it).isExhausted();
|
||||
}
|
||||
|
||||
@Test // gh-35817
|
||||
void keySetIteratorRemove() {
|
||||
this.map.put(123, "123");
|
||||
this.map.put(456, null);
|
||||
this.map.put(null, "789");
|
||||
Iterator<Integer> keySetIterator = this.map.keySet().iterator();
|
||||
while (keySetIterator.hasNext()) {
|
||||
Integer key = keySetIterator.next();
|
||||
if (key != null && key.equals(456)) {
|
||||
keySetIterator.remove();
|
||||
}
|
||||
}
|
||||
assertThat(this.map).containsOnlyKeys(123, null);
|
||||
}
|
||||
|
||||
@Test // gh-35817
|
||||
void keySetClear() {
|
||||
this.map.put(123, "123");
|
||||
this.map.put(456, null);
|
||||
this.map.put(null, "789");
|
||||
this.map.keySet().clear();
|
||||
assertThat(this.map).isEmpty();
|
||||
assertThat(this.map.keySet()).isEmpty();
|
||||
}
|
||||
|
||||
@Test // gh-35817
|
||||
void keySetAdd() {
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class)
|
||||
.isThrownBy(() -> this.map.keySet().add(12345));
|
||||
}
|
||||
|
||||
@Test // gh-35817
|
||||
void keySetStream() {
|
||||
this.map.put(123, "123");
|
||||
this.map.put(456, null);
|
||||
this.map.put(null, "789");
|
||||
Set<Integer> keys = this.map.keySet().stream().collect(Collectors.toSet());
|
||||
assertThat(keys).containsExactlyInAnyOrder(123, 456, null);
|
||||
}
|
||||
|
||||
@Test // gh-35817
|
||||
void keySetSpliteratorCharacteristics() {
|
||||
this.map.put(123, "123");
|
||||
this.map.put(456, null);
|
||||
this.map.put(null, "789");
|
||||
Spliterator<Integer> spliterator = this.map.keySet().spliterator();
|
||||
assertThat(spliterator).hasOnlyCharacteristics(Spliterator.CONCURRENT, Spliterator.DISTINCT);
|
||||
assertThat(spliterator.estimateSize()).isEqualTo(3L);
|
||||
assertThat(spliterator.getExactSizeIfKnown()).isEqualTo(-1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void valuesCollection() {
|
||||
this.map.put(123, "123");
|
||||
this.map.put(456, null);
|
||||
this.map.put(null, "789");
|
||||
assertThat(this.map.values()).containsExactlyInAnyOrder("123", null, "789");
|
||||
}
|
||||
|
||||
@Test // gh-35817
|
||||
void valuesCollectionAdd() {
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class)
|
||||
.isThrownBy(() -> this.map.values().add("12345"));
|
||||
}
|
||||
|
||||
@Test // gh-35817
|
||||
void valuesCollectionClear() {
|
||||
Collection<String> values = this.map.values();
|
||||
this.map.put(123, "123");
|
||||
this.map.put(456, null);
|
||||
this.map.put(null, "789");
|
||||
assertThat(values).hasSize(3);
|
||||
values.clear();
|
||||
assertThat(values).isEmpty();
|
||||
assertThat(this.map).isEmpty();
|
||||
}
|
||||
|
||||
@Test // gh-35817
|
||||
void valuesCollectionRemoval() {
|
||||
Collection<String> values = this.map.values();
|
||||
assertThat(values).isEmpty();
|
||||
this.map.put(123, "123");
|
||||
this.map.put(456, null);
|
||||
this.map.put(null, "789");
|
||||
assertThat(values).containsExactlyInAnyOrder("123", null, "789");
|
||||
values.remove(null);
|
||||
assertThat(values).containsExactlyInAnyOrder("123", "789");
|
||||
assertThat(map).containsOnly(entry(123, "123"), entry(null, "789"));
|
||||
values.remove("123");
|
||||
values.remove("789");
|
||||
assertThat(values).isEmpty();
|
||||
assertThat(map).isEmpty();
|
||||
}
|
||||
|
||||
@Test // gh-35817
|
||||
void valuesCollectionIterator() {
|
||||
Iterator<String> iterator = this.map.values().iterator();
|
||||
assertThat(iterator).isExhausted();
|
||||
this.map.put(123, "123");
|
||||
this.map.put(456, null);
|
||||
this.map.put(null, "789");
|
||||
iterator = this.map.values().iterator();
|
||||
assertThat(iterator).toIterable().containsExactlyInAnyOrder("123", null, "789");
|
||||
}
|
||||
|
||||
@Test // gh-35817
|
||||
void valuesCollectionIteratorRemoval() {
|
||||
this.map.put(123, "123");
|
||||
this.map.put(456, null);
|
||||
this.map.put(null, "789");
|
||||
Iterator<String> iterator = this.map.values().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
String value = iterator.next();
|
||||
if (value != null && value.equals("789")) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
assertThat(iterator).isExhausted();
|
||||
assertThat(this.map.values()).containsExactlyInAnyOrder("123", null);
|
||||
assertThat(this.map).containsOnlyKeys(123, 456);
|
||||
}
|
||||
|
||||
@Test // gh-35817
|
||||
void valuesCollectionStream() {
|
||||
this.map.put(123, "123");
|
||||
this.map.put(456, null);
|
||||
this.map.put(null, "789");
|
||||
List<String> values = this.map.values().stream().toList();
|
||||
assertThat(values).containsExactlyInAnyOrder("123", null, "789");
|
||||
}
|
||||
|
||||
@Test // gh-35817
|
||||
void valuesCollectionSpliteratorCharacteristics() {
|
||||
this.map.put(123, "123");
|
||||
this.map.put(456, null);
|
||||
this.map.put(null, "789");
|
||||
Spliterator<String> spliterator = this.map.values().spliterator();
|
||||
assertThat(spliterator).hasOnlyCharacteristics(Spliterator.CONCURRENT);
|
||||
assertThat(spliterator.estimateSize()).isEqualTo(3L);
|
||||
assertThat(spliterator.getExactSizeIfKnown()).isEqualTo(-1L);
|
||||
List<String> actual = new ArrayList<>(this.map.values());
|
||||
List<String> expected = new ArrayList<>();
|
||||
expected.add("123");
|
||||
expected.add(null);
|
||||
expected.add("789");
|
||||
actual.sort(NULL_SAFE_STRING_SORT);
|
||||
expected.sort(NULL_SAFE_STRING_SORT);
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -696,17 +541,6 @@ class ConcurrentReferenceHashMapTests {
|
||||
copy.forEach(entry -> assertThat(entrySet).doesNotContain(entry));
|
||||
}
|
||||
|
||||
@Test // gh-35817
|
||||
void entrySetSpliteratorCharacteristics() {
|
||||
this.map.put(1, "1");
|
||||
this.map.put(2, "2");
|
||||
this.map.put(3, "3");
|
||||
Spliterator<Map.Entry<Integer, String>> spliterator = this.map.entrySet().spliterator();
|
||||
assertThat(spliterator).hasOnlyCharacteristics(Spliterator.CONCURRENT, Spliterator.DISTINCT);
|
||||
assertThat(spliterator.estimateSize()).isEqualTo(3L);
|
||||
assertThat(spliterator.getExactSizeIfKnown()).isEqualTo(-1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void supportNullReference() {
|
||||
// GC could happen during restructure so we must be able to create a reference for a null entry
|
||||
|
||||
+4
-3
@@ -40,10 +40,11 @@ import org.springframework.util.Assert;
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see #loadBeanDefinitions
|
||||
* @deprecated in favor of Spring's common bean definition formats and/or
|
||||
* custom BeanDefinitionReader implementations
|
||||
* @see org.springframework.beans.factory.support.PropertiesBeanDefinitionReader
|
||||
* @deprecated as of 5.3, in favor of Spring's common bean definition formats
|
||||
* and/or custom reader implementations
|
||||
*/
|
||||
@Deprecated(since = "5.3")
|
||||
@Deprecated
|
||||
public class JdbcBeanDefinitionReader {
|
||||
|
||||
private final org.springframework.beans.factory.support.PropertiesBeanDefinitionReader propReader;
|
||||
|
||||
+3
-3
@@ -25,7 +25,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.Aware;
|
||||
import org.springframework.context.EmbeddedValueResolverAware;
|
||||
import org.springframework.core.MethodClassKey;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -167,8 +167,8 @@ public abstract class AbstractFallbackTransactionAttributeSource
|
||||
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
|
||||
return null;
|
||||
}
|
||||
// Skip setBeanFactory method on BeanFactoryAware.
|
||||
if (method.getDeclaringClass() == BeanFactoryAware.class) {
|
||||
// Skip methods declared on BeanFactoryAware and co.
|
||||
if (method.getDeclaringClass().isInterface() && Aware.class.isAssignableFrom(method.getDeclaringClass())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+5
-23
@@ -32,7 +32,6 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.testfixture.io.SerializationTestUtils;
|
||||
@@ -60,7 +59,6 @@ class AnnotationTransactionAttributeSourceTests {
|
||||
|
||||
private final AnnotationTransactionAttributeSource attributeSource = new AnnotationTransactionAttributeSource();
|
||||
|
||||
|
||||
@Test
|
||||
void serializable() throws Exception {
|
||||
TestBean1 tb = new TestBean1();
|
||||
@@ -125,10 +123,6 @@ class AnnotationTransactionAttributeSourceTests {
|
||||
void transactionAttributeDeclaredOnInterfaceMethodOnly() {
|
||||
TransactionAttribute actual = getTransactionAttribute(TestBean2.class, ITestBean2.class, "getAge");
|
||||
assertThat(actual).satisfies(hasNoRollbackRule());
|
||||
actual = getTransactionAttribute(TestBean2.class, ITestBean2X.class, "getAge");
|
||||
assertThat(actual).satisfies(hasNoRollbackRule());
|
||||
actual = getTransactionAttribute(ITestBean2X.class, ITestBean2X.class, "getAge");
|
||||
assertThat(actual).satisfies(hasNoRollbackRule());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -255,7 +249,6 @@ class AnnotationTransactionAttributeSourceTests {
|
||||
assertThat(actual.isReadOnly()).isTrue();
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class JtaAttributeTests {
|
||||
|
||||
@@ -283,7 +276,6 @@ class AnnotationTransactionAttributeSourceTests {
|
||||
assertThat(getNameAttr.getPropagationBehavior()).isEqualTo(TransactionAttribute.PROPAGATION_SUPPORTS);
|
||||
}
|
||||
|
||||
|
||||
static class JtaAnnotatedBean1 implements ITestBean1 {
|
||||
|
||||
private String name;
|
||||
@@ -313,6 +305,7 @@ class AnnotationTransactionAttributeSourceTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@jakarta.transaction.Transactional(jakarta.transaction.Transactional.TxType.SUPPORTS)
|
||||
static class JtaAnnotatedBean2 implements ITestBean1 {
|
||||
|
||||
@@ -369,6 +362,7 @@ class AnnotationTransactionAttributeSourceTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@jakarta.transaction.Transactional(jakarta.transaction.Transactional.TxType.SUPPORTS)
|
||||
interface ITestJta {
|
||||
|
||||
@@ -381,8 +375,8 @@ class AnnotationTransactionAttributeSourceTests {
|
||||
|
||||
void setName(String name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class Ejb3AttributeTests {
|
||||
@@ -454,6 +448,7 @@ class AnnotationTransactionAttributeSourceTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@jakarta.ejb.TransactionAttribute(TransactionAttributeType.SUPPORTS)
|
||||
static class Ejb3AnnotatedBean2 implements ITestBean1 {
|
||||
|
||||
@@ -511,7 +506,6 @@ class AnnotationTransactionAttributeSourceTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class GroovyTests {
|
||||
|
||||
@@ -525,7 +519,6 @@ class AnnotationTransactionAttributeSourceTests {
|
||||
assertThat(attributeSource.getTransactionAttribute(getMetaClassMethod, GroovyTestBean.class)).isNull();
|
||||
}
|
||||
|
||||
|
||||
@Transactional
|
||||
static class GroovyTestBean implements ITestBean1, GroovyObject {
|
||||
|
||||
@@ -578,7 +571,6 @@ class AnnotationTransactionAttributeSourceTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Consumer<TransactionAttribute> hasRollbackRules(RollbackRuleAttribute... rollbackRuleAttributes) {
|
||||
return transactionAttribute -> {
|
||||
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
|
||||
@@ -634,12 +626,7 @@ class AnnotationTransactionAttributeSourceTests {
|
||||
}
|
||||
|
||||
|
||||
interface ITestBean2X extends ITestBean2, BeanNameAware {
|
||||
|
||||
@Transactional
|
||||
int getAge();
|
||||
|
||||
void setAge(int age);
|
||||
interface ITestBean2X extends ITestBean2 {
|
||||
|
||||
String getName();
|
||||
|
||||
@@ -748,10 +735,6 @@ class AnnotationTransactionAttributeSourceTests {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanName(String name) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
@@ -934,7 +917,6 @@ class AnnotationTransactionAttributeSourceTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Transactional(label = {"retryable", "long-running"})
|
||||
static class TestBean11 {
|
||||
|
||||
|
||||
+5
-9
@@ -25,8 +25,8 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.ErrorResponse;
|
||||
|
||||
/**
|
||||
* Fatal binding exception, thrown when we want to treat binding exceptions
|
||||
* as unrecoverable.
|
||||
* Fatal binding exception, thrown when we want to
|
||||
* treat binding exceptions as unrecoverable.
|
||||
*
|
||||
* <p>Extends ServletException for convenient throwing in any Servlet resource
|
||||
* (such as a Filter), and NestedServletException for proper root cause handling
|
||||
@@ -38,14 +38,13 @@ import org.springframework.web.ErrorResponse;
|
||||
@SuppressWarnings("serial")
|
||||
public class ServletRequestBindingException extends ServletException implements ErrorResponse {
|
||||
|
||||
private final ProblemDetail body = ProblemDetail.forStatus(getStatusCode());
|
||||
|
||||
private final String messageDetailCode;
|
||||
|
||||
@Nullable
|
||||
private final Object[] messageDetailArguments;
|
||||
|
||||
@Nullable
|
||||
private ProblemDetail body;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor with a message only.
|
||||
@@ -109,10 +108,7 @@ public class ServletRequestBindingException extends ServletException implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ProblemDetail getBody() {
|
||||
if (this.body == null) {
|
||||
this.body = ProblemDetail.forStatus(getStatusCode());
|
||||
}
|
||||
public ProblemDetail getBody() {
|
||||
return this.body;
|
||||
}
|
||||
|
||||
|
||||
+1
-4
@@ -124,10 +124,7 @@ class HtmlCharacterEntityDecoder {
|
||||
int value = (!isHexNumberedReference ?
|
||||
Integer.parseInt(getReferenceSubstring(2)) :
|
||||
Integer.parseInt(getReferenceSubstring(3), 16));
|
||||
if (value > Character.MAX_CODE_POINT) {
|
||||
return false;
|
||||
}
|
||||
this.decodedMessage.appendCodePoint(value);
|
||||
this.decodedMessage.append((char) value);
|
||||
return true;
|
||||
}
|
||||
catch (NumberFormatException ex) {
|
||||
|
||||
@@ -78,6 +78,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void httpMediaTypeNotSupportedException() {
|
||||
|
||||
List<MediaType> mediaTypes =
|
||||
Arrays.asList(MediaType.APPLICATION_JSON, MediaType.APPLICATION_CBOR);
|
||||
|
||||
@@ -95,6 +96,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void httpMediaTypeNotSupportedExceptionWithParseError() {
|
||||
|
||||
ErrorResponse ex = new HttpMediaTypeNotSupportedException(
|
||||
"Could not parse Accept header: Invalid mime type \"foo\": does not contain '/'");
|
||||
|
||||
@@ -107,6 +109,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void httpMediaTypeNotAcceptableException() {
|
||||
|
||||
List<MediaType> mediaTypes = Arrays.asList(MediaType.APPLICATION_JSON, MediaType.APPLICATION_CBOR);
|
||||
HttpMediaTypeNotAcceptableException ex = new HttpMediaTypeNotAcceptableException(mediaTypes);
|
||||
|
||||
@@ -120,6 +123,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void httpMediaTypeNotAcceptableExceptionWithParseError() {
|
||||
|
||||
ErrorResponse ex = new HttpMediaTypeNotAcceptableException(
|
||||
"Could not parse Accept header: Invalid mime type \"foo\": does not contain '/'");
|
||||
|
||||
@@ -132,6 +136,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void asyncRequestTimeoutException() {
|
||||
|
||||
ErrorResponse ex = new AsyncRequestTimeoutException();
|
||||
assertDetailMessageCode(ex, null, null);
|
||||
|
||||
@@ -143,6 +148,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void httpRequestMethodNotSupportedException() {
|
||||
|
||||
HttpRequestMethodNotSupportedException ex =
|
||||
new HttpRequestMethodNotSupportedException("PUT", Arrays.asList("GET", "POST"));
|
||||
|
||||
@@ -156,6 +162,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void missingRequestHeaderException() {
|
||||
|
||||
MissingRequestHeaderException ex = new MissingRequestHeaderException("Authorization", this.methodParameter);
|
||||
|
||||
assertStatus(ex, HttpStatus.BAD_REQUEST);
|
||||
@@ -167,6 +174,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void missingServletRequestParameterException() {
|
||||
|
||||
MissingServletRequestParameterException ex = new MissingServletRequestParameterException("query", "String");
|
||||
|
||||
assertStatus(ex, HttpStatus.BAD_REQUEST);
|
||||
@@ -178,8 +186,10 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void missingMatrixVariableException() {
|
||||
|
||||
MissingMatrixVariableException ex = new MissingMatrixVariableException("region", this.methodParameter);
|
||||
|
||||
|
||||
assertStatus(ex, HttpStatus.BAD_REQUEST);
|
||||
assertDetail(ex, "Required path parameter 'region' is not present.");
|
||||
assertDetailMessageCode(ex, null, new Object[] {ex.getVariableName()});
|
||||
@@ -189,6 +199,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void missingPathVariableException() {
|
||||
|
||||
MissingPathVariableException ex = new MissingPathVariableException("id", this.methodParameter);
|
||||
|
||||
assertStatus(ex, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
@@ -198,19 +209,9 @@ class ErrorResponseExceptionTests {
|
||||
assertThat(ex.getHeaders()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingPathVariableExceptionAfterConversion() {
|
||||
MissingPathVariableException ex = new MissingPathVariableException("id", this.methodParameter, true);
|
||||
|
||||
assertStatus(ex, HttpStatus.BAD_REQUEST);
|
||||
assertDetail(ex, "Required path variable 'id' is not present.");
|
||||
assertDetailMessageCode(ex, null, new Object[] {ex.getVariableName()});
|
||||
|
||||
assertThat(ex.getHeaders().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingRequestCookieException() {
|
||||
|
||||
MissingRequestCookieException ex = new MissingRequestCookieException("oreo", this.methodParameter);
|
||||
|
||||
assertStatus(ex, HttpStatus.BAD_REQUEST);
|
||||
@@ -222,6 +223,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void unsatisfiedServletRequestParameterException() {
|
||||
|
||||
UnsatisfiedServletRequestParameterException ex = new UnsatisfiedServletRequestParameterException(
|
||||
new String[] { "foo=bar", "bar=baz" }, Collections.singletonMap("q", new String[] {"1"}));
|
||||
|
||||
@@ -234,6 +236,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void missingServletRequestPartException() {
|
||||
|
||||
MissingServletRequestPartException ex = new MissingServletRequestPartException("file");
|
||||
|
||||
assertStatus(ex, HttpStatus.BAD_REQUEST);
|
||||
@@ -245,6 +248,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void methodArgumentNotValidException() {
|
||||
|
||||
ValidationTestHelper testHelper = new ValidationTestHelper(MethodArgumentNotValidException.class);
|
||||
BindingResult result = testHelper.bindingResult();
|
||||
|
||||
@@ -276,6 +280,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void unsupportedMediaTypeStatusException() {
|
||||
|
||||
List<MediaType> mediaTypes =
|
||||
Arrays.asList(MediaType.APPLICATION_JSON, MediaType.APPLICATION_CBOR);
|
||||
|
||||
@@ -293,6 +298,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void unsupportedMediaTypeStatusExceptionWithParseError() {
|
||||
|
||||
ErrorResponse ex = new UnsupportedMediaTypeStatusException(
|
||||
"Could not parse Accept header: Invalid mime type \"foo\": does not contain '/'");
|
||||
|
||||
@@ -305,6 +311,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void notAcceptableStatusException() {
|
||||
|
||||
List<MediaType> mediaTypes = Arrays.asList(MediaType.APPLICATION_JSON, MediaType.APPLICATION_CBOR);
|
||||
NotAcceptableStatusException ex = new NotAcceptableStatusException(mediaTypes);
|
||||
|
||||
@@ -318,6 +325,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void notAcceptableStatusExceptionWithParseError() {
|
||||
|
||||
ErrorResponse ex = new NotAcceptableStatusException(
|
||||
"Could not parse Accept header: Invalid mime type \"foo\": does not contain '/'");
|
||||
|
||||
@@ -330,6 +338,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void serverErrorException() {
|
||||
|
||||
ServerErrorException ex = new ServerErrorException("Failure", null);
|
||||
|
||||
assertStatus(ex, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
@@ -341,6 +350,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void missingRequestValueException() {
|
||||
|
||||
MissingRequestValueException ex =
|
||||
new MissingRequestValueException("foo", String.class, "header", this.methodParameter);
|
||||
|
||||
@@ -353,6 +363,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void unsatisfiedRequestParameterException() {
|
||||
|
||||
UnsatisfiedRequestParameterException ex =
|
||||
new UnsatisfiedRequestParameterException(
|
||||
Arrays.asList("foo=bar", "bar=baz"),
|
||||
@@ -367,6 +378,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void webExchangeBindException() {
|
||||
|
||||
ValidationTestHelper testHelper = new ValidationTestHelper(WebExchangeBindException.class);
|
||||
BindingResult result = testHelper.bindingResult();
|
||||
|
||||
@@ -381,6 +393,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void methodNotAllowedException() {
|
||||
|
||||
List<HttpMethod> supportedMethods = Arrays.asList(HttpMethod.GET, HttpMethod.POST);
|
||||
MethodNotAllowedException ex = new MethodNotAllowedException(HttpMethod.PUT, supportedMethods);
|
||||
|
||||
@@ -394,6 +407,7 @@ class ErrorResponseExceptionTests {
|
||||
|
||||
@Test
|
||||
void methodNotAllowedExceptionWithoutSupportedMethods() {
|
||||
|
||||
MethodNotAllowedException ex = new MethodNotAllowedException(HttpMethod.PUT, Collections.emptyList());
|
||||
|
||||
assertStatus(ex, HttpStatus.METHOD_NOT_ALLOWED);
|
||||
@@ -403,8 +417,9 @@ class ErrorResponseExceptionTests {
|
||||
assertThat(ex.getHeaders()).isEmpty();
|
||||
}
|
||||
|
||||
@Test // gh-30300
|
||||
@Test // gh-30300
|
||||
void responseStatusException() {
|
||||
|
||||
Locale locale = Locale.UK;
|
||||
LocaleContextHolder.setLocale(locale);
|
||||
|
||||
@@ -504,6 +519,7 @@ class ErrorResponseExceptionTests {
|
||||
assertThat(BindErrorUtils.resolve(errors, this.messageSource, Locale.UK)).hasSize(4)
|
||||
.containsValues("Bean A message", "Bean B message", "name is required", "age is below minimum");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,8 +21,6 @@ import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link HtmlUtils}.
|
||||
*
|
||||
* @author Alef Arendsen
|
||||
* @author Martin Kersten
|
||||
* @author Rick Evans
|
||||
@@ -30,7 +28,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class HtmlUtilsTests {
|
||||
|
||||
@Test
|
||||
void htmlEscape() {
|
||||
void testHtmlEscape() {
|
||||
String unescaped = "\"This is a quote'";
|
||||
String escaped = HtmlUtils.htmlEscape(unescaped);
|
||||
assertThat(escaped).isEqualTo(""This is a quote'");
|
||||
@@ -41,7 +39,14 @@ class HtmlUtilsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void htmlEscapeIntoHtmlCharacterSet() {
|
||||
void testHtmlUnescape() {
|
||||
String escaped = ""This is a quote'";
|
||||
String unescaped = HtmlUtils.htmlUnescape(escaped);
|
||||
assertThat(unescaped).isEqualTo("\"This is a quote'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncodeIntoHtmlCharacterSet() {
|
||||
assertThat(HtmlUtils.htmlEscape("")).as("An empty string should be converted to an empty string").isEmpty();
|
||||
assertThat(HtmlUtils.htmlEscape("A sentence containing no special characters.")).as("A string containing no special characters should not be affected").isEqualTo("A sentence containing no special characters.");
|
||||
|
||||
@@ -55,11 +60,12 @@ class HtmlUtilsTests {
|
||||
assertThat(HtmlUtils.htmlEscapeDecimal("" + (char) 977)).as("The special character 977 should be encoded to 'ϑ'").isEqualTo("ϑ");
|
||||
}
|
||||
|
||||
@Test // SPR-9293
|
||||
void htmlEscapeIntoHtmlCharacterSetFromUtf8() {
|
||||
// SPR-9293
|
||||
@Test
|
||||
void testEncodeIntoHtmlCharacterSetFromUtf8() {
|
||||
String utf8 = ("UTF-8");
|
||||
|
||||
assertThat(HtmlUtils.htmlEscape("", utf8)).as("An empty string should be converted to an empty string").isEmpty();
|
||||
assertThat(HtmlUtils.htmlEscape("", utf8)).as("An empty string should be converted to an empty string")
|
||||
.isEmpty();
|
||||
assertThat(HtmlUtils.htmlEscape("A sentence containing no special characters.")).as("A string containing no special characters should not be affected").isEqualTo("A sentence containing no special characters.");
|
||||
|
||||
assertThat(HtmlUtils.htmlEscape("< >", utf8)).as("'< >' should be encoded to '< >'").isEqualTo("< >");
|
||||
@@ -69,38 +75,7 @@ class HtmlUtilsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void htmlUnescape() {
|
||||
String escaped = ""This is a quote'";
|
||||
String unescaped = HtmlUtils.htmlUnescape(escaped);
|
||||
assertThat(unescaped).isEqualTo("\"This is a quote'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void htmlUnescapeHandlesSupplementaryCharactersAsDecimal() {
|
||||
String expectedCharacter = "😀";
|
||||
String decimalEntity = "😀";
|
||||
String actualResultFromDecimal = HtmlUtils.htmlUnescape(decimalEntity);
|
||||
assertThat(actualResultFromDecimal).as("Decimal entity was not converted correctly.").isEqualTo(expectedCharacter);
|
||||
}
|
||||
|
||||
@Test
|
||||
void htmlUnescapeHandlesSupplementaryCharactersAsHexadecimal() {
|
||||
String expectedCharacter = "😀";
|
||||
String hexEntity = "😀";
|
||||
String actualResultFromHex = HtmlUtils.htmlUnescape(hexEntity);
|
||||
assertThat(actualResultFromHex).as("Hexadecimal entity was not converted correctly.").isEqualTo(expectedCharacter);
|
||||
}
|
||||
|
||||
@Test
|
||||
void htmlUnescapeHandlesBasicEntities() {
|
||||
String input = "<p>Tom & Jerry's "Show"</p>";
|
||||
String expectedOutput = "<p>Tom & Jerry's \"Show\"</p>";
|
||||
String actualOutput = HtmlUtils.htmlUnescape(input);
|
||||
assertThat(actualOutput).as("Basic HTML entities were not unescaped correctly.").isEqualTo(expectedOutput);
|
||||
}
|
||||
|
||||
@Test
|
||||
void htmlUnescapeFromHtmlCharacterSet() {
|
||||
void testDecodeFromHtmlCharacterSet() {
|
||||
assertThat(HtmlUtils.htmlUnescape("")).as("An empty string should be converted to an empty string").isEmpty();
|
||||
assertThat(HtmlUtils.htmlUnescape("This is a sentence containing no special characters.")).as("A string containing no special characters should not be affected").isEqualTo("This is a sentence containing no special characters.");
|
||||
|
||||
|
||||
@@ -128,7 +128,6 @@
|
||||
<suppress files="PatternParseException" checks="JavadocVariable"/>
|
||||
<suppress files="web[\\/]reactive[\\/]socket[\\/]CloseStatus" checks="JavadocStyle"/>
|
||||
<suppress files="RestClientResponseException" checks="MutableException"/>
|
||||
<suppress files="ServletRequestBindingException" checks="MutableException"/>
|
||||
|
||||
<!-- spring-webflux -->
|
||||
<suppress files="src[\\/]test[\\/]java[\\/]org[\\/]springframework[\\/]web[\\/]reactive[\\/]resource[\\/]GzipSupport" checks="IllegalImport" id="bannedJUnitJupiterImports"/>
|
||||
|
||||
Reference in New Issue
Block a user