153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot/*
253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot * Copyright (C) 2012 The Android Open Source Project
353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot *
453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot * Licensed under the Apache License, Version 2.0 (the "License");
553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot * you may not use this file except in compliance with the License.
653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot * You may obtain a copy of the License at
753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot *
853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot *      http://www.apache.org/licenses/LICENSE-2.0
953196f43b44ff02da07c243798168d7e5614ec34Brett Chabot *
1053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot * Unless required by applicable law or agreed to in writing, software
1153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot * distributed under the License is distributed on an "AS IS" BASIS,
1253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot * See the License for the specific language governing permissions and
1453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot * limitations under the License.
1553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot */
163604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabotpackage com.android.test.runner.junit4;
1753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
1853196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport android.app.Instrumentation;
1953196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport android.content.Context;
20fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabotimport android.os.Bundle;
2153196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport android.util.Log;
2253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
23fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabotimport com.android.test.InjectBundle;
2453196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport com.android.test.InjectContext;
2553196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport com.android.test.InjectInstrumentation;
2653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
2753196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport org.junit.runners.BlockJUnit4ClassRunner;
2853196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport org.junit.runners.model.FrameworkField;
2953196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport org.junit.runners.model.InitializationError;
3053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
3153196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport java.lang.reflect.Field;
3253196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport java.util.List;
3353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
3453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot/**
3553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot * A specialized {@link BlockJUnit4ClassRunner} that can handle {@link InjectContext} and
3653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot * {@link InjectInstrumentation}.
3753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot */
3853196f43b44ff02da07c243798168d7e5614ec34Brett Chabotclass AndroidJUnit4ClassRunner extends BlockJUnit4ClassRunner {
3953196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
4053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    private static final String LOG_TAG = "AndroidJUnit4ClassRunner";
4153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    private final Instrumentation mInstr;
42fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot    private final Bundle mBundle;
4353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
4453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    @SuppressWarnings("serial")
4553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    private static class InvalidInjectException extends Exception {
4653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        InvalidInjectException(String message) {
4753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            super(message);
4853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        }
4953196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    }
5053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
51fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot    public AndroidJUnit4ClassRunner(Class<?> klass, Instrumentation instr, Bundle bundle)
5253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            throws InitializationError {
5353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        super(klass);
5453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        mInstr = instr;
55fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot        mBundle = bundle;
5653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    }
5753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
5853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    @Override
5953196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    protected Object createTest() throws Exception {
6053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        Object test = super.createTest();
6153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        inject(test);
6253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        return test;
6353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    }
6453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
6553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    @Override
6653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    protected void collectInitializationErrors(List<Throwable> errors) {
6753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        super.collectInitializationErrors(errors);
6853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
6953196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        validateInjectFields(errors);
7053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    }
7153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
7253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    private void validateInjectFields(List<Throwable> errors) {
7353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        List<FrameworkField> instrFields = getTestClass().getAnnotatedFields(
7453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                InjectInstrumentation.class);
7553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        for (FrameworkField instrField : instrFields) {
7653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            validateInjectField(errors, instrField, Instrumentation.class);
7753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        }
7853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        List<FrameworkField> contextFields = getTestClass().getAnnotatedFields(
7953196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                InjectContext.class);
8053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        for (FrameworkField contextField : contextFields) {
8153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            validateInjectField(errors, contextField, Context.class);
8253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        }
83fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot        List<FrameworkField> bundleFields = getTestClass().getAnnotatedFields(
84fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot                InjectBundle.class);
85fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot        for (FrameworkField bundleField : bundleFields) {
861379df175a8ff337bae4d98da87219dda721012cBrett Chabot            validateInjectField(errors, bundleField, Bundle.class);
87fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot        }
8853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    }
8953196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
9053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    private void validateInjectField(List<Throwable> errors, FrameworkField instrField,
9153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            Class<?> expectedType) {
9253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        if (!instrField.isPublic()) {
9353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            errors.add(new InvalidInjectException(String.format(
9453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                    "field %s in class %s has an InjectInstrumentation annotation," +
9553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                    " but is not public", instrField.getName(), getTestClass().getName())));
9653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        }
9753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        if (!expectedType.isAssignableFrom(instrField.getType())) {
9853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            errors.add(new InvalidInjectException(String.format(
9953196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                    "field %s in class %s has an InjectInstrumentation annotation," +
10053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                    " but its not of %s type", instrField.getName(),
10153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                    getTestClass().getName(), expectedType.getName())));
10253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        }
10353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    }
10453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
10553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    private void inject(Object test) {
10653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        List<FrameworkField> instrFields = getTestClass().getAnnotatedFields(
10753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                InjectInstrumentation.class);
10853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        for (FrameworkField instrField : instrFields) {
10953196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            setFieldValue(test, instrField.getField(), mInstr);
11053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        }
11153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        List<FrameworkField> contextFields = getTestClass().getAnnotatedFields(
11253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                InjectContext.class);
11353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        for (FrameworkField contextField : contextFields) {
1143c941fb7233210b06245f52cd9630b33da15d344Brett Chabot            setFieldValue(test, contextField.getField(), mInstr.getTargetContext());
11553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        }
116fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot        List<FrameworkField> bundleFields = getTestClass().getAnnotatedFields(
117fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot                InjectBundle.class);
118fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot        for (FrameworkField bundleField : bundleFields) {
119fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot            setFieldValue(test, bundleField.getField(), mBundle);
120fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot        }
12153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    }
12253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
12353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    private void setFieldValue(Object test, Field field, Object value) {
12453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        try {
12553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            field.set(test, value);
12653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        } catch (IllegalArgumentException e) {
12753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            Log.e(LOG_TAG, String.format(
12853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                    "Failed to inject value for field %s in class %s", field.getName(),
12953196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                    test.getClass().getName()), e);
13053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        } catch (IllegalAccessException e) {
13153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            Log.e(LOG_TAG, String.format(
13253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                    "Failed to inject value for field %s in class %s", field.getName(),
13353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                    test.getClass().getName()), e);
13453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        }
13553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    }
13653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot}
137