Skip to content

Commit aa3fbf8

Browse files
authored
Avoid cloning MultiValueMapAdapter instances (assertj#2549)
1 parent 92548d8 commit aa3fbf8

3 files changed

Lines changed: 98 additions & 6 deletions

File tree

‎src/main/java/org/assertj/core/internal/Maps.java‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,8 @@ private static <K> Set<K> getNotExpectedKeys(Map<K, ?> actual, K[] expectedKeys)
419419

420420
@SuppressWarnings("unchecked")
421421
private static <K, V> Map<K, V> clone(Map<K, V> map) throws NoSuchMethodException {
422+
if (isMultiValueMapAdapterInstance(map)) throw new IllegalArgumentException("Cannot clone MultiValueMapAdapter");
423+
422424
try {
423425
if (map instanceof Cloneable) {
424426
return (Map<K, V>) map.getClass().getMethod("clone").invoke(map);
@@ -438,6 +440,19 @@ private static <K, V> Map<K, V> clone(Map<K, V> map) throws NoSuchMethodExceptio
438440
}
439441
}
440442

443+
private static boolean isMultiValueMapAdapterInstance(Map<?, ?> map) {
444+
return isInstanceOf(map, "org.springframework.util.MultiValueMapAdapter");
445+
}
446+
447+
private static boolean isInstanceOf(Object object, String className) {
448+
try {
449+
Class<?> type = Class.forName(className);
450+
return type.isInstance(object);
451+
} catch (ClassNotFoundException e) {
452+
return false;
453+
}
454+
}
455+
441456
public <K, V> void assertContainsValue(AssertionInfo info, Map<K, V> actual, V value) {
442457
assertNotNull(info, actual);
443458
if (!containsValue(actual, value)) throw failures.failure(info, shouldContainValue(actual, value));

‎src/test/java/org/assertj/core/internal/maps/Maps_assertContainsOnlyKeys_Test.java‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import static org.assertj.core.util.Arrays.array;
3030
import static org.assertj.core.util.AssertionsUtil.expectAssertionError;
3131
import static org.assertj.core.util.FailureMessages.actualIsNull;
32+
import static org.assertj.core.util.Lists.list;
3233
import static org.assertj.core.util.Sets.set;
3334
import static org.junit.jupiter.params.provider.Arguments.arguments;
3435

@@ -45,6 +46,7 @@
4546
import org.junit.jupiter.params.ParameterizedTest;
4647
import org.junit.jupiter.params.provider.Arguments;
4748
import org.junit.jupiter.params.provider.MethodSource;
49+
import org.springframework.util.MultiValueMapAdapter;
4850

4951
import com.google.common.collect.ImmutableMap;
5052

@@ -90,9 +92,13 @@ void should_fail_if_given_keys_array_is_empty() {
9092
"caseInsensitiveMapsSuccessfulTestCases",
9193
})
9294
void should_pass(Map<String, String> actual, String[] expected) {
95+
// GIVEN
96+
int initialSize = actual.size();
9397
// WHEN/THEN
9498
assertThatNoException().as(actual.getClass().getName())
9599
.isThrownBy(() -> maps.assertContainsOnlyKeys(info, actual, expected));
100+
101+
then(actual).hasSize(initialSize);
96102
}
97103

98104
private static Stream<Arguments> unmodifiableMapsSuccessfulTestCases() {
@@ -127,6 +133,18 @@ private static Stream<Arguments> caseInsensitiveMapsSuccessfulTestCases() {
127133
array("Job", "Name"))));
128134
}
129135

