Merge "Add support for building test apps."
Xavier Ducrohet [Tue, 28 Aug 2012 16:51:09 +0000 (09:51 -0700)]
63 files changed:
builder/build.gradle
builder/src/main/java/com/android/builder/AndroidBuilder.java
builder/src/main/java/com/android/builder/TemplateProcessor.java
builder/src/main/java/com/android/builder/TestManifestGenerator.java
builder/src/main/java/com/android/builder/VariantConfiguration.java
builder/src/main/resources/com/android/builder/AndroidManifest.template
gradle/src/main/groovy/com/android/build/gradle/AndroidBasePlugin.groovy
gradle/src/main/groovy/com/android/build/gradle/AndroidLogger.groovy
gradle/src/main/groovy/com/android/build/gradle/AndroidPlugin.groovy
gradle/src/main/groovy/com/android/build/gradle/InstallApplication.groovy
gradle/src/main/groovy/com/android/build/gradle/ProcessManifest.groovy [copied from gradle/src/main/groovy/com/android/build/gradle/MergeManifest.groovy with 87% similarity]
gradle/src/main/groovy/com/android/build/gradle/RunTestsTask.groovy [moved from gradle/src/main/groovy/com/android/build/gradle/GenerateTestManifest.groovy with 56% similarity]
gradle/src/main/groovy/com/android/build/gradle/UninstallApplication.groovy [moved from gradle/src/main/groovy/com/android/build/gradle/MergeManifest.groovy with 66% similarity]
gradle/src/main/groovy/com/android/build/gradle/internal/AndroidManifest.groovy [deleted file]
gradle/src/main/groovy/com/android/build/gradle/internal/AndroidSourceSet.groovy
gradle/src/main/groovy/com/android/build/gradle/internal/ApplicationVariant.groovy
gradle/src/main/groovy/com/android/build/gradle/internal/BuildTypeData.groovy
gradle/src/main/groovy/com/android/build/gradle/internal/ProductFlavorData.groovy
gradle/src/main/groovy/com/android/build/gradle/internal/ProductionAppVariant.groovy
gradle/src/main/groovy/com/android/build/gradle/internal/TestAppVariant.groovy
testapps/basic/AllTests.java [new file with mode: 0644]
testapps/basic/build.gradle
testapps/basic/src/main/AndroidManifest.xml
testapps/basic/src/main/java/com/android/tests/basic/Main.java [new file with mode: 0644]
testapps/basic/src/main/res/drawable/icon.png [new file with mode: 0644]
testapps/basic/src/main/res/layout/main.xml
testapps/basic/src/main/res/values/strings.xml
testapps/basic/src/release/res/values/strings.xml [new file with mode: 0644]
testapps/basic/src/test/java/com/android/tests/basic/MainTest.java [new file with mode: 0644]
testapps/dependencies/build.gradle [new file with mode: 0644]
testapps/dependencies/debug.keystore [new file with mode: 0644]
testapps/dependencies/src/main/AndroidManifest.xml [new file with mode: 0644]
testapps/dependencies/src/main/java/org/gradle/sample/BuildType.java [moved from testapps/basic/src/main/java/org/gradle/sample/BuildType.java with 100% similarity]
testapps/dependencies/src/main/java/org/gradle/sample/MainActivity.java [moved from testapps/basic/src/main/java/org/gradle/sample/MainActivity.java with 100% similarity]
testapps/dependencies/src/main/java/org/gradle/sample/Person.java [moved from testapps/basic/src/main/java/org/gradle/sample/Person.java with 100% similarity]
testapps/dependencies/src/main/java/org/gradle/sample/ShowPeopleActivity.java [moved from testapps/basic/src/main/java/org/gradle/sample/ShowPeopleActivity.java with 100% similarity]
testapps/dependencies/src/main/res/drawable-hdpi/ic_launcher.png [moved from testapps/basic/src/main/res/drawable-hdpi/ic_launcher.png with 100% similarity]
testapps/dependencies/src/main/res/drawable-ldpi/ic_launcher.png [moved from testapps/basic/src/main/res/drawable-ldpi/ic_launcher.png with 100% similarity]
testapps/dependencies/src/main/res/drawable-mdpi/ic_launcher.png [moved from testapps/basic/src/main/res/drawable-mdpi/ic_launcher.png with 100% similarity]
testapps/dependencies/src/main/res/drawable-xhdpi/ic_launcher.png [moved from testapps/basic/src/main/res/drawable-xhdpi/ic_launcher.png with 100% similarity]
testapps/dependencies/src/main/res/layout/main.xml [new file with mode: 0644]
testapps/dependencies/src/main/res/values/strings.xml [new file with mode: 0644]
testapps/flavored/build.gradle
testapps/flavored/src/f1/res/values/strings.xml [new file with mode: 0644]
testapps/flavored/src/f2/AndroidManifest.xml [new file with mode: 0644]
testapps/flavored/src/f2/java/com/android/tests/flavored/OtherActivity.java [new file with mode: 0644]
testapps/flavored/src/f2/res/layout/main2.xml [new file with mode: 0644]
testapps/flavored/src/f2/res/values/strings.xml [new file with mode: 0644]
testapps/flavored/src/main/AndroidManifest.xml
testapps/flavored/src/main/java/com/android/tests/flavored/Main.java [new file with mode: 0644]
testapps/flavored/src/main/java/org/gradle/sample/BuildType.java [deleted file]
testapps/flavored/src/main/java/org/gradle/sample/MainActivity.java [deleted file]
testapps/flavored/src/main/java/org/gradle/sample/Person.java [deleted file]
testapps/flavored/src/main/java/org/gradle/sample/ShowPeopleActivity.java [deleted file]
testapps/flavored/src/main/res/drawable-hdpi/ic_launcher.png [deleted file]
testapps/flavored/src/main/res/drawable-ldpi/ic_launcher.png [deleted file]
testapps/flavored/src/main/res/drawable-mdpi/ic_launcher.png [deleted file]
testapps/flavored/src/main/res/drawable-xhdpi/ic_launcher.png [deleted file]
testapps/flavored/src/main/res/drawable/icon.png [new file with mode: 0644]
testapps/flavored/src/main/res/layout/main.xml
testapps/flavored/src/main/res/values/strings.xml
testapps/flavored/src/test/java/com/android/tests/flavored/MainTest.java [new file with mode: 0644]
testapps/flavored/src/testF2/java/com/android/tests/flavored/OtherActivityTest.java [new file with mode: 0644]

index 3d3265e..348fb6d 100644 (file)
@@ -3,12 +3,13 @@ apply plugin: 'eclipse'
 apply plugin: 'idea'
 
 repositories {
-       mavenCentral()
+    mavenCentral()
 }
 
 dependencies {
-       // this is temporary
-       compile fileTree(dir: 'prebuilts', include: '*.jar')
-       testCompile 'junit:junit:3.8.1'
+    // this is temporary
+    compile fileTree(dir: 'prebuilts', include: '*.jar')
+
+    testCompile 'junit:junit:3.8.1'
 }
 
