Further tests for the TCK: method visibility and overrides.

These tests check for private method injections; that's not required by the spec but it's an important thing to test. I might need to configure the tool to report these as warnings

git-svn-id: https://atinject.googlecode.com/svn/trunk@29 3bc8319c-20ab-11de-9edc-3f40a397ab60
diff --git a/atinject.ipr b/atinject.ipr
index 4308036..8c1825a 100644
--- a/atinject.ipr
+++ b/atinject.ipr
@@ -255,5 +255,17 @@
   <component name="VcsDirectoryMappings">
     <mapping directory="" vcs="svn" />
   </component>
+  <component name="libraryTable">
+    <library name="Guice">
+      <CLASSES>
+        <root url="jar:///Users/jessewilson/Guice/google-guice/lib/aopalliance.jar!/" />
+        <root url="jar:///Users/jessewilson/Guice/google-guice/build/dist/guice-snapshot.jar!/" />
+      </CLASSES>
+      <JAVADOC />
+      <SOURCES>
+        <root url="file:///Users/jessewilson/Guice/google-guice/src" />
+      </SOURCES>
+    </library>
+  </component>
 </project>
 
diff --git a/tck/com/googlecode/atinject/Tck.java b/tck/com/googlecode/atinject/Tck.java
index 8a1a31e..e77dec1 100644
--- a/tck/com/googlecode/atinject/Tck.java
+++ b/tck/com/googlecode/atinject/Tck.java
@@ -40,7 +40,7 @@
         }
 
         if (car == null) {
-            tester.addProblem("Expected non-null result from Candidate.getCar()");
+            tester.addProblem("Null returned from Candidate.getCar()");
         } else {
             car.check(tester);
         }
diff --git a/tck/com/googlecode/atinject/auto/Convertible.java b/tck/com/googlecode/atinject/auto/Convertible.java
index 04788bd..0b18cb1 100644
--- a/tck/com/googlecode/atinject/auto/Convertible.java
+++ b/tck/com/googlecode/atinject/auto/Convertible.java
@@ -18,57 +18,177 @@
 package com.googlecode.atinject.auto;
 
 import com.googlecode.atinject.Tester;
