diff options
author | Raymond Chiu <chiur@google.com> | 2014-09-23 23:34:56 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2014-09-23 23:34:57 +0000 |
commit | 2ee5958b766768b2637ae51c75576c6bc9558a42 (patch) | |
tree | aa83b4521c980526540cf51ca5e2887caafc2890 | |
parent | 346dce52674589b00ead4e5b59446c95dd130457 (diff) | |
parent | 08e866602d910a3b88fc9e9055b1b789777641cc (diff) | |
download | base-2ee5958b766768b2637ae51c75576c6bc9558a42.tar.gz |
Merge "Create integration test framework for android gradle plugin." into idea133
11 files changed, 861 insertions, 0 deletions
diff --git a/build-system/.gitignore b/build-system/.gitignore index b09217ab33..7e6354f691 100644 --- a/build-system/.gitignore +++ b/build-system/.gitignore @@ -1,5 +1,6 @@ local.properties .idea +gradle/build tests/*/build tests/api/*/build tests/applibtest/*/build diff --git a/build-system/gradle/build.gradle b/build-system/gradle/build.gradle index 756b6bd990..86bfd56dfc 100644 --- a/build-system/gradle/build.gradle +++ b/build-system/gradle/build.gradle @@ -9,12 +9,18 @@ sourceSets { } buildTest { groovy.srcDir file('src/build-test/groovy') + groovy.srcDir file('src/integ-test/groovy') resources.srcDir file('src/build-test/resources') + resources.srcDir file('src/integ-test/resources') } deviceTest { groovy.srcDir file('src/device-test/groovy') resources.srcDir file('src/device-test/resources') } + integTest { + groovy.srcDir file('src/integ-test/groovy') + resources.srcDir file('src/integ-test/resources') + } } ext.proguardVersion = "4.11" @@ -39,6 +45,11 @@ dependencies { deviceTestCompile sourceSets.buildTest.output deviceTestCompile configurations.testCompile deviceTestCompile configurations.testRuntime + + integTestCompile sourceSets.main.output + integTestCompile sourceSets.test.output + integTestCompile configurations.testCompile + integTestCompile configurations.testRuntime } // configuration for dependencies provided by the runtime, @@ -79,6 +90,32 @@ task buildTest(type: Test) { description = "Runs the project build tests. This requires an SDK either from the Android source tree, under out/..., or an env var ANDROID_HOME." group = "verification" systemProperties['jar.path'] = jar.archivePath + + useJUnit { + excludeCategories "com.android.build.gradle.internal.test.category.DeviceTests" + } +} + +task integTest(type: Test) { + testClassesDir = sourceSets.integTest.output.classesDir + classpath = sourceSets.integTest.runtimeClasspath + description = + "Runs the project integration tests. This requires an SDK either from the Android " + + "source tree, under out/..., or an env var ANDROID_HOME and a device." + group = "verification" + systemProperties['jar.path'] = jar.archivePath + + // Allow user to include/exclude categories from the command line. + // e.g. add the following to skip device tests: + // -DintegTest.excludeCategories=com.android.build.gradle.internal.test.category.DeviceTests + useJUnit { + if (System.properties[name + ".includeCategories"] != null) { + includeCategories System.properties[name + ".includeCategories"] + } + if (System.properties[name + ".excludeCategories"] != null) { + excludeCategories System.properties[name + ".excludeCategories"] + } + } } buildTest.dependsOn ':publishLocal' diff --git a/build-system/gradle/gradle.iml b/build-system/gradle/gradle.iml index af6d5ba5ba..2ed4350131 100644 --- a/build-system/gradle/gradle.iml +++ b/build-system/gradle/gradle.iml @@ -10,6 +10,7 @@ <sourceFolder url="file://$MODULE_DIR$/src/test/groovy" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/src/device-test/groovy" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/src/build-test/groovy" isTestSource="true" /> + <sourceFolder url="file://$MODULE_DIR$/src/integ-test/groovy" isTestSource="true" /> <excludeFolder url="file://$MODULE_DIR$/../../../../out/host/gradle/tools/base/gradle" /> <excludeFolder url="file://$MODULE_DIR$/.gradle" /> </content> diff --git a/build-system/gradle/src/integ-test/groovy/com/android/build/gradle/NativeStlTest.groovy b/build-system/gradle/src/integ-test/groovy/com/android/build/gradle/NativeStlTest.groovy new file mode 100644 index 0000000000..2f86489e50 --- /dev/null +++ b/build-system/gradle/src/integ-test/groovy/com/android/build/gradle/NativeStlTest.groovy @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.build.gradle + +import com.android.build.gradle.internal.test.fixture.GradleProjectTestRule +import com.android.build.gradle.internal.test.fixture.app.HelloWorldJniApp +import org.gradle.tooling.BuildException +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +import java.util.zip.ZipFile + +import static org.junit.Assert.assertNotNull +import static org.junit.Assert.fail + +/** + * Integration test for STL containers. + * + * This unit test is parameterized and will be executed for various values of STL. + */ +@RunWith(Parameterized.class) +public class NativeStlTest { + + @Parameterized.Parameters + public static Collection<Object[]> data() { + return [ + ["system"].toArray(), + ["stlport_static"].toArray(), + ["stlport_shared"].toArray(), + ["gnustl_static"].toArray(), + ["gnustl_shared"].toArray(), + ["gabi++_static"].toArray(), + ["gabi++_shared"].toArray(), + ["c++_static"].toArray(), + ["c++_shared"].toArray(), + ["invalid"].toArray(), + ]; + } + + private String stl; + + NativeStlTest(String stl) { + this.stl = stl; + } + + @Rule + public GradleProjectTestRule fixture = new GradleProjectTestRule(); + + @Before + public void setup() { + new HelloWorldJniApp().writeSources(fixture.getSourceDir()) + fixture.getBuildFile() << """ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 19 + buildToolsVersion "19.1.0" + useNewNativePlugin true + ndk { + moduleName "hello-jni" + } + } +""" + } + + @Test + public void buildAppWithStl() { + fixture.getBuildFile() << """ +android { + ndk { + stl "$stl" + } +} +""" + if (!stl.equals("invalid")) { + fixture.execute("assembleDebug"); + + ZipFile apk = new ZipFile( + fixture.file("build/outputs/apk/${fixture.testDir.name}-debug.apk")); + assertNotNull(apk.getEntry("lib/x86/libhello-jni.so")); + assertNotNull(apk.getEntry("lib/mips/libhello-jni.so")); + assertNotNull(apk.getEntry("lib/armeabi/libhello-jni.so")); + assertNotNull(apk.getEntry("lib/armeabi-v7a/libhello-jni.so")); + } else { + // Fail if it's invalid. + try { + fixture.execute("assemebleDebug"); + fail(); + } catch (BuildException ignored) { + } + } + } +} + diff --git a/build-system/gradle/src/integ-test/groovy/com/android/build/gradle/NdkPluginIntegrationTest.groovy b/build-system/gradle/src/integ-test/groovy/com/android/build/gradle/NdkPluginIntegrationTest.groovy new file mode 100644 index 0000000000..21c93cf01e --- /dev/null +++ b/build-system/gradle/src/integ-test/groovy/com/android/build/gradle/NdkPluginIntegrationTest.groovy @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.build.gradle + +import com.android.build.gradle.internal.test.category.DeviceTests +import com.android.build.gradle.internal.test.fixture.GradleProjectTestRule +import com.android.build.gradle.internal.test.fixture.app.HelloWorldJniApp +import org.junit.BeforeClass +import org.junit.ClassRule +import org.junit.Test +import org.junit.experimental.categories.Category + +/** + * Basic integration test for native plugin. + */ +class NdkPluginIntegrationTest { + @ClassRule static public GradleProjectTestRule fixture = new GradleProjectTestRule(); + + @BeforeClass + static public void setup() { + new HelloWorldJniApp(true /* useCppSource */).writeSources(fixture.getSourceDir()) + fixture.getBuildFile() << """ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 19 + buildToolsVersion "19.1.0" + useNewNativePlugin true + ndk { + moduleName "hello-jni" + } +} +""" + } + + @Test + public void basicDebug() { + fixture.execute("assembleDebug"); + } + + @Test + @Category(DeviceTests.class) + public void connnectedAndroidTest() { + fixture.execute("connectedAndroidTest"); + } +} diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/test/category/DeviceTests.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/test/category/DeviceTests.java new file mode 100644 index 0000000000..6803ff53ff --- /dev/null +++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/test/category/DeviceTests.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.build.gradle.internal.test.category; + +/** + * JUnit category used to label tests that requires a connected device. + */ +public interface DeviceTests { +} diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/test/fixture/GradleProjectTestRule.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/test/fixture/GradleProjectTestRule.java new file mode 100644 index 0000000000..26770a3a54 --- /dev/null +++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/test/fixture/GradleProjectTestRule.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.build.gradle.internal.test.fixture; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; +import com.android.build.gradle.BasePlugin; +import com.android.io.StreamException; +import com.android.sdklib.internal.project.ProjectProperties; +import com.android.sdklib.internal.project.ProjectPropertiesWorkingCopy; +import com.google.common.base.Charsets; +import com.google.common.collect.Lists; +import com.google.common.io.Files; + +import org.gradle.tooling.GradleConnector; +import org.gradle.tooling.ProjectConnection; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.security.CodeSource; +import java.util.Collections; +import java.util.List; + +/** + * JUnit4 test rule for integration test. + * + * This rule create a gradle project in a temporary directory. + * It can be use with the @Rule or @ClassRule annotations. Using this class with @Rule will create + * a gradle project in separate directories for each unit test, whereas using it with @ClassRule + * creates a single gradle project. + * + * The test directory is always deleted if it already exist at the start of the test to ensure a + * clean environment. + */ +public class GradleProjectTestRule implements TestRule { + + private static final String ANDROID_GRADLE_VERSION = "0.13.0"; + + private File testDir; + + private File sourceDir; + + private File buildFile; + + private File ndkDir; + + private File sdkDir; + + public GradleProjectTestRule() { + sdkDir = findSdkDir(); + ndkDir = findNdkDir(); + } + + /** + * Recursively delete directory or file. + * + * @param root directory to delete + */ + private static void deleteRecursive(File root) { + if (root.exists()) { + if (root.isDirectory()) { + for (File file : root.listFiles()) { + deleteRecursive(file); + } + } + assertTrue(root.delete()); + } + } + + @Override + public Statement apply(final Statement base, Description description) { + testDir = new File("build/tmp/tests/" + + description.getTestClass().getName()); + + // Create separate directory based on test method name if @Rule is used. + // getMethodName() is null if this rule is used as a @ClassRule. + if (description.getMethodName() != null) { + testDir = new File(testDir, description.getMethodName()); + } + + buildFile = new File(testDir, "build.gradle"); + sourceDir = new File(testDir, "src"); + + return new Statement() { + @Override + public void evaluate() throws Throwable { + if (testDir.exists()) { + deleteRecursive(testDir); + } + assertTrue(testDir.mkdirs()); + assertTrue(sourceDir.mkdirs()); + + Files.write( + "buildscript {\n" + + " repositories {\n" + + " maven { url '" + getRepoDir().toString() + "' }\n" + + " }\n" + + " dependencies {\n" + + " classpath 'com.android.tools.build:gradle:" + ANDROID_GRADLE_VERSION + "'\n" + + " }\n" + + "}\n", + buildFile, + Charsets.UTF_8); + + createLocalProp(testDir, sdkDir, ndkDir); + base.evaluate(); + } + }; + } + + /** + * Return the directory containing the test project. + */ + public File getTestDir() { + return testDir; + } + + /** + * Return the build.gradle of the test project. + */ + public File getBuildFile() { + return buildFile; + } + + /** + * Return the directory containing the source files of the test project. + */ + public File getSourceDir() { + return sourceDir; + } + + /** + * Return the directory of the repository containing the necessary plugins for testing. + */ + private File getRepoDir() { + CodeSource source = getClass().getProtectionDomain().getCodeSource(); + assert (source != null); + URL location = source.getLocation(); + try { + File dir = new File(location.toURI()); + assertTrue(dir.getPath(), dir.exists()); + + File f = dir.getParentFile().getParentFile().getParentFile().getParentFile() + .getParentFile().getParentFile().getParentFile(); + return new File(f, "out" + File.separator + "repo"); + } catch (URISyntaxException e) { + fail(e.getLocalizedMessage()); + } + return null; + } + + /** + * Runs gradle on the project. Throws exception on failure. + * + * @param tasks Variadic list of tasks to execute. + */ + public void execute(String ... tasks) { + execute(Collections.<String>emptyList(), tasks); + } + + /** + * Runs gradle on the project. Throws exception on failure. + * + * @param arguments List of arguments for the gradle command. + * @param tasks Variadic list of tasks to execute. + */ + public void execute(List<String> arguments, String ... tasks) { + GradleConnector connector = GradleConnector.newConnector(); + + ProjectConnection connection = connector + .useGradleVersion(BasePlugin.GRADLE_TEST_VERSION) + .forProjectDirectory(testDir) + .connect(); + try { + List<String> args = Lists.newArrayListWithCapacity(2 + arguments.size()); + args.add("-i"); + args.add("-u"); + args.addAll(arguments); + + connection.newBuild().forTasks(tasks) + .withArguments(args.toArray(new String[args.size()])).run(); + } finally { + connection.close(); + } + } + + /** + * Create a File object. getTestDir will be the base directory if a relative path is supplied. + * + * @param path Full path of the file. May be a relative path. + */ + public File file(String path) { + File result = new File(path); + if (result.isAbsolute()) { + return result; + } else { + return new File(testDir, path); + } + } + + /** + * Returns the SDK folder as built from the Android source tree. + */ + private static File findSdkDir() { + String androidHome = System.getenv("ANDROID_HOME"); + if (androidHome != null) { + File f = new File(androidHome); + if (f.isDirectory()) { + return f; + } else { + System.out.println("Failed to find SDK in ANDROID_HOME=" + androidHome); + } + } + return null; + } + + /** + * Returns the NDK folder as built from the Android source tree. + */ + private static File findNdkDir() { + String androidHome = System.getenv("ANDROID_NDK_HOME"); + if (androidHome != null) { + File f = new File(androidHome); + if (f.isDirectory()) { + return f; + } else { + System.out.println("Failed to find NDK in ANDROID_NDK_HOME=" + androidHome); + } + } + return null; + } + + private static File createLocalProp( + @NonNull File project, + @NonNull File sdkDir, + @Nullable File ndkDir) throws IOException, StreamException { + ProjectPropertiesWorkingCopy localProp = ProjectProperties.create( + project.getAbsolutePath(), ProjectProperties.PropertyType.LOCAL); + localProp.setProperty(ProjectProperties.PROPERTY_SDK, sdkDir.getAbsolutePath()); + if (ndkDir != null) { + localProp.setProperty(ProjectProperties.PROPERTY_NDK, ndkDir.getAbsolutePath()); + } + localProp.save(); + + return (File) localProp.getFile(); + } +} diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/test/fixture/app/AbstractAndroidTestApp.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/test/fixture/app/AbstractAndroidTestApp.java new file mode 100644 index 0000000000..c620524e05 --- /dev/null +++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/test/fixture/app/AbstractAndroidTestApp.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.build.gradle.internal.test.fixture.app; + +import com.google.common.base.Joiner; +import com.google.common.base.Predicate; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Multimap; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.NoSuchElementException; + +/** + * Abstract class implementing AndroidTestApp. + */ +public abstract class AbstractAndroidTestApp implements AndroidTestApp { + private Multimap<String, TestSourceFile> sourceFiles = ArrayListMultimap.create(); + + protected void addFile(TestSourceFile ... files) { + for (TestSourceFile file : files) { + sourceFiles.put(file.getName(), file); + } + } + + @Override + public TestSourceFile getFile(String filename) { + Collection<TestSourceFile> files = sourceFiles.get(filename); + if (files.isEmpty()) { + throw new NoSuchElementException("Unable to file source file: " + filename + "."); + } else if (files.size() > 1) { + throw new IllegalArgumentException( + "Multiple source files named '" + filename + "'. Specify the path to get one " + + "of the following files: \n" + + Joiner.on('\n').join(files)); + } + return files.iterator().next(); + } + + @Override + public TestSourceFile getFile(String filename, final String path) { + Collection<TestSourceFile> files = sourceFiles.get(filename); + return Iterables.find(files, new Predicate<TestSourceFile>() { + @Override + public boolean apply(TestSourceFile testSourceFile) { + return path.equals(testSourceFile.getPath()); + } + }); + } + + @Override + public Collection<TestSourceFile> getAllSourceFiles() { + return sourceFiles.values(); + } + + @Override + public void writeSources(File sourceDir) throws IOException { + for (TestSourceFile srcFile : getAllSourceFiles()) { + srcFile.writeToDir(sourceDir); + } + } + +} diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/test/fixture/app/AndroidTestApp.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/test/fixture/app/AndroidTestApp.java new file mode 100644 index 0000000000..5514e5d5a4 --- /dev/null +++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/test/fixture/app/AndroidTestApp.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.build.gradle.internal.test.fixture.app; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; + +/** + * Interface for an Android test application. + * + * A test application is a collection of source code that may be reused for multiple tests. + */ +public interface AndroidTestApp { + /** + * Return a source file in the test app with the specified filename. + */ + TestSourceFile getFile(String filename); + + /** + * Return a source file in the test app matching the specified filename and path. + */ + TestSourceFile getFile(String filename, String path); + + /** + * Return all source files in this test app. + */ + Collection<TestSourceFile> getAllSourceFiles(); + + /** + * Create all source files in the specified directory. + * + * @param sourceDir Directory to create the source files in. + */ + void writeSources(File sourceDir) throws IOException; +} diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/test/fixture/app/HelloWorldJniApp.groovy b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/test/fixture/app/HelloWorldJniApp.groovy new file mode 100644 index 0000000000..ad0abb498b --- /dev/null +++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/test/fixture/app/HelloWorldJniApp.groovy @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.build.gradle.internal.test.fixture.app +/** + * Simple test application that uses JNI to print a "hello world!". + * + * NOTE: Android project must create an NDK module named "hello-jni". + */ +public class HelloWorldJniApp extends AbstractAndroidTestApp implements AndroidTestApp { + + static private final TestSourceFile javaSource = + new TestSourceFile("main/java/com/example/hellojni", "HelloJni.java", + """ +package com.example.hellojni; + +import android.app.Activity; +import android.widget.TextView; +import android.os.Bundle; + +public class HelloJni extends Activity { + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Create a TextView and set its content from a native function. + TextView tv = new TextView(this); + tv.setText( stringFromJNI() ); + setContentView(tv); + } + + // A native method that is implemented by the 'hello-jni' native library. + public native String stringFromJNI(); + + static { + System.loadLibrary("hello-jni"); + } +} +"""); + + // JNI Implementation in C. + static private final TestSourceFile cSource = + new TestSourceFile("main/jni", "hello-jni.c", +""" +#include <string.h> +#include <jni.h> + +// This is a trivial JNI example where we use a native method +// to return a new VM String. +jstring +Java_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv* env, jobject thiz) +{ + return (*env)->NewStringUTF(env, "hello world!"); +} +"""); + + // JNI Implementation in C++. + static private final TestSourceFile cppSource = + new TestSourceFile("main/jni", "hello-jni.cpp", +""" +#include <string.h> +#include <jni.h> +#include <cctype> + +// This is a trivial JNI example where we use a native method +// to return a new VM String. +extern "C" +jstring +Java_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv* env, jobject thiz) +{ + char greeting[] = "HELLO WORLD!"; + char* ptr = greeting; + while (*ptr) { + *ptr = std::tolower(*ptr); + ++ptr; + } + return env->NewStringUTF(greeting); +} +"""); + + + static private final TestSourceFile resSource = + new TestSourceFile("main/res/values", "strings.xml", + """<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">HelloJni</string> +</resources> +"""); + + static private final TestSourceFile manifest = + new TestSourceFile("main", "AndroidManifest.xml", +"""<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.hellojni" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk android:minSdkVersion="3" /> + <application android:label="@string/app_name"> + <activity android:name=".HelloJni" + android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> +"""); + + static private final TestSourceFile androidTestSource = + new TestSourceFile("androidTest/java/com/example/hellojni", "HelloJniTest.java", +""" +package com.example.hellojni; + +import android.test.ActivityInstrumentationTestCase; + +/** + * This is a simple framework for a test of an Application. See + * {@link android.test.ApplicationTestCase ApplicationTestCase} for more information on + * how to write and extend Application tests. + * <p/> + * To run this test, you can type: + * adb shell am instrument -w \ + * -e class com.example.hellojni.HelloJniTest \ + * com.example.hellojni.tests/android.test.InstrumentationTestRunner + */ +public class HelloJniTest extends ActivityInstrumentationTestCase<HelloJni> { + + public HelloJniTest() { + super("com.example.hellojni", HelloJni.class); + } + + + public void testJniName() { + final HelloJni a = getActivity(); + // ensure a valid handle to the activity has been returned + assertNotNull(a); + + assertTrue("hello world!".equals(a.stringFromJNI())); + } +} +"""); + + public HelloWorldJniApp() { + this(false); + } + public HelloWorldJniApp(boolean useCppSource) { + addFile(javaSource, useCppSource ? cSource : cppSource, resSource, manifest, androidTestSource); + } +} diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/test/fixture/app/TestSourceFile.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/test/fixture/app/TestSourceFile.java new file mode 100644 index 0000000000..0487a6f5e4 --- /dev/null +++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/test/fixture/app/TestSourceFile.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.build.gradle.internal.test.fixture.app; + +import com.google.common.base.Charsets; +import com.google.common.base.Joiner; +import com.google.common.io.Files; + +import java.io.File; +import java.io.IOException; + +/** + * Describes a source file for integration test. + */ +public class TestSourceFile { + private final String path; + private final String name; + private final String content; + + public TestSourceFile(String path, String name, String content) { + this.path = path; + this.name = name; + this.content = content; + } + + public String getPath() { + return path; + } + + public String getName() { + return name; + } + + public String getContent() { + return content; + } + + public File writeToDir(File base) throws IOException { + File file = new File(base, Joiner.on(File.separatorChar).join(path, name)); + writeToFile(file); + return file; + } + + public void writeToFile(File file) throws IOException { + if (!file.exists()) { + Files.createParentDirs(file); + } + Files.append(content, file, Charsets.UTF_8); + } +} + |