/*
 * Copyright (C) 2012 uPhyca Inc.
 *
 * 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.uphyca.testing;

import java.lang.reflect.InvocationTargetException;

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

import android.app.Instrumentation;
import android.os.Bundle;
import android.test.FlakyTest;
import android.test.RepetitiveTest;
import android.test.UiThreadTest;

class AndroidAnnotatedMethodRule implements TestRule {

    private Instrumentation fInstrumentation;

    public AndroidAnnotatedMethodRule() {
    }

    public AndroidAnnotatedMethodRule(Instrumentation instrumentation) {
        fInstrumentation = instrumentation;
    }

    @Override
    public Statement apply(final Statement statement,
                           final Description description) {
        return new Statement() {

            @Override
            public void evaluate() throws Throwable {
                //String methodName = description.getMethodName();
                //methodName.
                //FIXME How to get annotations via description ?
                //Method method = description.getTestClass().getMethod(methodName);

                //Following code taken from android.test.InstrumentationTestCase.

                int runCount = 1;
                boolean isRepetitive = false;
                if (description.getAnnotation(FlakyTest.class) != null) {
                    runCount = description.getAnnotation(FlakyTest.class).tolerance();
                } else if (description.getAnnotation(RepetitiveTest.class) != null) {
                    runCount = description.getAnnotation(RepetitiveTest.class).numIterations();
                    isRepetitive = true;
                }

                if (fInstrumentation != null && description.getAnnotation(UiThreadTest.class) != null) {
                    final int tolerance = runCount;
                    final boolean repetitive = isRepetitive;
                    final Throwable[] exceptions = new Throwable[1];
                    fInstrumentation.runOnMainSync(new Runnable() {
                        public void run() {
                            try {
                                runMethod(statement,
                                          tolerance,
                                          repetitive);
                            } catch (Throwable throwable) {
                                exceptions[0] = throwable;
                            }
                        }
                    });
                    if (exceptions[0] != null) {
                        throw exceptions[0];
                    }
                } else {
                    runMethod(statement,
                              runCount,
                              isRepetitive);
                }
            }
        };
    }

    //Taken from android.test.InstrumentationTestCase
    private void runMethod(Statement statement,
                           int tolerance,
                           boolean isRepetitive) throws Throwable {
        Throwable exception = null;

        int runCount = 0;
        do {
            try {
                statement.evaluate();
                exception = null;
            } catch (InvocationTargetException e) {
                e.fillInStackTrace();
                exception = e.getTargetException();
            } catch (IllegalAccessException e) {
                e.fillInStackTrace();
                exception = e;
            } catch (Throwable e) {
                exception = e;
            } finally {
                runCount++;
                // Report current iteration number, if test is repetitive
                if (isRepetitive) {
                    Bundle iterations = new Bundle();
                    iterations.putInt("currentiterations",
                                      runCount);
                    fInstrumentation.sendStatus(2,
                                                iterations);
                }
            }
        } while ((runCount < tolerance) && (isRepetitive || exception != null));

        if (exception != null) {
            throw exception;
        }
    }
}

@@ -3788,16 +3788,64 @@
  catch (
+Throwable e) %7B%0A                if (e instanceof 
 Invocati
@@ -3853,38 +3853,40 @@
 nTargetException
- e
 ) %7B%0A
+    
                 
@@ -3915,32 +3915,36 @@
                 
+    
 exception = e.ge
@@ -3939,17 +3939,46 @@
 ption = 
-e
+((InvocationTargetException)e)
 .getTarg
@@ -4004,25 +4004,44 @@
         
-%7D catch (
+    %7D else if (e instanceof 
 IllegalA
@@ -4054,18 +4054,16 @@
 xception
- e
 ) %7B%0A    
@@ -4066,32 +4066,36 @@
                 
+ 
+   
 e.fillInStackTra
@@ -4092,32 +4092,36 @@
 InStackTrace();%0A
+    
                 
@@ -4147,37 +4147,26 @@
         
-%7D catch (Throwable e)
+    %7D else
  %7B%0A     
@@ -4168,32 +4168,36 @@
                 
+ 
+   
 exception = e;%0A 
@@ -4187,32 +4187,50 @@
  exception = e;%0A
+                %7D%0A
             %7D fi

Correct Error handling