package com.timgroup.karg.reflection;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;

import java.util.concurrent.Callable;

import org.junit.Test;

public class DelegatorTest {

    public static class TestClass {
        public String knockKnock() {
            return "who's there?";
        }
    }
    
    private final TestClass testInstance = new TestClass();
    
    @Test public void
    can_delegate_a_single_method_interface_to_an_instance() throws Exception {
        Delegator<TestClass, Callable<String>> knockKnockDelegator = Delegator.ofMethod("knockKnock")
                                                                     .of(TestClass.class)
                                                                     .<Callable<String>>to(Callable.class);
        Callable<String> callable = knockKnockDelegator.delegateTo(testInstance);
        assertThat(callable.call(), is("who's there?"));
    }

    public static interface TwoSheds {
        String shed1();
        String shed2();
    }
    
    @Test(expected=Exception.class) public void
    throws_exception_if_interface_has_more_than_one_method() {
        Delegator.ofMethod("knockKnock")
                 .of(TestClass.class)
                 .to(TwoSheds.class);
    }
    
    public static class Multiplier {
        public double multiply(double x, double y) {
            return x * y;
        }
    }
    
    private final Multiplier multiplier = new Multiplier();
    
    public static interface WrongReturnType {
        int op(double lhs, double rhs);
    }
    
    public static interface WrongParameters {
        double op(int lhs, double rhs);
    }
    
    public static interface RightParameters {
        double op(double lhs, double rhs);
    }
    
    @Test(expected=Exception.class) public void
    throws_exception_if_interface_method_return_type_does_not_match_target_method() {
        Delegator.ofMethod("multiply")
                 .of(Multiplier.class)
                 .to(WrongReturnType.class);
    }
    
    @Test(expected=Exception.class) public void
    throws_exception_if_interface_method_signature_does_not_match_target_method() {
        Delegator.ofMethod("multiply")
                 .of(Multiplier.class)
                 .to(WrongParameters.class);
    }
    
    @Test public void
    can_delegate_methods_with_multiple_parameters() {
        Delegator<Multiplier, RightParameters> multiplication = Delegator.ofMethod("multiply")
                                                                         .of(Multiplier.class)
                                                                         .<RightParameters>to(RightParameters.class);
        RightParameters delegate = multiplication.delegateTo(multiplier);
        
        assertThat(delegate.op(4.0, 5.0), equalTo(4.0 * 5.0));
    }
    
    public static class MyException extends Exception {
        private static final long serialVersionUID = 1L; }
    
    public static interface Exceptional {
        void blowUp() throws Exception;
    }
    
    public static class Exploder {
        public void explode() throws MyException {
            throw new MyException();
        }
    }
    
    @Test(expected=MyException.class) public void
    passes_back_the_declared_exception_type_where_possible() throws Exception {
        Delegator<Exploder, Exceptional> delegator = Delegator.ofMethod("explode")
                                                              .of(Exploder.class)
                                                              .<Exceptional>to(Exceptional.class);
        
        Exceptional exceptional = delegator.delegateTo(new Exploder());
        exceptional.blowUp();
    }
    
    @Test public void
    can_bind_directly_to_instances() {
        RightParameters delegate = Delegator.ofMethod("multiply").ofInstance(multiplier).to(RightParameters.class);
        assertThat(delegate.op(4.0, 5.0), equalTo(4.0 * 5.0));
    }
}

@@ -723,32 +723,43 @@
                .
+%3CTestClass%3E
 of(TestClass.cla
@@ -836,26 +836,8 @@
    .
-%3CCallable%3CString%3E%3E
 to(C
@@ -2602,32 +2602,44 @@
                .
+%3CMultiplier%3E
 of(Multiplier.cl
@@ -2721,25 +2721,8 @@
    .
-%3CRightParameters%3E
 to(R
@@ -3534,16 +3534,26 @@
        .
+%3CExploder%3E
 of(Explo
@@ -3630,21 +3630,8 @@
    .
-%3CExceptional%3E
 to(E

Make tests compile too