136+
@Test
137+
void should_pass_with_MultiValueMapAdapter() {
138+
// GIVEN
139+
MultiValueMapAdapter<String, String> actual = new MultiValueMapAdapter<>(mapOf(entry("name", list("Yoda"))));
140+
String[] expected = array("name");
141+
int initialSize = actual.size();
142+
// WHEN
143+
maps.assertContainsOnlyKeys(info, actual, expected);
144+
// THEN
145+
then(actual).hasSize(initialSize);
146+
}
147+
130148
@ParameterizedTest
131149
@MethodSource({
132150
"unmodifiableMapsFailureTestCases",
@@ -135,12 +153,16 @@ private static Stream<Arguments> caseInsensitiveMapsSuccessfulTestCases() {
135153
"commonsCollectionsCaseInsensitiveMapFailureTestCases",
136154
})
137155
void should_fail(Map<String, String> actual, String[] expected, Set<String> notFound, Set<String> notExpected) {
156+
// GIVEN
157+
int initialSize = actual.size();
138158
// WHEN
139159
assertThatExceptionOfType(AssertionError.class).as(actual.getClass().getName())
140160
.isThrownBy(() -> maps.assertContainsOnlyKeys(info, actual, expected))
141161
// THEN
142162
.withMessage(shouldContainOnlyKeys(actual, expected,
143163
notFound, notExpected).create());
164+
165+
then(actual).hasSize(initialSize);
144166
}
145167

146168
private static Stream<Arguments> unmodifiableMapsFailureTestCases() {
@@ -213,4 +235,20 @@ private static Stream<Arguments> commonsCollectionsCaseInsensitiveMapFailureTest
213235
set("job"))); // internal keys are always lowercase
214236
}
215237

238+
@Test
239+
void should_fail_with_MultiValueMapAdapter() {
240+
// GIVEN
241+
MultiValueMapAdapter<String, String> actual = new MultiValueMapAdapter<>(mapOf(entry("name", list("Yoda")),
242+
entry("job", list("Jedi"))));
243+
String[] expected = array("name", "color");
244+
Set<String> notFound = set("color");
245+
Set<String> notExpected = set("job");
246+
int initialSize = actual.size();
247+
// WHEN
248+
AssertionError error = expectAssertionError(() -> maps.assertContainsOnlyKeys(info, actual, expected));
249+
// THEN
250+
then(error).hasMessage(shouldContainOnlyKeys(actual, expected, notFound, notExpected).create());
251+
then(actual).hasSize(initialSize);
252+
}
253+
216254
}

‎src/test/java/org/assertj/core/internal/maps/Maps_assertContainsOnly_Test.java‎

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@
2929
import static org.assertj.core.util.Arrays.array;
3030
import static org.assertj.core.util.AssertionsUtil.expectAssertionError;
3131
import static org.assertj.core.util.FailureMessages.actualIsNull;
32+
import static org.assertj.core.util.Lists.list;
3233
import static org.assertj.core.util.Sets.set;
3334
import static org.junit.jupiter.params.provider.Arguments.arguments;
3435

3536
import java.util.LinkedHashMap;
37+
import java.util.List;
3638
import java.util.Map;
3739
import java.util.Map.Entry;
3840
import java.util.Set;
@@ -41,11 +43,13 @@
4143
import org.apache.commons.collections4.map.CaseInsensitiveMap;
4244
import org.apache.commons.collections4.map.SingletonMap;
4345
import org.apache.commons.lang3.ArrayUtils;
46+
import org.assertj.core.data.MapEntry;
4447
import org.assertj.core.internal.MapsBaseTest;
4548
import org.junit.jupiter.api.Test;
4649
import org.junit.jupiter.params.ParameterizedTest;
4750
import org.junit.jupiter.params.provider.Arguments;
4851
import org.junit.jupiter.params.provider.MethodSource;
52+
import org.springframework.util.MultiValueMapAdapter;
4953

5054
import com.google.common.collect.ImmutableMap;
5155

@@ -84,16 +88,29 @@ void should_fail_if_given_entries_array_is_empty() {
8488
then(error).hasMessage(shouldBeEmpty(actual).create());
8589
}
8690

91+
@Test
92+
void should_pass_if_value_type_is_array() {
93+
// GIVEN
94+
Map<String, byte[]> actual = mapOf(entry("key1", new byte[] { 1, 2 }), entry("key2", new byte[] { 3, 4, 5 }));
95+
Entry<String, byte[]>[] expected = array(entry("key2", new byte[] { 3, 4, 5 }), entry("key1", new byte[] { 1, 2 }));
96+
// WHEN/THEN
97+
assertThatNoException().isThrownBy(() -> maps.assertContainsOnly(info, actual, expected));
98+
}
99+
87100
@ParameterizedTest
88101
@MethodSource({
89102
"unmodifiableMapsSuccessfulTestCases",
90103
"modifiableMapsSuccessfulTestCases",
91104
"caseInsensitiveMapsSuccessfulTestCases",
92105
})
93106
void should_pass(Map<String, String> actual, Entry<String, String>[] expected) {
107+
// GIVEN
108+
int initialSize = actual.size();
94109
// WHEN/THEN
95110
assertThatNoException().as(actual.getClass().getName())
96111
.isThrownBy(() -> maps.assertContainsOnly(info, actual, expected));
112+
113+
then(actual).hasSize(initialSize);
97114
}
98115

