package com.fasterxml.classmate;

import java.util.*;

@SuppressWarnings("serial")
public class TestSubtypeResolution extends BaseTest
{
    /*
    /**********************************************************************
    /* Helper types
    /**********************************************************************
     */

    static class IntArrayList extends ArrayList<Integer> { }
    
    static class StringIntMap extends HashMap<String,Integer> { }
    
    /*
    /**********************************************************************
    /* setup
    /**********************************************************************
     */

    protected TypeResolver typeResolver;
    
    protected void setUp()
    {
        // Let's use a single instance for all tests, to increase chance of seeing failures
        typeResolver = new TypeResolver();
    }
    
    /*
    /**********************************************************************
    /* Unit tests
    /**********************************************************************
     */

    public void testValidUntypedSubtype()
    {
        // First, make a concrete type that extends specified generic interface:
        ResolvedType supertype = typeResolver.resolve(HashMap.class, String.class, Integer.class);
        ResolvedType subtype = typeResolver.resolveSubtype(supertype, StringIntMap.class);
        assertSame(StringIntMap.class, subtype.getErasedType());

        // but resolution can't cheat; we must be able to find parameterization...
        List<ResolvedType> bindings = subtype.typeParametersFor(HashMap.class);
        assertEquals(2, bindings.size());
        assertSame(String.class, bindings.get(0).getErasedType());
        assertSame(Integer.class, bindings.get(1).getErasedType());
    }

    public void testValidGenericSubtype()
    {
        // First, make a concrete type that extends specified generic interface:
        ResolvedType supertype = typeResolver.resolve(Map.class, String.class, Long.class);
        ResolvedType subtype = typeResolver.resolveSubtype(supertype, HashMap.class);
        assertSame(HashMap.class, subtype.getErasedType());

        // in this case it's direct class, so we do have bindings
        TypeBindings bindings = subtype.getTypeBindings();
        assertEquals(2, bindings.size());
        assertSame(String.class, bindings.getBoundType(0).getErasedType());
        assertSame(Long.class, bindings.getBoundType(1).getErasedType());

        // and must look the same in other respects too:
        assertEquals("Ljava/util/HashMap<Ljava/lang/String;Ljava/lang/Long;>;", subtype.getSignature());
        assertEquals("java.util.HashMap<java.lang.String,java.lang.Long> extends java.util.AbstractMap<java.lang.String,java.lang.Long> implements java.util.Map<java.lang.String,java.lang.Long>,java.lang.Cloneable<java.lang.String,java.lang.Long>,java.io.Serializable<java.lang.String,java.lang.Long>",
                subtype.getFullDescription());
    }

    // Test to verify that type erasures are compatible
    public void testInvalidSubClass()
    {
        ResolvedType supertype = typeResolver.resolve(List.class, Integer.class);
        try {
            typeResolver.resolveSubtype(supertype, HashMap.class);
            fail("Expected failure");
        } catch (IllegalArgumentException e) {
            verifyException(e, "Can not sub-class java.util.List");
        }
    }

    // Test to further verify that type parameters are compatible
    public void testIncompatibleTypeParameters()
    {
        ResolvedType supertype = typeResolver.resolve(ArrayList.class, String.class);
        try {
            typeResolver.resolveSubtype(supertype, IntArrayList.class);
            fail("Expected failure");
        } catch (IllegalArgumentException e) {
            verifyException(e, "Type parameter #1/1 differs; expected java.lang.String");
        }
    }
    
}

@@ -441,32 +441,91 @@
 ing,Integer%3E %7B %7D
+%0A%0A    interface StringKeyMap%3CVT%3E extends Map%3CString,VT%3E %7B %7D
 %0A    %0A    /*%0A   
@@ -3035,32 +3035,1063 @@
 tion());%0A    %7D%0A%0A
+    /**%0A     * Let's test that we can also resolve to incomplete types; might%0A     * be useful occasionally%0A     */%0A    public void testValidIncompleteSubtype()%0A    %7B%0A        ResolvedType supertype = typeResolver.resolve(Map.class, String.class, Long.class);%0A        ResolvedType subtype = typeResolver.resolveSubtype(supertype, StringKeyMap.class);%0A        assertSame(StringKeyMap.class, subtype.getErasedType());%0A%0A        TypeBindings bindings = subtype.getTypeBindings();%0A        assertEquals(1, bindings.size());%0A        assertSame(Long.class, bindings.getBoundType(0).getErasedType());%0A%0A        // And should see full types for Map%0A        ResolvedType actualSupertype = subtype.findSupertype(Map.class);%0A        assertSame(Map.class, actualSupertype.getErasedType());%0A        bindings = actualSupertype.getTypeBindings();%0A        assertEquals(2, bindings.size());%0A        assertSame(String.class, bindings.getBoundType(0).getErasedType());%0A        assertSame(Long.class, bindings.getBoundType(1).getErasedType());%0A    %7D%0A    %0A
     // Test to v

Add some more unit testing