Add support for building test apps.
Xavier Ducrohet [Mon, 27 Aug 2012 21:02:11 +0000 (14:02 -0700)]
Support for test apps testing apps only. Not support
for library projects yet. Lots of bug fixes to get here.

Renamed 'basic' to 'dependencies'. This will still need
to be refactored.

'basic' and 'flavored' are two new projects with that are
very simple and meant to test the build variant mechanism
which is tested by the test apks.

This also adds uninstall tasks as well as runTests tasks,
and creates top level install tasks.

Change-Id: I11bf62b236a4df6752b4195fd1989da3f93e29bd

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);
+    }
+}
+