blob: d4dcc8a9162da5becee86fc546ab96ab10efa060 [file] [log] [blame]
package com.android.tools.idea.gradle.eclipse;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.sdklib.BuildToolInfo;
import com.android.sdklib.SdkManager;
import com.android.tools.idea.gradle.util.PropertiesUtil;
import com.android.tools.idea.templates.TemplateManager;
import com.android.utils.ILogger;
import com.android.utils.Pair;
import com.android.utils.SdkUtils;
import com.android.utils.StdLogger;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.io.Files;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.process.CapturingProcessHandler;
import com.intellij.execution.process.ProcessOutput;
import org.jetbrains.android.AndroidTestCase;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.*;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import static com.android.SdkConstants.*;
import static com.android.tools.idea.gradle.eclipse.GradleImport.*;
import static com.android.tools.idea.gradle.eclipse.ImportSummary.*;
import static com.google.common.base.Charsets.UTF_8;
import static java.io.File.separator;
import static java.io.File.separatorChar;
/**
* Unit tests for the Gradle importer.
* <p>
* TODO:
* - Test emitting proguard rules in android-library (check build.gradle declaration)
* - Test compile options
* - Skip instrumentation stuff if same as default?
* - Test having more than one SDK proguard list?
* - Escaped groovy paths
* - Path variable in .classpath
* - Case where a lib is both in libs/ and in .classpath (cull and only include once)
* - Filename clashes (collapsing into libs/, etc.)
* - Finding a lib that is using a workspace path which isn't found but is a direct child
* - Instrumentation libs/ .jar which gets replaced by a gradle dependency
*/
@SuppressWarnings("ConcatenationWithEmptyString")
public class GradleImportTest extends AndroidTestCase { // Only because we need VFS to locate the template folder for the gradle wrapper
private static File createProject(String name, String pkg) throws IOException {
File dir = Files.createTempDir();
return createProject(dir, name, pkg);
}
private static File createProject(File dir, String name, String pkg) throws IOException {
createDotProject(dir, name, true);
File src = new File("src");
File gen = new File("gen");
createSampleJavaSource(dir, "src", pkg, "MyActivity");
createSampleJavaSource(dir, "gen", pkg, "R");
createClassPath(dir,
new File("bin", "classes"),
Arrays.asList(src, gen),
Collections.<File>emptyList());
createProjectProperties(dir, "android-17", null, null, null,
Collections.<File>emptyList());
createAndroidManifest(dir, pkg, 8, 16, null);
createDefaultStrings(dir);
createDefaultIcon(dir);
return dir;
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testResolveExpressions() throws Exception {
File root = Files.createTempDir();
File projectDir = new File(root, "dir1" + separator + "dir2" + separator + "dir3" +
separator + "dir4" + separator + "prj");
projectDir.mkdirs();
createProject(projectDir, "test1", "test.pkg");
File var1 = new File(root, "sub1" + separator + "sub2" + separator + "sub3");
var1.mkdirs();
File var4 = new File(projectDir.getParentFile(), "var4");
var4.mkdirs();
File tpl = new File(projectDir.getParentFile().getParentFile().getParentFile(), "TARGET" +
separator + "android" + separator + "third-party");
tpl.mkdirs();
File supportLib = new File(tpl, "android-support-v4.r19.jar");
supportLib.createNewFile();
Files.write(""
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<projectDescription>\n"
+ "\t<name>UnitTest</name>\n"
+ "\t<comment></comment>\n"
+ "\t<projects>\n"
+ "\t</projects>\n"
+ "\t<buildSpec>\n"
+ "\t\t<buildCommand>\n"
+ "\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n"
+ "\t\t\t<arguments>\n"
+ "\t\t\t</arguments>\n"
+ "\t\t</buildCommand>\n"
+ "\t</buildSpec>\n"
+ "\t<natures>\n"
+ "\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n"
+ "\t</natures>\n"
+ "\t<linkedResources>\n"
+ "\t\t<link>\n"
+ "\t\t\t<name>MYLIBS</name>\n"
+ "\t\t\t<type>2</type>\n"
+ "\t\t\t<locationURI>MYLIBS</locationURI>\n"
+ "\t\t</link>\n"
+ "\t\t<link>\n"
+ "\t\t\t<name>3rd_java_libs</name>\n"
+ "\t\t\t<type>2</type>\n"
+ "\t\t\t<locationURI>PARENT-3-PROJECT_LOC/TARGET/android/third-party</locationURI>\n"
+ "\t\t</link>\n"
+ "\t\t<link>\n"
+ "\t\t\t<name>jnilibs</name>\n"
+ "\t\t\t<type>2</type>\n"
+ "\t\t\t<locationURI>virtual:/virtual</locationURI>\n"
+ "\t\t</link>\n"
+ "\t</linkedResources>\n"
+ "\t<variableList>\n"
+ "\t\t<variable>\n"
+ "\t\t\t<name>MY_VAR_1</name>\n"
+ "\t\t\t<value>" + SdkUtils.fileToUrl(var1) + "</value>\n"
+ "\t\t</variable>\n"
+ "\t\t<variable>\n"
+ "\t\t\t<name>MY_VAR_2</name>\n"
+ "\t\t\t<value>$%7BMY_VAR_1%7D</value>\n"
+ "\t\t</variable>\n"
+ "\t\t<variable>\n"
+ "\t\t\t<name>MY_VAR_3</name>\n"
+ "\t\t\t<value>$%7BPROJECT_LOC%7D/src</value>\n"
+ "\t\t</variable>\n"
+ "\t\t<variable>\n"
+ "\t\t\t<name>MY_VAR_4</name>\n"
+ "\t\t\t<value>$%7BPARENT-1-PROJECT_LOC%7D/var4</value>\n"
+ "\t\t</variable>\n"
+ "\t\t<variable>\n"
+ "\t\t\t<name>MY_VAR_5</name>\n"
+ "\t\t\t<value>$%7BPARENT_LOC%7D/var4</value>\n"
+ "\t\t</variable>\n"
+ "\t</variableList>\n"
+ "</projectDescription>",
new File(projectDir, ".project"), UTF_8);
GradleImport importer = new GradleImport();
EclipseProject project = EclipseProject.getProject(importer, projectDir);
importer.getPathMap();
// Test absolute paths
assertEquals(var1, project.resolveVariableExpression(var1.getPath()));
assertEquals(var1, project.resolveVariableExpression(var1.getAbsolutePath()));
assertEquals(var1.getCanonicalFile(),
project.resolveVariableExpression(var1.getCanonicalPath()));
assertEquals(var1, project.resolveVariableExpression(var1.getPath().replace('/',
separatorChar))); // on Windows, make sure we handle workspace files with forwards
// Test project relative paths
String relative = "src" + separator + "test" + separator + "pkg" + separator
+ "MyActivity.java";
assertEquals(new File(projectDir, relative), project.resolveVariableExpression(relative));
assertEquals(new File(projectDir, relative), project.resolveVariableExpression(
relative.replace('/', separatorChar)));
// Test workspace paths
// This is handled by testLibraries2
// Test path variables
assertEquals(var1, project.resolveVariableExpression("MY_VAR_1"));
assertEquals(var1, project.resolveVariableExpression("MY_VAR_2"));
assertEquals(new File(projectDir, "src"), project.resolveVariableExpression("MY_VAR_3"));
assertEquals(var4, project.resolveVariableExpression("MY_VAR_4"));
assertEquals(var4, project.resolveVariableExpression("MY_VAR_5"));
// Test linked variables
assertEquals(supportLib, project.resolveVariableExpression(
"3rd_java_libs/android-support-v4.r19.jar"));
// Test user-supplied values
assertEquals(var1, project.resolveVariableExpression("MY_VAR_1"));
importer.getPathMap().put("MY_VAR_1", projectDir);
assertEquals(projectDir, project.resolveVariableExpression("MY_VAR_1"));
importer.getPathMap().put("/some/unresolved/path", var4);
assertEquals(var4, project.resolveVariableExpression("/some/unresolved/path"));
// Setup for workspace tests
assertNull(project.resolveVariableExpression("MY_GLOBAL_VAR"));
final File workspace = new File(root, "workspace");
workspace.mkdirs();
File prefs = new File(workspace, ".metadata" + separator +
".plugins" + separator +
"org.eclipse.core.runtime" + separator +
".settings" + separator +
"org.eclipse.jdt.core.prefs");
prefs.getParentFile().mkdirs();
File global1 = var1.getParentFile();
Files.write(""
+ "eclipse.preferences.version=1\n"
+ "org.eclipse.jdt.core.classpathVariable.MY_GLOBAL_VAR="
+ global1.getPath().replace(separatorChar,'/').replace(":","\\:") + "\n"
+ "org.eclipse.jdt.core.codeComplete.visibilityCheck=enabled\n"
+ "org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled\n"
+ "org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6\n"
+ "org.eclipse.jdt.core.compiler.compliance=1.6\n"
+ "org.eclipse.jdt.core.compiler.problem.assertIdentifier=error\n"
+ "org.eclipse.jdt.core.compiler.problem.enumIdentifier=error\n"
+ "org.eclipse.jdt.core.compiler.source=1.6\n"
+ "org.eclipse.jdt.core.formatter.tabulation.char=space", prefs, UTF_8);
File global2 = var4.getParentFile();
prefs = new File(workspace, ".metadata" + separator +
".plugins" + separator +
"org.eclipse.core.runtime" + separator +
".settings" + separator +
"org.eclipse.core.resources.prefs");
prefs.getParentFile().mkdirs();
Files.write(""
+ "eclipse.preferences.version=1\n"
+ "pathvariable.MY_GLOBAL_VAR_2="
+ global2.getPath().replace(separatorChar,'/').replace(":", "\\:") + "\n"
+ "version=1", prefs, UTF_8);
importer.setEclipseWorkspace(workspace);
// Test global path variables
assertEquals(global1, project.resolveVariableExpression("MY_GLOBAL_VAR"));
assertEquals(var1, project.resolveVariableExpression("MY_GLOBAL_VAR/sub3"));
assertEquals(var1, project.resolveVariableExpression("MY_GLOBAL_VAR" + separator + "sub3"));
// Test workspace linked resources
assertEquals(global2, project.resolveVariableExpression("MY_GLOBAL_VAR_2"));
deleteDir(projectDir);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testBasic() throws Exception {
File projectDir = createProject("test1", "test.pkg");
// Add some files in there that we are ignoring
new File(projectDir, "ic_launcher-web.png").createNewFile();
new File(projectDir, "Android.mk").createNewFile();
new File(projectDir, "build.properties").createNewFile();
new File(projectDir, "local.properties").createNewFile();
new File(projectDir, "src" + separator + ".git").mkdir();
new File(projectDir, "src" + separator + ".svn").mkdir();
File unhandled = new File(projectDir, "unhandledDir1" + separator + "unhandledDir2");
unhandled.mkdirs();
new File(unhandled, "unhandledFile").createNewFile();
new File(unhandled, "unhandledDir3").mkdirs();
new File(unhandled.getParentFile(), "unhandledDir4").mkdirs();
new File(projectDir, "lint.xml").createNewFile();
// Make sure we handle less common file extensions: see issue 78459
createIcon(projectDir, "other_icon.PNG");
createLayout(projectDir, "other_layout.XML");
// Project being imported
assertEquals(""
+ ".classpath\n"
+ ".project\n"
+ "Android.mk\n"
+ "AndroidManifest.xml\n"
+ "build.properties\n"
+ "gen\n"
+ " test\n"
+ " pkg\n"
+ " R.java\n"
+ "ic_launcher-web.png\n"
+ "lint.xml\n"
+ "local.properties\n"
+ "project.properties\n"
+ "res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " drawable-hdpi\n"
+ " other_icon.PNG\n"
+ " layout\n"
+ " other_layout.XML\n"
+ " values\n"
+ " strings.xml\n"
+ "src\n"
+ " .git\n"
+ " .svn\n"
+ " test\n"
+ " pkg\n"
+ " MyActivity.java\n"
+ "unhandledDir1\n"
+ " unhandledDir2\n"
+ " unhandledDir3\n"
+ " unhandledFile\n"
+ " unhandledDir4\n",
fileTree(projectDir, true));
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_UNHANDLED
+ "* Android.mk\n"
+ "* build.properties\n"
+ "* ic_launcher-web.png\n"
+ "* unhandledDir1/\n"
+ "* unhandledDir1/unhandledDir2/\n"
+ "* unhandledDir1/unhandledDir2/unhandledFile\n"
+ MSG_FOLDER_STRUCTURE
+ "* AndroidManifest.xml => app/src/main/AndroidManifest.xml\n"
+ "* lint.xml => app/lint.xml\n"
+ "* res/ => app/src/main/res/\n"
+ "* src/ => app/src/main/java/\n"
+ "* other_icon.PNG => other_icon.png\n"
+ "* other_layout.XML => other_layout.xml\n"
+ MSG_FOOTER,
true /* checkBuild */);
// Imported contents
assertEquals(""
+ "app\n"
+ " build.gradle\n"
+ " lint.xml\n"
+ " src\n"
+ " main\n"
+ " AndroidManifest.xml\n"
+ " java\n"
+ " test\n"
+ " pkg\n"
+ " MyActivity.java\n"
+ " res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " drawable-hdpi\n"
+ " other_icon.png\n"
+ " layout\n"
+ " other_layout.xml\n"
+ " values\n"
+ " strings.xml\n"
+ "build.gradle\n"
+ "import-summary.txt\n"
+ (getTestSdkPathLocal() != null ? "local.properties\n" : "")
+ "settings.gradle\n",
fileTree(imported, true));
deleteDir(projectDir);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testImportWithoutMinSdkVersion() throws Exception {
// Regression test for importing project which does not explicitly set minSdkVersion
// and/or targetSdkVersion; this would earlier result in "-1" being written into
// build.gradle which fails the build with "> Cannot invoke method minus() on null object"
File projectDir = createProject("test1", "test.pkg");
// Remove <uses-sdk ...>
File manifestFile = new File(projectDir, FN_ANDROID_MANIFEST_XML);
String manifestContents = Files.toString(manifestFile, UTF_8);
int index = manifestContents.indexOf("<uses-sdk");
int endIndex = manifestContents.indexOf('>', index);
assertFalse(index == -1);
assertFalse(endIndex == -1);
manifestContents = manifestContents.substring(0, index) +
manifestContents.substring(endIndex + 1);
Files.write(manifestContents, manifestFile, UTF_8);
File imported = checkProject(projectDir, ""
+ MSG_HEADER
+ MSG_FOLDER_STRUCTURE
+ "* AndroidManifest.xml => app/src/main/AndroidManifest.xml\n"
+ "* res/ => app/src/main/res/\n"
+ "* src/ => app/src/main/java/\n"
+ MSG_FOOTER,
true /* checkBuild */);
deleteDir(projectDir);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testMoveRsResourcesAndAidl() throws Exception {
File projectDir = createProject("test1", "test.pkg");
createSampleAidlFile(projectDir, "src", "test.pkg");
createSampleTimeZoneData(projectDir, "src");
createSampleRsFile(projectDir, "src", "test.pkg");
// Project being imported
assertEquals(""
+ ".classpath\n"
+ ".project\n"
+ "AndroidManifest.xml\n"
+ "gen\n"
+ " test\n"
+ " pkg\n"
+ " R.java\n"
+ "project.properties\n"
+ "res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " values\n"
+ " strings.xml\n"
+ "src\n"
+ " test\n"
+ " pkg\n"
+ " IHardwareService.aidl\n"
+ " MyActivity.java\n"
+ " latency.rs\n"
+ " zoneinfo-global\n"
+ " Pacific\n"
+ " Honolulu.ics\n",
fileTree(projectDir, true));
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_FOLDER_STRUCTURE
+ DEFAULT_MOVED
+ "* src/test/pkg/IHardwareService.aidl => app/src/main/aidl/test/pkg/IHardwareService.aidl\n"
+ "* src/test/pkg/latency.rs => app/src/main/rs/latency.rs\n"
+ "* src/zoneinfo-global/Pacific/Honolulu.ics => app/src/main/resources/zoneinfo-global/Pacific/Honolulu.ics\n"
+ MSG_FOOTER,
true /* checkBuild */);
// Imported contents
assertEquals(""
+ "app\n"
+ " build.gradle\n"
+ " src\n"
+ " main\n"
+ " AndroidManifest.xml\n"
+ " aidl\n"
+ " test\n"
+ " pkg\n"
+ " IHardwareService.aidl\n"
+ " java\n"
+ " test\n"
+ " pkg\n"
+ " MyActivity.java\n"
+ " res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " values\n"
+ " strings.xml\n"
+ " resources\n"
+ " zoneinfo-global\n"
+ " Pacific\n"
+ " Honolulu.ics\n"
+ " rs\n"
+ " latency.rs\n"
+ "build.gradle\n"
+ "import-summary.txt\n"
+ (getTestSdkPathLocal() != null ? "local.properties\n" : "")
+ "settings.gradle\n",
fileTree(imported, true));
deleteDir(projectDir);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testImportAndroidSample() throws Exception {
String testSdkPath = getTestSdkPathLocal();
if (testSdkPath == null) {
return;
}
File samples = new File(testSdkPath, "samples");
if (!samples.exists()) {
return;
}
File[] platforms = samples.listFiles();
if (platforms == null) {
return;
}
File projectDir = null;
for (File platform : platforms) {
if (platform.isDirectory()) {
String name = platform.getName();
if (name.startsWith("android-")) {
try {
int version = Integer.parseInt(name.substring("android-".length()));
if (version > GradleImport.CURRENT_COMPILE_VERSION) {
// skip versions higher than the default compileSdkVersion since it's not specified in the ApiDemos
// project and we'll pick up the default
continue;
}
} catch (NumberFormatException e) {
// e.g. android-L
continue;
}
}
File apiDemos = new File(platform, "legacy" + separator + "ApiDemos");
if (apiDemos.isDirectory()) {
projectDir = apiDemos;
break;
}
}
}
if (projectDir == null) {
return;
}
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_UNHANDLED
+ "* README.txt\n"
+ "* tests/\n"
+ "* tests/build.properties\n"
+ MSG_FOLDER_STRUCTURE
+ "* AndroidManifest.xml => app/src/main/AndroidManifest.xml\n"
+ "* assets/ => app/src/main/assets/\n"
+ "* res/ => app/src/main/res/\n"
+ "* src/ => app/src/main/java/\n"
+ "* src/com/example/android/apis/app/IRemoteService.aidl => app/src/main/aidl/com/example/android/apis/app/IRemoteService.aidl\n"
+ "* src/com/example/android/apis/app/IRemoteServiceCallback.aidl => app/src/main/aidl/com/example/android/apis/app/IRemoteServiceCallback.aidl\n"
+ "* src/com/example/android/apis/app/ISecondary.aidl => app/src/main/aidl/com/example/android/apis/app/ISecondary.aidl\n"
+ "* tests/src/ => app/src/androidTest/java/\n"
+ MSG_FOOTER,
// Temporarily disabled: As of build tools 21, aapt no longer allows the (invalid) references
// to @+android:id in the samples, so building fails. The samples need to be updated, so don't
// attempt to build them for now.
//true /* checkBuild */);
false /* checkBuild */);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testNoProjectMetadata() throws Exception {
File projectDir = createProject("test1", "test.pkg");
// Add some files in there that we are ignoring
new File(projectDir, "ic_launcher-web.png").createNewFile();
new File(projectDir, "Android.mk").createNewFile();
new File(projectDir, "build.properties").createNewFile();
new File(projectDir, "local.properties").createNewFile();
new File(projectDir, "src" + separator + ".git").mkdir();
new File(projectDir, "src" + separator + ".svn").mkdir();
File unhandled = new File(projectDir, "unhandledDir1" + separator + "unhandledDir2");
unhandled.mkdirs();
new File(unhandled, "unhandledFile").createNewFile();
new File(unhandled, "unhandledDir3").mkdirs();
new File(unhandled.getParentFile(), "unhandledDir4").mkdirs();
new File(projectDir, ".classpath").delete();
new File(projectDir, ".project").delete();
// Project being imported
assertEquals(""
+ "Android.mk\n"
+ "AndroidManifest.xml\n"
+ "build.properties\n"
+ "gen\n"
+ " test\n"
+ " pkg\n"
+ " R.java\n"
+ "ic_launcher-web.png\n"
+ "local.properties\n"
+ "project.properties\n"
+ "res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " values\n"
+ " strings.xml\n"
+ "src\n"
+ " .git\n"
+ " .svn\n"
+ " test\n"
+ " pkg\n"
+ " MyActivity.java\n"
+ "unhandledDir1\n"
+ " unhandledDir2\n"
+ " unhandledDir3\n"
+ " unhandledFile\n"
+ " unhandledDir4\n",
fileTree(projectDir, true));
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_UNHANDLED
+ "* Android.mk\n"
+ "* build.properties\n"
+ "* ic_launcher-web.png\n"
+ "* unhandledDir1/\n"
+ "* unhandledDir1/unhandledDir2/\n"
+ "* unhandledDir1/unhandledDir2/unhandledFile\n"
+ MSG_FOLDER_STRUCTURE
+ DEFAULT_MOVED
+ MSG_FOOTER,
true /* checkBuild */);
// Imported contents
assertEquals(""
+ "app\n"
+ " build.gradle\n"
+ " src\n"
+ " main\n"
+ " AndroidManifest.xml\n"
+ " java\n"
+ " test\n"
+ " pkg\n"
+ " MyActivity.java\n"
+ " res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " values\n"
+ " strings.xml\n"
+ "build.gradle\n"
+ "import-summary.txt\n"
+ (getTestSdkPathLocal() != null ? "local.properties\n" : "")
+ "settings.gradle\n",
fileTree(imported, true));
deleteDir(projectDir);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testNoProjectProperties() throws Exception {
// Missing project.properties
File projectDir = createProject("testError3", "test.pkg");
new File(projectDir, FN_PROJECT_PROPERTIES).delete();
final AtomicReference<GradleImport> importReference = new AtomicReference<GradleImport>();
File imported = checkProject(projectDir, ""
+ MSG_HEADER
+ MSG_FOLDER_STRUCTURE
+ DEFAULT_MOVED
+ MSG_FOOTER,
true /* checkBuild */, new ImportCustomizer() {
@Override
public void customize(GradleImport importer) {
importReference.set(importer);
}
});
deleteDir(projectDir);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testLibraries() throws Exception {
File root = Files.createTempDir();
File app = createLibrary(root, "test.lib2.pkg", false);
// ADT Directory structure created by the above:
assertEquals(""
+ "App\n"
+ " .classpath\n"
+ " .gitignore\n"
+ " .project\n"
+ " AndroidManifest.xml\n"
+ " gen\n"
+ " test\n"
+ " pkg\n"
+ " R.java\n"
+ " project.properties\n"
+ " res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " values\n"
+ " strings.xml\n"
+ " src\n"
+ " test\n"
+ " pkg\n"
+ " MyActivity.java\n"
+ "Lib1\n"
+ " .classpath\n"
+ " .project\n"
+ " AndroidManifest.xml\n"
+ " gen\n"
+ " test\n"
+ " lib\n"
+ " pkg\n"
+ " R.java\n"
+ " project.properties\n"
+ " src\n"
+ " test\n"
+ " lib\n"
+ " pkg\n"
+ " MyLibActivity.java\n"
+ "Lib2\n"
+ " .classpath\n"
+ " .project\n"
+ " AndroidManifest.xml\n"
+ " gen\n"
+ " test\n"
+ " lib2\n"
+ " pkg\n"
+ " R.java\n"
+ " project.properties\n"
+ " src\n"
+ " test\n"
+ " lib2\n"
+ " pkg\n"
+ " MyLib2Activity.java\n"
+ "subdir1\n"
+ " subdir2\n"
+ " JavaLib\n"
+ " .classpath\n"
+ " .gitignore\n"
+ " .project\n"
+ " src\n"
+ " test\n"
+ " lib2\n"
+ " pkg\n"
+ " Utilities.java\n",
fileTree(root, true));
File imported = checkProject(app,
""
+ MSG_HEADER
+ MSG_MANIFEST
+ MSG_UNHANDLED
+ "From App:\n"
+ "* .gitignore\n"
+ "From JavaLib:\n"
+ "* .gitignore\n"
+ MSG_FOLDER_STRUCTURE
+ "In JavaLib:\n"
+ "* src/ => javaLib/src/main/java/\n"
+ "In Lib1:\n"
+ "* AndroidManifest.xml => lib1/src/main/AndroidManifest.xml\n"
+ "* src/ => lib1/src/main/java/\n"
+ "In Lib2:\n"
+ "* AndroidManifest.xml => lib2/src/main/AndroidManifest.xml\n"
+ "* src/ => lib2/src/main/java/\n"
+ "In App:\n"
+ "* AndroidManifest.xml => app/src/main/AndroidManifest.xml\n"
+ "* res/ => app/src/main/res/\n"
+ "* src/ => app/src/main/java/\n"
+ MSG_FOOTER,
true /* checkBuild */);
// Imported project
assertEquals(""
+ "app\n"
+ " build.gradle\n"
+ " src\n"
+ " main\n"
+ " AndroidManifest.xml\n"
+ " java\n"
+ " test\n"
+ " pkg\n"
+ " MyActivity.java\n"
+ " res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " values\n"
+ " strings.xml\n"
+ "build.gradle\n"
+ "import-summary.txt\n"
+ "javaLib\n"
+ " build.gradle\n"
+ " src\n"
+ " main\n"
+ " java\n"
+ " test\n"
+ " lib2\n"
+ " pkg\n"
+ " Utilities.java\n"
+ "lib1\n"
+ " build.gradle\n"
+ " src\n"
+ " main\n"
+ " AndroidManifest.xml\n"
+ " java\n"
+ " test\n"
+ " lib\n"
+ " pkg\n"
+ " MyLibActivity.java\n"
+ "lib2\n"
+ " build.gradle\n"
+ " src\n"
+ " main\n"
+ " AndroidManifest.xml\n"
+ " java\n"
+ " test\n"
+ " lib2\n"
+ " pkg\n"
+ " MyLib2Activity.java\n"
+ (getTestSdkPathLocal() != null ? "local.properties\n" : "")
+ "settings.gradle\n",
fileTree(imported, true));
// Let's peek at some of the key files to make sure we codegen'ed the right thing
//noinspection PointlessBooleanExpression,ConstantConditions
assertEquals(""
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"buildscript {\n"
+ " repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ " }\n"
+ "}\n" : "")
+ "apply plugin: 'java'\n",
Files.toString(new File(imported, "javaLib" + separator + "build.gradle"), UTF_8)
.replace(NL, "\n"));
// Let's peek at some of the key files to make sure we codegen'ed the right thing
//noinspection ConstantConditions
assertEquals(""
+ "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n"
+ (DECLARE_GLOBAL_REPOSITORIES ?
"buildscript {\n"
+ " repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ " }\n"
+ " dependencies {\n"
+ " classpath '" + ANDROID_GRADLE_PLUGIN + "'\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "allprojects {\n"
+ " repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ " }\n"
+ "}\n" : ""),
Files.toString(new File(imported, "build.gradle"), UTF_8)
.replace(NL, "\n"));
//noinspection PointlessBooleanExpression,ConstantConditions
assertEquals(""
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"buildscript {\n"
+ " repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ " }\n"
+ " dependencies {\n"
+ " classpath '" + ANDROID_GRADLE_PLUGIN + "'\n"
+ " }\n"
+ "}\n" : "")
+ "apply plugin: 'com.android.application'\n"
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"\n"
+ "repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ "}\n" : "")
+ "\n"
+ "android {\n"
+ " compileSdkVersion 17\n"
+ " buildToolsVersion \"" + BUILD_TOOLS_VERSION + "\"\n"
+ "\n"
+ " defaultConfig {\n"
+ " applicationId \"test.pkg\"\n"
+ " minSdkVersion 8\n"
+ " targetSdkVersion 16\n"
+ " }\n"
+ "\n"
+ " buildTypes {\n"
+ " release {\n"
+ " minifyEnabled false\n"
+ " proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "dependencies {\n"
+ " compile project(':lib1')\n"
+ " compile project(':lib2')\n"
+ " compile project(':javaLib')\n"
+ "}\n",
Files.toString(new File(imported, "app" + separator + "build.gradle"), UTF_8)
.replace(NL,"\n"));
//noinspection PointlessBooleanExpression,ConstantConditions
assertEquals(""
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"buildscript {\n"
+ " repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ " }\n"
+ " dependencies {\n"
+ " classpath '" + ANDROID_GRADLE_PLUGIN + "'\n"
+ " }\n"
+ "}\n" : "")
+ "apply plugin: 'com.android.library'\n"
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"\n"
+ "repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ "}\n" : "")
+ "\n"
+ "android {\n"
+ " compileSdkVersion 18\n"
+ " buildToolsVersion \"" + BUILD_TOOLS_VERSION + "\"\n"
+ "\n"
+ " defaultConfig {\n"
+ " minSdkVersion 8\n"
+ " targetSdkVersion 8\n"
+ " }\n"
+ "\n"
+ " buildTypes {\n"
+ " release {\n"
+ " minifyEnabled false\n"
+ " proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "dependencies {\n"
+ " compile project(':lib1')\n"
+ "}\n",
Files.toString(new File(imported, "lib2" + separator + "build.gradle"), UTF_8)
.replace(NL, "\n"));
assertEquals(""
+ "include ':javaLib'\n"
+ "include ':lib1'\n"
+ "include ':lib2'\n"
+ "include ':app'\n",
Files.toString(new File(imported, "settings.gradle"), UTF_8)
.replace(NL, "\n"));
deleteDir(root);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
private static File createLibrary(File root, String lib2Pkg, boolean startLibrariesAt1)
throws IOException {
// Plain Java library, used by Library 1 and App
String javaLibName = "JavaLib";
String javaLibRelative = "subdir1" + separator + "subdir2" + separator + javaLibName;
File javaLib = new File(root, javaLibRelative);
javaLib.mkdirs();
String javaLibPkg = "test.lib2.pkg";
createDotProject(javaLib, javaLibName, false);
File javaLibSrc = new File("src");
createSampleJavaSource(javaLib, "src", javaLibPkg, "Utilities");
createClassPath(javaLib,
new File("bin"),
Collections.singletonList(javaLibSrc),
Collections.<File>emptyList());
// Make Android library 1
String lib1Name = "Lib1";
File lib1 = new File(root, lib1Name);
lib1.mkdirs();
String lib1Pkg = "test.lib.pkg";
createDotProject(lib1, lib1Name, true);
File lib1Src = new File("src");
File lib1Gen = new File("gen");
createSampleJavaSource(lib1, "src", lib1Pkg, "MyLibActivity");
createSampleJavaSource(lib1, "gen", lib1Pkg, "R");
createClassPath(lib1,
new File("bin", "classes"),
Arrays.asList(lib1Src, lib1Gen),
Collections.<File>emptyList());
createProjectProperties(lib1, "android-19", null, true, null,
// Using \ instead of File.separator deliberately to test path conversion
// handling: you can import a Windows relative path on a non-Windows system
// and vice versa
Collections.singletonList(new File(".." + '\\' + javaLibRelative)),
startLibrariesAt1);
createAndroidManifest(lib1, lib1Pkg, -1, -1, "<application/>");
String lib2Name = "Lib2";
File lib2 = new File(root, lib2Name);
lib2.mkdirs();
createDotProject(lib2, lib2Name, true);
File lib2Src = new File("src");
File lib2Gen = new File("gen");
createSampleJavaSource(lib2, "src", lib2Pkg, "MyLib2Activity");
createSampleJavaSource(lib2, "gen", lib2Pkg, "R");
createClassPath(lib2,
new File("bin", "classes"),
Arrays.asList(lib2Src, lib2Gen),
Collections.<File>emptyList());
createProjectProperties(lib2, "android-18", null, true, null,
// Deliberately using / instead of Files.separator, for opposite
// test of file separator for lib1 above
Collections.singletonList(new File(".." + '/' + lib1Name)));
createAndroidManifest(lib2, lib2Pkg, 7, -1, "<application/>");
// Main app project, depends on library1, library2 and java lib
String appName = "App";
File app = new File(root, appName);
app.mkdirs();
String appPkg = "test.pkg";
createDotProject(app, appName, true);
File appSrc = new File("src");
File appGen = new File("gen");
createSampleJavaSource(app, "src", appPkg, "MyActivity");
createSampleJavaSource(app, "gen", appPkg, "R");
createClassPath(app,
new File("bin", "classes"),
Arrays.asList(appSrc, appGen),
Collections.<File>emptyList());
createProjectProperties(app, "android-17", null, null, null,
Arrays.asList(
new File(".." + separator + lib1Name),
new File(".." + separator + lib2Name),
new File(".." + separator + javaLibRelative)));
createAndroidManifest(app, appPkg, 8, 16, null);
createDefaultStrings(app);
createDefaultIcon(app);
// Add some files in there that we are ignoring
new File(app, ".gitignore").createNewFile();
new File(javaLib, ".gitignore").createNewFile();
return app;
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testReplaceJar() throws Exception {
// Add in some well known jars and make sure they get migrated as dependencies
File projectDir = createProject("test1", "test.pkg");
File libs = new File(projectDir, "libs");
libs.mkdirs();
new File(libs, "android-support-v4.jar").createNewFile();
new File(libs, "android-support-v7-gridlayout.jar").createNewFile();
new File(libs, "android-support-v7-appcompat.jar").createNewFile();
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_REPLACED_JARS
+ "android-support-v4.jar => com.android.support:support-v4:18.0.0\n"
+ "android-support-v7-appcompat.jar => com.android.support:appcompat-v7:18.0.0\n"
+ "android-support-v7-gridlayout.jar => com.android.support:gridlayout-v7:18.0.0\n"
+ MSG_FOLDER_STRUCTURE
+ DEFAULT_MOVED
+ (getTestSdkPathLocal() == null ? MSG_MISSING_REPO_1 + "null\n" + MSG_MISSING_REPO_2 : "")
+ MSG_FOOTER,
true /* checkBuild */);
// Imported contents
assertEquals(""
+ "app\n"
+ " build.gradle\n"
+ " src\n"
+ " main\n"
+ " AndroidManifest.xml\n"
+ " java\n"
+ " test\n"
+ " pkg\n"
+ " MyActivity.java\n"
+ " res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " values\n"
+ " strings.xml\n"
+ "build.gradle\n"
+ "import-summary.txt\n"
+ (getTestSdkPathLocal() != null ? "local.properties\n" : "")
+ "settings.gradle\n",
fileTree(imported, true));
//noinspection PointlessBooleanExpression,ConstantConditions
assertEquals(""
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"buildscript {\n"
+ " repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ " }\n"
+ " dependencies {\n"
+ " classpath '" + ANDROID_GRADLE_PLUGIN + "'\n"
+ " }\n"
+ "}\n" : "")
+ "apply plugin: 'com.android.application'\n"
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"\n"
+ "repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ "}\n" : "")
+ "\n"
+ "android {\n"
+ " compileSdkVersion 17\n"
+ " buildToolsVersion \"" + BUILD_TOOLS_VERSION + "\"\n"
+ "\n"
+ " defaultConfig {\n"
+ " applicationId \"test.pkg\"\n"
+ " minSdkVersion 8\n"
+ " targetSdkVersion 16\n"
+ " }\n"
+ "\n"
+ " buildTypes {\n"
+ " release {\n"
+ " minifyEnabled false\n"
+ " proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "dependencies {\n"
+ " compile 'com.android.support:support-v4:18.0.0'\n"
+ " compile 'com.android.support:appcompat-v7:18.0.0'\n"
+ " compile 'com.android.support:gridlayout-v7:18.0.0'\n"
+ "}\n",
Files.toString(new File(imported, "app" + separator + "build.gradle"), UTF_8)
.replace(NL, "\n"));
deleteDir(projectDir);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testOptions() throws Exception {
// Check options like turning off jar replacement and leaving module names capitalized
File projectDir = createProject("Test1", "test.pkg");
File libs = new File(projectDir, "libs");
libs.mkdirs();
new File(libs, "android-support-v4.jar").createNewFile();
new File(libs, "android-support-v7-gridlayout.jar").createNewFile();
new File(libs, "android-support-v7-appcompat.jar").createNewFile();
new File(libs, "armeabi").mkdirs();
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_FOLDER_STRUCTURE
+ "* AndroidManifest.xml => Test1/src/main/AndroidManifest.xml\n"
+ "* libs/android-support-v4.jar => Test1/libs/android-support-v4.jar\n"
+ "* libs/android-support-v7-appcompat.jar => Test1/libs/android-support-v7-appcompat.jar\n"
+ "* libs/android-support-v7-gridlayout.jar => Test1/libs/android-support-v7-gridlayout.jar\n"
+ "* res/ => Test1/src/main/res/\n"
+ "* src/ => Test1/src/main/java/\n"
+ MSG_FOOTER,
false /* checkBuild */,
new ImportCustomizer() {
@Override
public void customize(GradleImport importer) {
importer.setGradleNameStyle(false);
importer.setReplaceJars(false);
importer.setReplaceLibs(false);
}
});
// Imported contents
assertEquals(""
+ "Test1\n"
+ " build.gradle\n"
+ " libs\n"
+ " android-support-v4.jar\n"
+ " android-support-v7-appcompat.jar\n"
+ " android-support-v7-gridlayout.jar\n"
+ " src\n"
+ " main\n"
+ " AndroidManifest.xml\n"
+ " java\n"
+ " test\n"
+ " pkg\n"
+ " MyActivity.java\n"
+ " res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " values\n"
+ " strings.xml\n"
+ "build.gradle\n"
+ "import-summary.txt\n"
+ (getTestSdkPathLocal() != null ? "local.properties\n" : "")
+ "settings.gradle\n",
fileTree(imported, true));
//noinspection PointlessBooleanExpression,ConstantConditions
assertEquals(""
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"buildscript {\n"
+ " repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ " }\n"
+ " dependencies {\n"
+ " classpath '" + ANDROID_GRADLE_PLUGIN + "'\n"
+ " }\n"
+ "}\n" : "")
+ "apply plugin: 'com.android.application'\n"
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"\n"
+ "repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ "}\n" : "")
+ "\n"
+ "android {\n"
+ " compileSdkVersion 17\n"
+ " buildToolsVersion \"" + BUILD_TOOLS_VERSION + "\"\n"
+ "\n"
+ " defaultConfig {\n"
+ " applicationId \"test.pkg\"\n"
+ " minSdkVersion 8\n"
+ " targetSdkVersion 16\n"
+ " }\n"
+ "\n"
+ " buildTypes {\n"
+ " release {\n"
+ " minifyEnabled false\n"
+ " proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "dependencies {\n"
+ " compile files('libs/android-support-v4.jar')\n"
+ " compile files('libs/android-support-v7-appcompat.jar')\n"
+ " compile files('libs/android-support-v7-gridlayout.jar')\n"
+ "}\n",
Files.toString(new File(imported, "Test1" + separator + "build.gradle"), UTF_8)
.replace(NL, "\n"));
deleteDir(projectDir);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testJni() throws Exception {
File root = Files.createTempDir();
final File sdkLocation = new File(root, "sdk");
sdkLocation.mkdirs();
final File ndkLocation = new File(root, "ndk");
ndkLocation.mkdirs();
File projectDir = new File(root, "project");
projectDir.mkdirs();
createProject(projectDir, "testJni", "test.pkg");
createDotProject(projectDir, "testJni", true, true);
File jni = new File(projectDir, "jni");
jni.mkdirs();
File makefile = new File(jni, "Android.mk");
Files.write(""
+ "LOCAL_PATH := $(call my-dir)\n"
+ "\n"
+ "include $(CLEAR_VARS)\n"
+ "\n"
+ "LOCAL_MODULE := hello-jni\n"
+ "LOCAL_SRC_FILES := hello-jni.c\n"
+ "\n"
+ "include $(BUILD_SHARED_LIBRARY)",
makefile, UTF_8);
new File(jni, "Application.mk").createNewFile();
new File(jni, "HelloJni.cpp").createNewFile();
new File(jni, "hello-jni.c").createNewFile();
File libs = new File(projectDir, "libs");
libs.mkdirs();
File armeabi = new File(libs, "armeabi");
armeabi.mkdirs();
new File(armeabi, "libexternal.so").createNewFile();
new File(armeabi, "libhello-jni.so").createNewFile();
File mips = new File(libs, "mips");
mips.mkdirs();
new File(mips, "libexternal.so").createNewFile();
new File(mips, "libhello-jni.so").createNewFile();
Files.write(
escapeProperty("sdk.dir", sdkLocation.getPath()) + "\n" +
escapeProperty("ndk.dir", ndkLocation.getPath()) + "\n",
new File(projectDir, FN_LOCAL_PROPERTIES), UTF_8);
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_FOLDER_STRUCTURE
+ "* AndroidManifest.xml => testJni/src/main/AndroidManifest.xml\n"
+ "* jni/ => testJni/src/main/jni/\n"
+ "* libs/armeabi/libexternal.so => testJni/src/main/jniLibs/armeabi/libexternal.so\n"
+ "* libs/mips/libexternal.so => testJni/src/main/jniLibs/mips/libexternal.so\n"
+ "* res/ => testJni/src/main/res/\n"
+ "* src/ => testJni/src/main/java/\n"
+ MSG_FOOTER,
false /* checkBuild */,
new ImportCustomizer() {
@Override
public void customize(GradleImport importer) {
assertFalse(importer.isImportIntoExisting());
assertFalse(importer.isPerModuleRepositories());
importer.setGradleNameStyle(false);
importer.setSdkLocation(null);
importer.setReplaceJars(false);
importer.setReplaceLibs(false);
}
});
// Imported contents
assertEquals(""
+ "build.gradle\n"
+ "import-summary.txt\n"
+ "local.properties\n"
+ "settings.gradle\n"
+ "testJni\n"
+ " build.gradle\n"
+ " src\n"
+ " main\n"
+ " AndroidManifest.xml\n"
+ " java\n"
+ " test\n"
+ " pkg\n"
+ " MyActivity.java\n"
+ " jni\n"
+ " Android.mk\n"
+ " Application.mk\n"
+ " HelloJni.cpp\n"
+ " hello-jni.c\n"
+ " jniLibs\n"
+ " armeabi\n"
+ " libexternal.so\n"
+ " mips\n"
+ " libexternal.so\n"
+ " res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " values\n"
+ " strings.xml\n",
fileTree(imported, true));
//noinspection PointlessBooleanExpression,ConstantConditions
assertEquals(""
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"buildscript {\n"
+ " repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ " }\n"
+ " dependencies {\n"
+ " classpath '" + ANDROID_GRADLE_PLUGIN + "'\n"
+ " }\n"
+ "}\n" : "")
+ "apply plugin: 'com.android.application'\n"
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"\n"
+ "repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ "}\n" : "")
+ "\n"
+ "android {\n"
+ " compileSdkVersion 17\n"
+ " buildToolsVersion \"" + MIN_BUILD_TOOLS_VERSION + "\"\n"
+ "\n"
+ " defaultConfig {\n"
+ " applicationId \"test.pkg\"\n"
+ " minSdkVersion 8\n"
+ " targetSdkVersion 16\n"
+ "\n"
+ " ndk {\n"
+ " moduleName \"hello-jni\"\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " buildTypes {\n"
+ " release {\n"
+ " minifyEnabled false\n"
+ " proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'\n"
+ " }\n"
+ " }\n"
+ "}\n",
Files.toString(new File(imported, "testJni" + separator + "build.gradle"), UTF_8)
.replace(NL, "\n"));
assertEquals(sdkLocation.getPath(),
PropertiesUtil.getProperties(new File(imported, FN_LOCAL_PROPERTIES)).
getProperty("sdk.dir"));
assertEquals(ndkLocation.getPath(),
PropertiesUtil.getProperties(new File(imported, FN_LOCAL_PROPERTIES)).
getProperty("ndk.dir"));
deleteDir(root);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testJniLibs() throws Exception {
// Check that ABI libs are copied to the right place
File projectDir = createProject("Test1", "test.pkg");
File libs = new File(projectDir, "libs");
libs.mkdirs();
new File(libs, "android-support-v4.jar").createNewFile();
File armeabi = new File(libs, "armeabi");
armeabi.mkdirs();
new File(armeabi, "libfoo.so").createNewFile();
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_FOLDER_STRUCTURE
+ "* AndroidManifest.xml => Test1/src/main/AndroidManifest.xml\n"
+ "* libs/android-support-v4.jar => Test1/libs/android-support-v4.jar\n"
+ "* libs/armeabi/libfoo.so => Test1/src/main/jniLibs/armeabi/libfoo.so\n"
+ "* res/ => Test1/src/main/res/\n"
+ "* src/ => Test1/src/main/java/\n"
+ MSG_FOOTER,
false /* checkBuild */,
new ImportCustomizer() {
@Override
public void customize(GradleImport importer) {
importer.setGradleNameStyle(false);
importer.setReplaceJars(false);
importer.setReplaceLibs(false);
}
});
// Imported contents
assertEquals(""
+ "Test1\n"
+ " build.gradle\n"
+ " libs\n"
+ " android-support-v4.jar\n"
+ " src\n"
+ " main\n"
+ " AndroidManifest.xml\n"
+ " java\n"
+ " test\n"
+ " pkg\n"
+ " MyActivity.java\n"
+ " jniLibs\n"
+ " armeabi\n"
+ " libfoo.so\n"
+ " res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " values\n"
+ " strings.xml\n"
+ "build.gradle\n"
+ "import-summary.txt\n"
+ (getTestSdkPathLocal() != null ? "local.properties\n" : "")
+ "settings.gradle\n",
fileTree(imported, true));
deleteDir(projectDir);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testInstrumentation1() throws Exception {
File root = Files.createTempDir();
File projectDir = new File(root, "project");
projectDir.mkdirs();
createProject(projectDir, "Test2", "test.pkg");
createDotProject(projectDir, "Test2", true, true);
File tests = new File(projectDir, "tests");
tests.mkdirs();
Files.write(""
+ "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ " package=\"my.test.pkg.name\"\n"
+ " android:versionCode=\"1\"\n"
+ " android:versionName=\"1.0\" >\n"
+ "\n"
+ " <instrumentation\n"
+ " android:name=\"android.test.InstrumentationTestRunner\"\n"
+ " android:targetPackage=\"test.pkg\""
+ " android:functionalTest=\"false\"\n"
+ " android:handleProfiling=\"true\" />\n"
+ "\n"
+ " <uses-sdk\n"
+ " android:minSdkVersion=\"7\"\n"
+ " android:targetSdkVersion=\"15\" />\n"
+ "\n"
+ " <application\n"
+ " android:icon=\"@android:drawable/sym_def_app_icon\"\n"
+ " android:label=\"My Unit Test Instrumentation Tests\" >\n"
+ " <uses-library android:name=\"android.test.runner\" />\n"
+ " </application>\n"
+ "\n"
+ "</manifest>",
new File(tests, FN_ANDROID_MANIFEST_XML), UTF_8);
File testSrc = new File(tests, "src");
testSrc.mkdirs();
File testPkg = new File(testSrc, "mytestpkg");
testPkg.mkdirs();
new File(testPkg, "MyUnitTest.java").createNewFile();
File testRes = new File(tests, "res");
testRes.mkdirs();
File testValues = new File(testRes, "values");
testValues.mkdirs();
new File(testValues, "strings.xml").createNewFile();
File testLibs = new File(tests, "libs");
testLibs.mkdirs();
new File(testLibs, "myTestSupportLib.jar").createNewFile();
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_FOLDER_STRUCTURE
+ "* AndroidManifest.xml => Test2/src/main/AndroidManifest.xml\n"
+ "* res/ => Test2/src/main/res/\n"
+ "* src/ => Test2/src/main/java/\n"
+ "* tests/libs/myTestSupportLib.jar => Test2/libs/myTestSupportLib.jar\n"
+ "* tests/res/ => Test2/src/androidTest/res/\n"
+ "* tests/src/ => Test2/src/androidTest/java/\n"
+ MSG_FOOTER,
false /* checkBuild */,
new ImportCustomizer() {
@Override
public void customize(GradleImport importer) {
importer.setGradleNameStyle(false);
}
});
// Imported contents
assertEquals(""
+ "Test2\n"
+ " build.gradle\n"
+ " libs\n"
+ " myTestSupportLib.jar\n"
+ " src\n"
+ " androidTest\n"
+ " java\n"
+ " mytestpkg\n"
+ " MyUnitTest.java\n"
+ " res\n"
+ " values\n"
+ " strings.xml\n"
+ " main\n"
+ " AndroidManifest.xml\n"
+ " java\n"
+ " test\n"
+ " pkg\n"
+ " MyActivity.java\n"
+ " res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " values\n"
+ " strings.xml\n"
+ "build.gradle\n"
+ "import-summary.txt\n"
+ (getTestSdkPathLocal() != null ? "local.properties\n" : "")
+ "settings.gradle\n",
fileTree(imported, true));
//noinspection PointlessBooleanExpression,ConstantConditions
assertEquals(""
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"buildscript {\n"
+ " repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ " }\n"
+ " dependencies {\n"
+ " classpath '" + ANDROID_GRADLE_PLUGIN + "'\n"
+ " }\n"
+ "}\n" : "")
+ "apply plugin: 'com.android.application'\n"
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"\n"
+ "repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ "}\n" : "")
+ "\n"
+ "android {\n"
+ " compileSdkVersion 17\n"
+ " buildToolsVersion \"" + BUILD_TOOLS_VERSION + "\"\n"
+ "\n"
+ " defaultConfig {\n"
+ " applicationId \"test.pkg\"\n"
+ " minSdkVersion 8\n"
+ " targetSdkVersion 16\n"
+ "\n"
+ " testApplicationId \"my.test.pkg.name\"\n"
+ " testInstrumentationRunner \"android.test.InstrumentationTestRunner\"\n"
+ " testFunctionalTest false\n"
+ " testHandlingProfiling true\n"
+ " }\n"
+ "\n"
+ " buildTypes {\n"
+ " release {\n"
+ " minifyEnabled false\n"
+ " proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "dependencies {\n"
+ " androidTestCompile files('libs/myTestSupportLib.jar')\n"
+ "}\n",
Files.toString(new File(imported, "Test2" + separator + "build.gradle"), UTF_8)
.replace(NL, "\n"));
deleteDir(root);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testInstrumentation2() throws Exception {
// Like testInstrumentation1, but the unit test is found in a sibling directory
// (which also means various paths should be relative - ../ etc)
File root = Files.createTempDir();
File projectDir = new File(root, "project");
projectDir.mkdirs();
createProject(projectDir, "Test2", "test.pkg");
createDotProject(projectDir, "Test2", true, true);
File tests = new File(root, "tests");
tests.mkdirs();
Files.write(""
+ "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ " package=\"my.test.pkg.name\"\n"
+ " android:versionCode=\"1\"\n"
+ " android:versionName=\"1.0\" >\n"
+ "\n"
+ " <instrumentation\n"
+ " android:name=\"android.test.InstrumentationTestRunner\"\n"
+ " android:targetPackage=\"test.pkg\""
+ " android:functionalTest=\"false\"\n"
+ " android:handleProfiling=\"true\" />\n"
+ "\n"
+ " <uses-sdk\n"
+ " android:minSdkVersion=\"7\"\n"
+ " android:targetSdkVersion=\"15\" />\n"
+ "\n"
+ " <application\n"
+ " android:icon=\"@android:drawable/sym_def_app_icon\"\n"
+ " android:label=\"My Unit Test Instrumentation Tests\" >\n"
+ " <uses-library android:name=\"android.test.runner\" />\n"
+ " </application>\n"
+ "\n"
+ "</manifest>",
new File(tests, FN_ANDROID_MANIFEST_XML), UTF_8);
File testSrc = new File(tests, "src");
testSrc.mkdirs();
File testPkg = new File(testSrc, "mytestpkg");
testPkg.mkdirs();
new File(testPkg, "MyUnitTest.java").createNewFile();
File testRes = new File(tests, "res");
testRes.mkdirs();
File testValues = new File(testRes, "values");
testValues.mkdirs();
new File(testValues, "strings.xml").createNewFile();
File testLibs = new File(tests, "libs");
testLibs.mkdirs();
new File(testLibs, "myTestSupportLib.jar").createNewFile();
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_FOLDER_STRUCTURE
+ "* AndroidManifest.xml => app/src/main/AndroidManifest.xml\n"
+ "* res/ => app/src/main/res/\n"
+ "* src/ => app/src/main/java/\n"
+ "* $ROOT_PARENT/tests/libs/myTestSupportLib.jar => app/libs/myTestSupportLib.jar\n"
+ "* $ROOT_PARENT/tests/res/ => app/src/androidTest/res/\n"
+ "* $ROOT_PARENT/tests/src/ => app/src/androidTest/java/\n"
+ MSG_FOOTER,
false /* checkBuild */,
new ImportCustomizer() {
@Override
public void customize(GradleImport importer) {
}
});
deleteDir(root);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testReplaceSourceLibraryProject() throws Exception {
// Make a library project which looks like it can just be replaced by a project
File root = Files.createTempDir();
// Pretend lib2 is ActionBarSherlock; it should then be stripped out and replaced
// by a set of dependencies
File app = createLibrary(root, "com.actionbarsherlock", true);
File imported = checkProject(app, "" +
MSG_HEADER +
MSG_MANIFEST +
MSG_UNHANDLED +
"From App:\n" +
"* .gitignore\n" +
"From JavaLib:\n" +
"* .gitignore\n" +
MSG_REPLACED_LIBS +
"Lib2 =>\n" +
" com.actionbarsherlock:actionbarsherlock:4.4.0@aar\n" +
" com.android.support:support-v4:18.0.0\n" +
MSG_FOLDER_STRUCTURE
// TODO: The summary should describe the library!!
+
"In JavaLib:\n" +
"* src/ => javaLib/src/main/java/\n" +
"In Lib1:\n" +
"* AndroidManifest.xml => lib1/src/main/AndroidManifest.xml\n" +
"* src/ => lib1/src/main/java/\n" +
"In App:\n" +
"* AndroidManifest.xml => app/src/main/AndroidManifest.xml\n" +
"* res/ => app/src/main/res/\n" +
"* src/ => app/src/main/java/\n" +
(getTestSdkPathLocal() == null ? MSG_MISSING_REPO_1 + "null\n" + MSG_MISSING_REPO_2 : "") +
MSG_FOOTER, false /* checkBuild */);
// Imported project; note how lib2 is gone
assertEquals(""
+ "app\n"
+ " build.gradle\n"
+ " src\n"
+ " main\n"
+ " AndroidManifest.xml\n"
+ " java\n"
+ " test\n"
+ " pkg\n"
+ " MyActivity.java\n"
+ " res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " values\n"
+ " strings.xml\n"
+ "build.gradle\n"
+ "import-summary.txt\n"
+ "javaLib\n"
+ " build.gradle\n"
+ " src\n"
+ " main\n"
+ " java\n"
+ " test\n"
+ " lib2\n"
+ " pkg\n"
+ " Utilities.java\n"
+ "lib1\n"
+ " build.gradle\n"
+ " src\n"
+ " main\n"
+ " AndroidManifest.xml\n"
+ " java\n"
+ " test\n"
+ " lib\n"
+ " pkg\n"
+ " MyLibActivity.java\n"
+ (getTestSdkPathLocal() != null ? "local.properties\n" : "")
+ "settings.gradle\n",
fileTree(imported, true));
//noinspection PointlessBooleanExpression,ConstantConditions
assertEquals(""
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"buildscript {\n"
+ " repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ " }\n"
+ " dependencies {\n"
+ " classpath '" + ANDROID_GRADLE_PLUGIN + "'\n"
+ " }\n"
+ "}\n" : "")
+ "apply plugin: 'com.android.application'\n"
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"\n"
+ "repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ "}\n" : "")
+ "\n"
+ "android {\n"
+ " compileSdkVersion 17\n"
+ " buildToolsVersion \"" + BUILD_TOOLS_VERSION + "\"\n"
+ "\n"
+ " defaultConfig {\n"
+ " applicationId \"test.pkg\"\n"
+ " minSdkVersion 8\n"
+ " targetSdkVersion 16\n"
+ " }\n"
+ "\n"
+ " buildTypes {\n"
+ " release {\n"
+ " minifyEnabled false\n"
+ " proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "dependencies {\n"
+ " compile project(':lib1')\n"
+ " compile project(':javaLib')\n"
+ " compile 'com.actionbarsherlock:actionbarsherlock:4.4.0@aar'\n"
+ " compile 'com.android.support:support-v4:18.0.0'\n"
+ "}\n",
Files.toString(new File(imported, "app" + separator + "build.gradle"), UTF_8)
.replace(NL, "\n"));
deleteDir(root);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testMissingRepositories() throws Exception {
File root = Files.createTempDir();
final File sdkLocation = new File(root, "sdk");
sdkLocation.mkdirs();
File projectDir = new File(root, "project");
projectDir.mkdirs();
createProject(projectDir, "test1", "test.pkg");
File libs = new File(projectDir, "libs");
libs.mkdirs();
new File(libs, "android-support-v4.jar").createNewFile();
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_REPLACED_JARS
+ "android-support-v4.jar => com.android.support:support-v4:18.+\n"
+ MSG_FOLDER_STRUCTURE
+ DEFAULT_MOVED
+ MSG_MISSING_REPO_1
+ "$ROOT_PARENT/sdk\n"
+ MSG_MISSING_REPO_2
+ MSG_FOOTER,
false /* checkBuild */, new ImportCustomizer() {
@Override
public void customize(GradleImport importer) {
importer.setSdkLocation(sdkLocation);
}
});
deleteDir(root);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testMissingPlayRepositories() throws Exception {
File root = Files.createTempDir();
final File sdkLocation = new File(root, "sdk");
sdkLocation.mkdirs();
File projectDir = new File(root, "project");
projectDir.mkdirs();
createProject(projectDir, "test1", "test.pkg");
File libs = new File(projectDir, "libs");
libs.mkdirs();
new File(libs, "gcm.jar").createNewFile();
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_REPLACED_JARS
+ "gcm.jar => com.google.android.gms:play-services:+\n"
+ MSG_FOLDER_STRUCTURE
+ DEFAULT_MOVED
+ MSG_MISSING_GOOGLE_REPOSITORY_1
+ "$ROOT_PARENT/sdk\n"
+ MSG_MISSING_GOOGLE_REPOSITORY_2
+ MSG_FOOTER,
false /* checkBuild */, new ImportCustomizer() {
@Override
public void customize(GradleImport importer) {
importer.setSdkLocation(sdkLocation);
}
});
// Imported project: confirm that gcm.jar is not in the output tree
assertEquals(""
+ "app\n"
+ " build.gradle\n"
+ " src\n"
+ " main\n"
+ " AndroidManifest.xml\n"
+ " java\n"
+ " test\n"
+ " pkg\n"
+ " MyActivity.java\n"
+ " res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " values\n"
+ " strings.xml\n"
+ "build.gradle\n"
+ "import-summary.txt\n"
+ "local.properties\n"
+ "settings.gradle\n",
fileTree(imported, true));
deleteDir(root);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testGuessedVersion() throws Exception {
File root = Files.createTempDir();
final File sdkLocation = new File(root, "sdk");
sdkLocation.mkdirs();
File projectDir = new File(root, "project");
projectDir.mkdirs();
createProject(projectDir, "test1", "test.pkg");
File libs = new File(projectDir, "libs");
libs.mkdirs();
new File(libs, "guava-13.0.1.jar").createNewFile();
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_REPLACED_JARS
+ "guava-13.0.1.jar => com.google.guava:guava:13.0.1\n"
+ MSG_GUESSED_VERSIONS
+ "guava-13.0.1.jar => version 13.0.1 in com.google.guava:guava:13.0.1\n"
+ MSG_FOLDER_STRUCTURE
+ DEFAULT_MOVED
+ MSG_FOOTER,
false /* checkBuild */, new ImportCustomizer() {
@Override
public void customize(GradleImport importer) {
importer.setSdkLocation(sdkLocation);
}
});
deleteDir(root);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testClassPathFilePaths() throws Exception {
// Test a project where the .classpath file contains additional
// issues: workspace-local dependencies for projects,
// absolute paths to the framework, etc.
File root = Files.createTempDir();
File projectDir = new File(root, "prj");
projectDir.mkdirs();
projectDir = createProject(projectDir, "1 Weird 'name' of project!", "test.pkg");
File lib = new File(root, "android-support-v7-appcompat");
lib.mkdirs();
File classpath = new File(projectDir, ".classpath");
assertTrue(classpath.exists());
classpath.delete();
//noinspection SpellCheckingInspection
Files.write(""
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<classpath>\n"
+ "\t<classpathentry kind=\"src\" path=\"src\"/>\n"
+ "\t<classpathentry kind=\"src\" path=\"gen\"/>\n"
+ "\t<classpathentry kind=\"con\" path=\"com.android.ide.eclipse.adt.ANDROID_FRAMEWORK\"/>\n"
+ "\t<classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.DEPENDENCIES\"/>\n"
+ "\t<classpathentry kind=\"lib\" path=\"libs/basic-http-client-android-0.88.jar\"/>\n"
+ "\t<classpathentry kind=\"lib\" path=\"/opt/android-sdk/platforms/android-14/android.jar\">\n"
+ "\t\t<attributes>\n"
+ "\t\t\t<attribute name=\"javadoc_location\" value=\"file:/opt/android-sdk/docs/reference\"/>\n"
+ "\t\t</attributes>\n"
+ "\t\t<accessrules>\n"
+ "\t\t\t<accessrule kind=\"nonaccessible\" pattern=\"com/android/internal/**\"/>\n"
+ "\t\t</accessrules>\n"
+ "\t</classpathentry>\n"
+ "\t<classpathentry kind=\"lib\" path=\"libs/htmlcleaner-2.6.jar\"/>\n"
+ "\t<classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.LIBRARIES\"/>\n"
+ "\t<classpathentry combineaccessrules=\"false\" kind=\"src\" path=\"/android-support-v7-appcompat\"/>\n"
+ "\t<classpathentry kind=\"output\" path=\"bin/classes\"/>\n"
+ "</classpath>",
classpath, UTF_8);
//noinspection SpellCheckingInspection
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_FOLDER_STRUCTURE
+ "* $ROOT_PARENT/android-support-v7-appcompat/ => _1Weirdnameofproject/src/main/java/\n"
+ "* AndroidManifest.xml => _1Weirdnameofproject/src/main/AndroidManifest.xml\n"
+ "* res/ => _1Weirdnameofproject/src/main/res/\n"
+ "* src/ => _1Weirdnameofproject/src/main/java/\n"
+ MSG_FOOTER,
false /* checkBuild */, new ImportCustomizer() {
@Override
public void customize(GradleImport importer) {
importer.setGradleNameStyle(false);
}
});
deleteDir(projectDir);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
private static Pair<File,File> createLibrary2(File library1Dir) throws Exception {
File root = Files.createTempDir();
// Workspace Setup
// /Library1, compiled with 1.7, and depends on an external jar outside the project (guava)
// /Library2 (depends on /Library1)
// /AndroidLibraryProject (depend on /Library1, /Library2)
// /AndroidAppProject (depends on /AndroidLibraryProject)
// In addition to make things complicated, /Library1 can live outside the workspace
// (based on the path we pass in)
// and /Library2 lives in a subdirectory of the workspace
// Plain Java library, used by Library 1 and App
// Make Java Library library 1
String lib1Name = "Library1";
File lib1 = library1Dir.isAbsolute() ? library1Dir :
new File(root, library1Dir.getPath());
lib1.mkdirs();
String lib1Pkg = "test.lib1.pkg";
createDotProject(lib1, lib1Name, false);
createSampleJavaSource(lib1, "src", lib1Pkg, "Library1");
File guavaPath = new File(root, "some" + separator + "path" + separator +
"guava-13.0.1.jar");
guavaPath.getParentFile().mkdirs();
guavaPath.createNewFile();
Files.write(""
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<classpath>\n"
+ "\t<classpathentry kind=\"src\" path=\"src\"/>\n"
+ "\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/Java 7\"/>\n"
+ "\t<classpathentry exported=\"true\" kind=\"lib\" path=\"" + guavaPath.getAbsoluteFile().getCanonicalFile().getPath() + "\"/>\n"
+ "\t<classpathentry kind=\"output\" path=\"bin\"/>\n"
+ "</classpath>",
new File(lib1, ".classpath"), UTF_8);
createEclipseSettingsFile(lib1, "1.6");
// Make Java Library 2
String lib2Name = "Library2";
File lib2 = new File(root, lib2Name);
lib2.mkdirs();
createDotProject(lib2, lib2Name, false);
String lib2Pkg = "test.lib2.pkg";
createSampleJavaSource(lib2, "src", lib2Pkg, "Library2");
Files.write(""
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<classpath>\n"
+ "\t<classpathentry kind=\"src\" path=\"src\"/>\n"
+ "\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/Java 7\"/>\n"
+ "\t<classpathentry combineaccessrules=\"false\" kind=\"src\" path=\"/Library1\"/>\n"
+ "\t<classpathentry kind=\"output\" path=\"bin\"/>\n"
+ "</classpath>",
new File(lib2, ".classpath"), UTF_8);
createEclipseSettingsFile(lib2, "1.7");
// Make Android Library Project 1
String androidLibName = "AndroidLibrary";
File androidLib = new File(root, androidLibName);
androidLib.mkdirs();
createDotProject(androidLib, androidLibName, true);
String androidLibPkg = "test.android.lib.pkg";
createSampleJavaSource(androidLib, "src", androidLibPkg, "AndroidLibrary");
createSampleJavaSource(androidLib, "gen", androidLibPkg, "R");
Files.write(""
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<classpath>\n"
+ "\t<classpathentry kind=\"src\" path=\"src\"/>\n"
+ "\t<classpathentry kind=\"src\" path=\"gen\"/>\n"
+ "\t<classpathentry kind=\"con\" path=\"com.android.ide.eclipse.adt.ANDROID_FRAMEWORK\"/>\n"
+ "\t<classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.LIBRARIES\"/>\n"
+ "\t<classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.DEPENDENCIES\"/>\n"
+ "\t<classpathentry combineaccessrules=\"false\" exported=\"true\" kind=\"src\" path=\"/Library1\"/>\n"
+ "\t<classpathentry combineaccessrules=\"false\" exported=\"true\" kind=\"src\" path=\"/Library2\"/>\n"
+ "\t<classpathentry kind=\"output\" path=\"bin/classes\"/>\n"
+ "</classpath>", new File(androidLib, ".classpath"), UTF_8);
createProjectProperties(androidLib, "android-18", null, true, null,
// Note how Android library projects don't point to non-Android projects
// in the project.properties file; only via the .classpath file!
Collections.<File>emptyList());
createAndroidManifest(androidLib, androidLibPkg, 7, -1, "");
// Main app project, depends on library project
String appName = "AndroidApp";
File app = new File(root, appName);
app.mkdirs();
String appPkg = "test.pkg";
createDotProject(app, appName, true);
File appSrc = new File("src");
File appGen = new File("gen");
createSampleJavaSource(app, "src", appPkg, "AppActivity");
createSampleJavaSource(app, "gen", appPkg, "R");
createClassPath(app,
new File("bin", "classes"),
Arrays.asList(appSrc, appGen),
Collections.<File>emptyList());
createProjectProperties(app, "android-17", null, null, null,
Collections.singletonList(new File(".." + separator + androidLibName)));
createAndroidManifest(app, appPkg, 8, 16, null);
createDefaultStrings(app);
createDefaultIcon(app);
// Add some files in there that we are ignoring
new File(app, ".gitignore").createNewFile();
return Pair.of(root, app);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testLibraries2() throws Exception {
Pair<File,File> pair = createLibrary2(new File("Library1"));
File root = pair.getFirst();
File app = pair.getSecond();
// ADT Directory structure created by the above:
assertEquals(""
+ "AndroidApp\n"
+ " .classpath\n"
+ " .gitignore\n"
+ " .project\n"
+ " AndroidManifest.xml\n"
+ " gen\n"
+ " test\n"
+ " pkg\n"
+ " R.java\n"
+ " project.properties\n"
+ " res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " values\n"
+ " strings.xml\n"
+ " src\n"
+ " test\n"
+ " pkg\n"
+ " AppActivity.java\n"
+ "AndroidLibrary\n"
+ " .classpath\n"
+ " .project\n"
+ " AndroidManifest.xml\n"
+ " gen\n"
+ " test\n"
+ " android\n"
+ " lib\n"
+ " pkg\n"
+ " R.java\n"
+ " project.properties\n"
+ " src\n"
+ " test\n"
+ " android\n"
+ " lib\n"
+ " pkg\n"
+ " AndroidLibrary.java\n"
+ "Library1\n"
+ " .classpath\n"
+ " .project\n"
+ " .settings\n"
+ " org.eclipse.jdt.core.prefs\n"
+ " src\n"
+ " test\n"
+ " lib1\n"
+ " pkg\n"
+ " Library1.java\n"
+ "Library2\n"
+ " .classpath\n"
+ " .project\n"
+ " .settings\n"
+ " org.eclipse.jdt.core.prefs\n"
+ " src\n"
+ " test\n"
+ " lib2\n"
+ " pkg\n"
+ " Library2.java\n"
+ "some\n"
+ " path\n"
+ " guava-13.0.1.jar\n",
fileTree(root, true));
final AtomicReference<GradleImport> importReference = new AtomicReference<GradleImport>();
File imported = checkProject(app,
""
+ MSG_HEADER
+ MSG_MANIFEST
+ MSG_UNHANDLED
+ "* .gitignore\n"
+ MSG_REPLACED_JARS
+ "guava-13.0.1.jar => com.google.guava:guava:13.0.1\n"
+ MSG_GUESSED_VERSIONS
+ "guava-13.0.1.jar => version 13.0.1 in com.google.guava:guava:13.0.1\n"
+ MSG_FOLDER_STRUCTURE
+ "In Library1:\n"
+ "* src/ => library1/src/main/java/\n"
+ "In Library2:\n"
+ "* src/ => library2/src/main/java/\n"
+ "In AndroidLibrary:\n"
+ "* AndroidManifest.xml => androidLibrary/src/main/AndroidManifest.xml\n"
+ "* src/ => androidLibrary/src/main/java/\n"
+ "In AndroidApp:\n"
+ "* AndroidManifest.xml => androidApp/src/main/AndroidManifest.xml\n"
+ "* res/ => androidApp/src/main/res/\n"
+ "* src/ => androidApp/src/main/java/\n"
+ MSG_FOOTER,
false /* checkBuild */, new ImportCustomizer() {
@Override
public void customize(GradleImport importer) {
importReference.set(importer);
}
});
assertEquals("{/Library1=" + new File(root, "Library1").getCanonicalPath() +
", /Library2=" + new File(root, "Library2").getCanonicalPath() +"}",
describePathMap(importReference.get()));
// Imported project
assertEquals(""
+ "androidApp\n"
+ " build.gradle\n"
+ " src\n"
+ " main\n"
+ " AndroidManifest.xml\n"
+ " java\n"
+ " test\n"
+ " pkg\n"
+ " AppActivity.java\n"
+ " res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " values\n"
+ " strings.xml\n"
+ "androidLibrary\n"
+ " build.gradle\n"
+ " src\n"
+ " main\n"
+ " AndroidManifest.xml\n"
+ " java\n"
+ " test\n"
+ " android\n"
+ " lib\n"
+ " pkg\n"
+ " AndroidLibrary.java\n"
+ "build.gradle\n"
+ "import-summary.txt\n"
+ "library1\n"
+ " build.gradle\n"
+ " src\n"
+ " main\n"
+ " java\n"
+ " test\n"
+ " lib1\n"
+ " pkg\n"
+ " Library1.java\n"
+ "library2\n"
+ " build.gradle\n"
+ " src\n"
+ " main\n"
+ " java\n"
+ " test\n"
+ " lib2\n"
+ " pkg\n"
+ " Library2.java\n"
+ (getTestSdkPathLocal() != null ? "local.properties\n" : "")
+ "settings.gradle\n",
fileTree(imported, true));
// Let's peek at some of the key files to make sure we codegen'ed the right thing
//noinspection PointlessBooleanExpression,ConstantConditions
assertEquals(""
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"buildscript {\n"
+ " repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ " }\n"
+ "}\n" : "")
+ "apply plugin: 'java'\n"
+ "\n"
+ "dependencies {\n"
+ " compile 'com.google.guava:guava:13.0.1'\n"
+ "}\n",
Files.toString(new File(imported, "library1" + separator + "build.gradle"), UTF_8)
.replace(NL, "\n"));
//noinspection PointlessBooleanExpression,ConstantConditions
assertEquals(""
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"buildscript {\n"
+ " repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ " }\n"
+ " dependencies {\n"
+ " classpath '" + ANDROID_GRADLE_PLUGIN + "'\n"
+ " }\n"
+ "}\n" : "")
+ "apply plugin: 'com.android.application'\n"
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"\n"
+ "repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ "}\n" : "")
+ "\n"
+ "android {\n"
+ " compileSdkVersion 17\n"
+ " buildToolsVersion \"" + BUILD_TOOLS_VERSION + "\"\n"
+ "\n"
+ " defaultConfig {\n"
+ " applicationId \"test.pkg\"\n"
+ " minSdkVersion 8\n"
+ " targetSdkVersion 16\n"
+ " }\n"
+ "\n"
+ " buildTypes {\n"
+ " release {\n"
+ " minifyEnabled false\n"
+ " proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "dependencies {\n"
+ " compile project(':androidLibrary')\n"
+ "}\n",
Files.toString(new File(imported, "androidApp" + separator + "build.gradle"), UTF_8)
.replace(NL,"\n"));
//noinspection PointlessBooleanExpression,ConstantConditions
assertEquals(""
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"buildscript {\n"
+ " repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ " }\n"
+ " dependencies {\n"
+ " classpath '" + ANDROID_GRADLE_PLUGIN + "'\n"
+ " }\n"
+ "}\n" : "")
+ "apply plugin: 'com.android.library'\n"
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"\n"
+ "repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ "}\n" : "")
+ "\n"
+ "android {\n"
+ " compileSdkVersion 18\n"
+ " buildToolsVersion \"" + BUILD_TOOLS_VERSION + "\"\n"
+ "\n"
+ " defaultConfig {\n"
+ " minSdkVersion 8\n"
+ " targetSdkVersion 8\n"
+ " }\n"
+ "\n"
+ " buildTypes {\n"
+ " release {\n"
+ " minifyEnabled false\n"
+ " proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "dependencies {\n"
+ " compile project(':library1')\n"
+ " compile project(':library2')\n"
+ "}\n",
Files.toString(new File(imported, "androidLibrary" + separator + "build.gradle"), UTF_8)
.replace(NL,"\n"));
//noinspection PointlessBooleanExpression,ConstantConditions
assertEquals(""
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"buildscript {\n"
+ " repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ " }\n"
+ "}\n" : "")
+ "apply plugin: 'java'\n"
+ "\n"
+ "dependencies {\n"
+ " compile 'com.google.guava:guava:13.0.1'\n"
+ "}\n",
Files.toString(new File(imported, "library1" + separator + "build.gradle"), UTF_8)
.replace(NL, "\n"));
//noinspection PointlessBooleanExpression,ConstantConditions
assertEquals(""
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"buildscript {\n"
+ " repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ " }\n"
+ "}\n" : "")
+ "apply plugin: 'java'\n"
+ "\n"
+ "sourceCompatibility = \"1.7\"\n"
+ "targetCompatibility = \"1.7\"\n"
+ "\n"
+ "dependencies {\n"
+ " compile project(':library1')\n"
+ "}\n",
Files.toString(new File(imported, "library2" + separator + "build.gradle"), UTF_8)
.replace(NL, "\n"));
// TODO: Should this ONLY include the root module?
assertEquals(""
+ "include ':library1'\n"
+ "include ':library2'\n"
+ "include ':androidLibrary'\n"
+ "include ':androidApp'\n",
Files.toString(new File(imported, "settings.gradle"), UTF_8)
.replace(NL, "\n"));
//noinspection ConstantConditions
assertEquals(""
+ "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n"
+ (DECLARE_GLOBAL_REPOSITORIES ?
"buildscript {\n"
+ " repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ " }\n"
+ " dependencies {\n"
+ " classpath '" + ANDROID_GRADLE_PLUGIN + "'\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "allprojects {\n"
+ " repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ " }\n"
+ "}\n" : ""),
Files.toString(new File(imported, "build.gradle"), UTF_8)
.replace(NL, "\n"));
deleteDir(root);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testLibrariesWithWorkspaceMapping1() throws Exception {
// Provide manually edited workspace mapping /Library1 = actual dir
final String library1Path = "subdir1" + separator + "subdir2" + separator +
"UnrelatedName";
final File library1Dir = new File(library1Path);
Pair<File,File> pair = createLibrary2(library1Dir);
final File root = pair.getFirst();
File app = pair.getSecond();
final AtomicReference<GradleImport> importReference = new AtomicReference<GradleImport>();
File imported = checkProject(app,
""
+ MSG_HEADER
+ MSG_MANIFEST
+ MSG_UNHANDLED
+ "* .gitignore\n"
+ MSG_REPLACED_JARS
+ "guava-13.0.1.jar => com.google.guava:guava:13.0.1\n"
+ MSG_GUESSED_VERSIONS
+ "guava-13.0.1.jar => version 13.0.1 in com.google.guava:guava:13.0.1\n"
+ MSG_FOLDER_STRUCTURE
+ "In Library1:\n"
+ "* src/ => library1/src/main/java/\n"
+ "In Library2:\n"
+ "* src/ => library2/src/main/java/\n"
+ "In AndroidLibrary:\n"
+ "* AndroidManifest.xml => androidLibrary/src/main/AndroidManifest.xml\n"
+ "* src/ => androidLibrary/src/main/java/\n"
+ "In AndroidApp:\n"
+ "* AndroidManifest.xml => androidApp/src/main/AndroidManifest.xml\n"
+ "* res/ => androidApp/src/main/res/\n"
+ "* src/ => androidApp/src/main/java/\n"
+ MSG_FOOTER,
false /* checkBuild */, new ImportCustomizer() {
@Override
public void customize(GradleImport importer) {
importReference.set(importer);
importer.getPathMap().put("/Library1", new File(root, library1Path));
}
});
assertEquals("{/Library1=" + new File(root, library1Path).getCanonicalPath()
+ ", /Library2=" + new File(root, "Library2").getCanonicalPath() + "}",
describePathMap(importReference.get()));
deleteDir(root);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void test65167() throws Exception {
// Regression test for https://code.google.com/p/android/issues/detail?id=65167
Pair<File,File> pair = createLibrary2(new File("Library1"));
File root = pair.getFirst();
File app = pair.getSecond();
File libs = new File(app, "libs");
libs.mkdirs();
new File(libs, "unknown-lib.jar").createNewFile();
final AtomicReference<GradleImport> importReference = new AtomicReference<GradleImport>();
File imported = checkProject(app,
""
+ MSG_HEADER
+ MSG_MANIFEST
+ MSG_UNHANDLED
+ "* .gitignore\n"
+ MSG_REPLACED_JARS
+ "guava-13.0.1.jar => com.google.guava:guava:13.0.1\n"
+ MSG_GUESSED_VERSIONS
+ "guava-13.0.1.jar => version 13.0.1 in com.google.guava:guava:13.0.1\n"
+ MSG_FOLDER_STRUCTURE
+ "In Library1:\n"
+ "* src/ => library1/src/main/java/\n"
+ "In Library2:\n"
+ "* src/ => library2/src/main/java/\n"
+ "In AndroidLibrary:\n"
+ "* AndroidManifest.xml => androidLibrary/src/main/AndroidManifest.xml\n"
+ "* src/ => androidLibrary/src/main/java/\n"
+ "In AndroidApp:\n"
+ "* AndroidManifest.xml => androidApp/src/main/AndroidManifest.xml\n"
+ "* libs/unknown-lib.jar => androidApp/libs/unknown-lib.jar\n"
+ "* res/ => androidApp/src/main/res/\n"
+ "* src/ => androidApp/src/main/java/\n"
+ MSG_FOOTER,
false /* checkBuild */, new ImportCustomizer() {
@Override
public void customize(GradleImport importer) {
importReference.set(importer);
}
});
deleteDir(root);
deleteDir(imported);
}
@SuppressWarnings({"ResultOfMethodCallIgnored", "SpellCheckingInspection"})
public void testImportModule() throws Exception {
// Create a Gradle project that we'll be importing a new module into
File projectDir = createProject("test1", "test.pkg");
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_FOLDER_STRUCTURE
+ DEFAULT_MOVED
+ MSG_FOOTER,
false /* checkBuild */);
// Pre-module import state of the Gradle project:
assertEquals(""
+ "app\n"
+ " build.gradle\n"
+ " src\n"
+ " main\n"
+ " AndroidManifest.xml\n"
+ " java\n"
+ " test\n"
+ " pkg\n"
+ " MyActivity.java\n"
+ " res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " values\n"
+ " strings.xml\n"
+ "build.gradle\n"
+ "import-summary.txt\n"
+ (getTestSdkPathLocal() != null ? "local.properties\n" : "")
+ "settings.gradle\n",
fileTree(imported, true));
// Now create a second project, and import that as a *module*
File moduleDir = createProject("test2", "test.my.pkg");
File destDir = new File(imported, "newmodule");
destDir.mkdirs();
checkImport(imported, moduleDir,
""
+ MSG_HEADER
+ MSG_FOLDER_STRUCTURE
+ "* AndroidManifest.xml => newmodule/src/main/AndroidManifest.xml\n"
+ "* res/ => newmodule/src/main/res/\n"
+ "* src/ => newmodule/src/main/java/\n"
+ MSG_FOOTER,
true /* checkBuild */, null, destDir, imported);
// Imported contents
assertEquals(""
+ "build.gradle\n"
+ "src\n"
+ " main\n"
+ " AndroidManifest.xml\n"
+ " java\n"
+ " test\n"
+ " my\n"
+ " pkg\n"
+ " MyActivity.java\n"
+ " res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " values\n"
+ " strings.xml\n",
fileTree(destDir, true));
// Check that it's in the right place:
assertEquals(""
+ "app\n"
+ " build.gradle\n"
+ " src\n"
+ " main\n"
+ " AndroidManifest.xml\n"
+ " java\n"
+ " test\n"
+ " pkg\n"
+ " MyActivity.java\n"
+ " res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " values\n"
+ " strings.xml\n"
+ "build.gradle\n"
+ "import-summary.txt\n"
+ (getTestSdkPathLocal() != null ? "local.properties\n" : "")
+ "newmodule\n"
+ " build.gradle\n"
+ " src\n"
+ " main\n"
+ " AndroidManifest.xml\n"
+ " java\n"
+ " test\n"
+ " my\n"
+ " pkg\n"
+ " MyActivity.java\n"
+ " res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " values\n"
+ " strings.xml\n"
+ "settings.gradle\n",
fileTree(imported, true));
// Make sure that settings.gradle did the right thing
assertEquals(""
+ "include ':app'\n"
+ "include ':test2'\n",
Files.toString(new File(imported, "settings.gradle"), UTF_8)
.replace(NL, "\n"));
deleteDir(moduleDir);
deleteDir(projectDir);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testModuleNamesFromDir() throws Exception {
// Regression test for issue where there is no .project file and
// the project name has to be inferred from the directory name instead
Pair<File,File> pair = createLibrary2(new File("Library1"));
File root = pair.getFirst();
File app = pair.getSecond();
File propertyFile = new File(app, ".project");
propertyFile.delete();
final AtomicReference<GradleImport> importReference = new AtomicReference<GradleImport>();
File imported = checkProject(app,
""
+ MSG_HEADER
+ MSG_MANIFEST
+ MSG_UNHANDLED
+ "* .gitignore\n"
+ MSG_REPLACED_JARS
+ "guava-13.0.1.jar => com.google.guava:guava:13.0.1\n"
+ MSG_GUESSED_VERSIONS
+ "guava-13.0.1.jar => version 13.0.1 in com.google.guava:guava:13.0.1\n"
+ MSG_FOLDER_STRUCTURE
+ "In Library1:\n"
+ "* src/ => library1/src/main/java/\n"
+ "In Library2:\n"
+ "* src/ => library2/src/main/java/\n"
+ "In AndroidLibrary:\n"
+ "* AndroidManifest.xml => androidLibrary/src/main/AndroidManifest.xml\n"
+ "* src/ => androidLibrary/src/main/java/\n"
+ "In AndroidApp:\n"
+ "* AndroidManifest.xml => androidApp/src/main/AndroidManifest.xml\n"
+ "* res/ => androidApp/src/main/res/\n"
+ "* src/ => androidApp/src/main/java/\n"
+ MSG_FOOTER,
false /* checkBuild */, new ImportCustomizer() {
@Override
public void customize(GradleImport importer) {
importReference.set(importer);
}
});
deleteDir(root);
deleteDir(imported);
}
private static String describePathMap(GradleImport importer) throws IOException {
Map<String, File> map = importer.getPathMap();
List<String> keys = Lists.newArrayList(map.keySet());
Collections.sort(keys);
StringBuilder sb = new StringBuilder();
sb.append("{");
boolean first = true;
for (String key : keys) {
File file = map.get(key);
if (file != null) {
file = file.getCanonicalFile();
}
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append(key);
sb.append("=");
sb.append(file);
}
sb.append("}");
return sb.toString();
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testLibrariesWithWorkspaceMapping2() throws Exception {
// Provide manually edited workspace location; importer reads workspace data
// to find it
final String library1Path = "subdir1" + separator + "subdir2" + separator +
"UnrelatedName";
final File library1Dir = new File(library1Path);
Pair<File,File> pair = createLibrary2(library1Dir);
final File root = pair.getFirst();
File app = pair.getSecond();
final File library1AbsDir = new File(root, library1Path);
final File workspace = new File(root, "workspace");
workspace.mkdirs();
File metadata = new File(workspace, ".metadata");
metadata.mkdirs();
new File(metadata, "version.ini").createNewFile();
assertTrue(GradleImport.isEclipseWorkspaceDir(workspace));
File projects = new File(metadata, ".plugins" + separator + "org.eclipse.core.resources" +
separator + ".projects");
projects.mkdirs();
File library1 = new File(projects, "Library1");
library1.mkdirs();
File location = new File(library1, ".location");
byte[] data = ("blahblahblahURI//" + SdkUtils.fileToUrl(library1AbsDir) +
"\000blahblahblah").getBytes(Charsets.UTF_8);
Files.write(data, location);
final AtomicReference<GradleImport> importReference = new AtomicReference<GradleImport>();
File imported = checkProject(app,
""
+ MSG_HEADER
+ MSG_MANIFEST
+ MSG_UNHANDLED
+ "* .gitignore\n"
+ MSG_REPLACED_JARS
+ "guava-13.0.1.jar => com.google.guava:guava:13.0.1\n"
+ MSG_GUESSED_VERSIONS
+ "guava-13.0.1.jar => version 13.0.1 in com.google.guava:guava:13.0.1\n"
+ MSG_FOLDER_STRUCTURE
+ "In Library1:\n"
+ "* src/ => library1/src/main/java/\n"
+ "In Library2:\n"
+ "* src/ => library2/src/main/java/\n"
+ "In AndroidLibrary:\n"
+ "* AndroidManifest.xml => androidLibrary/src/main/AndroidManifest.xml\n"
+ "* src/ => androidLibrary/src/main/java/\n"
+ "In AndroidApp:\n"
+ "* AndroidManifest.xml => androidApp/src/main/AndroidManifest.xml\n"
+ "* res/ => androidApp/src/main/res/\n"
+ "* src/ => androidApp/src/main/java/\n"
+ MSG_FOOTER,
false /* checkBuild */, new ImportCustomizer() {
@Override
public void customize(GradleImport importer) {
importReference.set(importer);
importer.setEclipseWorkspace(workspace);
}
});
assertEquals("{/Library1=" + new File(root, library1Path).getCanonicalPath()
+ ", /Library2=" + new File(root, "Library2").getCanonicalPath() + "}",
describePathMap(importReference.get()));
deleteDir(root);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testLibrariesWithWorkspacePathVars() throws Exception {
// Provide manually edited workspace location which contains workspace locations
final String library1Path = "subdir1" + separator + "subdir2" + separator +
"UnrelatedName";
final File library1Dir = new File(library1Path);
Pair<File,File> pair = createLibrary2(library1Dir);
final File root = pair.getFirst();
File app = pair.getSecond();
final File library1AbsDir = new File(root, library1Path);
final File workspace = new File(root, "workspace");
workspace.mkdirs();
File metadata = new File(workspace, ".metadata");
metadata.mkdirs();
new File(metadata, "version.ini").createNewFile();
assertTrue(GradleImport.isEclipseWorkspaceDir(workspace));
File projects = new File(metadata, ".plugins" + separator + "org.eclipse.core.resources" +
separator + ".projects");
projects.mkdirs();
File library1 = new File(projects, "Library1");
library1.mkdirs();
File location = new File(library1, ".location");
byte[] data = ("blahblahblahURI//" + SdkUtils.fileToUrl(library1AbsDir) +
"\000blahblahblah").getBytes(Charsets.UTF_8);
Files.write(data, location);
final AtomicReference<GradleImport> importReference = new AtomicReference<GradleImport>();
File imported = checkProject(app,
""
+ MSG_HEADER
+ MSG_MANIFEST
+ MSG_UNHANDLED
+ "* .gitignore\n"
+ MSG_REPLACED_JARS
+ "guava-13.0.1.jar => com.google.guava:guava:13.0.1\n"
+ MSG_GUESSED_VERSIONS
+ "guava-13.0.1.jar => version 13.0.1 in com.google.guava:guava:13.0.1\n"
+ MSG_FOLDER_STRUCTURE
+ "In Library1:\n"
+ "* src/ => library1/src/main/java/\n"
+ "In Library2:\n"
+ "* src/ => library2/src/main/java/\n"
+ "In AndroidLibrary:\n"
+ "* AndroidManifest.xml => androidLibrary/src/main/AndroidManifest.xml\n"
+ "* src/ => androidLibrary/src/main/java/\n"
+ "In AndroidApp:\n"
+ "* AndroidManifest.xml => androidApp/src/main/AndroidManifest.xml\n"
+ "* res/ => androidApp/src/main/res/\n"
+ "* src/ => androidApp/src/main/java/\n"
+ MSG_FOOTER,
false /* checkBuild */, new ImportCustomizer() {
@Override
public void customize(GradleImport importer) {
importReference.set(importer);
importer.setEclipseWorkspace(workspace);
}
});
assertEquals("{/Library1=" + new File(root, library1Path).getCanonicalPath()
+ ", /Library2=" + new File(root, "Library2").getCanonicalPath() + "}",
describePathMap(importReference.get()));
deleteDir(root);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testErrorHandling1() throws Exception {
// Broken .classpath file
File projectDir = createProject("testError1", "test.pkg");
File classPath = new File(projectDir, ".classpath");
Files.write(""
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<classpath>\n"
+ " <classpathentry kind=\"src\" path=\"src\"/\n" // <== XML error
+ " <classpathentry kind=\"src\" path=\"gen\"/>\n"
+ " <classpathentry kind=\"con\" path=\"com.android.ide.eclipse.adt.ANDROID_FRAMEWORK\"/>\n"
+ " <classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.LIBRARIES\"/>\n"
+ " <classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.DEPENDENCIES\"/>\n"
+ " <classpathentry kind=\"output\" path=\"bin/classes\"/>\n"
+ "</classpath>", classPath, UTF_8);
final AtomicReference<GradleImport> importReference = new AtomicReference<GradleImport>();
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ "\n"
+ " * $ROOT/.classpath:\n"
+ "Invalid XML file: $ROOT/.classpath:\n"
+ "Element type \"classpathentry\" must be followed by either attribute specifications, \">\" or \"/>\".\n"
+ MSG_FOOTER,
false /* checkBuild */, new ImportCustomizer() {
@Override
public void customize(GradleImport importer) {
importReference.set(importer);
}
});
assertEquals("[$CLASSPATH_FILE:\n"
+ "Invalid XML file: $CLASSPATH_FILE:\n"
+ "Element type \"classpathentry\" must be followed by either attribute "
+ "specifications, \">\" or \"/>\".]",
importReference.get().getErrors().toString().replace(
classPath.getPath(), "$CLASSPATH_FILE").
replace(classPath.getCanonicalPath(), "$CLASSPATH_FILE"));
deleteDir(projectDir);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testErrorHandling2() throws Exception {
// Broken manifest
File root = Files.createTempDir();
// Place project in a deep subdirectory such that it does not leave a broken
// sibling project for other unit tests to discover as an instrumentation test
File projectDir = new File(root, "sub1" + separator + "sub2" + separator + "sub3");
projectDir.mkdirs();
createProject(projectDir, "testError2", "test.pkg");
File manifest = new File(projectDir, "AndroidManifest.xml");
Files.write(""
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<x>\n", manifest, UTF_8);
final AtomicReference<GradleImport> importReference = new AtomicReference<GradleImport>();
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ "\n"
+ " * $ROOT/AndroidManifest.xml:\n"
+ "Invalid XML file: $ROOT/AndroidManifest.xml:\n"
+ "XML document structures must start and end within the same entity.\n"
+ MSG_FOOTER,
false /* checkBuild */, new ImportCustomizer() {
@Override
public void customize(GradleImport importer) {
importReference.set(importer);
}
});
assertEquals("[$MANIFEST_FILE:\n"
+ "Invalid XML file: $MANIFEST_FILE:\n"
+ "XML document structures must start and end within the same entity.]",
importReference.get().getErrors().toString().replace(
manifest.getPath(), "$MANIFEST_FILE").
replace(manifest.getCanonicalPath(), "$MANIFEST_FILE"));
deleteDir(root);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testProguardMigration() throws Exception {
// Check that ABI libs are copied to the right place
File projectDir = createProject("Test1", "test.pkg");
Files.write(""
+ "proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt:proguard/proguard.pro:${user.home}/proguard/shared.pro\n"
+ "\n"
+ "# Indicates whether an apk should be generated for each density.\n"
+ "split.density=false\n"
+ "# Project target.\n"
+ "target=android-16\n",
new File(projectDir, FN_PROJECT_PROPERTIES), UTF_8);
File proguard = new File(projectDir, "proguard");
proguard.mkdirs();
Files.write(""
+ "-optimizationpasses 2\n"
+ "-dontusemixedcaseclassnames\n"
+ "-dontskipnonpubliclibraryclasses\n"
+ "-dontpreverify\n",
new File(proguard, "proguard.pro"), UTF_8);
new File(projectDir, "proguard-project.txt").createNewFile();
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_FOLDER_STRUCTURE
+ "* AndroidManifest.xml => app/src/main/AndroidManifest.xml\n"
+ "* proguard-project.txt => app/proguard-project.txt\n"
+ "* proguard/proguard.pro => app/proguard.pro\n"
+ "* res/ => app/src/main/res/\n"
+ "* src/ => app/src/main/java/\n"
+ MSG_USER_HOME_PROGUARD
+ "${user.home}/proguard/shared.pro\n"
+ MSG_FOOTER,
false /* checkBuild */);
// Imported contents
assertEquals(""
+ "app\n"
+ " build.gradle\n"
+ " proguard-project.txt\n"
+ " proguard.pro\n"
+ " src\n"
+ " main\n"
+ " AndroidManifest.xml\n"
+ " java\n"
+ " test\n"
+ " pkg\n"
+ " MyActivity.java\n"
+ " res\n"
+ " drawable\n"
+ " ic_launcher.xml\n"
+ " values\n"
+ " strings.xml\n"
+ "build.gradle\n"
+ "import-summary.txt\n"
+ (getTestSdkPathLocal() != null ? "local.properties\n" : "")
+ "settings.gradle\n",
fileTree(imported, true));
//noinspection PointlessBooleanExpression,ConstantConditions
assertEquals(""
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"buildscript {\n"
+ " repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ " }\n"
+ " dependencies {\n"
+ " classpath '" + ANDROID_GRADLE_PLUGIN + "'\n"
+ " }\n"
+ "}\n" : "")
+ "apply plugin: 'com.android.application'\n"
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"\n"
+ "repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ "}\n" : "")
+ "\n"
+ "android {\n"
+ " compileSdkVersion 16\n"
+ " buildToolsVersion \"" + BUILD_TOOLS_VERSION + "\"\n"
+ "\n"
+ " defaultConfig {\n"
+ " applicationId \"test.pkg\"\n"
+ " minSdkVersion 8\n"
+ " targetSdkVersion 16\n"
+ " }\n"
+ "\n"
+ " buildTypes {\n"
+ " release {\n"
+ " minifyEnabled true\n"
+ " proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt', 'proguard.pro'\n"
+ " }\n"
+ " }\n"
+ "}\n",
Files.toString(new File(imported, "app" + separator + "build.gradle"), UTF_8)
.replace(NL, "\n"));
deleteDir(projectDir);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testPreviewPlatform() throws Exception {
File root = Files.createTempDir();
File projectDir = new File(root, "project");
projectDir.mkdirs();
createProject(projectDir, "Test2", "test.pkg");
createDotProject(projectDir, "Test2", true, true);
// Write out Manifest and project.properties files which point to L as a preview platform
Files.write(""
+ "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ " package=\"test.pkg\"\n"
+ " android:versionCode=\"1\"\n"
+ " android:versionName=\"1.0\" >\n"
+ "\n"
+ " <uses-sdk\n"
+ " android:minSdkVersion=\"L\"\n"
+ " android:targetSdkVersion=\"L\" />\n"
+ "\n"
+ " <application\n"
+ " android:icon=\"@android:drawable/sym_def_app_icon\"\n"
+ " android:label=\"My Unit Test Instrumentation Tests\" >\n"
+ " <uses-library android:name=\"android.test.runner\" />\n"
+ " </application>\n"
+ "\n"
+ "</manifest>",
new File(projectDir, FN_ANDROID_MANIFEST_XML), UTF_8);
Files.write("# blah blah blah\n"
+ "target=android-L\n",
new File(projectDir, FN_PROJECT_PROPERTIES), UTF_8);
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_FOLDER_STRUCTURE
+ "* AndroidManifest.xml => Test2/src/main/AndroidManifest.xml\n"
+ "* res/ => Test2/src/main/res/\n"
+ "* src/ => Test2/src/main/java/\n"
+ MSG_FOOTER,
false /* checkBuild */,
new ImportCustomizer() {
@Override
public void customize(GradleImport importer) {
importer.setGradleNameStyle(false);
}
});
//noinspection PointlessBooleanExpression,ConstantConditions
assertEquals(""
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"buildscript {\n"
+ " repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ " }\n"
+ " dependencies {\n"
+ " classpath '" + ANDROID_GRADLE_PLUGIN + "'\n"
+ " }\n"
+ "}\n" : "")
+ "apply plugin: 'com.android.application'\n"
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"\n"
+ "repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ "}\n" : "")
+ "\n"
+ "android {\n"
+ " compileSdkVersion 'android-L'\n"
+ " buildToolsVersion \"" + BUILD_TOOLS_VERSION + "\"\n"
+ "\n"
+ " defaultConfig {\n"
+ " applicationId \"test.pkg\"\n"
+ " minSdkVersion 'L'\n"
+ " targetSdkVersion 'L'\n"
+ " }\n"
+ "\n"
+ " buildTypes {\n"
+ " release {\n"
+ " minifyEnabled false\n"
+ " proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'\n"
+ " }\n"
+ " }\n"
+ "}\n",
Files.toString(new File(imported, "Test2" + separator + "build.gradle"), UTF_8)
.replace(NL, "\n"));
deleteDir(root);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testAddOnPlatform() throws Exception {
File root = Files.createTempDir();
File projectDir = new File(root, "project");
projectDir.mkdirs();
createProject(projectDir, "Test2", "test.pkg");
createDotProject(projectDir, "Test2", true, true);
// Write project.properties file which points to the add-on
Files.write("# Project target.\n" +
"target=Google Inc.:Google APIs:18\n",
new File(projectDir, FN_PROJECT_PROPERTIES), UTF_8);
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_FOLDER_STRUCTURE
+ "* AndroidManifest.xml => Test2/src/main/AndroidManifest.xml\n"
+ "* res/ => Test2/src/main/res/\n"
+ "* src/ => Test2/src/main/java/\n"
+ MSG_FOOTER,
false /* checkBuild */,
new ImportCustomizer() {
@Override
public void customize(GradleImport importer) {
importer.setGradleNameStyle(false);
}
});
//noinspection PointlessBooleanExpression,ConstantConditions
assertEquals(""
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"buildscript {\n"
+ " repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ " }\n"
+ " dependencies {\n"
+ " classpath '" + ANDROID_GRADLE_PLUGIN + "'\n"
+ " }\n"
+ "}\n" : "")
+ "apply plugin: 'com.android.application'\n"
+ (!DECLARE_GLOBAL_REPOSITORIES ?
"\n"
+ "repositories {\n"
+ " " + MAVEN_REPOSITORY + "\n"
+ "}\n" : "")
+ "\n"
+ "android {\n"
+ " compileSdkVersion 'Google Inc.:Google APIs:18'\n"
+ " buildToolsVersion \"" + BUILD_TOOLS_VERSION + "\"\n"
+ "\n"
+ " defaultConfig {\n"
+ " applicationId \"test.pkg\"\n"
+ " minSdkVersion 8\n"
+ " targetSdkVersion 16\n"
+ " }\n"
+ "\n"
+ " buildTypes {\n"
+ " release {\n"
+ " minifyEnabled false\n"
+ " proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'\n"
+ " }\n"
+ " }\n"
+ "}\n",
Files.toString(new File(imported, "Test2" + separator + "build.gradle"), UTF_8).replace(NL, "\n"));
deleteDir(root);
deleteDir(imported);
}
@SuppressWarnings({"ResultOfMethodCallIgnored", "SpellCheckingInspection"})
public void testRiskyPathChars() throws Exception {
File root = Files.createTempDir();
File projectDir = createProject("PathChars", "test.pkg");
final File destDir = new File(root, "My Code" + separator + "Source & Data" + separator + "Foo's Bar");
destDir.mkdirs();
checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_RISKY_PROJECT_LOCATION
+ "$DESTDIR/My Code/Source & Data/Foo's Bar\n"
+ " - --- - - \n"
+ MSG_FOLDER_STRUCTURE
+ "* AndroidManifest.xml => app/src/main/AndroidManifest.xml\n"
+ "* res/ => app/src/main/res/\n"
+ "* src/ => app/src/main/java/\n"
+ MSG_FOOTER,
false /* checkBuild */, null, destDir, root);
deleteDir(root);
}
@SuppressWarnings("SpellCheckingInspection")
public void testIgnoreFile() {
assertTrue(GradleImport.isIgnoredFile(new File(".git")));
assertTrue(GradleImport.isIgnoredFile(new File(".hg")));
assertTrue(GradleImport.isIgnoredFile(new File(".svn")));
assertTrue(GradleImport.isIgnoredFile(new File("foo" + File.separator + ".git")));
assertTrue(GradleImport.isIgnoredFile(new File("foo" + File.separator + ".hg")));
assertTrue(GradleImport.isIgnoredFile(new File("foo" + File.separator + ".svn")));
assertTrue(GradleImport.isIgnoredFile(new File("foo~")));
assertFalse(GradleImport.isIgnoredFile(new File(".gitt")));
assertFalse(GradleImport.isIgnoredFile(new File("~")));
assertFalse(GradleImport.isIgnoredFile(new File("~foo")));
}
public void testSdkNdkSetters() {
GradleImport importer = new GradleImport();
File ndkLocation = new File("ndk");
File sdkLocation = new File("sdk");
importer.setNdkLocation(ndkLocation);
importer.setSdkLocation(sdkLocation);
assertSame(sdkLocation, importer.getSdkLocation());
assertSame(ndkLocation, importer.getNdkLocation());
String sdkPath = getTestSdkPathLocal();
if (sdkPath != null) {
ILogger logger = new StdLogger(StdLogger.Level.INFO);
SdkManager sdkManager = SdkManager.createManager(sdkPath, logger);
if (sdkManager != null) {
importer.setSdkManager(sdkManager);
assertSame(sdkManager, importer.getSdkManager());
assertEquals(new File(sdkPath), importer.getSdkLocation());
}
}
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testEncoding() throws Exception {
// Checks that we properly convert source files from other encodings to UTF-8.
// The following scenarios are tested:
// - For files that have a specific encoding associated with that file, use it
// - For XML files that specify an encoding in the prologue, use it
// - For files that have a specific encoding specified by a BOM, use it
// - For files that are in a project where there is a project-specific encoding, use it
// - For all other files, use the default encoding specified in the workspace
File root = Files.createTempDir();
File app = createLibrary(root, "test.lib2.pkg", false);
// Write some source files where encoding matters
// The Project App will have a default project encoding of MacRoman
// The workspace will have a default encoding of windows1252
// Some individual files will specify a file encoding of iso-8859-1
Charset macRoman;
Charset windows1252;
Charset utf32;
Charset iso8859 = Charsets.ISO_8859_1;
try {
macRoman = Charset.forName("MacRoman");
windows1252 = Charset.forName("windows-1252");
utf32 = Charset.forName("UTF_32");
} catch (UnsupportedCharsetException uce) {
System.err.println("This test machine does not have all the charsets we need: "
+ uce.getCharsetName() + ": skipping test");
return;
}
String java = ""
+ "package test.pkg;\n"
+ "\n"
+ "public class Text {\n"
+ "\tpublic static final String TEXT_1 = \"This is plain\";\n"
+ "\tpublic static final String TEXT_2 = \"\u00e6\u00d8\u00e5\";\n"
+ "\tpublic static final String TEXT_3 = \"10\u00a3\";\n"
+ "}\n";
String xml = ""
+ "<?xml version=\"1.0\" encoding=\"" + windows1252.name() + "\"?>\n"
+ "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ " android:layout_width=\"match_parent\"\n"
+ " android:layout_height=\"match_parent\"\n"
+ " android:orientation=\"vertical\" >\n"
+ "<!-- \u00a3 -->\n"
+ "</LinearLayout>";
File appFile = new File(root, "App/src/test/pkg/Text.java".replace('/', separatorChar));
File lib1File = new File(root, "Lib1/src/test/pkg/Text.java".replace('/', separatorChar));
File lib2File = new File(root, "Lib2/src/test/pkg/Text.java".replace('/', separatorChar));
File xmlFile = new File(root, "App/res/layout/foo.xml".replace('/', separatorChar));
appFile.getParentFile().mkdirs();
lib1File.getParentFile().mkdirs();
lib2File.getParentFile().mkdirs();
xmlFile.getParentFile().mkdirs();
Files.write(java, appFile, iso8859);
Files.write(java, lib1File, macRoman);
Files.write(java, lib2File, windows1252);
Files.write(xml, xmlFile, windows1252);
assertEquals(java, Files.toString(appFile, iso8859));
assertEquals(java, Files.toString(lib1File, macRoman));
assertEquals(java, Files.toString(lib2File, windows1252));
assertEquals(xml, Files.toString(xmlFile, windows1252));
// Make sure that these contents don't happen to be the same regardless of encoding
assertFalse(java.equals(Files.toString(appFile, UTF_8)));
assertFalse(java.equals(Files.toString(lib1File, UTF_8)));
assertFalse(java.equals(Files.toString(lib2File, UTF_8)));
assertFalse(xml.equals(Files.toString(xmlFile, UTF_8)));
// Write App project specific encoding, and file specific encoding
File file = new File(root, "App" + separator + ".settings" + separator
+ "org.eclipse.core.resources.prefs");
file.getParentFile().mkdirs();
Files.write(""
+ "eclipse.preferences.version=1\n"
+ "encoding//src/test/pkg/Text.java=" + iso8859.name() + "\n"
+ "encoding/<project>=" + macRoman.name(), file, Charsets.US_ASCII);
// Write Lib1 project specific encoding
file = new File(root, "Lib1" + separator + ".settings" + separator
+ "org.eclipse.core.resources.prefs");
file.getParentFile().mkdirs();
Files.write(""
+ "eclipse.preferences.version=1\n"
+ "encoding/<project>=" + macRoman.name(), file, Charsets.US_ASCII);
// Write workspace default encoding, used for the Lib2 file
final File workspace = new File(root, "workspace");
workspace.mkdirs();
File metadata = new File(workspace, ".metadata");
metadata.mkdirs();
new File(metadata, "version.ini").createNewFile();
assertTrue(GradleImport.isEclipseWorkspaceDir(workspace));
File resourceFile = new File(workspace, (".metadata/.plugins/org.eclipse.core.runtime/"
+ ".settings/org.eclipse.core.resources.prefs").replace('/', separatorChar));
resourceFile.getParentFile().mkdirs();
Files.write(""
+ "eclipse.preferences.version=1\n"
+ "encoding=" + windows1252.name() + "\n"
+ "version=1", resourceFile, Charsets.US_ASCII);
File imported = checkProject(app,
""
+ MSG_HEADER
+ MSG_MANIFEST
+ MSG_UNHANDLED
+ "From App:\n"
+ "* .gitignore\n"
+ "From JavaLib:\n"
+ "* .gitignore\n"
+ MSG_FOLDER_STRUCTURE
+ "In JavaLib:\n"
+ "* src/ => javaLib/src/main/java/\n"
+ "In Lib1:\n"
+ "* AndroidManifest.xml => lib1/src/main/AndroidManifest.xml\n"
+ "* src/ => lib1/src/main/java/\n"
+ "In Lib2:\n"
+ "* AndroidManifest.xml => lib2/src/main/AndroidManifest.xml\n"
+ "* src/ => lib2/src/main/java/\n"
+ "In App:\n"
+ "* AndroidManifest.xml => app/src/main/AndroidManifest.xml\n"
+ "* res/ => app/src/main/res/\n"
+ "* src/ => app/src/main/java/\n"
+ MSG_FOOTER,
false /* checkBuild */, new ImportCustomizer() {
@Override
public void customize(GradleImport importer) {
importer.setEclipseWorkspace(workspace);
}
});
// Read back text files *as UTF-8* and make sure it's correct
File newAppFile = new File(imported, "app/src/main/java/test/pkg/Text.java".replace('/',
separatorChar));
File newLib1File = new File(imported, "lib1/src/main/java/test/pkg/Text.java".replace('/',
separatorChar));
File newLib2File = new File(imported, "lib2/src/main/java/test/pkg/Text.java".replace('/',
separatorChar));
File newXmlFile = new File(imported, "app/src/main/res/layout/foo.xml".replace('/',
separatorChar));
assertTrue(newAppFile.exists());
assertTrue(newLib1File.exists());
assertTrue(newLib2File.exists());
assertTrue(newXmlFile.exists());
assertEquals(java, Files.toString(newAppFile, UTF_8));
assertEquals(java, Files.toString(newLib1File, UTF_8));
assertEquals(java, Files.toString(newLib2File, UTF_8));
assertFalse(xml.equals(Files.toString(newXmlFile, UTF_8))); // references old encoding
assertEquals(xml.replace(windows1252.name(), "utf-8"), Files.toString(newXmlFile, UTF_8));
deleteDir(root);
deleteDir(imported);
}
public void testIsTextFile() {
assertTrue(GradleImport.isTextFile(new File("foo.java")));
assertTrue(GradleImport.isTextFile(new File("foo.xml")));
assertTrue(GradleImport.isTextFile(new File("parent" + separator + "foo.xml")));
assertTrue(GradleImport.isTextFile(new File("parent" + separator + "foo.h")));
assertTrue(GradleImport.isTextFile(new File("parent" + separator + "foo.c")));
assertTrue(GradleImport.isTextFile(new File("parent" + separator + "foo.cpp")));
assertTrue(GradleImport.isTextFile(new File("parent" + separator + "foo.properties")));
assertTrue(GradleImport.isTextFile(new File("parent" + separator + "foo.aidl")));
assertTrue(GradleImport.isTextFile(new File("parent" + separator + "foo.rs")));
assertTrue(GradleImport.isTextFile(new File("parent" + separator + "foo.fs")));
assertTrue(GradleImport.isTextFile(new File("parent" + separator + "foo.rsh")));
assertTrue(GradleImport.isTextFile(new File("parent" + separator + "README.txt")));
assertTrue(GradleImport.isTextFile(new File("parent" + separator + "build.gradle")));
assertTrue(GradleImport.isTextFile(new File("parent" + separator + "proguard.cfg")));
assertTrue(GradleImport.isTextFile(new File("parent" + separator + "optimize.pro")));
assertFalse(GradleImport.isTextFile(new File("parent" + separator + "Foo.class")));
assertFalse(GradleImport.isTextFile(new File("parent" + separator + "foo.jar")));
assertFalse(GradleImport.isTextFile(new File("parent" + separator + "foo.png")));
assertFalse(GradleImport.isTextFile(new File("parent" + separator + "foo.9.png")));
assertFalse(GradleImport.isTextFile(new File("parent" + separator + "foo.jpg")));
assertFalse(GradleImport.isTextFile(new File("parent" + separator + "foo.so")));
assertFalse(GradleImport.isTextFile(new File("parent" + separator + "foo.dll")));
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testFindLatestSupportLib1() throws Exception {
// Test that we find the latest available version of the 19.x support libraries
File projectDir = createProject("test1", "test.pkg");
createProjectProperties(projectDir, "android-19", null, null, null,
Collections.<File>emptyList());
File libs = new File(projectDir, "libs");
libs.mkdirs();
new File(libs, "android-support-v4.jar").createNewFile();
new File(libs, "android-support-v7-gridlayout.jar").createNewFile();
new File(libs, "android-support-v7-appcompat.jar").createNewFile();
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_REPLACED_JARS
+ "android-support-v4.jar => com.android.support:support-v4:19.1.0\n"
+ "android-support-v7-appcompat.jar => com.android.support:appcompat-v7:19.1.0\n"
+ "android-support-v7-gridlayout.jar => com.android.support:gridlayout-v7:19.1.0\n"
+ MSG_FOLDER_STRUCTURE
+ DEFAULT_MOVED
+ (getTestSdkPathLocal() == null ? MSG_MISSING_REPO_1 + "null\n" + MSG_MISSING_REPO_2 : "")
+ MSG_FOOTER,
false /* checkBuild */);
deleteDir(projectDir);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testFindLatestSupportLib2() throws Exception {
// Like testFindLatestSupportLib1, but uses a preview platform (L)
// Test that we find the latest available version of the 19.x support libraries
File projectDir = createProject("test1", "test.pkg");
createProjectProperties(projectDir, "android-L", null, null, null,
Collections.<File>emptyList());
File libs = new File(projectDir, "libs");
libs.mkdirs();
new File(libs, "android-support-v4.jar").createNewFile();
new File(libs, "android-support-v7-gridlayout.jar").createNewFile();
new File(libs, "android-support-v7-appcompat.jar").createNewFile();
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_REPLACED_JARS
+ "android-support-v4.jar => com.android.support:support-v4:21.0.3\n"
+ "android-support-v7-appcompat.jar => com.android.support:appcompat-v7:21.0.3\n"
+ "android-support-v7-gridlayout.jar => com.android.support:gridlayout-v7:21.0.3\n"
+ MSG_FOLDER_STRUCTURE
+ DEFAULT_MOVED
+ (getTestSdkPathLocal() == null ? MSG_MISSING_REPO_1 + "null\n" + MSG_MISSING_REPO_2 : "")
+ MSG_FOOTER,
false /* checkBuild */);
deleteDir(projectDir);
deleteDir(imported);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testUnresolvedLibrary() throws Exception {
// Test that we recognize and handle unresolved support libraries
File projectDir = createProject("test1", "test.pkg");
//noinspection ConstantConditions
createProjectProperties(projectDir, "android-19", null, null, null,
Collections.<File>emptyList());
// Append unresolved library project references
String s = Files.toString(new File(projectDir, FN_PROJECT_PROPERTIES), Charsets.UTF_8);
s += "\n";
s += String.format(ANDROID_LIBRARY_REFERENCE_FORMAT, 1) + "=../appcompat_v7\n";
s += String.format(ANDROID_LIBRARY_REFERENCE_FORMAT, 2) + "=../support-v4\n";
//noinspection SpellCheckingInspection
s += String.format(ANDROID_LIBRARY_REFERENCE_FORMAT, 3) + "=../extras/android/compatibility/v7/gridlayout\n";
Files.write(s, new File(projectDir, FN_PROJECT_PROPERTIES), Charsets.UTF_8);
File imported = checkProject(projectDir,
""
+ MSG_HEADER
+ MSG_REPLACED_LIBS
+ "appcompat-v7 => [com.android.support:appcompat-v7:19.1.0]\n"
+ "gridlayout-v7 => [com.android.support:gridlayout-v7:19.1.0]\n"
+ "support-v4 => [com.android.support:support-v4:19.1.0]\n"
+ MSG_FOLDER_STRUCTURE
+ DEFAULT_MOVED
+ (getTestSdkPathLocal() == null ? MSG_MISSING_REPO_1 + "null\n" + MSG_MISSING_REPO_2 : "")
+ MSG_FOOTER,
false /* checkBuild */);
deleteDir(projectDir);
deleteDir(imported);
}
// --- Unit test infrastructure from this point on ----
@SuppressWarnings({"ResultOfMethodCallIgnored", "SpellCheckingInspection"})
private static void createEclipseSettingsFile(File prj, String languageLevel)
throws IOException {
File file = new File(prj, ".settings" + separator + "org.eclipse.jdt.core.prefs");
file.getParentFile().mkdirs();
Files.write("" +
"eclipse.preferences.version=1\n" +
"org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled\n" +
"org.eclipse.jdt.core.compiler.codegen.targetPlatform=" +
languageLevel +
"\n" +
"org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve\n" +
"org.eclipse.jdt.core.compiler.compliance=" +
languageLevel +
"\n" +
"org.eclipse.jdt.core.compiler.debug.lineNumber=generate\n" +
"org.eclipse.jdt.core.compiler.debug.localVariable=generate\n" +
"org.eclipse.jdt.core.compiler.debug.sourceFile=generate\n" +
"org.eclipse.jdt.core.compiler.problem.assertIdentifier=error\n" +
"org.eclipse.jdt.core.compiler.problem.enumIdentifier=error\n" +
"org.eclipse.jdt.core.compiler.source=" +
languageLevel, file, Charsets.UTF_8);
}
interface ImportCustomizer {
void customize(GradleImport importer);
}
private static File checkProject(File adtProjectDir,
String expectedSummary, boolean checkBuild) throws Exception {
return checkProject(adtProjectDir, expectedSummary, checkBuild, null);
}
private static File checkProject(File adtProjectDir,
String expectedSummary, boolean checkBuild,
@Nullable ImportCustomizer customizer) throws Exception {
File destDir = Files.createTempDir();
return checkProject(adtProjectDir, expectedSummary, checkBuild, customizer, destDir, destDir);
}
private static File checkProject(File adtProjectDir,
String expectedSummary, boolean checkBuild,
@Nullable ImportCustomizer customizer,
File destDir, File rootDir) throws Exception {
return checkImport(null, adtProjectDir, expectedSummary, checkBuild, customizer, destDir, rootDir);
}
private static File checkImport(
@Nullable File gradleProjectDir,
@NonNull File adtProjectDir,
@NonNull String expectedSummary,
boolean checkBuild,
@Nullable ImportCustomizer customizer,
@NonNull File destDir,
@NonNull File rootDir) throws Exception {
assertTrue(GradleImport.isAdtProjectDir(adtProjectDir));
List<File> projects = Collections.singletonList(adtProjectDir);
GradleImport importer = new GradleImport();
boolean isImport = gradleProjectDir != null;
if (isImport) {
importer.setImportIntoExisting(true);
} else {
gradleProjectDir = destDir;
}
String sdkPath = getTestSdkPathLocal();
if (sdkPath != null) {
importer.setSdkLocation(new File(sdkPath));
}
File wrapper = findGradleWrapper();
if (wrapper != null) {
importer.setGradleWrapperLocation(wrapper);
}
if (customizer != null) {
customizer.customize(importer);
}
importer.importProjects(projects);
importer.getSummary().setWrapErrorMessages(false);
if (isImport) {
Map<File,File> map = Maps.newHashMap();
map.put(adtProjectDir, destDir);
importer.exportIntoProject(gradleProjectDir, true, true, map);
} else {
importer.exportProject(destDir, false);
}
String summary = Files.toString(new File(gradleProjectDir, IMPORT_SUMMARY_TXT), UTF_8);
summary = summary.replace("\r", "");
summary = stripOutRiskyPathMessage(summary, rootDir);
String testSdkPath = getTestSdkPathLocal();
if (testSdkPath != null) {
summary = summary.replace(testSdkPath, "$ADT_TEST_SDK_PATH");
}
summary = summary.replace(separatorChar, '/');
summary = summary.replace(adtProjectDir.getPath().replace(separatorChar,'/'), "$ROOT");
File parentFile = adtProjectDir.getParentFile();
if (parentFile != null) {
summary = summary.replace(parentFile.getPath().replace(separatorChar,'/'),
"$ROOT_PARENT");
}
assertEquals(expectedSummary, summary);
if (checkBuild) {
assertBuildsCleanly(gradleProjectDir, true);
}
return gradleProjectDir;
}
private static String stripOutRiskyPathMessage(String summary, File rootDir) {
int index = summary.indexOf(MSG_RISKY_PROJECT_LOCATION);
if (index == -1) {
return summary;
}
index += MSG_RISKY_PROJECT_LOCATION.length();
String path = rootDir.getPath();
assertTrue(summary.startsWith(path, index));
int nextLineIndex = summary.indexOf('\n', index) + 1;
return summary.substring(0, index) + "$DESTDIR" +
summary.substring(index + path.length(), nextLineIndex) +
" " + summary.substring(nextLineIndex + path.length());
}
@Nullable
private static File findGradleWrapper() throws IOException {
File templates = TemplateManager.getTemplateRootFolder();
if (templates != null) {
File wrapper = TemplateManager.getWrapperLocation(templates);
if (wrapper.exists()) {
return wrapper;
}
}
return null;
}
private static boolean isWindows() {
return SdkUtils.startsWithIgnoreCase(System.getProperty("os.name"), "windows");
}
public static void assertBuildsCleanly(File base, boolean allowWarnings) throws Exception {
File gradlew = new File(base, isWindows() ? FN_GRADLE_WRAPPER_WIN : FN_GRADLE_WRAPPER_UNIX);
if (!gradlew.exists()) {
// Not using a wrapper; can't easily test building (we don't have a gradle prebuilt)
return;
}
File pwd = base.getAbsoluteFile();
GeneralCommandLine cmdLine = new GeneralCommandLine(new String[]{gradlew.getPath(), "assembleDebug"}).withWorkDirectory(pwd);
CapturingProcessHandler process = new CapturingProcessHandler(cmdLine);
// Building currently takes about 30s, so a 5min timeout should give a safe margin.
int timeoutInMilliseconds = 5 * 60 * 1000;
ProcessOutput processOutput = process.runProcess(timeoutInMilliseconds, true);
if (processOutput.isTimeout()) {
throw new TimeoutException("\"gradlew assembleDebug\" did not terminate within test timeout value.\n" +
"[stdout]\n" +
processOutput.getStdout() + "\n" +
"[stderr]\n" +
processOutput.getStderr() + "\n");
}
String errors = processOutput.getStderr();
String output = processOutput.getStdout();
int exitCode = processOutput.getExitCode();
int expectedExitCode = 0;
if (output.contains("BUILD FAILED") && errors.contains(
"Could not find any version that matches com.android.tools.build:gradle:")) {
// We ignore this assertion. We got here because we are using a version of the
// Android Gradle plug-in that is not available in Maven Central yet.
expectedExitCode = 1;
} else {
assertTrue(output + "\n" + errors, output.contains("BUILD SUCCESSFUL"));
if (!allowWarnings) {
assertEquals(output + "\n" + errors, "", errors);
}
}
assertEquals(expectedExitCode, exitCode);
System.out.println("Built project successfully; output was:\n" + output);
}
private static String fileTree(File file, boolean includeDirs) {
StringBuilder sb = new StringBuilder(1000);
appendFiles(sb, includeDirs, file, 0);
return sb.toString();
}
private static void appendFiles(StringBuilder sb, boolean includeDirs, File file, int depth) {
// Skip output
if ((depth == 1 || depth == 2) && file.getName().equals("build")) {
return;
}
// Skip wrapper, since it may or may not be present for unit tests
if (depth == 1) {
String name = file.getName();
if (name.equals(DOT_GRADLE)
|| name.equals(FD_GRADLE)
|| name.equals(FN_GRADLE_WRAPPER_UNIX)
|| name.equals(FN_GRADLE_WRAPPER_WIN)) {
return;
}
}
boolean isDirectory = file.isDirectory();
if (depth > 0 && (!isDirectory || includeDirs)) {
for (int i = 0; i < depth - 1; i++) {
sb.append(" ");
}
sb.append(file.getName());
sb.append("\n");
}
if (isDirectory) {
File[] children = file.listFiles();
if (children != null) {
Arrays.sort(children, new Comparator<File>() {
@Override
public int compare(File file, File file2) {
return file.getName().compareTo(file2.getName());
}
});
for (File child : children) {
appendFiles(sb, includeDirs, child, depth + 1);
}
}
}
}
private static void createDotProject(
@NonNull File projectDir,
String name,
boolean addAndroidNature) throws IOException {
createDotProject(projectDir, name, addAndroidNature, addAndroidNature);
}
private static void createDotProject(
@NonNull File projectDir,
String name,
boolean addAndroidNature, boolean addNdkNature) throws IOException {
StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<projectDescription>\n"
+ "\t<name>").append(name).append("</name>\n"
+ "\t<comment></comment>\n"
+ "\t<projects>\n"
+ "\t</projects>\n"
+ "\t<buildSpec>\n"
+ "\t\t<buildCommand>\n"
+ "\t\t\t<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>\n"
+ "\t\t\t<arguments>\n"
+ "\t\t\t</arguments>\n"
+ "\t\t</buildCommand>\n"
+ "\t\t<buildCommand>\n"
+ "\t\t\t<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>\n"
+ "\t\t\t<arguments>\n"
+ "\t\t\t</arguments>\n"
+ "\t\t</buildCommand>\n"
+ "\t\t<buildCommand>\n"
+ "\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n"
+ "\t\t\t<arguments>\n"
+ "\t\t\t</arguments>\n"
+ "\t\t</buildCommand>\n"
+ "\t\t<buildCommand>\n"
+ "\t\t\t<name>com.android.ide.eclipse.adt.ApkBuilder</name>\n"
+ "\t\t\t<arguments>\n"
+ "\t\t\t</arguments>\n"
+ "\t\t</buildCommand>\n"
+ "\t</buildSpec>\n"
+ "\t<natures>\n");
if (addAndroidNature) {
sb.append("\t\t<nature>com.android.ide.eclipse.adt.AndroidNature</nature>\n");
}
if (addNdkNature) {
sb.append("\t\t<nature>org.eclipse.cdt.core.cnature</nature>\n");
sb.append("\t\t<nature>org.eclipse.cdt.core.ccnature</nature>\n");
}
sb.append("\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n"
+ "\t</natures>\n"
+ "</projectDescription>\n");
Files.write(sb.toString(), new File(projectDir, ".project"), UTF_8);
}
private static void createClassPath(
@NonNull File projectDir,
@Nullable File output,
@NonNull List<File> sources,
@NonNull List<File> jars) throws IOException {
StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<classpath>\n");
for (File source : sources) {
sb.append("\t<classpathentry kind=\"src\" path=\"").append(source.getPath()).
append("\"/>\n");
}
sb.append("\t<classpathentry kind=\"con\" path=\"com.android.ide.eclipse.adt.ANDROID_FRAMEWORK\"/>\n"
+ "\t<classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.LIBRARIES\"/>\n"
+ "\t<classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.DEPENDENCIES\"/>\n");
for (File jar : jars) {
sb.append("<classpathentry exported=\"true\" kind=\"lib\" path=\"").append(jar.getPath()).append("\"/>\n");
}
if (output != null) {
sb.append("\t<classpathentry kind=\"output\" path=\"").append(output.getPath()).append("\"/>\n");
}
sb.append("</classpath>");
Files.write(sb.toString(), new File(projectDir, ".classpath"), UTF_8);
}
private static void createProjectProperties(
@NonNull File projectDir,
@Nullable String target,
Boolean mergeManifest,
Boolean isLibrary,
@Nullable String proguardConfig,
@NonNull List<File> libraries) throws IOException {
createProjectProperties(projectDir, target, mergeManifest, isLibrary, proguardConfig, libraries, true);
}
private static void createProjectProperties(
@NonNull File projectDir,
@Nullable String target,
Boolean mergeManifest,
Boolean isLibrary,
@Nullable String proguardConfig,
@NonNull List<File> libraries,
boolean startLibrariesAt1) throws IOException {
StringBuilder sb = new StringBuilder();
sb.append("# This file is automatically generated by Android Tools.\n"
+ "# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n"
+ "#\n"
+ "# This file must be checked in Version Control Systems.\n"
+ "#\n"
+ "# To customize properties used by the Ant build system edit\n"
+ "# \"ant.properties\", and override values to adapt the script to your\n"
+ "# project structure.\n"
+ "#\n");
if (proguardConfig != null) {
sb.append("# To enable ProGuard to shrink and obfuscate your code, uncomment this "
+ "(available properties: sdk.dir, user.home):\n");
// TODO: When using this, escape proguard properly
sb.append(proguardConfig);
sb.append("\n");
}
if (target != null) {
String escaped = escapeProperty("target", target);
sb.append("# Project target.\n").append(escaped).append("\n");
}
if (mergeManifest != null) {
String escaped = escapeProperty("manifestmerger.enabled",
Boolean.toString(mergeManifest));
sb.append(escaped).append("\n");
}
if (isLibrary != null) {
String escaped = escapeProperty("android.library", Boolean.toString(isLibrary));
sb.append(escaped).append("\n");
}
for (int i = 0, n = libraries.size(); i < n; i++) {
String path = libraries.get(i).getPath();
// Libraries normally start at index 1, but I want to handle cases where they start
// at 0 too so we have a test parameter for that
int index = i + (startLibrariesAt1 ? 1 : 0);
String escaped = escapeProperty("android.library.reference." + Integer.toString(index),
path);
sb.append(escaped).append("\n");
}
Files.write(sb.toString(), new File(projectDir, "project.properties"), UTF_8);
}
private static String escapeProperty(@NonNull String key, @NonNull String value)
throws IOException {
Properties properties = new Properties();
properties.setProperty(key, value);
StringWriter writer = new StringWriter();
properties.store(writer, null);
return writer.toString();
}
private static void createAndroidManifest(
@NonNull File projectDir,
@NonNull String packageName,
int minSdkVersion,
int targetSdkVersion,
@Nullable String customApplicationBlock) throws IOException {
StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ " package=\"").append(packageName).append("\"\n"
+ " android:versionCode=\"1\"\n"
+ " android:versionName=\"1.0\" >\n"
+ "\n");
if (minSdkVersion != -1 || targetSdkVersion != -1) {
sb.append(" <uses-sdk\n");
if (minSdkVersion >= 1) {
sb.append(" android:minSdkVersion=\"8\"\n");
}
if (targetSdkVersion >= 1) {
sb.append(" android:targetSdkVersion=\"16\"\n");
}
sb.append(" />\n");
sb.append("\n");
}
if (customApplicationBlock != null) {
sb.append(customApplicationBlock);
} else {
sb.append(""
+ " <application\n"
+ " android:allowBackup=\"true\"\n"
+ " android:icon=\"@drawable/ic_launcher\"\n"
+ " android:label=\"@string/app_name\"\n"
+ " >\n"
+ " </application>\n");
}
sb.append("\n"
+ "</manifest>\n");
Files.write(sb.toString(), new File(projectDir, ANDROID_MANIFEST_XML), UTF_8);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
private static File createSourceFile(@NonNull File projectDir, String relative,
String contents) throws IOException {
File file = new File(projectDir, relative.replace('/', separatorChar));
file.getParentFile().mkdirs();
Files.write(contents, file, UTF_8);
return file;
}
private static File createSampleJavaSource(@NonNull File projectDir, String src, String pkg,
String name) throws IOException {
return createSourceFile(projectDir, src + '/' + pkg.replace('.','/') + '/' + name +
DOT_JAVA, ""
+ "package " + pkg + ";\n"
+ "public class " + name + " {\n"
+ "}\n");
}
private static File createSampleTimeZoneData(@NonNull File projectDir, String src) throws IOException {
return createSourceFile(projectDir, src + "/zoneinfo-global/Pacific/Honolulu.ics", ""
+ "BEGIN:VCALENDAR\n"
+ "END:VCALENDAR\n");
}
private static File createSampleAidlFile(@NonNull File projectDir, String src, String pkg)
throws IOException {
return createSourceFile(projectDir, src + '/' + pkg.replace('.','/') +
"/IHardwareService.aidl", ""
+ "package " + pkg + ";\n"
+ "\n"
+ "/** {@hide} */\n"
+ "interface IHardwareService\n"
+ "{\n"
+ " // Vibrator support\n"
+ " void vibrate(long milliseconds);\n"
+ " void vibratePattern(in long[] pattern, int repeat, IBinder token);\n"
+ " void cancelVibrate();\n"
+ "\n"
+ " // flashlight support\n"
+ " boolean getFlashlightEnabled();\n"
+ " void setFlashlightEnabled(boolean on);\n"
+ " void enableCameraFlash(int milliseconds);\n"
+ "\n"
+ " // sets the brightness of the backlights (screen, keyboard, button) 0-255\n"
+ " void setBacklights(int brightness);\n"
+ "\n"
+ " // for the phone\n"
+ " void setAttentionLight(boolean on);\n"
+ "}");
}
private static File createSampleRsFile(@NonNull File projectDir, String src, String pkg)
throws IOException {
return createSourceFile(projectDir, src + '/' + pkg.replace('.', '/') + '/' + "latency.rs",
""
+ "#pragma version(1)\n"
+ "#pragma rs java_package_name(com.android.rs.cpptests)\n"
+ "#pragma rs_fp_relaxed\n"
+ "\n"
+ "void root(const uint32_t *v_in, uint32_t *v_out) {\n"
+ "\n"
+ "}");
}
@SuppressWarnings("ResultOfMethodCallIgnored")
private static void createDefaultStrings(File dir) throws IOException {
File strings = new File(dir, "res" + separator + "values" + separator + "strings.xml");
strings.getParentFile().mkdirs();
Files.write(""
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<resources>\n"
+ "\n"
+ " <string name=\"app_name\">Unit Test</string>\n"
+ "\n"
+ "</resources>", strings, UTF_8);
}
private static void createDefaultIcon(File dir) throws IOException {
createIcon(dir, "ic_launcher.xml");
}
@SuppressWarnings("ResultOfMethodCallIgnored")
private static void createIcon(File dir, String name) throws IOException {
boolean isPng = name.toLowerCase(Locale.US).endsWith(DOT_PNG);
String folder = isPng ? "drawable-hdpi" : "drawable";
File icon = new File(dir, "res" + separator + folder + separator + name);
icon.getParentFile().mkdirs();
if (isPng) {
//noinspection UndesirableClassUsage
BufferedImage image = new BufferedImage(50, 50, BufferedImage.TYPE_INT_ARGB);
ImageIO.write(image, "PNG", icon);
} else {
assertTrue("Unsupported file extension for " + name, name.toLowerCase(Locale.US).endsWith(DOT_XML));
Files.write("" +
"<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n" +
" <solid android:color=\"#00000000\"/>\n" +
" <stroke android:width=\"1dp\" color=\"#ff000000\"/>\n" +
" <padding android:left=\"1dp\" android:top=\"1dp\"\n" +
" android:right=\"1dp\" android:bottom=\"1dp\" />\n" +
"</shape>", icon, UTF_8);
}
}
@SuppressWarnings("ResultOfMethodCallIgnored")
private static void createLayout(File dir, String name) throws IOException {
File strings = new File(dir, "res" + separator + "layout" + separator + name);
strings.getParentFile().mkdirs();
Files.write(""
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<merge/>\n", strings, UTF_8);
}
private static void deleteDir(File root) {
if (root.exists()) {
File[] files = root.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
deleteDir(file);
} else {
boolean deleted = file.delete();
assert deleted : file;
}
}
}
boolean deleted = root.delete();
assert deleted : root;
}
}
/** Environment variable or system property containing the full path to an SDK install */
public static final String SDK_PATH_PROPERTY = "ADT_TEST_SDK_PATH";
@Nullable
protected static String getTestSdkPathLocal() {
String override = System.getProperty(SDK_PATH_PROPERTY);
if (override != null) {
assertTrue(override, new File(override).exists());
return override;
}
override = System.getenv(SDK_PATH_PROPERTY);
if (override != null) {
return override;
}
return null;
}
private static final String BUILD_TOOLS_VERSION;
static {
String candidate = CURRENT_BUILD_TOOLS_VERSION;
String sdkLocation = getTestSdkPathLocal();
if (sdkLocation != null) {
ILogger logger = new StdLogger(StdLogger.Level.INFO);
SdkManager sdkManager = SdkManager.createManager(sdkLocation, logger);
if (sdkManager != null) {
final BuildToolInfo buildTool = sdkManager.getLatestBuildTool();
if (buildTool != null) {
candidate = buildTool.getRevision().toString();
}
}
}
BUILD_TOOLS_VERSION = candidate;
}
private static final String DEFAULT_MOVED = ""
+ "* AndroidManifest.xml => app/src/main/AndroidManifest.xml\n"
+ "* res/ => app/src/main/res/\n"
+ "* src/ => app/src/main/java/\n";
}