| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Eclipse Public License, Version 1.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.eclipse.org/org/documents/epl-v10.php |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.ide.eclipse.adt.internal.wizards.templates; |
| |
| import static com.android.SdkConstants.CURRENT_PLATFORM; |
| import static com.android.SdkConstants.FD_TOOLS; |
| import static com.android.SdkConstants.PLATFORM_WINDOWS; |
| import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectWizard.ATTR_MIN_API; |
| import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectWizard.ATTR_MIN_BUILD_API; |
| import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_ID; |
| |
| import com.android.annotations.NonNull; |
| import com.android.annotations.Nullable; |
| import com.android.sdklib.SdkVersionInfo; |
| import com.android.ide.eclipse.adt.AdtPlugin; |
| import com.android.ide.eclipse.adt.AdtUtils; |
| import com.android.ide.eclipse.adt.internal.lint.EclipseLintClient; |
| import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; |
| import com.android.ide.eclipse.adt.internal.sdk.Sdk; |
| import com.android.ide.eclipse.tests.SdkLoadingTestCase; |
| import com.android.sdklib.IAndroidTarget; |
| import com.android.utils.GrabProcessOutput; |
| import com.android.utils.GrabProcessOutput.IProcessOutput; |
| import com.android.utils.GrabProcessOutput.Wait; |
| import com.android.tools.lint.checks.ManifestDetector; |
| import com.android.tools.lint.checks.SecurityDetector; |
| import com.android.tools.lint.client.api.Configuration; |
| import com.android.tools.lint.client.api.DefaultConfiguration; |
| import com.android.tools.lint.client.api.JavaParser; |
| import com.android.tools.lint.client.api.LintClient; |
| import com.android.tools.lint.client.api.LintDriver; |
| import com.android.tools.lint.client.api.XmlParser; |
| import com.android.tools.lint.detector.api.Category; |
| import com.android.tools.lint.detector.api.Context; |
| import com.android.tools.lint.detector.api.Issue; |
| import com.android.tools.lint.detector.api.Location; |
| import com.android.tools.lint.detector.api.Project; |
| import com.android.tools.lint.detector.api.Scope; |
| import com.android.tools.lint.detector.api.Severity; |
| import com.android.tools.lint.detector.api.TextFormat; |
| import com.google.common.base.Charsets; |
| import com.google.common.base.Stopwatch; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Sets; |
| import com.google.common.io.Files; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.QualifiedName; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.ltk.core.refactoring.Change; |
| import org.eclipse.ltk.core.refactoring.CompositeChange; |
| import org.w3c.dom.Element; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * Unit tests for template instantiation. |
| * <p> |
| * Note: This test can take multiple hours to run! |
| * |
| * <p> |
| * TODO: Test all permutations of variables (it currently just varies one at a time with the |
| * rest of the defaults) |
| * TODO: Test trying to change strings arguments (currently just varies enums and booleans) |
| * TODO: Test adding multiple instances of the templates (to look for resource conflicts) |
| */ |
| @SuppressWarnings("javadoc") |
| public class TemplateHandlerTest extends SdkLoadingTestCase { |
| /** |
| * Flag used to quickly check each template once (for one version), to get |
| * quicker feedback on whether something is broken instead of waiting for |
| * all the versions for each template first |
| */ |
| private static final boolean TEST_FEWER_API_VERSIONS = true; |
| private static final boolean TEST_JUST_ONE_MIN_SDK = false; |
| private static final boolean TEST_JUST_ONE_BUILD_TARGET = true; |
| private static final boolean TEST_JUST_ONE_TARGET_SDK_VERSION = true; |
| private QualifiedName ERROR_KEY = new QualifiedName(AdtPlugin.PLUGIN_ID, "JobErrorKey"); |
| private static int sCount = 0; |
| /** |
| * If true, check this template with all the interesting ( |
| * {@link #isInterestingApiLevel(int)}) api versions |
| */ |
| private boolean mApiSensitiveTemplate; |
| /** |
| * Set of templates already tested with separate unit test; remainder is |
| * checked in {@link #testCreateRemainingProjects()} |
| */ |
| private static final Set<File> sProjectTestedSeparately = Sets.newHashSet(); |
| /** |
| * Set of templates already tested with separate unit test; remainder is |
| * checked in {@link #testCreateRemainingTemplates()} |
| */ |
| private static final Set<File> sTemplateTestedSeparately = Sets.newHashSet(); |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| mApiSensitiveTemplate = true; |
| } |
| |
| /** |
| * Is the given api level interesting for testing purposes? This is used to |
| * skip gaps, such that we for example only check say api 8, 9, 11, 14, etc |
| * -- versions where the <b>templates</b> are doing conditional changes. To |
| * be EXTRA comprehensive, occasionally try returning true unconditionally |
| * here to test absolutely everything. |
| */ |
| private boolean isInterestingApiLevel(int api) { |
| // For templates that aren't API sensitive, only test with API = 16 |
| if (!mApiSensitiveTemplate) { |
| return api == 16; |
| } |
| |
| switch (api) { |
| case 1: |
| case 8: |
| return true; |
| case 11: |
| return true; |
| case 14: |
| return true; |
| case 9: |
| case 16: |
| return !TEST_FEWER_API_VERSIONS; |
| default: |
| return false; |
| } |
| } |
| |
| public void testNewBlankProject() throws Exception { |
| Stopwatch stopwatch = Stopwatch.createUnstarted(); |
| stopwatch.start(); |
| checkProjectWithActivity(null); |
| stopwatch.stop(); |
| System.out.println("Checked blank project successfully in " |
| + stopwatch.toString()); |
| } |
| |
| public void testNewBlankActivity() throws Exception { |
| checkCreateTemplate("activities", "BlankActivity"); |
| } |
| |
| public void testBlankActivityInProject() throws Exception { |
| checkCreateActivityInProject("BlankActivity"); |
| } |
| |
| public void testNewMasterDetailFlow() throws Exception { |
| checkCreateTemplate("activities", "MasterDetailFlow"); |
| } |
| |
| public void testMasterDetailFlowInProject() throws Exception { |
| checkCreateActivityInProject("MasterDetailFlow"); |
| } |
| |
| public void testNewFullscreen() throws Exception { |
| checkCreateTemplate("activities", "FullscreenActivity"); |
| } |
| |
| public void testFullscreenInProject() throws Exception { |
| checkCreateActivityInProject("FullscreenActivity"); |
| } |
| |
| public void testNewLoginActivity() throws Exception { |
| checkCreateTemplate("activities", "LoginActivity"); |
| } |
| |
| public void testLoginActivityInProject() throws Exception { |
| checkCreateActivityInProject("MasterDetailFlow"); |
| } |
| |
| public void testNewSettingsActivity() throws Exception { |
| checkCreateTemplate("activities", "SettingsActivity"); |
| } |
| |
| public void testSettingsActivityInProject() throws Exception { |
| checkCreateActivityInProject("SettingsActivity"); |
| } |
| |
| public void testNewBroadcastReceiver() throws Exception { |
| // No need to try this template with multiple platforms, one is adequate |
| mApiSensitiveTemplate = false; |
| checkCreateTemplate("other", "BroadcastReceiver"); |
| } |
| |
| public void testNewContentProvider() throws Exception { |
| mApiSensitiveTemplate = false; |
| checkCreateTemplate("other", "ContentProvider"); |
| } |
| |
| public void testNewCustomView() throws Exception { |
| mApiSensitiveTemplate = false; |
| checkCreateTemplate("other", "CustomView"); |
| } |
| |
| public void testNewService() throws Exception { |
| mApiSensitiveTemplate = false; |
| checkCreateTemplate("other", "Service"); |
| } |
| |
| public void testCreateRemainingTemplates() throws Exception { |
| sCount = 0; |
| long begin = System.currentTimeMillis(); |
| TemplateManager manager = new TemplateManager(); |
| List<File> other = manager.getTemplates("other"); |
| for (File templateFile : other) { |
| if (sTemplateTestedSeparately.contains(templateFile)) { |
| continue; |
| } |
| checkTemplate(templateFile); |
| } |
| // Also try creating templates, not as part of creating a project |
| List<File> activities = manager.getTemplates("activities"); |
| for (File templateFile : activities) { |
| if (sTemplateTestedSeparately.contains(templateFile)) { |
| continue; |
| } |
| checkTemplate(templateFile); |
| } |
| long end = System.currentTimeMillis(); |
| System.out.println("Successfully checked " + sCount + " template permutations in " |
| + ((end - begin) / (1000 * 60)) + " minutes"); |
| } |
| |
| public void testCreateRemainingProjects() throws Exception { |
| sCount = 0; |
| long begin = System.currentTimeMillis(); |
| TemplateManager manager = new TemplateManager(); |
| List<File> templates = manager.getTemplates("activities"); |
| for (File activityFile : templates) { |
| if (sTemplateTestedSeparately.contains(activityFile)) { |
| continue; |
| } |
| checkProjectWithActivity(activityFile.getName()); |
| } |
| long end = System.currentTimeMillis(); |
| System.out.println("Successfully checked " + sCount + " project permutations in " |
| + ((end - begin) / (1000 * 60)) + " minutes"); |
| } |
| |
| // ---- Test support code below ---- |
| |
| private void checkCreateActivityInProject(String activityName) throws Exception { |
| Stopwatch stopwatch = Stopwatch.createUnstarted(); |
| stopwatch.start(); |
| File templateFile = findTemplate("activities", activityName); |
| sProjectTestedSeparately.add(templateFile); |
| checkProjectWithActivity(templateFile.getName()); |
| stopwatch.stop(); |
| System.out.println("Checked " + templateFile.getName() + " successfully in " |
| + stopwatch.toString()); |
| } |
| |
| private void checkCreateTemplate(String category, String name) throws Exception { |
| Stopwatch stopwatch = Stopwatch.createUnstarted(); |
| stopwatch.start(); |
| File templateFile = findTemplate(category, name); |
| assertNotNull(templateFile); |
| sTemplateTestedSeparately.add(templateFile); |
| checkTemplate(templateFile); |
| stopwatch.stop(); |
| System.out.println("Checked " + templateFile.getName() + " successfully in " |
| + stopwatch.toString()); |
| } |
| |
| private static File findTemplate(String category, String name) { |
| File templateRootFolder = TemplateManager.getTemplateRootFolder(); |
| assertNotNull(templateRootFolder); |
| File file = new File(templateRootFolder, category + File.separator + name); |
| assertTrue(file.getPath(), file.exists()); |
| return file; |
| } |
| |
| private void checkTemplate(File templateFile) throws Exception { |
| NewProjectWizardState values = new NewProjectWizardState(); |
| values.applicationName = "My Application"; |
| values.packageName = "my.pkg2"; |
| |
| values.isLibrary = false; |
| values.createIcon = false; |
| values.useDefaultLocation = true; |
| values.createActivity = false; |
| |
| String projectNameBase = "MyTemplateProject_" + templateFile.getName(); |
| values.projectName = projectNameBase; |
| values.createActivity = false; |
| |
| // Create the new template |
| |
| NewTemplateWizardState state = new NewTemplateWizardState(); |
| state.setTemplateLocation(templateFile); |
| state.minSdkLevel = values.minSdkLevel; |
| |
| // Iterate over all (valid) combinations of build target, minSdk and targetSdk |
| IAndroidTarget[] targets = Sdk.getCurrent().getTargets(); |
| for (int i = targets.length - 1; i >= 0; i--) { |
| IAndroidTarget target = targets[i]; |
| if (!target.isPlatform()) { |
| continue; |
| } |
| if (!isInterestingApiLevel(target.getVersion().getApiLevel())) { |
| continue; |
| } |
| |
| for (int minSdk = 1; |
| minSdk <= SdkVersionInfo.HIGHEST_KNOWN_API; |
| minSdk++) { |
| // Don't bother checking *every* single minSdk, just pick some interesting ones |
| if (!isInterestingApiLevel(minSdk)) { |
| continue; |
| } |
| |
| for (int targetSdk = minSdk; |
| targetSdk <= SdkVersionInfo.HIGHEST_KNOWN_API; |
| targetSdk++) { |
| if (!isInterestingApiLevel(targetSdk)) { |
| continue; |
| } |
| |
| // Make sure this template is supported with these versions |
| IStatus status = values.template.validateTemplate( |
| minSdk, target.getVersion().getApiLevel()); |
| if (status != null && !status.isOK()) { |
| continue; |
| } |
| |
| // Also make sure activity is enabled for these versions |
| status = state.getTemplateHandler().validateTemplate( |
| minSdk, target.getVersion().getApiLevel()); |
| if (status != null && !status.isOK()) { |
| continue; |
| } |
| |
| // Iterate over all new new project templates |
| |
| // should I try all options of theme with all platforms? |
| // or just try all platforms, with one setting for each? |
| // doesn't seem like I need to multiply |
| // just pick the best setting that applies instead for each platform |
| List<Parameter> parameters = values.template.getTemplate().getParameters(); |
| projectParameters: |
| for (Parameter parameter : parameters) { |
| List<Element> options = parameter.getOptions(); |
| if (parameter.type == Parameter.Type.ENUM) { |
| for (Element element : options) { |
| Option option = Option.get(element); |
| String optionId = option.id; |
| int optionMinSdk = option.minSdk; |
| int optionMinBuildApi = option.minBuild; |
| if (optionMinSdk <= minSdk && |
| optionMinBuildApi <= target.getVersion().getApiLevel()) { |
| values.parameters.put(parameter.id, optionId); |
| if (parameter.id.equals("baseTheme")) { |
| String base = projectNameBase + "_min_" + minSdk |
| + "_target_" + targetSdk |
| + "_build_" + target.getVersion().getApiLevel() |
| + "_theme_" + optionId; |
| System.out.println("checking base " + base); |
| |
| checkApiTarget(minSdk, targetSdk, target, values, base, |
| state); |
| break projectParameters; |
| } |
| } |
| } |
| } |
| } |
| |
| if (TEST_JUST_ONE_TARGET_SDK_VERSION) { |
| break; |
| } |
| } |
| |
| if (TEST_JUST_ONE_MIN_SDK) { |
| break; |
| } |
| } |
| |
| if (TEST_JUST_ONE_BUILD_TARGET) { |
| break; |
| } |
| } |
| } |
| |
| private void checkProjectWithActivity(String activity) throws Exception { |
| NewProjectWizardState values = new NewProjectWizardState(); |
| values.applicationName = "My Application"; |
| values.packageName = "my.pkg"; |
| |
| values.isLibrary = false; |
| values.createIcon = false; |
| values.useDefaultLocation = true; |
| |
| // These are basically unused; passed as defaults |
| values.activityName = activity == null ? "Blank" : activity; |
| values.activityTitle = "My Activity Title"; |
| |
| String projectNameBase = "MyProject_" + values.activityName; |
| values.projectName = projectNameBase; |
| |
| values.createActivity = activity != null; |
| NewTemplateWizardState activityValues = values.activityValues; |
| assertNotNull(activityValues); |
| activityValues.minSdkLevel = values.minSdkLevel; |
| |
| |
| // Iterate over all (valid) combinations of build target, minSdk and targetSdk |
| IAndroidTarget[] targets = Sdk.getCurrent().getTargets(); |
| for (int i = targets.length - 1; i >= 0; i--) { |
| IAndroidTarget target = targets[i]; |
| if (!target.isPlatform()) { |
| continue; |
| } |
| if (!isInterestingApiLevel(target.getVersion().getApiLevel())) { |
| continue; |
| } |
| |
| for (int minSdk = 1; |
| minSdk <= SdkVersionInfo.HIGHEST_KNOWN_API; |
| minSdk++) { |
| // Don't bother checking *every* single minSdk, just pick some interesting ones |
| if (!isInterestingApiLevel(minSdk)) { |
| continue; |
| } |
| |
| for (int targetSdk = minSdk; |
| targetSdk <= SdkVersionInfo.HIGHEST_KNOWN_API; |
| targetSdk++) { |
| if (!isInterestingApiLevel(targetSdk)) { |
| continue; |
| } |
| |
| // Make sure this template is supported with these versions |
| IStatus status = values.template.validateTemplate( |
| values.minSdkLevel, values.getBuildApi()); |
| if (status != null && !status.isOK()) { |
| continue; |
| } |
| |
| // Also make sure activity is enabled for these versions |
| status = values.activityValues.getTemplateHandler().validateTemplate( |
| values.minSdkLevel, values.getBuildApi()); |
| if (status != null && !status.isOK()) { |
| continue; |
| } |
| |
| // Iterate over all new new project templates |
| |
| // should I try all options of theme with all platforms? |
| // or just try all platforms, with one setting for each? |
| // doesn't seem like I need to multiply |
| // just pick the best setting that applies instead for each platform |
| List<Parameter> parameters = values.template.getTemplate().getParameters(); |
| for (Parameter parameter : parameters) { |
| List<Element> options = parameter.getOptions(); |
| if (parameter.type == Parameter.Type.ENUM) { |
| for (Element element : options) { |
| Option option = Option.get(element); |
| String optionId = option.id; |
| int optionMinSdk = option.minSdk; |
| int optionMinBuildApi = option.minBuild; |
| if (optionMinSdk <= minSdk && |
| optionMinBuildApi <= target.getVersion().getApiLevel()) { |
| values.parameters.put(parameter.id, optionId); |
| if (parameter.id.equals("baseTheme")) { |
| String base = projectNameBase + "_min_" + minSdk |
| + "_target_" + targetSdk |
| + "_build_" + target.getVersion().getApiLevel() |
| + "_theme_" + optionId; |
| System.out.println("checking base " + base); |
| |
| checkApiTarget(minSdk, targetSdk, target, values, base, |
| null); |
| |
| } |
| } |
| } |
| } |
| } |
| |
| if (TEST_JUST_ONE_TARGET_SDK_VERSION) { |
| break; |
| } |
| } |
| |
| if (TEST_JUST_ONE_MIN_SDK) { |
| break; |
| } |
| } |
| |
| if (TEST_JUST_ONE_BUILD_TARGET) { |
| break; |
| } |
| } |
| } |
| |
| private void checkApiTarget( |
| int minSdk, |
| int targetSdk, |
| @NonNull IAndroidTarget target, |
| @NonNull NewProjectWizardState projectValues, |
| @NonNull String projectNameBase, |
| @Nullable NewTemplateWizardState templateValues) |
| throws Exception { |
| NewTemplateWizardState values = |
| projectValues.createActivity ? projectValues.activityValues : templateValues; |
| |
| projectValues.minSdk = Integer.toString(minSdk); |
| projectValues.minSdkLevel = minSdk; |
| projectValues.targetSdkLevel = targetSdk; |
| projectValues.target = target; |
| |
| if (values == null) { |
| checkProject(projectValues, templateValues); |
| return; |
| } |
| |
| // Next check all other parameters, cycling through booleans and enums. |
| TemplateHandler templateHandler = values.getTemplateHandler(); |
| TemplateMetadata template = templateHandler.getTemplate(); |
| assertNotNull(template); |
| List<Parameter> parameters = template.getParameters(); |
| |
| if (!projectValues.createActivity) { |
| for (Parameter parameter : parameters) { |
| values.parameters.put(parameter.id, parameter.value); |
| } |
| } |
| |
| for (Parameter parameter : parameters) { |
| if (parameter.type == Parameter.Type.SEPARATOR |
| || parameter.type == Parameter.Type.STRING) { |
| // TODO: Consider whether we should attempt some strings here |
| continue; |
| } |
| |
| // The initial (default value); revert to this one after cycling, |
| Object initial = values.parameters.get(parameter.id); |
| |
| if (parameter.type == Parameter.Type.ENUM) { |
| List<Element> options = parameter.getOptions(); |
| for (Element element : options) { |
| Option option = Option.get(element); |
| String optionId = option.id; |
| int optionMinSdk = option.minSdk; |
| int optionMinBuildApi = option.minBuild; |
| if (projectValues.minSdkLevel >= optionMinSdk && |
| projectValues.getBuildApi() >= optionMinBuildApi) { |
| values.parameters.put(parameter.id, optionId); |
| projectValues.projectName = projectNameBase + "_" + parameter.id |
| + "_" + optionId; |
| checkProject(projectValues, templateValues); |
| } |
| } |
| } else { |
| assert parameter.type == Parameter.Type.BOOLEAN; |
| if (parameter.id.equals("isLauncher") && projectValues.createActivity) { |
| // Skipping this one: always true when launched from new project |
| continue; |
| } |
| boolean value = false; |
| values.parameters.put(parameter.id, value); |
| projectValues.projectName = projectNameBase + "_" + parameter.id |
| + "_" + value; |
| checkProject(projectValues, templateValues); |
| |
| value = true; |
| values.parameters.put(parameter.id, value); |
| projectValues.projectName = projectNameBase + "_" + parameter.id |
| + "_" + value; |
| checkProject(projectValues, templateValues); |
| } |
| |
| values.parameters.put(parameter.id, initial); |
| } |
| } |
| |
| private final class OutputGrabber implements IProcessOutput { |
| private final List<String> output = Lists.newArrayList(); |
| private final List<String> error = Lists.newArrayList(); |
| |
| @Override |
| public void out(@Nullable String line) { |
| if (line != null) { |
| output.add(line); |
| } |
| } |
| |
| @Override |
| public void err(@Nullable String line) { |
| if (line != null) { |
| error.add(line); |
| } |
| } |
| |
| @NonNull |
| private List<String> getOutput() { |
| return output; |
| } |
| |
| @NonNull |
| private List<String> getError() { |
| return error; |
| } |
| } |
| |
| private static class Option { |
| private String id; |
| private int minSdk; |
| private int minBuild; |
| |
| public Option(String id, int minSdk, int minBuild) { |
| this.id = id; |
| this.minSdk = minSdk; |
| this.minBuild = minBuild; |
| } |
| |
| private static Option get(Element option) { |
| String optionId = option.getAttribute(ATTR_ID); |
| String minApiString = option.getAttribute(ATTR_MIN_API); |
| int optionMinSdk = 1; |
| if (minApiString != null && !minApiString.isEmpty()) { |
| try { |
| optionMinSdk = Integer.parseInt(minApiString); |
| } catch (NumberFormatException nufe) { |
| // Templates aren't allowed to contain codenames, should |
| // always be an integer |
| AdtPlugin.log(nufe, null); |
| optionMinSdk = 1; |
| } |
| } |
| String minBuildApiString = option.getAttribute(ATTR_MIN_BUILD_API); |
| int optionMinBuildApi = 1; |
| if (minBuildApiString != null && !minBuildApiString.isEmpty()) { |
| try { |
| optionMinBuildApi = Integer.parseInt(minBuildApiString); |
| } catch (NumberFormatException nufe) { |
| // Templates aren't allowed to contain codenames, should |
| // always be an integer |
| AdtPlugin.log(nufe, null); |
| optionMinBuildApi = 1; |
| } |
| } |
| |
| |
| return new Option(optionId, optionMinSdk, optionMinBuildApi); |
| } |
| } |
| |
| private void checkProject( |
| @NonNull NewProjectWizardState projectValues, |
| @Nullable NewTemplateWizardState templateValues) throws Exception { |
| NewTemplateWizardState values = |
| projectValues.createActivity ? projectValues.activityValues : templateValues; |
| if (values != null) { // if not, creating blank project |
| // Validate that a template is only being used in a context it is compatible with! |
| IStatus status = values.getTemplateHandler().validateTemplate( |
| projectValues.minSdkLevel, projectValues.getBuildApi()); |
| if (status != null && !status.isOK()) { |
| fail(status.toString()); |
| } |
| } |
| |
| assertNotNull(projectValues.projectName); |
| projectValues.projectName = AdtUtils.getUniqueProjectName(projectValues.projectName, ""); |
| IPath workspace = Platform.getLocation(); |
| String projectLocation = workspace.append(projectValues.projectName).toOSString(); |
| projectValues.projectLocation = projectLocation; |
| |
| // Create project with the given parameter map |
| final IProject project = createProject(projectValues); |
| assertNotNull(project); |
| |
| if (templateValues != null) { |
| templateValues.project = project; |
| List<Change> changes = templateValues.computeChanges(); |
| if (!changes.isEmpty()) { |
| try { |
| CompositeChange composite = new CompositeChange("", |
| changes.toArray(new Change[changes.size()])); |
| composite.perform(new NullProgressMonitor()); |
| } catch (CoreException e) { |
| fail(e.getLocalizedMessage()); |
| } |
| } |
| } |
| |
| // Project creation has some async hooks so don't attempt to build it *right* away |
| Job job = new Job("Validate project") { |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| try { |
| ensureValidProject(this, project); |
| return Status.OK_STATUS; |
| } catch (Exception e) { |
| fail(e.toString()); |
| } |
| return null; |
| } |
| }; |
| job.schedule(1000); |
| job.join(); |
| Object property = job.getProperty(ERROR_KEY); |
| assertNull(property); |
| } |
| |
| private IProject createProject(NewProjectWizardState values) throws InvocationTargetException { |
| NewProjectWizard wizard = new NewProjectWizard(); |
| wizard.setValues(values); |
| wizard.performFinish(new NullProgressMonitor()); |
| |
| if (TemplateHandler.sMostRecentException != null) { |
| fail(values.projectName + ": " + TemplateHandler.sMostRecentException.toString()); |
| } |
| |
| IProject project = wizard.getProject(); |
| assertNotNull(project); |
| assertTrue(project.exists()); |
| System.out.println("Created project " + project + " : " + AdtUtils.getAbsolutePath(project)); |
| return project; |
| } |
| |
| private void ensureValidProject(@NonNull Job job, @NonNull IProject project) throws Exception { |
| System.out.println("Begin build error check"); |
| ensureNoBuildErrors(job, project); |
| System.out.println("Finished build error check"); |
| |
| System.out.println("Begin lint check"); |
| ensureNoLintWarnings(job, project); |
| System.out.println("Finished lint check"); |
| |
| sCount++; |
| } |
| |
| private void ensureNoLintWarnings(final Job job, IProject project) { |
| System.setProperty("com.android.tools.lint.bindir", AdtPrefs.getPrefs().getOsSdkFolder() |
| + File.separator + FD_TOOLS); |
| |
| LintDriver driver = new LintDriver(EclipseLintClient.getRegistry(), new LintClient() { |
| @Override |
| public void report(@NonNull Context context, |
| @NonNull Issue issue, @NonNull Severity severity, |
| @Nullable Location location, @NonNull String message, @NonNull TextFormat format) { |
| String s = "Found lint error: " + issue.getId() + ": " + message + " at " + location; |
| job.setProperty(ERROR_KEY, s); |
| fail(s); |
| } |
| |
| @Override |
| public Configuration getConfiguration(@NonNull Project p) { |
| return new DefaultConfiguration(this, p, null, new File("dummy.xml")) { |
| @Override |
| public boolean isEnabled(@NonNull Issue issue) { |
| // Doesn't work: hangs in unit test context, something about |
| // loading native libs. |
| if (issue.getCategory() == Category.ICONS){ |
| return false; |
| } |
| |
| if (issue == ManifestDetector.TARGET_NEWER) { |
| // Don't complain about targetSdk < latest: we're deliberately |
| // testing that (to make sure templates compile etc in compat |
| // mode) |
| return false; |
| } |
| |
| if (issue == SecurityDetector.EXPORTED_SERVICE |
| || issue == SecurityDetector.EXPORTED_PROVIDER |
| || issue == SecurityDetector.EXPORTED_RECEIVER) { |
| // Don't complain about missing permissions when exporting: the |
| // unit test is deliberately turning on exported |
| return false; |
| } |
| |
| return true; |
| } |
| }; |
| } |
| |
| @Override |
| @NonNull |
| public String readFile(@NonNull File file) { |
| try { |
| return Files.toString(file, Charsets.UTF_8); |
| } catch (IOException e) { |
| fail(e.toString() + " for " + file.getPath()); |
| return ""; |
| } |
| } |
| |
| @Override |
| public void log(@NonNull Severity severity, @Nullable Throwable exception, |
| @Nullable String format, @Nullable Object... args) { |
| if (exception != null) { |
| exception.printStackTrace(); |
| } |
| if (format != null) { |
| if (args != null) { |
| System.err.println("Log: " + String.format(format, args)); |
| } else { |
| System.err.println("Unexpected log message " + format); |
| } |
| } |
| } |
| |
| @Override |
| @Nullable |
| public JavaParser getJavaParser(@Nullable Project project) { |
| return new EclipseLintClient(null, null, null, false).getJavaParser(project); |
| } |
| |
| @Override |
| public XmlParser getXmlParser() { |
| return new EclipseLintClient(null, null, null, false).getXmlParser(); |
| } |
| }); |
| File projectDir = AdtUtils.getAbsolutePath(project).toFile(); |
| assertNotNull(projectDir); |
| assertTrue(projectDir.getPath(), projectDir.isDirectory()); |
| driver.analyze(Collections.singletonList(projectDir), Scope.ALL); |
| } |
| |
| // Wait for test build support. |
| // This is copied from {@link SampleProjectTest} |
| |
| private void ensureNoBuildErrors(final Job job, final IProject project) throws Exception { |
| File projectDir = AdtUtils.getAbsolutePath(project).toFile(); |
| |
| // Checking the build in Eclipse doesn't work well, because of asynchronous issues |
| // (it looks like not all necessary changes are applied, and even adding waits works |
| // unpredictably.) |
| // |
| // So instead we do it via the command line. |
| // First add ant support: |
| // $ android update project -p . |
| // Then we run ant and look at the exit code to make sure it worked. |
| |
| List<String> command = new ArrayList<String>(); |
| command.add(AdtPlugin.getOsSdkToolsFolder() + "android" + |
| (CURRENT_PLATFORM == PLATFORM_WINDOWS ? ".bat" : "")); |
| command.add("update"); |
| command.add("project"); |
| command.add("-p"); |
| command.add(projectDir.getPath()); |
| |
| // launch the command line process |
| Process process = Runtime.getRuntime().exec(command.toArray(new String[command.size()])); |
| |
| |
| OutputGrabber processOutput = new OutputGrabber(); |
| int status = GrabProcessOutput.grabProcessOutput( |
| process, |
| Wait.WAIT_FOR_READERS, // we really want to make sure we get all the output! |
| processOutput); |
| if (status != 0) { |
| fail(processOutput.getOutput().toString() + processOutput.getError().toString()); |
| } |
| assertEquals(0, status); |
| |
| // Run ant |
| String antCmd = "ant" + (CURRENT_PLATFORM == PLATFORM_WINDOWS ? ".bat" : ""); |
| String antTarget = "debug"; |
| process = Runtime.getRuntime().exec(antCmd + " " + antTarget, null, projectDir); |
| processOutput = new OutputGrabber(); |
| status = GrabProcessOutput.grabProcessOutput( |
| process, |
| Wait.WAIT_FOR_READERS, // we really want to make sure we get all the output! |
| processOutput); |
| if (status != 0) { |
| fail(processOutput.getOutput().toString() + processOutput.getError().toString()); |
| } |
| assertEquals(0, status); |
| System.out.println("Ant succeeded (code=" + status + ")"); |
| } |
| } |