AndroidJUnit4ClassRunner.java revision 3604db5bcd124dfd1396fb51434d3f4337690cff
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.test.runner.junit4;
17
18import android.app.Instrumentation;
19import android.content.Context;
20import android.util.Log;
21
22import com.android.test.InjectContext;
23import com.android.test.InjectInstrumentation;
24
25import org.junit.runners.BlockJUnit4ClassRunner;
26import org.junit.runners.model.FrameworkField;
27import org.junit.runners.model.InitializationError;
28
29import java.lang.reflect.Field;
30import java.util.List;
31
32/**
33 * A specialized {@link BlockJUnit4ClassRunner} that can handle {@link InjectContext} and
34 * {@link InjectInstrumentation}.
35 */
36class AndroidJUnit4ClassRunner extends BlockJUnit4ClassRunner {
37
38    private static final String LOG_TAG = "AndroidJUnit4ClassRunner";
39    private final Instrumentation mInstr;
40
41    @SuppressWarnings("serial")
42    private static class InvalidInjectException extends Exception {
43        InvalidInjectException(String message) {
44            super(message);
45        }
46    }
47
48    public AndroidJUnit4ClassRunner(Class<?> klass, Instrumentation instr)
49            throws InitializationError {
50        super(klass);
51        mInstr = instr;
52    }
53
54    @Override
55    protected Object createTest() throws Exception {
56        Object test = super.createTest();
57        inject(test);
58        return test;
59    }
60
61    @Override
62    protected void collectInitializationErrors(List<Throwable> errors) {
63        super.collectInitializationErrors(errors);
64
65        validateInjectFields(errors);
66    }
67
68    private void validateInjectFields(List<Throwable> errors) {
69        List<FrameworkField> instrFields = getTestClass().getAnnotatedFields(
70                InjectInstrumentation.class);
71        for (FrameworkField instrField : instrFields) {
72            validateInjectField(errors, instrField, Instrumentation.class);
73        }
74        List<FrameworkField> contextFields = getTestClass().getAnnotatedFields(
75                InjectContext.class);
76        for (FrameworkField contextField : contextFields) {
77            validateInjectField(errors, contextField, Context.class);
78        }
79    }
80
81    private void validateInjectField(List<Throwable> errors, FrameworkField instrField,
82            Class<?> expectedType) {
83        if (!instrField.isPublic()) {
84            errors.add(new InvalidInjectException(String.format(
85                    "field %s in class %s has an InjectInstrumentation annotation," +
86                    " but is not public", instrField.getName(), getTestClass().getName())));
87        }
88        if (!expectedType.isAssignableFrom(instrField.getType())) {
89            errors.add(new InvalidInjectException(String.format(
90                    "field %s in class %s has an InjectInstrumentation annotation," +
91                    " but its not of %s type", instrField.getName(),
92                    getTestClass().getName(), expectedType.getName())));
93        }
94    }
95
96    private void inject(Object test) {
97        List<FrameworkField> instrFields = getTestClass().getAnnotatedFields(
98                InjectInstrumentation.class);
99        for (FrameworkField instrField : instrFields) {
100            setFieldValue(test, instrField.getField(), mInstr);
101        }
102        List<FrameworkField> contextFields = getTestClass().getAnnotatedFields(
103                InjectContext.class);
104        for (FrameworkField contextField : contextFields) {
105            setFieldValue(test, contextField.getField(), mInstr.getTargetContext());
106        }
107    }
108
109    private void setFieldValue(Object test, Field field, Object value) {
110        try {
111            field.set(test, value);
112        } catch (IllegalArgumentException e) {
113            Log.e(LOG_TAG, String.format(
114                    "Failed to inject value for field %s in class %s", field.getName(),
115                    test.getClass().getName()), e);
116        } catch (IllegalAccessException e) {
117            Log.e(LOG_TAG, String.format(
118                    "Failed to inject value for field %s in class %s", field.getName(),
119                    test.getClass().getName()), e);
120        }
121    }
122}
123