diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandlerTest.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandlerTest.java | 901 |
1 files changed, 901 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandlerTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandlerTest.java new file mode 100644 index 000000000..9d3bc60c6 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandlerTest.java @@ -0,0 +1,901 @@ +/* + * 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 + ")"); + } +} |