+import com.googlecode.atinject.auto.accessories.SpareTire;
 
 import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
 import java.util.ArrayList;
 import java.util.List;
 
 public class Convertible implements Car {
 
-    private static final V8Engine NEVER_INJECTED = new V8Engine();
-
-    @Inject V8Engine engineA = NEVER_INJECTED;
-
     @Inject DriversSeat driversSeatA;
     @Inject DriversSeat driversSeatB;
+    @Inject V8Engine engine;
+    @Inject SpareTire spareTire;
 
-    private boolean constructorInjected = false;
-    private boolean methodsInjected = false;
+    private boolean methodWithZeroParamsInjected;
+    private boolean methodWithMultipleParamsInjected;
+    private boolean methodWithNonVoidReturnInjected;
 
     private List<String> moreProblems = new ArrayList<String>();
 
+    private Seat constructorPlainSeat;
+    private Seat constructorDriversSeat;
+    private Tire constructorPlainTire;
+    private Tire constructorSpareTire;
+    private Provider<Seat> constructorPlainSeatProvider;
+    private Provider<Seat> constructorDriversSeatProvider;
+    private Provider<Tire> constructorPlainTireProvider;
+    private Provider<Tire> constructorSpareTireProvider;
 
-    @Inject Convertible(Seat a, Seat b, Seat c) {
-        if (methodsInjected) {
-            moreProblems.add("Expected constructor to be injected before methods");
-        }
-        if (engineA != NEVER_INJECTED) {
-            moreProblems.add("Expected constructor to be injected before fields");
-        }
-        constructorInjected = true;
+    @Inject Seat fieldPlainSeat;
+    @Inject @Drivers Seat fieldDriversSeat;
+    @Inject Tire fieldPlainTire;
+    @Inject @Named("spare") Tire fieldSpareTire;
+    @Inject Provider<Seat> fieldPlainSeatProvider;
+    @Inject @Drivers Provider<Seat> fieldDriversSeatProvider;
+    @Inject Provider<Tire> fieldPlainTireProvider;
+    @Inject @Named("spare") Provider<Tire> fieldSpareTireProvider;
+
+    private Seat methodPlainSeat;
+    private Seat methodDriversSeat;
+    private Tire methodPlainTire;
+    private Tire methodSpareTire;
+    private Provider<Seat> methodPlainSeatProvider;
+    private Provider<Seat> methodDriversSeatProvider;
+    private Provider<Tire> methodPlainTireProvider;
+    private Provider<Tire> methodSpareTireProvider;
+
+    @Inject Convertible(
+            Seat plainSeat,
+            @Drivers Seat driversSeat,
+            Tire plainTire,
+            @Named("spare") Tire spareTire,
+            Provider<Seat> plainSeatProvider,
+            @Drivers Provider<Seat> driversSeatProvider,
+            Provider<Tire> plainTireProvider,
+            @Named("spare") Provider<Tire> spareTireProvider) {
+        constructorPlainSeat = plainSeat;
+        constructorDriversSeat = driversSeat;
+        constructorPlainTire = plainTire;
+        constructorSpareTire = spareTire;
+        constructorPlainSeatProvider = plainSeatProvider;
+        constructorDriversSeatProvider = driversSeatProvider;
+        constructorPlainTireProvider = plainTireProvider;
+        constructorSpareTireProvider = spareTireProvider;
     }
 
-    Convertible() {}
+    Convertible() {
+        throw new AssertionError("Unexpected call to non-injectable constructor");
+    }
 
-    @Inject void injectSomeStuff(Seat a, Seat b) {
-        if (methodsInjected) {
-            moreProblems.add("Expected methods to be injected only once");
-        }
-        if (engineA == NEVER_INJECTED) {
-            moreProblems.add("Expected fields to be injected before methods");
-        }
-        methodsInjected = true;
+    void setSeat(Seat unused) {
+        throw new AssertionError("Unexpected call to non-injectable method");
+    }
+
+    @Inject void injectMethodWithZeroArgs() {
+        methodWithZeroParamsInjected = true;
+    }
+
+    @Inject String injectMethodWithNonVoidReturn() {
+        methodWithNonVoidReturnInjected = true;
+        return "unused";
+    }
+
+    @Inject void injectMethodWithManyArgs(
+            Seat plainSeat,
+            @Drivers Seat driversSeat,
+            Tire plainTire,
+            @Named("spare") Tire spareTire,
+            Provider<Seat> plainSeatProvider,
+            @Drivers Provider<Seat> driversSeatProvider,
+            Provider<Tire> plainTireProvider,
+            @Named("spare") Provider<Tire> spareTireProvider) {
+        methodWithMultipleParamsInjected = true;
+
+        methodPlainSeat = plainSeat;
+        methodDriversSeat = driversSeat;
+        methodPlainTire = plainTire;
+        methodSpareTire = spareTire;
+        methodPlainSeatProvider = plainSeatProvider;
+        methodDriversSeatProvider = driversSeatProvider;
+        methodPlainTireProvider = plainTireProvider;
+        methodSpareTireProvider = spareTireProvider;
     }
 
     public void check(Tester tester) {
         tester.addProblems(moreProblems);
 
         // values are injected
-        tester.test(constructorInjected, "Expected constructor to be injected");
-        tester.test(methodsInjected, "Expected methods to be injected");
-        tester.test(engineA != NEVER_INJECTED, "Expected fields to be injected");
+        tester.test(methodWithZeroParamsInjected, "Zero-parmeter method not injected");
+        tester.test(methodWithMultipleParamsInjected, "Multi-parameter method not injected");
+        tester.test(methodWithNonVoidReturnInjected, "Non-void method not injected");
 
         // singleton is not @Inherited
-        tester.test(driversSeatA != driversSeatB, "Expected @Singleton to not be inherited");
+        tester.test(driversSeatA != driversSeatB, "@Singleton inherited from supertype");
+
+        testInjectedValues(tester);
+        testProviders(tester);
+
+        spareTire.check(tester);
+        engine.check(tester);
+    }
+
+    private void testInjectedValues(Tester tester) {
+        testExpectedValues(tester, "injected constructor",
+                constructorPlainSeat, constructorDriversSeat, constructorPlainTire, constructorSpareTire);
+        testExpectedValues(tester, "provider injected into a constructor", constructorPlainSeatProvider.get(),
+                constructorDriversSeatProvider.get(), constructorPlainTireProvider.get(), constructorSpareTireProvider.get());
+        testExpectedValues(tester, "injected field",
+                fieldPlainSeat, fieldDriversSeat, fieldPlainTire, fieldSpareTire);
+        testExpectedValues(tester, "provider injected into a field", fieldPlainSeatProvider.get(),
+                fieldDriversSeatProvider.get(), fieldPlainTireProvider.get(), fieldSpareTireProvider.get());
+        testExpectedValues(tester, "injected method",
+                methodPlainSeat, methodDriversSeat, methodPlainTire, methodSpareTire);
+        testExpectedValues(tester, "provider injected into a method", methodPlainSeatProvider.get(),
+                methodDriversSeatProvider.get(), methodPlainTireProvider.get(), methodSpareTireProvider.get());
+    }
+
+    private void testProviders(Tester tester) {
+        testProviderProvidesNewValuesEachTime(tester,
+                constructorDriversSeatProvider, constructorPlainTireProvider, constructorSpareTireProvider);
+        testProviderProvidesNewValuesEachTime(tester,
+                fieldDriversSeatProvider, fieldPlainTireProvider, fieldSpareTireProvider);
+        testProviderProvidesNewValuesEachTime(tester,
+                methodDriversSeatProvider, methodPlainTireProvider, methodSpareTireProvider);
+        testProviderProvidesSameValueEachTime(tester, constructorPlainSeatProvider);
+        testProviderProvidesSameValueEachTime(tester, fieldPlainSeatProvider);
+        testProviderProvidesSameValueEachTime(tester, methodPlainSeatProvider);
+    }
+
+    private void testProviderProvidesSameValueEachTime(Tester tester, Provider<Seat> provider) {
+        tester.test(provider.get() == provider.get(),
+                "Different instance returned by repeated calls to Provider.get()");
+    }
+
+    private void testExpectedValues(Tester tester, String injectionMechanism,
+                Seat plainSeat, Seat driversSeat, Tire plainTire, Tire spareTire) {
+        tester.test(!(plainSeat instanceof DriversSeat),
+                "Wrong type injected for " + injectionMechanism);
+        tester.test(driversSeat instanceof DriversSeat,
+                "Wrong type injected for qualified " + injectionMechanism);
+        tester.test(!(plainTire instanceof SpareTire),
+                "Wrong type injected for " + injectionMechanism);
+        tester.test(spareTire instanceof SpareTire,
+                "Wrong type injected for @Named " + injectionMechanism);
+    }
+
+    private void testProviderProvidesNewValuesEachTime(Tester tester, Provider<?>... providers) {
+        for (Provider provider : providers) {
+            tester.test(provider.get() != provider.get(),
+                    "Same instance returned by repeated calls to Provider.get()");
+        }
     }
 }
diff --git a/tck/com/googlecode/atinject/auto/Engine.java b/tck/com/googlecode/atinject/auto/Engine.java
index 14fd19a..c427b04 100644
--- a/tck/com/googlecode/atinject/auto/Engine.java
+++ b/tck/com/googlecode/atinject/auto/Engine.java
@@ -17,6 +17,22 @@
 
 package com.googlecode.atinject.auto;
 
+import javax.inject.Inject;
+import java.util.List;
+import java.util.ArrayList;
+
 public abstract class Engine {
     
+    protected final List<String> moreProblems = new ArrayList<String>();
+
+    protected boolean packagePrivateMethodInjected;
+    protected boolean packagePrivateMethodForOverrideInjected;
+
+    @Inject void injectPackagePrivateMethod() {
+        moreProblems.add("Unexpected call to supertype package private method");
+    }
+
+    @Inject void injectPackagePrivateMethodForOverride() {
+        moreProblems.add("Unexpected call to supertype package private method");
+    }
 }
diff --git a/tck/com/googlecode/atinject/auto/Tire.java b/tck/com/googlecode/atinject/auto/Tire.java
index 7fa1423..a4a2f3e 100644
--- a/tck/com/googlecode/atinject/auto/Tire.java
+++ b/tck/com/googlecode/atinject/auto/Tire.java
@@ -17,6 +17,113 @@
 
 package com.googlecode.atinject.auto;
 
+import javax.inject.Inject;
+import java.util.List;
+import java.util.ArrayList;
+
 public class Tire {
 
+    protected static final V8Engine NEVER_INJECTED = new V8Engine();
+
+    protected final List<String> moreProblems = new ArrayList<String>();
+
+    V8Engine constructorInjection = NEVER_INJECTED;
+    @Inject V8Engine fieldInjection = NEVER_INJECTED;
+    V8Engine methodInjection = NEVER_INJECTED;
+
+    boolean constructorInjected;
+
+    protected boolean superPrivateMethodInjected;
+    protected boolean superPackagePrivateMethodInjected;
+    protected boolean superProtectedMethodInjected;
+    protected boolean superPublicMethodInjected;
+    protected boolean subPrivateMethodInjected;
+    protected boolean subPackagePrivateMethodInjected;
+    protected boolean subProtectedMethodInjected;
+    protected boolean subPublicMethodInjected;
+
+    protected boolean superPrivateMethodForOverrideInjected;
+    protected boolean superPackagePrivateMethodForOverrideInjected;
+    protected boolean subPrivateMethodForOverrideInjected;
+    protected boolean subPackagePrivateMethodForOverrideInjected;
+    protected boolean protectedMethodForOverrideInjected;
+    protected boolean publicMethodForOverrideInjected;
+
+    @Inject
+    public Tire(V8Engine constructorInjection) {
+        this.constructorInjection = constructorInjection;
+    }
+
+    @Inject void supertypeMethodInjection(V8Engine methodInjection) {
+        if (!hasTireBeenFieldInjected()) {
+            moreProblems.add("Method injected before fields");
+        }
+        if (hasSpareTireBeenFieldInjected()) {
+            moreProblems.add("Subtype field injected before supertype method");
+        }
+        if (hasSpareTireBeenMethodInjected()) {
+            moreProblems.add("Subtype method injected before supertype method");
+        }
+        this.methodInjection = methodInjection;
+    }
+
+    @Inject private void injectPrivateMethod() {
+        if (superPrivateMethodInjected) {
+            moreProblems.add("Overridden private method injected twice");
+        }
+        superPrivateMethodInjected = true;
+    }
+
+    @Inject void injectPackagePrivateMethod() {
+        if (superPackagePrivateMethodInjected) {
+            moreProblems.add("Overridden package private method injected twice");
+        }
+        superPackagePrivateMethodInjected = true;
+    }
+
+    @Inject protected void injectProtectedMethod() {
+        if (superProtectedMethodInjected) {
+            moreProblems.add("Overridden protected method injected twice");
+        }
+        superProtectedMethodInjected = true;
+    }
+
+    @Inject public void injectPublicMethod() {
+        if (superPublicMethodInjected) {
+            moreProblems.add("Overridden public method injected twice");
+        }
+        superPublicMethodInjected = true;
+    }
+
+    @Inject private void injectPrivateMethodForOverride() {
+        subPrivateMethodForOverrideInjected = true;
+    }
+
+    @Inject void injectPackagePrivateMethodForOverride() {
+        subPackagePrivateMethodForOverrideInjected = true;
+    }
+
+    @Inject protected void injectProtectedMethodForOverride() {
+        protectedMethodForOverrideInjected = true;
+    }
+
+    @Inject public void injectPublicMethodForOverride() {
+        publicMethodForOverrideInjected = true;
+    }
+
+    protected final boolean hasTireBeenFieldInjected() {
+        return fieldInjection != NEVER_INJECTED;
+    }
+
+    protected boolean hasSpareTireBeenFieldInjected() {
+        return false;
+    }
+
+    protected final boolean hasTireBeenMethodInjected() {
+        return methodInjection != NEVER_INJECTED;
+    }
+
+    protected boolean hasSpareTireBeenMethodInjected() {
+        return false;
+    }
 }
diff --git a/tck/com/googlecode/atinject/auto/V8Engine.java b/tck/com/googlecode/atinject/auto/V8Engine.java
index b273e69..9762903 100644
--- a/tck/com/googlecode/atinject/auto/V8Engine.java
+++ b/tck/com/googlecode/atinject/auto/V8Engine.java
@@ -17,5 +17,28 @@
 
 package com.googlecode.atinject.auto;
 
+import com.googlecode.atinject.Tester;
+
+import javax.inject.Inject;
+
 public class V8Engine extends Engine {
+
+    @Inject void injectPackagePrivateMethod() {
+        if (packagePrivateMethodInjected) {
+            moreProblems.add("Overridden package private method injected twice");
+        }
+        packagePrivateMethodInjected = true;
+    }
+
+    void injectPackagePrivateMethodForOverride() {
+        packagePrivateMethodForOverrideInjected = true;
+    }
+
+    public void check(Tester tester) {
+        tester.addProblems(moreProblems);
+
+        tester.test(packagePrivateMethodInjected, "Packge private method not injected");
+        tester.test(!packagePrivateMethodForOverrideInjected,
+                "Package private method injected, even though its override lacks @Inject");
+    }
 }
diff --git a/tck/com/googlecode/atinject/auto/accessories/SpareTire.java b/tck/com/googlecode/atinject/auto/accessories/SpareTire.java
index 75df6dd..00ad9e6 100644
--- a/tck/com/googlecode/atinject/auto/accessories/SpareTire.java
+++ b/tck/com/googlecode/atinject/auto/accessories/SpareTire.java
@@ -17,7 +17,116 @@
 
 package com.googlecode.atinject.auto.accessories;
 
+import com.googlecode.atinject.Tester;
 import com.googlecode.atinject.auto.Tire;
+import com.googlecode.atinject.auto.V8Engine;
+
+import javax.inject.Inject;
 
 public class SpareTire extends Tire {
+
+    V8Engine constructorInjection = NEVER_INJECTED;
+    @Inject V8Engine fieldInjection = NEVER_INJECTED;
+    V8Engine methodInjection = NEVER_INJECTED;
+
+
+    @Inject public SpareTire(V8Engine forSupertype, V8Engine forSubtype) {
+        super(forSupertype);
+        this.constructorInjection = forSubtype;
+    }
+
+    @Inject void subtypeMethodInjection(V8Engine methodInjection) {
+        if (!hasSpareTireBeenFieldInjected()) {
+            moreProblems.add("Methods injected before fields");
+        }
+        this.methodInjection = methodInjection;
+    }
+
+    @Inject private void injectPrivateMethod() {
+        if (subPrivateMethodInjected) {
+            moreProblems.add("Overridden private method injected twice");
+        }
+        subPrivateMethodInjected = true;
+    }
+
+    @Inject void injectPackagePrivateMethod() {
+        if (subPackagePrivateMethodInjected) {
+            moreProblems.add("Overridden package private method injected twice");
+        }
+        subPackagePrivateMethodInjected = true;
+    }
+
+    @Inject protected void injectProtectedMethod() {
+        if (subProtectedMethodInjected) {
+            moreProblems.add("Overridden protected method injected twice");
+        }
+        subProtectedMethodInjected = true;
+    }
+
+    @Inject public void injectPublicMethod() {
+        if (subPublicMethodInjected) {
+            moreProblems.add("Overridden public method injected twice");
+        }
+        subPublicMethodInjected = true;
+    }
+
+    private void injectPrivateMethodForOverride() {
+        superPrivateMethodForOverrideInjected = true;
+    }
+
+    void injectPackagePrivateMethodForOverride() {
+        superPackagePrivateMethodForOverrideInjected = true;
+    }
+
+    protected void injectProtectedMethodForOverride() {
+        protectedMethodForOverrideInjected = true;
+    }
+
+    public void injectPublicMethodForOverride() {
+        publicMethodForOverrideInjected = true;
+    }
+
+    protected boolean hasSpareTireBeenFieldInjected() {
+        return fieldInjection != NEVER_INJECTED;
+    }
+
+    protected boolean hasSpareTireBeenMethodInjected() {
+        return methodInjection != NEVER_INJECTED;
+    }
+
+    /**
+     * This class is used to check inheritance. By existing in a separate package
+     * from Tire, it can make sure the injector does the right thing with overrides
+     * of package-private methods.
+     */
+    public void check(Tester tester) {
+        tester.addProblems(moreProblems);
+
+        tester.test(hasSpareTireBeenFieldInjected(), "Subtype fields not injected");
+        tester.test(hasSpareTireBeenMethodInjected(), "Subtype methods not injected");
+        tester.test(hasTireBeenFieldInjected(), "Supertype fields not injected");
+        tester.test(hasTireBeenMethodInjected(), "Supertype methods not injected");
+
+        tester.test(superPrivateMethodInjected, "Supertype private method not injected");
+        tester.test(superPackagePrivateMethodInjected, "Supertype private method not injected");
+        tester.test(!superProtectedMethodInjected, "Overridden protected method injected!");
+        tester.test(!superPublicMethodInjected, "Overridden public method injected!");
+        tester.test(subPrivateMethodInjected, "Subtype private method not injected");
+        tester.test(subPackagePrivateMethodInjected, "Subtype private method not injected");
+        tester.test(subProtectedMethodInjected, "Subtype protected method not injected");
+        tester.test(subPublicMethodInjected, "Subtype public method not injected");
+
+        tester.test(subPrivateMethodForOverrideInjected,
+                "Private method not injected when a subtype has a similar method that lacks @Inject");
+        tester.test(subPackagePrivateMethodForOverrideInjected,
+                "Package private method not injected when a subtype has a similar method that lacks @Inject");
+        tester.test(!superPrivateMethodForOverrideInjected,
+                "Private method injected when a supertype has a similar method annotated @Inject");
+        tester.test(!superPackagePrivateMethodForOverrideInjected,
+                "Package private method injected when a supertype has a similar method annotated @Inject");
+        tester.test(!protectedMethodForOverrideInjected,
+                "Protected method injected, even though its override lacks @Inject");
+        tester.test(!publicMethodForOverrideInjected,
+                "Public method injected, even though its override lacks @Inject");
+    }
 }
diff --git a/tck/tck.iml b/tck/tck.iml
index bddcdf8..3695f9a 100644
--- a/tck/tck.iml
+++ b/tck/tck.iml
@@ -8,6 +8,7 @@
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
     <orderEntry type="module" module-name="atinject" />
+    <orderEntry type="library" name="Guice" level="project" />
   </component>
 </module>