blob: 9d3bc60c6974435be35dfc29b4e40c5460e195b9 [file] [log] [blame]
/*
* 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 + ")");
}
}