blob: 0dd2318e97768860c77a38dce6dfeed6c8c4c4f8 [file] [log] [blame]
/*
* Copyright (C) 2016 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.common.reflect;
import static com.google.common.truth.Truth.assertThat;
import java.io.Serializable;
import java.util.Comparator;
import java.util.List;
import junit.framework.TestCase;
@AndroidIncompatible // lots of failures, possibly some related to bad equals() implementations?
public class TypeTokenSubtypeTest extends TestCase {
public void testOwnerTypeSubtypes() throws Exception {
new OwnerTypeSubtypingTests().testAllDeclarations();
}
public void testWildcardSubtypes() throws Exception {
new WildcardSubtypingTests().testAllDeclarations();
}
/**
* This test reproduces the bug in canonicalizeWildcardType() when the type variable is
* recursively bounded.
*/
public void testRecursiveWildcardSubtypeBug() throws Exception {
try {
new RecursiveTypeBoundBugExample<>().testAllDeclarations();
fail();
} catch (Exception e) {
assertThat(e).hasCauseThat().isInstanceOf(AssertionError.class);
}
}
public void testSubtypeOfInnerClass_nonStaticAnonymousClass() {
TypeToken<?> supertype = new TypeToken<Mall<Outdoor>.Shop<Electronics>>() {};
Class<?> subclass = new Mall<Outdoor>().new Shop<Electronics>() {}.getClass();
assertTrue(TypeToken.of(subclass).isSubtypeOf(supertype));
}
public void testSubtypeOfInnerClass_nonStaticAnonymousClass_typeParameterOfOwnerTypeNotMatch() {
TypeToken<?> supertype = new TypeToken<Mall<Outdoor>.Shop<Electronics>>() {};
Class<?> subclass = new Mall<Indoor>().new Shop<Electronics>() {}.getClass();
assertFalse(TypeToken.of(subclass).isSubtypeOf(supertype));
}
public void testSubtypeOfInnerClass_nonStaticAnonymousClass_typeParameterOfInnerTypeNotMatch() {
TypeToken<?> supertype = new TypeToken<Mall<Outdoor>.Shop<Electronics>>() {};
Class<?> subclass = new Mall<Outdoor>().new Shop<Grocery>() {}.getClass();
assertFalse(TypeToken.of(subclass).isSubtypeOf(supertype));
}
public static void testSubtypeOfInnerClass_staticAnonymousClass() {
TypeToken<?> supertype = new TypeToken<Mall<Outdoor>.Shop<Electronics>>() {};
Class<?> subclass = new Mall<Outdoor>().new Shop<Electronics>() {}.getClass();
assertTrue(TypeToken.of(subclass).isSubtypeOf(supertype));
}
public static void testSubtypeOfStaticAnonymousClass() {
Class<?> superclass = new Mall<Outdoor>().new Shop<Electronics>() {}.getClass();
assertTrue(TypeToken.of(superclass).isSubtypeOf(superclass));
assertFalse(
TypeToken.of(new Mall<Outdoor>().new Shop<Electronics>() {}.getClass())
.isSubtypeOf(superclass));
}
public void testSubtypeOfNonStaticAnonymousClass() {
Class<?> superclass = new Mall<Outdoor>().new Shop<Electronics>() {}.getClass();
assertTrue(TypeToken.of(superclass).isSubtypeOf(superclass));
assertFalse(
TypeToken.of(new Mall<Outdoor>().new Shop<Electronics>() {}.getClass())
.isSubtypeOf(superclass));
}
public void testGetSubtypeOf_impossibleWildcard() {
TypeToken<List<? extends Number>> numberList = new TypeToken<List<? extends Number>>() {};
abstract class StringList implements List<String> {}
try {
numberList.getSubtype(StringList.class);
fail();
} catch (IllegalArgumentException expected) {
}
}
private static class OwnerTypeSubtypingTests extends SubtypeTester {
@TestSubtype
public Mall<Outdoor>.Shop<Electronics> innerTypeIsSubtype(
Mall<Outdoor>.Retailer<Electronics> retailer) {
return isSubtype(retailer);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Mall<Outdoor>.Shop<? extends Electronics> innerTypeIsSubtype_supertypeWithWildcard(
Mall<Outdoor>.Retailer<Electronics> retailer) {
return isSubtype(retailer);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Mall<?>.Shop<?> innerTypeIsSubtype_withWildcards(
Mall<? extends Indoor>.Retailer<? extends Electronics> retailer) {
return isSubtype(retailer);
}
@TestSubtype
public Mall<Outdoor>.Shop<Electronics> ownerTypeIsSubtype(
Outlet<Outdoor>.Shop<Electronics> shop) {
return isSubtype(shop);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Mall<? extends Outdoor>.Shop<Electronics> ownerTypeIsSubtype_supertypeWithWildcard(
Outlet<Outdoor>.Shop<Electronics> shop) {
return isSubtype(shop);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Mall<?>.Shop<Electronics> ownerTypeIsSubtype_withWildcards(
Outlet<? extends Outdoor>.Shop<Electronics> shop) {
return isSubtype(shop);
}
@TestSubtype
public Mall<Outdoor>.Shop<Electronics> bothOwnerTypeAndInnerTypeAreSubtypes(
Outlet<Outdoor>.Retailer<Electronics> retailer) {
return isSubtype(retailer);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Mall<? super Outdoor>.Shop<? extends Electronics>
bothOwnerTypeAndInnerTypeAreSubtypes_supertypeWithWildcard(
Outlet<Outdoor>.Retailer<Electronics> retailer) {
return isSubtype(retailer);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Mall<? super Outdoor>.Shop<? extends Electronics>
bothOwnerTypeAndInnerTypeAreSubtypes_withWildcards(
Outlet<Outdoor>.Retailer<Electronics> retailer) {
return isSubtype(retailer);
}
@TestSubtype
public Mall<Outdoor>.Shop<Electronics> ownerTypeDoesNotMatch(
Mall<Indoor>.Shop<Electronics> shop) {
return notSubtype(shop);
}
@TestSubtype
public Mall<Outdoor>.Shop<Electronics> ownerTypeDoesNotMatch_subtypeWithWildcard(
Mall<? extends Outdoor>.Shop<Electronics> shop) {
return notSubtype(shop);
}
@TestSubtype
public Mall<? extends Outdoor>.Shop<Electronics> ownerTypeDoesNotMatch_supertypeWithWildcard(
Mall<?>.Shop<Electronics> shop) {
return notSubtype(shop);
}
@TestSubtype
public Mall<Outdoor>.Retailer<Electronics> innerTypeDoesNotMatch(
Mall<Outdoor>.Shop<Grocery> shop) {
return notSubtype(shop);
}
@TestSubtype
public Mall<Outdoor>.Shop<Electronics> innerTypeDoesNotMatch_subtypeWithWildcard(
Mall<Outdoor>.Shop<? extends Electronics> shop) {
return notSubtype(shop);
}
@TestSubtype
public Mall<Outdoor>.Shop<? extends Electronics> innerTypeDoesNotMatch_supertypeWithWildcard(
Mall<Outdoor>.Shop<Grocery> shop) {
return notSubtype(shop);
}
@TestSubtype(suppressGetSubtype = true)
public ConsumerFacing<Electronics> supertypeIsNestedClass(
Mall<Indoor>.Retailer<Electronics> shop) {
return isSubtype(shop);
}
@TestSubtype
public ConsumerFacing<Grocery> nestedClassIsNotSupertypeDueToTypeParameter(
Mall<Indoor>.Shop<Electronics> shop) {
return notSubtype(shop);
}
@TestSubtype
public ConsumerFacing<Grocery> nestedClassIsNotSupertype(Mall<Indoor>.Shop<Grocery> shop) {
return notSubtype(shop);
}
@TestSubtype(suppressGetSubtype = true)
public Comparator<Electronics> supertypeIsTopLevelClass(
Mall<Indoor>.Retailer<Electronics> shop) {
return isSubtype(shop);
}
@TestSubtype
public Comparator<Electronics> topLevelClassIsNotSupertypeDueToTypeParameter(
Mall<Indoor>.Retailer<Grocery> shop) {
return notSubtype(shop);
}
@TestSubtype
public Comparator<Electronics> topLevelClassIsNotSupertype(
Mall<Indoor>.Shop<Electronics> shop) {
return notSubtype(shop);
}
}
private static class RecursiveTypeBoundBugExample<T extends RecursiveTypeBoundBugExample<T>>
extends SubtypeTester {
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public List<RecursiveTypeBoundBugExample<?>> ifYouUseTheTypeVariableOnTheClassAndItIsRecursive(
List<RecursiveTypeBoundBugExample<? extends RecursiveTypeBoundBugExample<T>>> arg) {
return notSubtype(arg); // isSubtype() currently incorectly considers it a subtype.
}
}
private static class WildcardSubtypingTests extends SubtypeTester {
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Iterable<?> noBounds(List<?> list) {
return isSubtype(list);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Iterable<List<?>> listOfListOfWildcard(List<List<?>> listOfList) {
return isSubtype(listOfList);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Iterable<? extends List<?>> listOfWildcardListOfWildcard(
List<? extends List<?>> listOfList) {
return isSubtype(listOfList);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Enum<? extends Enum<?>> implicitlyBoundedEnumIsSubtypeOfExplicitlyBoundedEnum(
Enum<?> e) {
return isSubtype(e);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Enum<?> implicitlyBoundedEnum(Enum<?> e) {
return isSubtype(e);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Enum<?> explicitlyBoundedEnumIsSubtypeOfImplicitlyBoundedEnum(
Enum<? extends Enum<?>> obj) {
return isSubtype(obj);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Iterable<Enum<?>> listOfEnums(List<Enum<?>> listOfEnums) {
return isSubtype(listOfEnums);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public UseList<? extends List<Enum<? extends Enum<?>>>>
wildcardBoundUsesImplicitlyRecursiveBoundedWildcard(UseList<? extends List<Enum<?>>> arg) {
return isSubtype(arg);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public UseList<? extends List<Enum<? extends Enum<?>>>>
wildcardBoundHasImplicitBoundAtsInvariantPosition(UseList<? extends List<Enum<?>>> arg) {
return isSubtype(arg);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Iterable<Enum<?>> listOfEnumsWithExplicitBoundIsSubtypeOfIterableOfEnumWithImplicitBound(
List<Enum<? extends Enum<?>>> listOfEnums) {
return isSubtype(listOfEnums);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Iterable<Enum<?>> nestedExplicitEnumBoundIsSubtypeOfImplicitEnumBound(
List<Enum<? extends Enum<? extends Enum<?>>>> listOfEnums) {
return isSubtype(listOfEnums);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Iterable<Enum<? extends Enum<? extends Enum<?>>>>
implicitEnumBoundIsSubtypeOfNestedExplicitEnumBound(List<Enum<?>> listOfEnums) {
return isSubtype(listOfEnums);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Iterable<Enum<? extends Enum<?>>>
listOfEnumsWithImplicitBoundIsSubtypeOfIterableOfEnumWithExplicitBound(
List<Enum<?>> listOfEnums) {
return isSubtype(listOfEnums);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public List<Enum<? extends Enum<?>>> listOfSubEnumsIsNotSubtypeOfListOfEnums(
List<MyEnum> listOfEnums) {
return notSubtype(listOfEnums);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public List<MyTypeBoundUsesImplicitBound<? extends Enum<?>>> typeVariableBoundOmitsItsOwnBound(
List<MyTypeBoundUsesImplicitBound<?>> arg) {
return isSubtype(arg);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public List<MyTypeBoundUsesImplicitBound<? extends MyEnum>>
wildcardUpperBoundIsNotSubtypeOfTypeVariableBound(
List<MyTypeBoundUsesImplicitBound<?>> arg) {
return notSubtype(arg);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public List<List<? extends Iterable<UseList<? extends List<?>>>>>
wildcardBoundUsesParameterizedTypeWithImplicitBound(
List<List<? extends Iterable<UseList<?>>>> arg) {
return isSubtype(arg);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public List<List<? extends Iterable<Enum<? extends Enum<?>>>>>
wildcardBoundUsesRecursiveParameterizedTypeWithImplicitBound(
List<List<? extends Iterable<Enum<?>>>> arg) {
return isSubtype(arg);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public List<List<? extends Iterable<MyTypeBoundUsesImplicitBound<? extends Enum<?>>>>>
wildcardBoundUsesParameterizedTypeDefinedWithImplicitBound(
List<List<? extends Iterable<MyTypeBoundUsesImplicitBound<?>>>> arg) {
return isSubtype(arg);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Iterable<UseIterable<? extends Iterable<?>>>
wildcardOfImplicitBoundedIsSubtypeOfWildcardOfExplicitlyBounded(
List<UseIterable<?>> withImplicitBounds) {
return isSubtype(withImplicitBounds);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Iterable<UseSerializableIterable<? extends Iterable<?>>>
wildcardOfImplicitBoundedIsSubtypeOfWildcardOfExplicitlyPartialBounded(
List<UseSerializableIterable<?>> withImplicitBounds) {
return isSubtype(withImplicitBounds);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Iterable<UseList<? extends Iterable<?>>> useListOfIterableWildcard(
List<UseList<?>> withImplicitBounds) {
return isSubtype(withImplicitBounds);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Iterable<UseIterable<?>> listOfExplicitBoundedIsSubtypeOfListOfImplicitlyBounded(
List<UseIterable<? extends Iterable<?>>> withExplicitBounds) {
return isSubtype(withExplicitBounds);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Iterable<UseIterable<? extends Iterable<?>>>
wildcardOfImplicitBoundedIsNotSubtypeOfNonWildcardOfExplicitlyBounded(
List<? extends UseIterable<?>> withImplicitBounds) {
return notSubtype(withImplicitBounds);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public Iterable<UseIterable<? extends List<?>>>
wildcardOfImplicitBoundedIsNotSubtypeOfWildcardWithNarrowerBounds(
List<UseIterable<?>> withImplicitBounds) {
return notSubtype(withImplicitBounds);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public <T> Iterable<? extends T> supertypeWithWildcardUpperBound(List<T> list) {
return isSubtype(list);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public <T> Iterable<? extends T> supertypeWithWildcardUpperBound_notMatch(List<String> list) {
return notSubtype(list);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public <T> Iterable<? super T> supertypeWithWildcardULowerBound(List<T> list) {
return isSubtype(list);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public <T> Iterable<? extends T> supertypeWithWildcardULowerBound_notMatch(List<String> list) {
return notSubtype(list);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public <T> Iterable<?> wildcardsMatchByUpperBound(List<? extends T> list) {
return isSubtype(list);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public <T> Iterable<? extends T> wildCardsDoNotMatchByUpperBound(List<?> list) {
return notSubtype(list);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public <T> Iterable<? super String> wildcardsMatchByLowerBound(
List<? super CharSequence> list) {
return isSubtype(list);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public <T> Iterable<? super CharSequence> wildCardsDoNotMatchByLowerBound(
List<? super String> list) {
return notSubtype(list);
}
// Can't test getSupertype() or getSubtype() because JDK reflection doesn't consider
// Foo<?> and Foo<? extends Bar> equal for class Foo<T extends Bar>
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public UseIterable<?> explicitTypeBoundIsSubtypeOfImplicitTypeBound(
UseIterable<? extends Iterable<?>> obj) {
return isSubtype(obj);
}
// Can't test getSupertype() or getSubtype() because JDK reflection doesn't consider
// Foo<?> and Foo<? extends Bar> equal for class Foo<T extends Bar>
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public UseIterable<? extends Iterable<?>> implicitTypeBoundIsSubtypeOfExplicitTypeBound(
UseIterable<?> obj) {
return isSubtype(obj);
}
// Can't test getSupertype() or getSubtype() because JDK reflection doesn't consider
// Foo<?> and Foo<? extends Bar> equal for class Foo<T extends Bar>
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public UseIterable<? extends Iterable<?>> omittedTypeBoundIsSubtypeOfExplicitTypeBound(
UseIterable<? extends CharSequence> obj) {
return isSubtype(obj);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public UseSerializableIterable<? extends Serializable>
implicitTypeBoundIsSubtypeOfPartialExplicitTypeBound(UseSerializableIterable<?> obj) {
return isSubtype(obj);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public UseSerializableIterable<?> partialImplicitTypeBoundIsSubtypeOfImplicitTypeBound(
UseSerializableIterable<? extends Iterable<?>> obj) {
return isSubtype(obj);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public UseSerializableIterable<? extends CharSequence>
implicitTypeBoundIsNotSubtypeOfDifferentTypeBound(UseSerializableIterable<?> obj) {
return notSubtype(obj);
}
@TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true)
public UseSerializableIterable<? extends CharSequence>
partialExplicitTypeBoundIsNotSubtypeOfDifferentTypeBound(
UseSerializableIterable<? extends Serializable> obj) {
return notSubtype(obj);
}
}
// TODO(benyu): migrate all subtyping tests from TypeTokenTest to this class using SubtypeTester.
private interface Outdoor {}
private interface Indoor {}
private interface Grocery {}
private interface Electronics {}
private interface ConsumerFacing<T> {}
private static class Mall<T> {
class Shop<ProductT> {}
abstract class Retailer<ProductT> extends Shop<ProductT>
implements Comparator<ProductT>, ConsumerFacing<ProductT> {}
}
private static class Outlet<T> extends Mall<T> {}
private interface UseIterable<T extends Iterable<?>> {}
private interface UseSerializableIterable<T extends Serializable & Iterable<?>> {}
private interface UseList<T extends List<?>> {}
private enum MyEnum {}
private interface MyTypeBoundUsesImplicitBound<E extends Enum<?>> {}
}