Skip to content

Commit

Permalink
WFLY-7801 Add immutability test for singleton and unmodifiable collec…
Browse files Browse the repository at this point in the history
…tions.
  • Loading branch information
pferraro committed Dec 14, 2016
1 parent 6933682 commit f1cf0ce
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 62 deletions.
@@ -0,0 +1,73 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2016, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

package org.wildfly.clustering.ee;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* Tests the immutability of {@link Collections} wrappers.
* N.B. Strictly speaking, an unmodifiable collection is not necessarily immutable since the collection can still be modified through a reference to the delegate collection.
* Were this the case, the immutability test would also run against the delegate collection - and fail, forcing replication.
* @author Paul Ferraro
*/
public class CollectionImmutability implements Predicate<Object> {

private final Set<Class<?>> unmodifiableClasses = Immutability.createIdentitySet(Stream.of(
Collections.singleton(null),
Collections.singletonList(null),
Collections.singletonMap(null, null),
Collections.unmodifiableCollection(Collections.emptyList()),
Collections.unmodifiableList(Collections.emptyList()),
Collections.unmodifiableMap(Collections.emptyMap()),
Collections.unmodifiableNavigableMap(Collections.emptyNavigableMap()),
Collections.unmodifiableNavigableSet(Collections.emptyNavigableSet()),
Collections.unmodifiableSet(Collections.emptySet()),
Collections.unmodifiableSortedMap(Collections.emptySortedMap()),
Collections.unmodifiableSortedSet(Collections.emptySortedSet())
).map(o -> o.getClass()).collect(Collectors.toList()));

private final Predicate<Object> elementImmutability;

public CollectionImmutability(Predicate<Object> elementImmutability) {
this.elementImmutability = elementImmutability;
}

@Override
public boolean test(Object object) {
if (this.unmodifiableClasses.stream().anyMatch(immutableClass -> immutableClass.isInstance(object))) {
// An unmodifiable set should be immutable.
if (object instanceof Set) return true;
// An unmodifiable collection is immutable if its members are immutable.
// An unmodifiable map should be immutable if its values are immutable.
Collection<?> collection = (object instanceof Map) ? ((Map<?, ?>) object).values() : (Collection<?>) object;
return collection.stream().allMatch(e -> this.elementImmutability.test(e));
}
return false;
}
}
Expand Up @@ -169,6 +169,12 @@ public boolean test(Object object) {
return this.immutableClasses.stream().anyMatch(immutableClass -> immutableClass.isInstance(object));
}
},
COLLECTION() {
@Override
public boolean test(Object object) {
return COLLECTION_INSTANCE.test(object);
}
},
ANNOTATION() {
@Override
public boolean test(Object object) {
Expand All @@ -178,6 +184,7 @@ public boolean test(Object object) {
;

public static final Predicate<Object> INSTANCE = object -> EnumSet.allOf(Immutability.class).stream().anyMatch(predicate -> predicate.test(object));
static final Predicate<Object> COLLECTION_INSTANCE = new CollectionImmutability(INSTANCE);

static <T> Set<T> createIdentitySet(Collection<T> list) {
Set<T> result = Collections.newSetFromMap(new IdentityHashMap<>(list.size()));
Expand Down
Expand Up @@ -53,11 +53,14 @@
import java.time.zone.ZoneOffsetTransitionRule;
import java.time.zone.ZoneOffsetTransitionRule.TimeDefinition;
import java.time.zone.ZoneRules;
import java.util.Arrays;
import java.util.Collections;
import java.util.Currency;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
Expand All @@ -79,67 +82,88 @@ public void test() throws Exception {
}

protected void test(Predicate<Object> immutability) throws Exception {
assertFalse(Immutability.INSTANCE.test(new Object()));
assertFalse(Immutability.INSTANCE.test(new Date()));
assertFalse(Immutability.INSTANCE.test(new AtomicInteger()));
assertFalse(Immutability.INSTANCE.test(new AtomicLong()));
assertTrue(Immutability.INSTANCE.test(null));
assertTrue(Immutability.INSTANCE.test(Collections.emptyEnumeration()));
assertTrue(Immutability.INSTANCE.test(Collections.emptyIterator()));
assertTrue(Immutability.INSTANCE.test(Collections.emptyList()));
assertTrue(Immutability.INSTANCE.test(Collections.emptyListIterator()));
assertTrue(Immutability.INSTANCE.test(Collections.emptyMap()));
assertTrue(Immutability.INSTANCE.test(Collections.emptyNavigableMap()));
assertTrue(Immutability.INSTANCE.test(Collections.emptyNavigableSet()));
assertTrue(Immutability.INSTANCE.test(Collections.emptySet()));
assertTrue(Immutability.INSTANCE.test(Collections.emptySortedMap()));
assertTrue(Immutability.INSTANCE.test(Collections.emptySortedSet()));
assertTrue(Immutability.INSTANCE.test(Boolean.TRUE));
assertTrue(Immutability.INSTANCE.test(Character.valueOf('a')));
assertTrue(Immutability.INSTANCE.test(this.getClass()));
assertTrue(Immutability.INSTANCE.test(Currency.getInstance(Locale.US)));
assertTrue(Immutability.INSTANCE.test(Locale.getDefault()));
assertTrue(Immutability.INSTANCE.test(Byte.valueOf(Integer.valueOf(1).byteValue())));
assertTrue(Immutability.INSTANCE.test(Short.valueOf(Integer.valueOf(1).shortValue())));
assertTrue(Immutability.INSTANCE.test(Integer.valueOf(1)));
assertTrue(Immutability.INSTANCE.test(Long.valueOf(1)));
assertTrue(Immutability.INSTANCE.test(Float.valueOf(1)));
assertTrue(Immutability.INSTANCE.test(Double.valueOf(1)));
assertTrue(Immutability.INSTANCE.test(BigInteger.valueOf(1)));
assertTrue(Immutability.INSTANCE.test(BigDecimal.valueOf(1)));
assertTrue(Immutability.INSTANCE.test(InetAddress.getLocalHost()));
assertTrue(Immutability.INSTANCE.test(new InetSocketAddress(InetAddress.getLocalHost(), 80)));
assertTrue(Immutability.INSTANCE.test(MathContext.UNLIMITED));
assertTrue(Immutability.INSTANCE.test("test"));
assertTrue(Immutability.INSTANCE.test(TimeZone.getDefault()));
assertTrue(Immutability.INSTANCE.test(UUID.randomUUID()));
assertTrue(Immutability.INSTANCE.test(TimeUnit.DAYS));
assertFalse(immutability.test(new Object()));
assertFalse(immutability.test(new Date()));
assertFalse(immutability.test(new AtomicInteger()));
assertFalse(immutability.test(new AtomicLong()));
assertTrue(immutability.test(null));
assertTrue(immutability.test(Collections.emptyEnumeration()));
assertTrue(immutability.test(Collections.emptyIterator()));
assertTrue(immutability.test(Collections.emptyList()));
assertTrue(immutability.test(Collections.emptyListIterator()));
assertTrue(immutability.test(Collections.emptyMap()));
assertTrue(immutability.test(Collections.emptyNavigableMap()));
assertTrue(immutability.test(Collections.emptyNavigableSet()));
assertTrue(immutability.test(Collections.emptySet()));
assertTrue(immutability.test(Collections.emptySortedMap()));
assertTrue(immutability.test(Collections.emptySortedSet()));
assertTrue(immutability.test(Boolean.TRUE));
assertTrue(immutability.test(Character.valueOf('a')));
assertTrue(immutability.test(this.getClass()));
assertTrue(immutability.test(Currency.getInstance(Locale.US)));
assertTrue(immutability.test(Locale.getDefault()));
assertTrue(immutability.test(Byte.valueOf(Integer.valueOf(1).byteValue())));
assertTrue(immutability.test(Short.valueOf(Integer.valueOf(1).shortValue())));
assertTrue(immutability.test(Integer.valueOf(1)));
assertTrue(immutability.test(Long.valueOf(1)));
assertTrue(immutability.test(Float.valueOf(1)));
assertTrue(immutability.test(Double.valueOf(1)));
assertTrue(immutability.test(BigInteger.valueOf(1)));
assertTrue(immutability.test(BigDecimal.valueOf(1)));
assertTrue(immutability.test(InetAddress.getLocalHost()));
assertTrue(immutability.test(new InetSocketAddress(InetAddress.getLocalHost(), 80)));
assertTrue(immutability.test(MathContext.UNLIMITED));
assertTrue(immutability.test("test"));
assertTrue(immutability.test(TimeZone.getDefault()));
assertTrue(immutability.test(UUID.randomUUID()));
assertTrue(immutability.test(TimeUnit.DAYS));
File file = new File(System.getProperty("user.home"));
assertTrue(Immutability.INSTANCE.test(file));
assertTrue(Immutability.INSTANCE.test(file.toURI()));
assertTrue(Immutability.INSTANCE.test(file.toURI().toURL()));
assertTrue(Immutability.INSTANCE.test(FileSystems.getDefault().getRootDirectories().iterator().next()));
assertTrue(Immutability.INSTANCE.test(new AllPermission()));
assertTrue(immutability.test(file));
assertTrue(immutability.test(file.toURI()));
assertTrue(immutability.test(file.toURI().toURL()));
assertTrue(immutability.test(FileSystems.getDefault().getRootDirectories().iterator().next()));
assertTrue(immutability.test(new AllPermission()));

assertTrue(Immutability.INSTANCE.test(DateTimeFormatter.BASIC_ISO_DATE));
assertTrue(Immutability.INSTANCE.test(DecimalStyle.STANDARD));
assertTrue(Immutability.INSTANCE.test(Duration.ZERO));
assertTrue(Immutability.INSTANCE.test(Instant.now()));
assertTrue(Immutability.INSTANCE.test(LocalDate.now()));
assertTrue(Immutability.INSTANCE.test(LocalDateTime.now()));
assertTrue(Immutability.INSTANCE.test(LocalTime.now()));
assertTrue(Immutability.INSTANCE.test(MonthDay.now()));
assertTrue(Immutability.INSTANCE.test(Period.ZERO));
assertTrue(Immutability.INSTANCE.test(ValueRange.of(0L, 10L)));
assertTrue(Immutability.INSTANCE.test(WeekFields.ISO));
assertTrue(Immutability.INSTANCE.test(Year.now()));
assertTrue(Immutability.INSTANCE.test(YearMonth.now()));
assertTrue(Immutability.INSTANCE.test(ZoneOffset.UTC));
assertTrue(Immutability.INSTANCE.test(ZoneRules.of(ZoneOffset.UTC).nextTransition(Instant.now())));
assertTrue(Immutability.INSTANCE.test(ZoneOffsetTransitionRule.of(Month.JANUARY, 1, DayOfWeek.SUNDAY, LocalTime.MIDNIGHT, true, TimeDefinition.STANDARD, ZoneOffset.UTC, ZoneOffset.ofHours(1), ZoneOffset.ofHours(2))));
assertTrue(Immutability.INSTANCE.test(ZoneRules.of(ZoneOffset.UTC)));
assertTrue(Immutability.INSTANCE.test(ZonedDateTime.now()));
assertTrue(Immutability.INSTANCE.test(new JCIPImmutableObject()));
assertTrue(immutability.test(DateTimeFormatter.BASIC_ISO_DATE));
assertTrue(immutability.test(DecimalStyle.STANDARD));
assertTrue(immutability.test(Duration.ZERO));
assertTrue(immutability.test(Instant.now()));
assertTrue(immutability.test(LocalDate.now()));
assertTrue(immutability.test(LocalDateTime.now()));
assertTrue(immutability.test(LocalTime.now()));
assertTrue(immutability.test(MonthDay.now()));
assertTrue(immutability.test(Period.ZERO));
assertTrue(immutability.test(ValueRange.of(0L, 10L)));
assertTrue(immutability.test(WeekFields.ISO));
assertTrue(immutability.test(Year.now()));
assertTrue(immutability.test(YearMonth.now()));
assertTrue(immutability.test(ZoneOffset.UTC));
assertTrue(immutability.test(ZoneRules.of(ZoneOffset.UTC).nextTransition(Instant.now())));
assertTrue(immutability.test(ZoneOffsetTransitionRule.of(Month.JANUARY, 1, DayOfWeek.SUNDAY, LocalTime.MIDNIGHT, true, TimeDefinition.STANDARD, ZoneOffset.UTC, ZoneOffset.ofHours(1), ZoneOffset.ofHours(2))));
assertTrue(immutability.test(ZoneRules.of(ZoneOffset.UTC)));
assertTrue(immutability.test(ZonedDateTime.now()));
assertTrue(immutability.test(new JCIPImmutableObject()));

assertTrue(immutability.test(Collections.singleton("1")));
assertTrue(immutability.test(Collections.singletonList("1")));
assertTrue(immutability.test(Collections.singletonMap("1", "2")));

assertTrue(immutability.test(Collections.singleton(new JCIPImmutableObject())));
assertTrue(immutability.test(Collections.singletonList(new JCIPImmutableObject())));
assertTrue(immutability.test(Collections.singletonMap("1", new JCIPImmutableObject())));

assertTrue(immutability.test(Collections.unmodifiableCollection(Arrays.asList("1", "2"))));
assertTrue(immutability.test(Collections.unmodifiableList(Arrays.asList("1", "2"))));
assertTrue(immutability.test(Collections.unmodifiableMap(Collections.singletonMap("1", "2"))));
assertTrue(immutability.test(Collections.unmodifiableNavigableMap(new TreeMap<>(Collections.singletonMap("1", "2")))));
assertTrue(immutability.test(Collections.unmodifiableNavigableSet(new TreeSet<>(Collections.singleton("1")))));
assertTrue(immutability.test(Collections.unmodifiableSet(Collections.singleton("1"))));
assertTrue(immutability.test(Collections.unmodifiableSortedMap(new TreeMap<>(Collections.singletonMap("1", "2")))));
assertTrue(immutability.test(Collections.unmodifiableSortedSet(new TreeSet<>(Collections.singleton("1")))));

Object mutableObject = new AtomicInteger();
assertFalse(immutability.test(Collections.singletonList(mutableObject)));
assertFalse(immutability.test(Collections.singletonMap("1", mutableObject)));
}

@net.jcip.annotations.Immutable
Expand Down
Expand Up @@ -25,6 +25,7 @@
import java.util.EnumSet;
import java.util.function.Predicate;

import org.wildfly.clustering.ee.CollectionImmutability;
import org.wildfly.clustering.ee.Immutability;
import org.wildfly.clustering.web.annotation.Immutable;

Expand All @@ -35,7 +36,14 @@ public enum SessionAttributeImmutability implements Predicate<Object> {
JDK() {
@Override
public boolean test(Object object) {
return Immutability.INSTANCE.test(object);
// Skip Collection test, we override this below to extend the immutability test for collection elements.
return EnumSet.complementOf(EnumSet.of(Immutability.COLLECTION)).stream().anyMatch(predicate -> predicate.test(object));
}
},
COLLECTION() {
@Override
public boolean test(Object object) {
return COLLECTION_INSTANCE.test(object);
}
},
ANNOTATION() {
Expand All @@ -47,4 +55,5 @@ public boolean test(Object object) {
;

public static final Predicate<Object> INSTANCE = object -> EnumSet.allOf(SessionAttributeImmutability.class).stream().anyMatch(predicate -> predicate.test(object));
static final Predicate<Object> COLLECTION_INSTANCE = new CollectionImmutability(INSTANCE);
}
Expand Up @@ -24,6 +24,9 @@

import static org.junit.Assert.assertTrue;

import java.util.Collections;
import java.util.function.Predicate;

import org.junit.Test;
import org.wildfly.clustering.ee.ImmutabilityTestCase;
import org.wildfly.clustering.web.annotation.Immutable;
Expand All @@ -36,9 +39,17 @@ public class SessionAttributeImmutabilityTestCase extends ImmutabilityTestCase {
@Override
@Test
public void test() throws Exception {
super.test(SessionAttributeImmutability.INSTANCE);
this.test(SessionAttributeImmutability.INSTANCE);
}

@Override
protected void test(Predicate<Object> immutability) throws Exception {
super.test(immutability);

assertTrue(SessionAttributeImmutability.INSTANCE.test(new ImmutableObject()));
assertTrue(immutability.test(new ImmutableObject()));
assertTrue(immutability.test(Collections.singleton(new ImmutableObject())));
assertTrue(immutability.test(Collections.singletonList(new ImmutableObject())));
assertTrue(immutability.test(Collections.singletonMap("1", new ImmutableObject())));
}

@Immutable
Expand Down

0 comments on commit f1cf0ce

Please sign in to comment.