index bdf065d..3126732 100644 (file)
@@ -72,8 +72,9 @@ public class AndroidBuilder {
 
     private IAndroidTarget mTarget;
 
-    // config for the main app. In case of a test app this is used to
+    // config for the main app.
     private VariantConfiguration mVariant;
+    // config for the tested app.
     private VariantConfiguration mTestedVariant;
 
     /**
@@ -183,9 +184,12 @@ public class AndroidBuilder {
             throw new IllegalArgumentException("Target not set.");
         }
 
-        File manifest = mVariant.getDefaultSourceSet().getAndroidManifest();
-
-        String packageName = mVariant.getPackageName(mTestedVariant);
+        String packageName;
+        if (mVariant.getType() == VariantConfiguration.Type.TEST) {
+            packageName = mVariant.getPackageName(mTestedVariant);
+        } else {
+            packageName = mVariant.getPackageFromManifest();
+        }
 
         BuildConfigGenerator generator = new BuildConfigGenerator(
                 sourceOutputDir, packageName, mVariant.getBuildType().isDebuggable());
@@ -223,10 +227,15 @@ public class AndroidBuilder {
             command.add("-v");
         }
 
-        File typeResLocation = mVariant.getBuildTypeSourceSet().getAndroidResources();
-        if (typeResLocation != null && typeResLocation.isDirectory()) {
-            command.add("-S");
-            command.add(typeResLocation.getAbsolutePath());
+        boolean hasResToCrunch = false;
+
+        if (mVariant.getBuildTypeSourceSet() != null) {
+            File typeResLocation = mVariant.getBuildTypeSourceSet().getAndroidResources();
+            if (typeResLocation != null && typeResLocation.isDirectory()) {
+                command.add("-S");
+                command.add(typeResLocation.getAbsolutePath());
+                hasResToCrunch = true;
+            }
         }
 
         for (SourceSet sourceSet : mVariant.getFlavorSourceSets()) {
@@ -234,6 +243,7 @@ public class AndroidBuilder {
             if (flavorResLocation != null && flavorResLocation.isDirectory()) {
                 command.add("-S");
                 command.add(flavorResLocation.getAbsolutePath());
+                hasResToCrunch = true;
             }
         }
 
@@ -241,12 +251,15 @@ public class AndroidBuilder {
         if (mainResLocation != null && mainResLocation.isDirectory()) {
             command.add("-S");
             command.add(mainResLocation.getAbsolutePath());
+            hasResToCrunch = true;
         }
 
         command.add("-C");
         command.add(resOutputDir);
 
-        mCmdLineRunner.runCmdLine(command);
+        if (hasResToCrunch) {
+            mCmdLineRunner.runCmdLine(command);
+        }
     }
 
     /**
@@ -265,12 +278,25 @@ public class AndroidBuilder {
         }
 
         if (mTestedVariant != null) {
-
+            generateTestManifest(outManifestLocation);
         } else {
             mergeManifest(outManifestLocation);
         }
     }
 
+    private void generateTestManifest(String outManifestLocation) {
+        TestManifestGenerator generator = new TestManifestGenerator(outManifestLocation,
+                mVariant.getPackageName(mTestedVariant),
+                mTestedVariant.getPackageName(null),
+                mTestedVariant.getInstrumentationRunner());
+
+        try {
+            generator.generate();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     private void mergeManifest(String outManifestLocation) {
         try {
             File mainLocation = mVariant.getDefaultSourceSet().getAndroidManifest();
@@ -282,7 +308,7 @@ public class AndroidBuilder {
             List<File> flavorManifests = new ArrayList<File>();
             for (SourceSet sourceSet : mVariant.getFlavorSourceSets()) {
                 File f = sourceSet.getAndroidManifest();
-                if (f != null && f.isDirectory()) {
+                if (f != null && f.isFile()) {
                     flavorManifests.add(f);
                 }
             }
@@ -399,22 +425,26 @@ public class AndroidBuilder {
         // TODO: handle libraries!
         boolean useOverlay =  false;
         if (preprocessResDir != null) {
-            command.add("-S");
-            command.add(preprocessResDir);
-            useOverlay = true;
+            File preprocessResFile = new File(preprocessResDir);
+            if (preprocessResFile.isDirectory()) {
+                command.add("-S");
+                command.add(preprocessResDir);
+                useOverlay = true;
+            }
         }
 
-        File typeResLocation = mVariant.getBuildTypeSourceSet().getAndroidResources();
-        if (typeResLocation != null && typeResLocation.isDirectory()) {
-            command.add("-S");
-            command.add(typeResLocation.getAbsolutePath());
-            useOverlay = true;
+        if (mVariant.getBuildTypeSourceSet() != null) {
+            File typeResLocation = mVariant.getBuildTypeSourceSet().getAndroidResources();
+            if (typeResLocation != null && typeResLocation.isDirectory()) {
+                command.add("-S");
+                command.add(typeResLocation.getAbsolutePath());
+                useOverlay = true;
+            }
         }
 
-
         for (SourceSet sourceSet : mVariant.getFlavorSourceSets()) {
             File flavorResLocation = sourceSet.getAndroidResources();
-            if (flavorResLocation != null && typeResLocation.isDirectory()) {
+            if (flavorResLocation != null && flavorResLocation.isDirectory()) {
                 command.add("-S");
                 command.add(flavorResLocation.getAbsolutePath());
                 useOverlay = true;
@@ -422,8 +452,10 @@ public class AndroidBuilder {
         }
 
         File mainResLocation = mVariant.getDefaultSourceSet().getAndroidResources();
-        command.add("-S");
-        command.add(mainResLocation.getAbsolutePath());
+        if (mainResLocation != null && mainResLocation.isDirectory()) {
+            command.add("-S");
+            command.add(mainResLocation.getAbsolutePath());
+        }
 
         if (useOverlay) {
             command.add("--auto-add-overlay");
@@ -536,6 +568,8 @@ public class AndroidBuilder {
             }
         }
 
+        mLogger.verbose("aapt command: %s", command.toString());
+
         mCmdLineRunner.runCmdLine(command);
     }
 
@@ -643,10 +677,14 @@ public class AndroidBuilder {
             // figure out conflicts!
             JavaResourceProcessor resProcessor = new JavaResourceProcessor(packager);
 
-            Set<File> buildTypeJavaResLocations = mVariant.getBuildTypeSourceSet().getJavaResources();
-            for (File buildTypeJavaResLocation : buildTypeJavaResLocations) {
-                if (buildTypeJavaResLocation != null && buildTypeJavaResLocation.isDirectory()) {
-                    resProcessor.addSourceFolder(buildTypeJavaResLocation.getAbsolutePath());
+            if (mVariant.getBuildTypeSourceSet() != null) {
+                Set<File> buildTypeJavaResLocations =
+                        mVariant.getBuildTypeSourceSet().getJavaResources();
+                for (File buildTypeJavaResLocation : buildTypeJavaResLocations) {
+                    if (buildTypeJavaResLocation != null &&
+                            buildTypeJavaResLocation.isDirectory()) {
+                        resProcessor.addSourceFolder(buildTypeJavaResLocation.getAbsolutePath());
+                    }
                 }
             }
 
index 591d9a9..11ec932 100644 (file)
@@ -65,14 +65,18 @@ class TemplateProcessor {
     private String readEmbeddedTextFile(InputStream templateStream) throws IOException {
         BufferedReader reader = new BufferedReader(new InputStreamReader(templateStream));
 
-        String line;
-        StringBuilder total = new StringBuilder(reader.readLine());
-        while ((line = reader.readLine()) != null) {
-            total.append('\n');
-            total.append(line);
-        }
+        try {
+            String line;
+            StringBuilder total = new StringBuilder(reader.readLine());
+            while ((line = reader.readLine()) != null) {
+                total.append('\n');
+                total.append(line);
+            }
 
-        return total.toString();
+            return total.toString();
+        } finally {
+            reader.close();
+        }
     }
 
     private void writeFile(File file, String content) throws IOException {
index fa1805b..cd335ed 100644 (file)
@@ -15,6 +15,8 @@
  */
 package com.android.builder;
 
+import com.android.annotations.NonNull;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.HashMap;
@@ -25,32 +27,30 @@ import java.util.Map;
  */
 public class TestManifestGenerator {
 
-    private final static String TEMPLATE = "BuildConfig.template";
+    private final static String TEMPLATE = "AndroidManifest.template";
     private final static String PH_PACKAGE = "#PACKAGE#";
-    private final static String PH_TEST_PACKAGE = "#TESTPACKAGE#";
+    private final static String PH_TESTED_PACKAGE = "#TESTEDPACKAGE#";
     private final static String PH_TEST_RUNNER = "#TESTRUNNER#";
 
-    private final static String DEFAULT_TEST_RUNNER = "android.test.InstrumentationTestRunner";
-
     private final String mOutputFile;
     private final String mPackageName;
-    private final String mTestPackageName;
+    private final String mTestedPackageName;
     private final String mTestRunnerName;
 
-    TestManifestGenerator(String outputFile,
-                          String packageName,
-                          String testPackageName,
-                          String testRunnerName) {
+    TestManifestGenerator(@NonNull String outputFile,
+                          @NonNull String packageName,
+                          @NonNull String testedPackageName,
+                          @NonNull String testRunnerName) {
         mOutputFile = outputFile;
         mPackageName = packageName;
-        mTestPackageName = testPackageName;
-        mTestRunnerName = testRunnerName != null ? testRunnerName : DEFAULT_TEST_RUNNER;
+        mTestedPackageName = testedPackageName;
+        mTestRunnerName = testRunnerName;
     }
 
     public void generate() throws IOException {
         Map<String, String> map = new HashMap<String, String>();
         map.put(PH_PACKAGE, mPackageName);
-        map.put(PH_TEST_PACKAGE, mTestPackageName);
+        map.put(PH_TESTED_PACKAGE, mTestedPackageName);
         map.put(PH_TEST_RUNNER, mTestRunnerName);
 
         TemplateProcessor processor = new TemplateProcessor(
index 0d211fa..082b38c 100644 (file)
@@ -89,7 +89,8 @@ public class VariantConfiguration {
      *
      * @param sourceSet the configured product flavor
      */
-    public void addProductFlavor(@NonNull ProductFlavor productFlavor, @NonNull SourceSet sourceSet) {
+    public void addProductFlavor(@NonNull ProductFlavor productFlavor,
+                                 @NonNull SourceSet sourceSet) {
         mFlavorConfigs.add(productFlavor);
         mFlavorSourceSets.add(sourceSet);
         mMergedFlavor = productFlavor.mergeOver(mMergedFlavor);
@@ -233,7 +234,7 @@ public class VariantConfiguration {
         String packageName = mMergedFlavor.getPackageName();
         String packageSuffix = mBuildType.getPackageNameSuffix();
 
-        if (packageSuffix != null) {
+        if (packageSuffix != null && packageSuffix.length() > 0) {
             if (packageName == null) {
                 packageName = getPackageFromManifest();
             }
@@ -263,6 +264,13 @@ public class VariantConfiguration {
         return testPackage;
     }
 
+    private final static String DEFAULT_TEST_RUNNER = "android.test.InstrumentationTestRunner";
+
+    public String getInstrumentationRunner() {
+        String runner = mMergedFlavor.getTestInstrumentationRunner();
+        return runner != null ? runner : DEFAULT_TEST_RUNNER;
+    }
+
     /**
      * Reads the package name from the manifest.
      * @return
index b4a792f..30d392b 100644 (file)
@@ -7,6 +7,6 @@
     </application>
 
     <instrumentation android:name="#TESTRUNNER#"
-                     android:targetPackage="#TESTPACKAGE#"
-                     android:label="Tests for #TESTPACKAGE#"/>
+                     android:targetPackage="#TESTEDPACKAGE#"
+                     android:label="Tests for #TESTEDPACKAGE#"/>
 </manifest>
index 93e0d38..87edf73 100644 (file)
@@ -52,7 +52,7 @@ abstract class AndroidBasePlugin {
         mainSourceSet = project.sourceSets.add("main")
         testSourceSet = project.sourceSets.add("test")
 
-        project.tasks.assemble.description = "Assembles all variants of all applications, and secondary packages."
+        project.tasks.assemble.description = "Assembles all variants of all applications and secondary packages."
     }
 
     protected setDefaultConfig(ProductFlavor defaultConfig) {
index e33019b..0241b4f 100644 (file)
@@ -34,6 +34,7 @@ class AndroidLogger implements ILogger {
     void error(Throwable throwable, String s, Object... objects) {
         if (throwable == null) {
             logger.log(LogLevel.ERROR, String.format(s, objects))
+
         } else {
             logger.log(LogLevel.ERROR, String.format(s, objects), throwable)
         }
@@ -41,16 +42,16 @@ class AndroidLogger implements ILogger {
 
     @Override
     void warning(String s, Object... objects) {
-        logger.log(LogLevel.WARN, s, objects)
+        logger.log(LogLevel.WARN, String.format(s, objects))
     }
 
     @Override
     void info(String s, Object... objects) {
-        logger.log(LogLevel.INFO, s, objects)
+        logger.log(LogLevel.INFO, String.format(s, objects))
     }
 
     @Override
     void verbose(String s, Object... objects) {
-        logger.log(LogLevel.INFO, s, objects)
+        logger.log(LogLevel.INFO, String.format(s, objects))
     }
 }
index 3898578..de3b1f9 100644 (file)
@@ -15,7 +15,6 @@
  */
 package com.android.build.gradle
 
-import com.android.build.gradle.internal.AndroidManifest
 import com.android.build.gradle.internal.AndroidSourceSet
 import com.android.build.gradle.internal.ApplicationVariant
 import com.android.build.gradle.internal.BuildTypeData
@@ -36,7 +35,11 @@ class AndroidPlugin extends AndroidBasePlugin implements Plugin<Project> {
     private final Map<String, ProductFlavorData> productFlavors = [:]
 
     private AndroidExtension extension
-    private AndroidManifest mainManifest
+
+    private Task installAllForTests
+    private Task runAllTests
+    private Task uninstallAll
+    private Task assembleTest
 
     @Override
     void apply(Project project) {
@@ -48,7 +51,8 @@ class AndroidPlugin extends AndroidBasePlugin implements Plugin<Project> {
             project.services.get(Instantiator).newInstance(ProductFlavor, name)
         }
 
-        extension = project.extensions.create('android', AndroidExtension, buildTypes, productFlavors)
+        extension = project.extensions.create('android', AndroidExtension,
+                buildTypes, productFlavors)
         setDefaultConfig(extension.defaultConfig)
 
         findSdk(project)
@@ -72,6 +76,19 @@ class AndroidPlugin extends AndroidBasePlugin implements Plugin<Project> {
                     "Removing product flavors is not implemented yet.")
         }
 
+        installAllForTests = project.tasks.add("installAllForTests")
+        installAllForTests.group = "Verification"
+        installAllForTests.description = "Installs all applications needed to run tests."
+
+        runAllTests = project.tasks.add("runAllTests")
+        runAllTests.group = "Verification"
+        runAllTests.description = "Runs all tests."
+
+        uninstallAll = project.tasks.add("uninstallAll")
+        uninstallAll.description = "Uninstall all applications."
+        uninstallAll.group = "Install"
+
+        project.tasks.check.dependsOn installAllForTests, runAllTests
 
         project.afterEvaluate {
             createAndroidTasks()
@@ -116,12 +133,22 @@ class AndroidPlugin extends AndroidBasePlugin implements Plugin<Project> {
         if (productFlavors.isEmpty()) {
             createTasksForDefaultBuild()
         } else {
+            // there'll be more than one test app, so we need a top level assembleTest
+            assembleTest = project.tasks.add("assembleTest")
+            assembleTest.group = "Build"
+            assembleTest.description = "Assembles all the Test applications"
+
             productFlavors.values().each { ProductFlavorData productFlavorData ->
                 createTasksForFlavoredBuild(productFlavorData)
             }
         }
     }
 
+    /**
+     * Creates Tasks for non-flavored build. This means assembleDebug, assembleRelease, and other
+     * assemble<Type> are directly building the <type> build instead of all build of the given
+     * <type>.
+     */
     private createTasksForDefaultBuild() {
 
         BuildTypeData testData = buildTypes[extension.testBuildType]
@@ -141,10 +168,12 @@ class AndroidPlugin extends AndroidBasePlugin implements Plugin<Project> {
                     buildTypeData.buildType, buildTypeData.androidSourceSet,
                     VariantConfiguration.Type.DEFAULT)
 
+            boolean isTestedVariant =  buildTypeData == testData
+
             ProductionAppVariant productionAppVariant = addVariant(variantConfig,
-                    buildTypeData.assembleTask)
+                    buildTypeData.assembleTask, isTestedVariant)
 
-            if (buildTypeData == testData) {
+            if (isTestedVariant) {
                 testedVariantConfig = variantConfig
                 testedVariant = productionAppVariant
             }
@@ -153,13 +182,18 @@ class AndroidPlugin extends AndroidBasePlugin implements Plugin<Project> {
         assert testedVariantConfig != null && testedVariant != null
 
         def variantTestConfig = new VariantConfiguration(
-                defaultConfigData.productFlavor, defaultConfigData.androidSourceSet,
+                defaultConfigData.productFlavor, defaultConfigData.androidTestSourceSet,
                 testData.buildType, null,
                 VariantConfiguration.Type.TEST)
 
         addTestVariant(variantTestConfig, testedVariantConfig, testedVariant)
     }
 
+    /**
+     * Creates Task for a given flavor. This will create tasks for all build types for the given
+     * flavor.
+     * @param productFlavorData the flavor to build.
+     */
     private createTasksForFlavoredBuild(ProductFlavorData productFlavorData) {
 
         BuildTypeData testData = buildTypes[extension.testBuildType]
@@ -180,12 +214,15 @@ class AndroidPlugin extends AndroidBasePlugin implements Plugin<Project> {
             variantConfig.addProductFlavor(productFlavorData.productFlavor,
                     productFlavorData.androidSourceSet)
 
-            ProductionAppVariant productionAppVariant = addVariant(variantConfig, null)
+            boolean isTestedVariant =  buildTypeData == testData
+
+            ProductionAppVariant productionAppVariant = addVariant(variantConfig, null,
+                    isTestedVariant)
 
             buildTypeData.assembleTask.dependsOn productionAppVariant.assembleTask
             productFlavorData.assembleTask.dependsOn productionAppVariant.assembleTask
 
-            if (buildTypeData == testData) {
+            if (isTestedVariant) {
                 testedVariantConfig = variantConfig
                 testedVariant = productionAppVariant
             }
@@ -203,32 +240,24 @@ class AndroidPlugin extends AndroidBasePlugin implements Plugin<Project> {
         addTestVariant(variantTestConfig, testedVariantConfig, testedVariant)
     }
 
-
-
-    private AndroidManifest getMainManifest() {
-        if (mainManifest == null) {
-            mainManifest = new AndroidManifest()
-            mainManifest.load(project.file("src/main/AndroidManifest.xml"))
-        }
-        return mainManifest
-    }
-
     /**
      * Creates build tasks for a given variant.
      * @param variantConfig
      * @param assembleTask an optional assembleTask to be used. If null, a new one is created in the
      *                     returned ProductAppVariant instance.
+     * @param isTestApk whether this apk is needed for running tests
      * @return
      */
-    private ProductionAppVariant addVariant(VariantConfiguration variantConfig, Task assembleTask) {
+    private ProductionAppVariant addVariant(VariantConfiguration variantConfig, Task assembleTask,
+                                            boolean isTestApk) {
 
         def variant = new ProductionAppVariant(variantConfig)
 
-        // Add a task to merge the manifest(s)
-        def mergeManifestTask = project.tasks.add("process${variant.name}Manifest", MergeManifest)
-        mergeManifestTask.plugin = this
-        mergeManifestTask.variant = variant
-        mergeManifestTask.conventionMapping.mergedManifest = { project.file("$project.buildDir/manifests/$variant.dirName/AndroidManifest.xml") }
+        // Add a task to process the manifest(s)
+        def processManifestTask = project.tasks.add("process${variant.name}Manifest", ProcessManifest)
+        processManifestTask.plugin = this
+        processManifestTask.variant = variant
+        processManifestTask.conventionMapping.mergedManifest = { project.file("$project.buildDir/manifests/$variant.dirName/AndroidManifest.xml") }
 
         // Add a task to crunch resource files
         def crunchTask = project.tasks.add("crunchAndroid${variant.name}Res", CrunchResources)
@@ -244,10 +273,10 @@ class AndroidPlugin extends AndroidBasePlugin implements Plugin<Project> {
 
         // Add a task to generate resource source files
         def processResources = project.tasks.add("processAndroid${variant.name}Res", ProcessResources)
-        processResources.dependsOn mergeManifestTask, crunchTask
+        processResources.dependsOn processManifestTask, crunchTask
         processResources.plugin = this
         processResources.variant = variant
-        processResources.conventionMapping.manifestFile = { mergeManifestTask.mergedManifest }
+        processResources.conventionMapping.manifestFile = { processManifestTask.mergedManifest }
         processResources.conventionMapping.crunchDir = { crunchTask.outputDir }
         // TODO: unify with generateBuilderConfig somehow?
         processResources.conventionMapping.sourceOutputDir = { project.file("$project.buildDir/source/$variant.dirName") }
@@ -286,7 +315,7 @@ class AndroidPlugin extends AndroidBasePlugin implements Plugin<Project> {
         variant.resourcePackage = project.files({processResources.packageFile}) { builtBy processResources }
         variant.compileTask = compileTask
 
-        Task returnTask = addPackageTasks(variant, assembleTask)
+        Task returnTask = addPackageTasks(variant, assembleTask, isTestApk)
         if (returnTask != null) {
             variant.assembleTask = returnTask
         }
@@ -300,11 +329,11 @@ class AndroidPlugin extends AndroidBasePlugin implements Plugin<Project> {
 
         def variant = new TestAppVariant(variantConfig, testedVariantConfig)
 
-        // Add a task to merge the manifest(s)
-        def mergeManifestTask = project.tasks.add("process${variant.name}Manifest", MergeManifest)
-        mergeManifestTask.plugin = this
-        mergeManifestTask.variant = variant
-        mergeManifestTask.conventionMapping.mergedManifest = { project.file("$project.buildDir/manifests/$variant.dirName/AndroidManifest.xml") }
+        // Add a task to process the manifest
+        def processManifestTask = project.tasks.add("process${variant.name}Manifest", ProcessManifest)
+        processManifestTask.plugin = this
+        processManifestTask.variant = variant
+        processManifestTask.conventionMapping.mergedManifest = { project.file("$project.buildDir/manifests/$variant.dirName/AndroidManifest.xml") }
 
         // Add a task to crunch resource files
         def crunchTask = project.tasks.add("crunchAndroid${variant.name}Res", CrunchResources)
@@ -314,16 +343,17 @@ class AndroidPlugin extends AndroidBasePlugin implements Plugin<Project> {
 
         // Add a task to create the BuildConfig class
         def generateBuildConfigTask = project.tasks.add("generate${variant.name}BuildConfig", GenerateBuildConfigTask)
+        generateBuildConfigTask.dependsOn processManifestTask // this is because the manifest can be generated.
         generateBuildConfigTask.plugin = this
         generateBuildConfigTask.variant = variant
         generateBuildConfigTask.conventionMapping.sourceOutputDir = { project.file("$project.buildDir/source/${variant.dirName}") }
 
         // Add a task to generate resource source files
         def processResources = project.tasks.add("processAndroid${variant.name}Res", ProcessResources)
-        processResources.dependsOn mergeManifestTask, crunchTask
+        processResources.dependsOn processManifestTask, crunchTask
         processResources.plugin = this
         processResources.variant = variant
-        processResources.conventionMapping.manifestFile = { mergeManifestTask.mergedManifest }
+        processResources.conventionMapping.manifestFile = { processManifestTask.mergedManifest }
         processResources.conventionMapping.crunchDir = { crunchTask.outputDir }
         // TODO: unify with generateBuilderConfig somehow?
         processResources.conventionMapping.sourceOutputDir = { project.file("$project.buildDir/source/$variant.dirName") }
@@ -345,22 +375,30 @@ class AndroidPlugin extends AndroidBasePlugin implements Plugin<Project> {
         }
 
         testCompile.classpath = ((AndroidSourceSet)variantConfig.defaultSourceSet).sourceSet.compileClasspath + testedVariant.runtimeClasspath
+
         testCompile.conventionMapping.destinationDir = { project.file("$project.buildDir/classes/$variant.dirName") }
         testCompile.doFirst {
             testCompile.options.bootClasspath = getRuntimeJars(variant)
         }
 
-        // TODO: remove the classpath once the jar deps are set in the Variantconfig, so that we can pre-dex
-        variant.runtimeClasspath = testCompile.outputs.files + testCompile.classpath
+        variant.runtimeClasspath = testCompile.outputs.files
         variant.resourcePackage = project.files({processResources.packageFile}) { builtBy processResources }
         variant.compileTask = testCompile
 
-        Task assembleTask = addPackageTasks(variant, null)
+        Task assembleTask = addPackageTasks(variant, null, true /*isTestApk*/)
+
+        if (assembleTest != null) {
+            assembleTest.dependsOn assembleTask
+        }
+
+        def runTestsTask = project.tasks.add("Run${variant.name}Tests", RunTestsTask)
+        runTestsTask.sdkDir = sdkDir
+        runTestsTask.variant = variant
 
-        project.tasks.check.dependsOn assembleTask
+        runAllTests.dependsOn runTestsTask
     }
 
-    private Task addPackageTasks(ApplicationVariant variant, Task assembleTask) {
+    private Task addPackageTasks(ApplicationVariant variant, Task assembleTask, boolean isTestApk) {
         // Add a dex task
         def dexTaskName = "dex${variant.name}"
         def dexTask = project.tasks.add(dexTaskName, Dex)
@@ -403,21 +441,36 @@ class AndroidPlugin extends AndroidBasePlugin implements Plugin<Project> {
 
             // Add a task to install the application package
             def installApp = project.tasks.add("install${variant.name}", InstallApplication)
+            installApp.description = "Installs the " + variant.description
+            installApp.group = "Install"
             installApp.dependsOn appTask
             installApp.conventionMapping.packageFile = { appTask.outputFile }
             installApp.sdkDir = sdkDir
+
+            if (isTestApk) {
+                installAllForTests.dependsOn installApp
+            }
         }
 
         // Add an assemble task
         Task returnTask = null
         if (assembleTask == null) {
             assembleTask = project.tasks.add("assemble${variant.name}")
-            assembleTask.description = variant.description
+            assembleTask.description = "Assembles the " + variant.description
             assembleTask.group = "Build"
             returnTask = assembleTask
         }
         assembleTask.dependsOn appTask
 
+        // add an uninstall task
+        def uninstallApp = project.tasks.add("uninstall${variant.name}", UninstallApplication)
+        uninstallApp.description = "Uninstalls the " + variant.description
+        uninstallApp.group = "Install"
+        uninstallApp.variant = variant
+        uninstallApp.sdkDir = sdkDir
+
+        uninstallAll.dependsOn uninstallApp
+
         return returnTask
     }
 
index 58cf2e7..83a2aa0 100644 (file)
@@ -30,6 +30,7 @@ class InstallApplication extends DefaultTask {
         project.exec {
             executable = new File(getSdkDir(), "platform-tools/adb")
             args 'install'
+            args '-r'
             args getPackageFile()
         }
     }
@@ -20,13 +20,13 @@ import org.gradle.api.tasks.TaskAction
 
 /**
  */
-class MergeManifest extends BaseAndroidTask {
+class ProcessManifest extends BaseAndroidTask {
 
     @OutputFile
     File mergedManifest
 
     @TaskAction
     void generate() {
-        getBuilder().mergeManifest(getMergedManifest().absolutePath)
+        getBuilder().processManifest(getMergedManifest().absolutePath)
     }
 }
  */
 package com.android.build.gradle
 
-import org.gradle.api.DefaultTask
-import org.gradle.api.tasks.InputFile
-import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.TaskAction
-import org.gradle.api.tasks.OutputFile
-import com.android.build.gradle.internal.AndroidManifest
-import org.gradle.api.tasks.Optional
-
-class GenerateTestManifest extends DefaultTask {
-    @InputFile @Optional
-    File sourceFile
+import org.gradle.api.tasks.Input
 
-    @OutputFile
-    File outputFile
+/**
+ * Run tests for a given variant
+ */
+class RunTestsTask extends BaseAndroidTask {
 
     @Input
-    String packageName
+    File sdkDir
 
     @TaskAction
-    def generate() {
-        AndroidManifest manifest = new AndroidManifest()
-        if (getSourceFile() != null) {
-            manifest.load(getSourceFile())
+    void generate() {
+        List<String> command = variant.runCommand
+
+        logger.info("Running tests with command: " + command)
+        project.exec {
+            executable = new File(getSdkDir(), "platform-tools/adb")
+            args command
         }
-        manifest.packageName = getPackageName()
-        manifest.save(getOutputFile())
     }
 }
-
  */
 package com.android.build.gradle
 
-import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.TaskAction
 
-/**
- */
-class MergeManifest extends BaseAndroidTask {
-
-    @OutputFile
-    File mergedManifest
+class UninstallApplication extends BaseAndroidTask {
+    @Input
+    File sdkDir
 
     @TaskAction
     void generate() {
-        getBuilder().mergeManifest(getMergedManifest().absolutePath)
+        String packageName = variant.package
+        logger.info("Uninstalling app: " + packageName)
+        project.exec {
+            executable = new File(getSdkDir(), "platform-tools/adb")
+            args "uninstall"
+            args packageName
+        }
     }
 }
diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/AndroidManifest.groovy b/gradle/src/main/groovy/com/android/build/gradle/internal/AndroidManifest.groovy
deleted file mode 100644 (file)
index 816e6e1..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2012 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
-
-import javax.xml.parsers.DocumentBuilderFactory
-import org.w3c.dom.Element
-import javax.xml.transform.TransformerFactory
-import javax.xml.transform.dom.DOMSource
-import javax.xml.transform.stream.StreamResult
-
-class AndroidManifest {
-    private static final String ANDROID_NAME_SPACE = "http://schemas.android.com/apk/res/android"
-    String packageName
-    Integer versionCode
-    String versionName
-    Element manifestXml
-
-    void load(File sourceFile) {
-        def builderFactory = DocumentBuilderFactory.newInstance()
-        builderFactory.setNamespaceAware(true)
-        manifestXml = builderFactory.newDocumentBuilder().parse(sourceFile).documentElement
-
-        packageName = manifestXml.getAttribute("package")
-        versionCode = manifestXml.getAttributeNS(ANDROID_NAME_SPACE, "versionCode").toInteger()
-        versionName = manifestXml.getAttributeNS(ANDROID_NAME_SPACE, "versionName")
-    }
-
-    void save(File destFile) {
-        if (manifestXml == null) {
-            def builderFactory = DocumentBuilderFactory.newInstance()
-            builderFactory.setNamespaceAware(true)
-            def doc = builderFactory.newDocumentBuilder().newDocument()
-            manifestXml = doc.createElement("manifest")
-            manifestXml.setAttributeNS(ANDROID_NAME_SPACE, "versionCode", "")
-            manifestXml.setAttributeNS(ANDROID_NAME_SPACE, "versionName", "")
-        }
-        manifestXml.setAttribute("package", packageName)
-        manifestXml.getAttributeNodeNS(ANDROID_NAME_SPACE, "versionCode").setValue(versionCode.toString())
-        manifestXml.getAttributeNodeNS(ANDROID_NAME_SPACE, "versionName").setValue(versionName)
-
-        TransformerFactory.newInstance().newTransformer().transform(new DOMSource(manifestXml), new StreamResult(destFile))
-    }
-}
-
index fb7b06a..71cb98a 100644 (file)
@@ -27,9 +27,9 @@ public class AndroidSourceSet implements com.android.builder.SourceSet {
     private final String name
     private final Project project
 
-    public AndroidSourceSet(SourceSet sourceSet, String name, Project project) {
+    public AndroidSourceSet(SourceSet sourceSet, Project project) {
         this.sourceSet = sourceSet
-        this.name = name
+        this.name = sourceSet.name
         this.project = project
     }
 
index da39b7e..3b03549 100644 (file)
@@ -42,5 +42,9 @@ public interface ApplicationVariant {
 
     Compile getCompileTask()
 
+    List<String> getRunCommand()
+
+    String getPackage()
+
     AndroidBuilder createBuilder(AndroidBasePlugin androidBasePlugin)
 }
index 47027bb..65799e6 100644 (file)
@@ -31,7 +31,7 @@ class BuildTypeData {
     BuildTypeData(BuildType buildType, SourceSet mainSource, Project project) {
         this.buildType = buildType
         this.mainSource = mainSource
-        androidSourceSet = new AndroidSourceSet(mainSource, buildType.name, project)
+        androidSourceSet = new AndroidSourceSet(mainSource, project)
 
         assembleTask = project.tasks.add("assemble${buildType.name.capitalize()}")
         assembleTask.description = "Assembles all ${buildType.name.capitalize()} builds"
index ac6382a..2334199 100644 (file)
@@ -36,10 +36,10 @@ class ProductFlavorData {
         this.productFlavor = productFlavor
 
         this.mainSource = mainSource
-        androidSourceSet = new AndroidSourceSet(mainSource, productFlavor.name, project)
+        androidSourceSet = new AndroidSourceSet(mainSource, project)
 
         this.testSource = testSource
-        androidTestSourceSet = new AndroidSourceSet(testSource, productFlavor.name, project)
+        androidTestSourceSet = new AndroidSourceSet(testSource, project)
 
         if (productFlavor.name != "main") {
             assembleTask = project.tasks.add("assemble${productFlavor.name.capitalize()}")
index 3b4d050..9c29b5f 100644 (file)
@@ -41,9 +41,9 @@ class ProductionAppVariant implements ApplicationVariant {
 
     String getDescription() {
         if (variant.hasFlavors()) {
-            return "Assembles the ${variant.buildType.name.capitalize()} build for flavor ${variant.firstFlavor.name.capitalize()}"
+            return "${variant.buildType.name.capitalize()} build for flavor ${variant.firstFlavor.name.capitalize()}"
         } else {
-            return "Assembles the ${variant.buildType.name.capitalize()} build"
+            return "${variant.buildType.name.capitalize()} build"
         }
     }
 
@@ -75,6 +75,15 @@ class ProductionAppVariant implements ApplicationVariant {
     }
 
     @Override
+    List<String> getRunCommand() {
+        throw new UnsupportedOperationException()
+    }
+
+    String getPackage() {
+        return variant.getPackageName(null)
+    }
+
+    @Override
     AndroidBuilder createBuilder(AndroidBasePlugin androidBasePlugin) {
         AndroidBuilder androidBuilder = new AndroidBuilder(
                 androidBasePlugin.sdkParser,
index b452724..eabf665 100644 (file)
@@ -42,9 +42,9 @@ class TestAppVariant implements ApplicationVariant {
     @Override
     String getDescription() {
         if (variant.hasFlavors()) {
-            return "Assembles the Test build for the ${variant.firstFlavor.name.capitalize()}${variant.buildType.name.capitalize()} build"
+            return "Test build for the ${variant.firstFlavor.name.capitalize()}${variant.buildType.name.capitalize()} build"
         } else {
-            return "Assembles the Test for the ${variant.buildType.name.capitalize()} build"
+            return "Test for the ${variant.buildType.name.capitalize()} build"
         }
     }
 
@@ -75,6 +75,19 @@ class TestAppVariant implements ApplicationVariant {
     }
 
     @Override
+    List<String> getRunCommand() {
+        String[] args = [ "shell", "am", "instrument", "-w",
+                variant.getPackageName(testedVariant) + "/" + testedVariant.instrumentationRunner]
+
+        return Arrays.asList(args)
+    }
+
+    @Override
+    String getPackage() {
+        return variant.getPackageName(testedVariant)
+    }
+
+    @Override
     AndroidBuilder createBuilder(AndroidBasePlugin androidBasePlugin) {
         AndroidBuilder androidBuilder = new AndroidBuilder(
                 androidBasePlugin.sdkParser,
diff --git a/testapps/basic/AllTests.java b/testapps/basic/AllTests.java
new file mode 100644 (file)
index 0000000..b19ddfc
--- /dev/null
@@ -0,0 +1,15 @@
+package com.android.tests.basic;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import android.test.suitebuilder.TestSuiteBuilder;
+
+public class AllTests extends TestSuite {
+
+    public static Test suite() {
+        return new TestSuiteBuilder(AllTests.class)
+                .includeAllPackagesUnderHere()
+                .build();
+    }
+}
index 7a56c1b..5ad9b29 100644 (file)
@@ -11,29 +11,20 @@ buildscript {
 }
 apply plugin: 'android'
 
-version='1.0'
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    compile 'com.google.guava:guava:11.0.2'
-}
-
 android {
     target = "android-15"
+    testBuildType = "debug"
 
-       defaultConfig {
-               signingStoreLocation = "debug.keystore"
-               signingStorePassword = "android"
-               signingKeyAlias = "androiddebugkey"
-               signingKeyPassword = "android"
-       }
+    defaultConfig {
+        signingStoreLocation = "debug.keystore"
+        signingStorePassword = "android"
+        signingKeyAlias = "androiddebugkey"
+        signingKeyPassword = "android"
+    }
 
-       buildTypes {
-               blah {
-                       packageNameSuffix = ".blah"
-               }
-       }
+    buildTypes {
+        debug {
+            packageNameSuffix = ".debug"
+        }
+    }
 }
\ No newline at end of file
index 1f411e0..a34d937 100644 (file)
@@ -1,22 +1,13 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-      package="org.gradle.sample"
-      android:versionCode="1"
-      android:versionName="1.0">
-    <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
-        <activity android:name="MainActivity"
+      package="com.android.tests.basic">
+    <application android:label="@string/app_name" android:icon="@drawable/icon">
+        <activity android:name=".Main"
                   android:label="@string/app_name">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        <activity
-                android:name="ShowPeopleActivity"
-                android:label="@string/title_activity_display_message" >
-                <meta-data
-                    android:name="android.support.PARENT_ACTIVITY"
-                    android:value="org.gradle.sample.MainActivity" />
-            </activity>
     </application>
 </manifest>
diff --git a/testapps/basic/src/main/java/com/android/tests/basic/Main.java b/testapps/basic/src/main/java/com/android/tests/basic/Main.java
new file mode 100644 (file)
index 0000000..2b0e698
--- /dev/null
@@ -0,0 +1,15 @@
+package com.android.tests.basic;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class Main extends Activity
+{
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+}
diff --git a/testapps/basic/src/main/res/drawable/icon.png b/testapps/basic/src/main/res/drawable/icon.png
new file mode 100644 (file)
index 0000000..a07c69f
Binary files /dev/null and b/testapps/basic/src/main/res/drawable/icon.png differ
index ccc59fb..b199751 100644 (file)
@@ -1,12 +1,14 @@
 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="horizontal"
+    android:orientation="vertical"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     >
-    <Button
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/button_send"
-            android:onClick="sendMessage" />
+<TextView
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:text="Test App - Basic"
+    android:id="@+id/text"
+    />
 </LinearLayout>
+
index 8eda275..60ea2d0 100644 (file)
@@ -1,6 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-    <string name="app_name">Basic App</string>
-    <string name="button_send">Go</string>
-    <string name="title_activity_display_message">People</string>
+    <string name="app_name">_Test-Basic</string>
 </resources>
diff --git a/testapps/basic/src/release/res/values/strings.xml b/testapps/basic/src/release/res/values/strings.xml
new file mode 100644 (file)
index 0000000..532909c
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">_Test-Basic-Release</string>
+</resources>
diff --git a/testapps/basic/src/test/java/com/android/tests/basic/MainTest.java b/testapps/basic/src/test/java/com/android/tests/basic/MainTest.java
new file mode 100644 (file)
index 0000000..7cf7329
--- /dev/null
@@ -0,0 +1,38 @@
+package com.android.tests.basic;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.widget.TextView;
+
+public class MainTest extends ActivityInstrumentationTestCase2<Main> {
+
+    private TextView mTextView;
+
+    /**
+     * Creates an {@link ActivityInstrumentationTestCase2} that tests the {@link Main} activity.
+     */
+    public MainTest() {
+        super(Main.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        final Main a = getActivity();
+        // ensure a valid handle to the activity has been returned
+        assertNotNull(a);
+        mTextView = (TextView) a.findViewById(R.id.text);
+    }
+
+    /**
+     * The name 'test preconditions' is a convention to signal that if this
+     * test doesn't pass, the test case was not set up properly and it might
+     * explain any and all failures in other tests.  This is not guaranteed
+     * to run before other tests, as junit uses reflection to find the tests.
+     */
+    @MediumTest
+    public void testPreconditions() {
+        assertNotNull(mTextView);
+    }
+}
+
diff --git a/testapps/dependencies/build.gradle b/testapps/dependencies/build.gradle
new file mode 100644 (file)
index 0000000..7a56c1b
--- /dev/null
@@ -0,0 +1,39 @@
+//
+// A basic Android application that follows all the conventions
+//
+buildscript {
+    repositories {
+        maven { url '../../repo' }
+    }
+    dependencies {
+        classpath 'com.android.build:gradle-android:0.1-SNAPSHOT'
+    }
+}
+apply plugin: 'android'
+
+version='1.0'
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    compile 'com.google.guava:guava:11.0.2'
+}
+
+android {
+    target = "android-15"
+
+       defaultConfig {
+               signingStoreLocation = "debug.keystore"
+               signingStorePassword = "android"
+               signingKeyAlias = "androiddebugkey"
+               signingKeyPassword = "android"
+       }
+
+       buildTypes {
+               blah {
+                       packageNameSuffix = ".blah"
+               }
+       }
+}
\ No newline at end of file
diff --git a/testapps/dependencies/debug.keystore b/testapps/dependencies/debug.keystore
new file mode 100644 (file)
index 0000000..389278e
Binary files /dev/null and b/testapps/dependencies/debug.keystore differ
diff --git a/testapps/dependencies/src/main/AndroidManifest.xml b/testapps/dependencies/src/main/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..1f411e0
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="org.gradle.sample"
+      android:versionCode="1"
+      android:versionName="1.0">
+    <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
+        <activity android:name="MainActivity"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity
+                android:name="ShowPeopleActivity"
+                android:label="@string/title_activity_display_message" >
+                <meta-data
+                    android:name="android.support.PARENT_ACTIVITY"
+                    android:value="org.gradle.sample.MainActivity" />
+            </activity>
+    </application>
+</manifest>
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..ccc59fb8e8a1b19c8e4a93dcd4533e17b8bd01a4
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+    <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/button_send"
+            android:onClick="sendMessage" />
+</LinearLayout>
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..8eda2758dbae850d2bd07f3dca547b45bf3443d7
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">Basic App</string>
+    <string name="button_send">Go</string>
+    <string name="title_activity_display_message">People</string>
+</resources>
index 9e670c5ade90a538f0ab5fc673aea8b4e8ba6427..6a210809c1d25350ece2ecbc271820e34f3ca362 100644 (file)
@@ -11,41 +11,34 @@ buildscript {
 }
 apply plugin: 'android'
 
-version='1.0'
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    compile 'com.google.guava:guava:11.0.2'
-}
-
 android {
     target = "android-15"
+    testBuildType = "staging"
+    
 
-       defaultConfig {
-               signingStoreLocation = "debug.keystore"
-               signingStorePassword = "android"
-               signingKeyAlias = "androiddebugkey"
-               signingKeyPassword = "android"
-       }
+    defaultConfig {
+        signingStoreLocation = "debug.keystore"
+        signingStorePassword = "android"
+        signingKeyAlias = "androiddebugkey"
+        signingKeyPassword = "android"
+    }
 
-       productFlavors {
-           foo {
-                       packageName = "org.gradle.sample.foo2"
-                       signingStoreLocation = "debug.keystore2"
-                       signingStorePassword = "android2"
-               }
-           bloo {
-                       packageName = "org.gradle.sample.bloo"
-               }
-       }
-       
-       buildTypes {
-               blah {
-                       packageNameSuffix = ".blah"
-                       debugSigned = true
-               }
-       }
+    productFlavors {
+        f1 {
+            packageName = "com.android.tests.flavored.f1"
+        }
+        f2 {
+            packageName = "com.android.tests.flavored.f2"
+        }
+    }
+    
+    buildTypes {
+        debug {
+            packageNameSuffix = ".debug"
+        }
+        staging {
+            packageNameSuffix = ".staging"
+            debugSigned = true
+        }
+    }
 }
\ No newline at end of file
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..f081f40eab049a090b707fe695f165372465242b
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">_Test-Flavored-f1</string>
+</resources>
diff --git a/testapps/dependencies/src/main/res/layout/main.xml b/testapps/dependencies/src/main/res/layout/main.xml
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..ce0bb8d83d0dd60eafd70213b3851712fff0a1ce
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="">
+    <application>
+        <activity android:name="com.android.tests.flavored.OtherActivity"
+                  android:label="@string/other_activity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/testapps/dependencies/src/main/res/values/strings.xml b/testapps/dependencies/src/main/res/values/strings.xml
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..6ffac9c99c324f6c2df5765521cb789665c3aadf
--- /dev/null
@@ -0,0 +1,15 @@
+package com.android.tests.flavored;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class OtherActivity extends Activity
+{
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main2);
+    }
+}
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..90c3c43f25cabd020ea7674b9d818682ab9e668a
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+<TextView
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:text="Test App - Flavored - f2"
+    android:id="@+id/text2"
+    />
+</LinearLayout>
+
diff --git a/testapps/flavored/src/f1/res/values/strings.xml b/testapps/flavored/src/f1/res/values/strings.xml
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..bb53fd4d9c4b5a4ddf3c8e859c7762c01bf25486
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">_Test-Flavored-f2</string>
+    <string name="other_activity">_Test-f2-act2</string>
+</resources>
diff --git a/testapps/flavored/src/f2/AndroidManifest.xml b/testapps/flavored/src/f2/AndroidManifest.xml
index 1f411e0cfe873cd158a2fb775fbf1ed26732e7ed..0d1c338350251b8bee82cb4d330d24abee249e49 100644 (file)
--- a/testapps/flavored/src/f2/AndroidManifest.xml
@@ -1,22 +1,13 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-      package="org.gradle.sample"
-      android:versionCode="1"
-      android:versionName="1.0">
-    <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
-        <activity android:name="MainActivity"
+      package="com.android.tests.flavored">
+    <application android:label="@string/app_name" android:icon="@drawable/icon">
+        <activity android:name=".Main"
                   android:label="@string/app_name">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        <activity
-                android:name="ShowPeopleActivity"
-                android:label="@string/title_activity_display_message" >
-                <meta-data
-                    android:name="android.support.PARENT_ACTIVITY"
-                    android:value="org.gradle.sample.MainActivity" />
-            </activity>
     </application>
 </manifest>
diff --git a/testapps/flavored/src/f2/java/com/android/tests/flavored/OtherActivity.java b/testapps/flavored/src/f2/java/com/android/tests/flavored/OtherActivity.java
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..26debd391cfdd7290e558dc9596b4007fa62740c
--- /dev/null
@@ -0,0 +1,15 @@
+package com.android.tests.flavored;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class Main extends Activity
+{
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+}
diff --git a/testapps/flavored/src/f2/res/layout/main2.xml b/testapps/flavored/src/f2/res/layout/main2.xml
deleted file mode 100644 (file)
index 436402608be54f8acdaec892f01075e5789d959b..0000000000000000000000000000000000000000
--- a/testapps/flavored/src/f2/res/layout/main2.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-package org.gradle.sample;
-
-public interface BuildType {
-    String getBuildType();
-}
diff --git a/testapps/flavored/src/f2/res/values/strings.xml b/testapps/flavored/src/f2/res/values/strings.xml
deleted file mode 100644 (file)
index b6b2fb7c18bdc0fe54810484f787b1c01227dcdd..0000000000000000000000000000000000000000
--- a/testapps/flavored/src/f2/res/values/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.gradle.sample;
-
-import android.app.Activity;
-import android.view.View;
-import android.content.Intent;
-import android.os.Bundle;
-
-import android.annotation.TargetApi;
-
-public class MainActivity extends Activity {
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.main);
-    }
-
-    @TargetApi(10)
-    public void sendMessage(View view) {
-        Intent intent = new Intent(this, ShowPeopleActivity.class);
-        startActivity(intent);
-    }
-}
deleted file mode 100644 (file)
index b6fcb270f393a2cf284d6a8354974d54dcfed78a..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.gradle.sample;
-
-public class Person {
-    private final String name;
-
-    public Person(String name) {
-        this.name = name;
-    }
-
-    public String getName() {
-        return name;
-    }
-}
diff --git a/testapps/flavored/src/main/java/com/android/tests/flavored/Main.java b/testapps/flavored/src/main/java/com/android/tests/flavored/Main.java
deleted file mode 100644 (file)
index 24a739a31f082839901e553102bf902a85d67b04..0000000000000000000000000000000000000000
--- a/testapps/flavored/src/main/java/com/android/tests/flavored/Main.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package org.gradle.sample;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.content.Intent;
-import android.widget.TextView;
-import com.google.common.collect.Lists;
-
-import java.lang.String;
-import java.util.Arrays;
-
-public class ShowPeopleActivity extends Activity {
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        String message = "People:";
-
-        Iterable<Person> people = Lists.newArrayList(new Person("fred"));
-        for (Person person : people) {
-            message += "\n * ";
-            message += person.getName();
-        }
-
-        TextView textView = new TextView(this);
-        textView.setTextSize(20);
-        textView.setText(message);
-
-        setContentView(textView);
-    }
-}
diff --git a/testapps/flavored/src/main/java/org/gradle/sample/BuildType.java b/testapps/flavored/src/main/java/org/gradle/sample/BuildType.java
deleted file mode 100644 (file)
index 96a442e5b8e9394ccf50bab9988cb2316026245d..0000000000000000000000000000000000000000
Binary files a/testapps/flavored/src/main/res/drawable-hdpi/ic_launcher.png and /dev/null differ
diff --git a/testapps/flavored/src/main/java/org/gradle/sample/MainActivity.java b/testapps/flavored/src/main/java/org/gradle/sample/MainActivity.java
deleted file mode 100644 (file)
index 99238729d8753585237a65b91c7cde426c90baef..0000000000000000000000000000000000000000
Binary files a/testapps/flavored/src/main/res/drawable-ldpi/ic_launcher.png and /dev/null differ
diff --git a/testapps/flavored/src/main/java/org/gradle/sample/Person.java b/testapps/flavored/src/main/java/org/gradle/sample/Person.java
deleted file mode 100644 (file)
index 359047dfa4ed206e41e2354f9c6b307e713efe32..0000000000000000000000000000000000000000
Binary files a/testapps/flavored/src/main/res/drawable-mdpi/ic_launcher.png and /dev/null differ
diff --git a/testapps/flavored/src/main/java/org/gradle/sample/ShowPeopleActivity.java b/testapps/flavored/src/main/java/org/gradle/sample/ShowPeopleActivity.java
deleted file mode 100644 (file)
index 71c6d760f05183ef8a47c614d8d13380c8528499..0000000000000000000000000000000000000000
Binary files a/testapps/flavored/src/main/res/drawable-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/testapps/flavored/src/main/res/drawable-hdpi/ic_launcher.png b/testapps/flavored/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..a07c69fa5a0f4da5d5efe96eea12a543154dbab6
Binary files /dev/null and b/testapps/flavored/src/main/res/drawable/icon.png differ
diff --git a/testapps/flavored/src/main/res/drawable-ldpi/ic_launcher.png b/testapps/flavored/src/main/res/drawable-ldpi/ic_launcher.png
index ccc59fb8e8a1b19c8e4a93dcd4533e17b8bd01a4..058715f9546044c6e3876ddf832fcc7a8b2ac2a4 100644 (file)
+++ b/testapps/flavored/src/main/res/drawable-ldpi/ic_launcher.png
@@ -1,12 +1,14 @@
 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="horizontal"
+    android:orientation="vertical"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     >
-    <Button
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/button_send"
-            android:onClick="sendMessage" />
+<TextView
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:text="Test App - Flavored"
+    android:id="@+id/text"
+    />
 </LinearLayout>
+
diff --git a/testapps/flavored/src/main/res/drawable-mdpi/ic_launcher.png b/testapps/flavored/src/main/res/drawable-mdpi/ic_launcher.png
index 8eda2758dbae850d2bd07f3dca547b45bf3443d7..24db5456e72c4c21f462c643fc32784361af0d3c 100644 (file)
+++ b/testapps/flavored/src/main/res/drawable-mdpi/ic_launcher.png
@@ -1,6 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-    <string name="app_name">Basic App</string>
-    <string name="button_send">Go</string>
-    <string name="title_activity_display_message">People</string>
+    <string name="app_name">###</string>
 </resources>
diff --git a/testapps/flavored/src/main/res/drawable-xhdpi/ic_launcher.png b/testapps/flavored/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..f44e0f07f2f1833dfe163a526cf59754d146ee6f
--- /dev/null
+++ b/testapps/flavored/src/main/res/drawable-xhdpi/ic_launcher.png
@@ -0,0 +1,38 @@
+package com.android.tests.flavored;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.widget.TextView;
+
+public class MainTest extends ActivityInstrumentationTestCase2<Main> {
+
+    private TextView mTextView;
+
+    /**
+     * Creates an {@link ActivityInstrumentationTestCase2} that tests the {@link Main} activity.
+     */
+    public MainTest() {
+        super(Main.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        final Main a = getActivity();
+        // ensure a valid handle to the activity has been returned
+        assertNotNull(a);
+        mTextView = (TextView) a.findViewById(R.id.text);
+    }
+
+    /**
+     * The name 'test preconditions' is a convention to signal that if this
+     * test doesn't pass, the test case was not set up properly and it might
+     * explain any and all failures in other tests.  This is not guaranteed
+     * to run before other tests, as junit uses reflection to find the tests.
+     */
+    @MediumTest
+    public void testPreconditions() {
+        assertNotNull(mTextView);
+    }
+}
+
diff --git a/testapps/flavored/src/main/res/drawable/icon.png b/testapps/flavored/src/main/res/drawable/icon.png
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..11d8c64cef2c9024c5d75eaf938f56aae79b146a
--- /dev/null
@@ -0,0 +1,38 @@
+package com.android.tests.flavored;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.widget.TextView;
+
+public class OtherActivityTest extends ActivityInstrumentationTestCase2<OtherActivity> {
+
+    private TextView mTextView;
+
+    /**
+     * Creates an {@link ActivityInstrumentationTestCase2} that tests the {@link OtherActivity} activity.
+     */
+    public OtherActivityTest() {
+        super(OtherActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        final OtherActivity a = getActivity();
+        // ensure a valid handle to the activity has been returned
+        assertNotNull(a);
+        mTextView = (TextView) a.findViewById(R.id.text2);
+    }
+
+    /**
+     * The name 'test preconditions' is a convention to signal that if this
+     * test doesn't pass, the test case was not set up properly and it might
+     * explain any and all failures in other tests.  This is not guaranteed
+     * to run before other tests, as junit uses reflection to find the tests.
+     */
+    @MediumTest
+    public void testPreconditions() {
+        assertNotNull(mTextView);
+    }
+}
+