• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
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 
17 package com.android.ide.eclipse.adt.internal.launch.junit;
18 
19 import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner.TestSize;
20 import com.android.ide.eclipse.adt.AdtPlugin;
21 import com.android.ide.eclipse.adt.AdtConstants;
22 import com.android.ide.eclipse.adt.internal.launch.AndroidLaunch;
23 import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchConfiguration;
24 import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchController;
25 import com.android.ide.eclipse.adt.internal.launch.IAndroidLaunchAction;
26 import com.android.ide.eclipse.adt.internal.launch.LaunchConfigDelegate;
27 import com.android.ide.eclipse.adt.internal.launch.LaunchMessages;
28 import com.android.ide.eclipse.adt.internal.launch.junit.runtime.AndroidJUnitLaunchInfo;
29 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
30 import com.android.sdklib.SdkConstants;
31 import com.android.sdklib.xml.ManifestData;
32 import com.android.sdklib.xml.ManifestData.Instrumentation;
33 
34 import org.eclipse.core.resources.IFile;
35 import org.eclipse.core.resources.IProject;
36 import org.eclipse.core.runtime.CoreException;
37 import org.eclipse.core.runtime.IProgressMonitor;
38 import org.eclipse.debug.core.ILaunchConfiguration;
39 import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
40 import org.eclipse.jdt.core.IJavaElement;
41 import org.eclipse.jdt.core.JavaCore;
42 import org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants;
43 import org.eclipse.jdt.internal.junit.launcher.TestKindRegistry;
44 import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
45 
46 /**
47  * Run configuration that can execute JUnit tests on an Android platform.
48  * <p/>
49  * Will deploy apps on target Android platform by reusing functionality from ADT
50  * LaunchConfigDelegate, and then run JUnits tests by reusing functionality from JDT
51  * JUnitLaunchConfigDelegate.
52  */
53 @SuppressWarnings("restriction")
54 public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
55 
56     /** Launch config attribute that stores instrumentation runner. */
57     static final String ATTR_INSTR_NAME = AdtPlugin.PLUGIN_ID + ".instrumentation"; //$NON-NLS-1$
58 
59     /** Launch config attribute that stores the test size annotation to run. */
60     static final String ATTR_TEST_SIZE = AdtPlugin.PLUGIN_ID + ".testSize"; //$NON-NLS-1$
61 
62     private static final String EMPTY_STRING = ""; //$NON-NLS-1$
63 
64     @Override
doLaunch(final ILaunchConfiguration configuration, final String mode, IProgressMonitor monitor, IProject project, final AndroidLaunch androidLaunch, AndroidLaunchConfiguration config, AndroidLaunchController controller, IFile applicationPackage, ManifestData manifestData)65     protected void doLaunch(final ILaunchConfiguration configuration, final String mode,
66             IProgressMonitor monitor, IProject project, final AndroidLaunch androidLaunch,
67             AndroidLaunchConfiguration config, AndroidLaunchController controller,
68             IFile applicationPackage, ManifestData manifestData) {
69 
70         String runner = getRunner(project, configuration, manifestData);
71         if (runner == null) {
72             AdtPlugin.displayError(LaunchMessages.LaunchDialogTitle,
73                     String.format(LaunchMessages.AndroidJUnitDelegate_NoRunnerMsg_s,
74                             project.getName()));
75             androidLaunch.stopLaunch();
76             return;
77         }
78         // get the target app's package
79         String targetAppPackage = getTargetPackage(manifestData, runner);
80         if (targetAppPackage == null) {
81             AdtPlugin.displayError(LaunchMessages.LaunchDialogTitle,
82                     String.format(LaunchMessages.AndroidJUnitDelegate_NoTargetMsg_3s,
83                             project.getName(), runner, SdkConstants.FN_ANDROID_MANIFEST_XML));
84             androidLaunch.stopLaunch();
85             return;
86         }
87         String testAppPackage = manifestData.getPackage();
88         AndroidJUnitLaunchInfo junitLaunchInfo = new AndroidJUnitLaunchInfo(project,
89                 testAppPackage, runner);
90         junitLaunchInfo.setTestClass(getTestClass(configuration));
91         junitLaunchInfo.setTestPackage(getTestPackage(configuration));
92         junitLaunchInfo.setTestMethod(getTestMethod(configuration));
93         junitLaunchInfo.setLaunch(androidLaunch);
94         junitLaunchInfo.setTestSize(getTestSize(configuration));
95         IAndroidLaunchAction junitLaunch = new AndroidJUnitLaunchAction(junitLaunchInfo);
96 
97         controller.launch(project, mode, applicationPackage, testAppPackage, targetAppPackage,
98                 manifestData.getDebuggable(), manifestData.getMinSdkVersionString(),
99                 junitLaunch, config, androidLaunch, monitor);
100     }
101 
102     /**
103      * Get the target Android application's package for the given instrumentation runner, or
104      * <code>null</code> if it could not be found.
105      *
106      * @param manifestParser the {@link ManifestData} for the test project
107      * @param runner the instrumentation runner class name
108      * @return the target package or <code>null</code>
109      */
getTargetPackage(ManifestData manifestParser, String runner)110     private String getTargetPackage(ManifestData manifestParser, String runner) {
111         for (Instrumentation instr : manifestParser.getInstrumentations()) {
112             if (instr.getName().equals(runner)) {
113                 return instr.getTargetPackage();
114             }
115         }
116         return null;
117     }
118 
119     /**
120      * Returns the test package stored in the launch configuration, or <code>null</code> if not
121      * specified.
122      *
123      * @param configuration the {@link ILaunchConfiguration} to retrieve the test package info from
124      * @return the test package or <code>null</code>.
125      */
getTestPackage(ILaunchConfiguration configuration)126     private String getTestPackage(ILaunchConfiguration configuration) {
127         // try to retrieve a package name from the JUnit container attribute
128         String containerHandle = getStringLaunchAttribute(
129                 JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER, configuration);
130         if (containerHandle != null && containerHandle.length() > 0) {
131             IJavaElement element = JavaCore.create(containerHandle);
132             // containerHandle could be a IProject, check if its a java package
133             if (element.getElementType() == IJavaElement.PACKAGE_FRAGMENT) {
134                 return element.getElementName();
135             }
136         }
137         return null;
138     }
139 
140     /**
141      * Returns the test class stored in the launch configuration.
142      *
143      * @param configuration the {@link ILaunchConfiguration} to retrieve the test class info from
144      * @return the test class. <code>null</code> if not specified.
145      */
getTestClass(ILaunchConfiguration configuration)146     private String getTestClass(ILaunchConfiguration configuration) {
147         return getStringLaunchAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME,
148                 configuration);
149     }
150 
151     /**
152      * Returns the test method stored in the launch configuration.
153      *
154      * @param configuration the {@link ILaunchConfiguration} to retrieve the test method info from
155      * @return the test method. <code>null</code> if not specified.
156      */
getTestMethod(ILaunchConfiguration configuration)157     private String getTestMethod(ILaunchConfiguration configuration) {
158         return getStringLaunchAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_METHOD_NAME,
159                 configuration);
160     }
161 
162     /**
163      * Returns the test sizes to run as saved in the launch configuration.
164      * @return {@link TestSize} if only tests of specific sizes should be run,
165      *         null if all tests should be run
166      */
getTestSize(ILaunchConfiguration configuration)167     private TestSize getTestSize(ILaunchConfiguration configuration) {
168         String testSizeAnnotation = getStringLaunchAttribute(
169                 AndroidJUnitLaunchConfigDelegate.ATTR_TEST_SIZE,
170                 configuration);
171         if (testSizeAnnotation.equals(
172                 AndroidJUnitLaunchConfigurationTab.SMALL_TEST_ANNOTATION)) {
173             return TestSize.SMALL;
174         } else if (testSizeAnnotation.equals(
175                 AndroidJUnitLaunchConfigurationTab.MEDIUM_TEST_ANNOTATION)) {
176             return TestSize.MEDIUM;
177         } else if (testSizeAnnotation.equals(
178                 AndroidJUnitLaunchConfigurationTab.LARGE_TEST_ANNOTATION)) {
179             return TestSize.LARGE;
180         } else {
181             return null;
182         }
183     }
184 
185     /**
186      * Gets a instrumentation runner for the launch.
187      * <p/>
188      * If a runner is stored in the given <code>configuration</code>, will return that.
189      * Otherwise, will try to find the first valid runner for the project.
190      * If a runner can still not be found, will return <code>null</code>, and will log an error
191      * to the console.
192      *
193      * @param project the {@link IProject} for the app
194      * @param configuration the {@link ILaunchConfiguration} for the launch
195      * @param manifestData the {@link ManifestData} for the project
196      *
197      * @return <code>null</code> if no instrumentation runner can be found, otherwise return
198      *   the fully qualified runner name.
199      */
getRunner(IProject project, ILaunchConfiguration configuration, ManifestData manifestData)200     private String getRunner(IProject project, ILaunchConfiguration configuration,
201             ManifestData manifestData) {
202         try {
203             String runner = getRunnerFromConfig(configuration);
204             if (runner != null) {
205                 return runner;
206             }
207             final InstrumentationRunnerValidator instrFinder = new InstrumentationRunnerValidator(
208                     BaseProjectHelper.getJavaProject(project), manifestData);
209             runner = instrFinder.getValidInstrumentationTestRunner();
210             if (runner != null) {
211                 AdtPlugin.printErrorToConsole(project, String.format(
212                         LaunchMessages.AndroidJUnitDelegate_NoRunnerConfigMsg_s, runner));
213                 return runner;
214             }
215             AdtPlugin.printErrorToConsole(project, String.format(
216                     LaunchMessages.AndroidJUnitDelegate_NoRunnerConsoleMsg_4s,
217                     project.getName(),
218                     SdkConstants.CLASS_INSTRUMENTATION_RUNNER,
219                     AdtConstants.LIBRARY_TEST_RUNNER,
220                     SdkConstants.FN_ANDROID_MANIFEST_XML));
221             return null;
222         } catch (CoreException e) {
223             AdtPlugin.log(e, "Error when retrieving instrumentation info"); //$NON-NLS-1$
224         }
225 
226         return null;
227     }
228 
getRunnerFromConfig(ILaunchConfiguration configuration)229     private String getRunnerFromConfig(ILaunchConfiguration configuration) throws CoreException {
230         return getStringLaunchAttribute(ATTR_INSTR_NAME, configuration);
231     }
232 
233     /**
234      * Helper method to retrieve a string attribute from the launch configuration
235      *
236      * @param attributeName name of the launch attribute
237      * @param configuration the {@link ILaunchConfiguration} to retrieve the attribute from
238      * @return the attribute's value. <code>null</code> if not found.
239      */
getStringLaunchAttribute(String attributeName, ILaunchConfiguration configuration)240     private String getStringLaunchAttribute(String attributeName,
241             ILaunchConfiguration configuration) {
242         try {
243             String attrValue = configuration.getAttribute(attributeName, EMPTY_STRING);
244             if (attrValue.length() < 1) {
245                 return null;
246             }
247             return attrValue;
248         } catch (CoreException e) {
249             AdtPlugin.log(e, String.format("Error when retrieving launch info %1$s",  //$NON-NLS-1$
250                     attributeName));
251         }
252         return null;
253     }
254 
255     /**
256      * Helper method to set JUnit-related attributes expected by JDT JUnit runner
257      *
258      * @param config the launch configuration to modify
259      */
setJUnitDefaults(ILaunchConfigurationWorkingCopy config)260     static void setJUnitDefaults(ILaunchConfigurationWorkingCopy config) {
261         // set the test runner to JUnit3 to placate JDT JUnit runner logic
262         config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_RUNNER_KIND,
263                 TestKindRegistry.JUNIT3_TEST_KIND_ID);
264     }
265 }
266