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;
2053196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport android.util.Log;
2153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
2253196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport com.android.test.InjectContext;
2353196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport com.android.test.InjectInstrumentation;
2453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
2553196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport org.junit.runners.BlockJUnit4ClassRunner;
2653196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport org.junit.runners.model.FrameworkField;
2753196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport org.junit.runners.model.InitializationError;
2853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
2953196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport java.lang.reflect.Field;
3053196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport java.util.List;
3153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
3253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot/**
3353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot * A specialized {@link BlockJUnit4ClassRunner} that can handle {@link InjectContext} and
3453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot * {@link InjectInstrumentation}.
3553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot */
3653196f43b44ff02da07c243798168d7e5614ec34Brett Chabotclass AndroidJUnit4ClassRunner extends BlockJUnit4ClassRunner {
3753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
3853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    private static final String LOG_TAG = "AndroidJUnit4ClassRunner";
3953196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    private final Instrumentation mInstr;
4053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
4153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    @SuppressWarnings("serial")
4253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    private static class InvalidInjectException extends Exception {
4353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        InvalidInjectException(String message) {
4453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            super(message);
4553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        }
4653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    }
4753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
4853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    public AndroidJUnit4ClassRunner(Class<?> klass, Instrumentation instr)
4953196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            throws InitializationError {
5053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        super(klass);
5153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        mInstr = instr;
5253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    }
5353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
5453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    @Override
5553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    protected Object createTest() throws Exception {
5653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        Object test = super.createTest();
5753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        inject(test);
5853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        return test;
5953196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    }
6053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
6153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    @Override
6253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    protected void collectInitializationErrors(List<Throwable> errors) {
6353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        super.collectInitializationErrors(errors);
6453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
6553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        validateInjectFields(errors);
6653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    }
6753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
6853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    private void validateInjectFields(List<Throwable> errors) {
6953196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        List<FrameworkField> instrFields = getTestClass().getAnnotatedFields(
7053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                InjectInstrumentation.class);
7153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        for (FrameworkField instrField : instrFields) {
7253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            validateInjectField(errors, instrField, Instrumentation.class);
7353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        }
7453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        List<FrameworkField> contextFields = getTestClass().getAnnotatedFields(
7553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                InjectContext.class);
7653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        for (FrameworkField contextField : contextFields) {
7753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            validateInjectField(errors, contextField, Context.class);
7853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        }
7953196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    }
8053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
8153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    private void validateInjectField(List<Throwable> errors, FrameworkField instrField,
8253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            Class<?> expectedType) {
8353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        if (!instrField.isPublic()) {
8453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            errors.add(new InvalidInjectException(String.format(
8553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                    "field %s in class %s has an InjectInstrumentation annotation," +
8653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                    " but is not public", instrField.getName(), getTestClass().getName())));
8753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        }
8853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        if (!expectedType.isAssignableFrom(instrField.getType())) {
8953196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            errors.add(new InvalidInjectException(String.format(
9053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                    "field %s in class %s has an InjectInstrumentation annotation," +
9153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                    " but its not of %s type", instrField.getName(),
9253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                    getTestClass().getName(), expectedType.getName())));
9353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        }
9453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    }
9553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
9653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    private void inject(Object test) {
9753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        List<FrameworkField> instrFields = getTestClass().getAnnotatedFields(
9853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                InjectInstrumentation.class);
9953196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        for (FrameworkField instrField : instrFields) {
10053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            setFieldValue(test, instrField.getField(), mInstr);
10153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        }
10253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        List<FrameworkField> contextFields = getTestClass().getAnnotatedFields(
10353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                InjectContext.class);
10453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        for (FrameworkField contextField : contextFields) {
1053c941fb7233210b06245f52cd9630b33da15d344Brett Chabot            setFieldValue(test, contextField.getField(), mInstr.getTargetContext());
10653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        }
10753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    }
10853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
10953196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    private void setFieldValue(Object test, Field field, Object value) {
11053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        try {
11153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            field.set(test, value);
11253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        } catch (IllegalArgumentException e) {
11353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            Log.e(LOG_TAG, String.format(
11453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                    "Failed to inject value for field %s in class %s", field.getName(),
11553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                    test.getClass().getName()), e);
11653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        } catch (IllegalAccessException e) {
11753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            Log.e(LOG_TAG, String.format(
11853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                    "Failed to inject value for field %s in class %s", field.getName(),
11953196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                    test.getClass().getName()), e);
12053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        }
12153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    }
12253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot}
123