99116
private static Stream<Arguments> unmodifiableMapsSuccessfulTestCases() {
@@ -132,6 +149,18 @@ private static Stream<Arguments> caseInsensitiveMapsSuccessfulTestCases() {
132149
array(entry("Job", "Jedi"), entry("Name", "Yoda")))));
133150
}
134151

152+
@Test
153+
void should_pass_with_MultiValueMapAdapter() {
154+
// GIVEN
155+
MultiValueMapAdapter<String, String> actual = new MultiValueMapAdapter<>(mapOf(entry("name", list("Yoda"))));
156+
Entry<String, List<String>>[] expected = array(entry("name", list("Yoda")));
157+
int initialSize = actual.size();
158+
// WHEN
159+
maps.assertContainsOnly(info, actual, expected);
160+
// THEN
161+
then(actual).hasSize(initialSize);
162+
}
163+
135164
@ParameterizedTest
136165
@MethodSource({
137166
"unmodifiableMapsFailureTestCases",
@@ -142,12 +171,16 @@ private static Stream<Arguments> caseInsensitiveMapsSuccessfulTestCases() {
142171
})
143172
void should_fail(Map<String, String> actual, Entry<String, String>[] expected,
144173
Set<Entry<String, String>> notFound, Set<Entry<String, String>> notExpected) {
174+
// GIVEN
175+
int initialSize = actual.size();
145176
// WHEN
146177
assertThatExceptionOfType(AssertionError.class).as(actual.getClass().getName())
147178
.isThrownBy(() -> maps.assertContainsOnly(info, actual, expected))
148179
// THEN
149180
.withMessage(shouldContainOnly(actual, expected,
150181
notFound, notExpected).create());
182+
183+
then(actual).hasSize(initialSize);
151184
}
152185

153186
private static Stream<Arguments> unmodifiableMapsFailureTestCases() {
@@ -219,14 +252,20 @@ private static Stream<Arguments> orderDependentFailureTestCases() {
219252
set(entry("name", "Yoda"), entry("job", "Jedi"))));
220253
}
221254

222-
@SuppressWarnings("unchecked")
223255
@Test
224-
void should_pass_if_value_type_is_array() {
256+
void should_fail_with_MultiValueMapAdapter() {
225257
// GIVEN
226-
Map<String, byte[]> actual = mapOf(entry("key1", new byte[] { 1, 2 }), entry("key2", new byte[] { 3, 4, 5 }));
227-
Entry<String, byte[]>[] expected = new Entry[] { entry("key2", new byte[] { 3, 4, 5 }), entry("key1", new byte[] { 1, 2 }) };
228-
// WHEN/THEN
229-
assertThatNoException().isThrownBy(() -> maps.assertContainsOnly(info, actual, expected));
258+
MultiValueMapAdapter<String, String> actual = new MultiValueMapAdapter<>(mapOf(entry("name", list("Yoda")),
259+
entry("job", list("Jedi"))));
260+
MapEntry<String, List<String>>[] expected = array(entry("name", list("Yoda")), entry("color", list("Green")));
261+
Set<MapEntry<String, List<String>>> notFound = set(entry("color", list("Green")));
262+
Set<MapEntry<String, List<String>>> notExpected = set(entry("job", list("Jedi")));
263+
int initialSize = actual.size();
264+
// WHEN
265+
AssertionError error = expectAssertionError(() -> maps.assertContainsOnly(info, actual, expected));
266+
// THEN
267+
then(error).hasMessage(shouldContainOnly(actual, expected, notFound, notExpected).create());
268+
then(actual).hasSize(initialSize);
230269
}
231270

232271
}

0 commit comments

Comments
 (0)