1/*
2 * Copyright (C) 2013 DroidDriver committers
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 */
16
17package com.google.android.droiddriver.runner;
18
19import android.app.Activity;
20import android.os.Build;
21import android.os.Bundle;
22import android.test.AndroidTestRunner;
23import android.test.InstrumentationTestRunner;
24import android.test.suitebuilder.TestMethod;
25import android.util.Log;
26
27import com.android.internal.util.Predicate;
28import com.google.android.droiddriver.util.ActivityUtils;
29import com.google.android.droiddriver.util.Logs;
30import com.google.common.base.Supplier;
31import com.google.common.collect.Lists;
32import com.google.common.collect.Sets;
33
34import junit.framework.AssertionFailedError;
35import junit.framework.Test;
36import junit.framework.TestListener;
37
38import java.lang.annotation.Annotation;
39import java.util.Iterator;
40import java.util.List;
41import java.util.Set;
42
43/**
44 * Adds activity watcher to InstrumentationTestRunner.
45 */
46public class TestRunner extends InstrumentationTestRunner {
47  private final Set<Activity> activities = Sets.newIdentityHashSet();
48  private final AndroidTestRunner androidTestRunner = new AndroidTestRunner();
49  private Activity runningActivity;
50
51  /**
52   * Returns an {@link AndroidTestRunner} that is shared by this and super, such
53   * that we can add custom {@link TestListener}s.
54   */
55  @Override
56  protected AndroidTestRunner getAndroidTestRunner() {
57    return androidTestRunner;
58  }
59
60  /**
61   * {@inheritDoc}
62   * <p>
63   * Adds a {@link TestListener} that finishes all created activities.
64   */
65  @Override
66  public void onStart() {
67    getAndroidTestRunner().addTestListener(new TestListener() {
68      @Override
69      public void endTest(Test test) {
70        runOnMainSync(new Runnable() {
71          @Override
72          public void run() {
73            Iterator<Activity> iterator = activities.iterator();
74            while (iterator.hasNext()) {
75              Activity activity = iterator.next();
76              iterator.remove();
77              if (!activity.isFinishing()) {
78                try {
79                  Logs.log(Log.INFO, "Stopping activity: " + activity);
80                  activity.finish();
81                } catch (RuntimeException e) {
82                  Logs.log(Log.ERROR, e, "Failed to stop activity");
83                }
84              }
85            }
86          }
87        });
88      }
89
90      @Override
91      public void addError(Test arg0, Throwable arg1) {}
92
93      @Override
94      public void addFailure(Test arg0, AssertionFailedError arg1) {}
95
96      @Override
97      public void startTest(Test arg0) {}
98    });
99
100    ActivityUtils.setRunningActivitySupplier(new Supplier<Activity>() {
101      @Override
102      public Activity get() {
103        return runningActivity;
104      }
105    });
106
107    super.onStart();
108  }
109
110  // Overrides InstrumentationTestRunner
111  List<Predicate<TestMethod>> getBuilderRequirements() {
112    List<Predicate<TestMethod>> requirements = Lists.newArrayList();
113    requirements.add(new Predicate<TestMethod>() {
114      @Override
115      public boolean apply(TestMethod arg0) {
116        MinSdkVersion minSdkVersion = getAnnotation(arg0, MinSdkVersion.class);
117        if (minSdkVersion != null && minSdkVersion.value() > Build.VERSION.SDK_INT) {
118          Logs.logfmt(Log.INFO, "filtered %s#%s: MinSdkVersion=%d", arg0.getEnclosingClassname(),
119              arg0.getName(), minSdkVersion.value());
120          return false;
121        }
122
123        UseUiAutomation useUiAutomation = getAnnotation(arg0, UseUiAutomation.class);
124        if (useUiAutomation != null && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
125          Logs.logfmt(Log.INFO,
126              "filtered %s#%s: Has @UseUiAutomation, but ro.build.version.sdk=%d",
127              arg0.getEnclosingClassname(), arg0.getName(), Build.VERSION.SDK_INT);
128          return false;
129        }
130        return true;
131      }
132
133      private <T extends Annotation> T getAnnotation(TestMethod testMethod, Class<T> clazz) {
134        T annotation = testMethod.getAnnotation(clazz);
135        if (annotation == null) {
136          annotation = testMethod.getEnclosingClass().getAnnotation(clazz);
137        }
138        return annotation;
139      }
140    });
141    return requirements;
142  }
143
144  @Override
145  public void callActivityOnDestroy(Activity activity) {
146    super.callActivityOnDestroy(activity);
147    activities.remove(activity);
148  }
149
150  @Override
151  public void callActivityOnCreate(Activity activity, Bundle bundle) {
152    super.callActivityOnCreate(activity, bundle);
153    activities.add(activity);
154  }
155
156  @Override
157  public void callActivityOnResume(Activity activity) {
158    super.callActivityOnResume(activity);
159    runningActivity = activity;
160  }
161
162  @Override
163  public void callActivityOnPause(Activity activity) {
164    super.callActivityOnPause(activity);
165    if (activity == ActivityUtils.getRunningActivity()) {
166      runningActivity = null;
167    }
168  }
169}
170