am 95144447: am bca20286: am 0fa4cfb9: am 7046c809: am b939fc6e: am 7cbb511c: am a0a74101: Adding xml-generator and updating java scanner

* commit '951444477e4b141256b9283d5c38bb8dca02199c':
  Adding xml-generator and updating java scanner
diff --git a/common/host-side/java-scanner/Android.mk b/common/host-side/java-scanner/Android.mk
index 3acf988..7c101ff 100644
--- a/common/host-side/java-scanner/Android.mk
+++ b/common/host-side/java-scanner/Android.mk
@@ -18,12 +18,12 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
+LOCAL_JAVA_LIBRARIES := compatibility-common-util-hostsidelib_v2
+
 LOCAL_JAR_MANIFEST := MANIFEST.mf
 
 LOCAL_CLASSPATH := $(HOST_JDK_TOOLS_JAR)
 
-LOCAL_JAVA_LIBRARIES := junit
-
 LOCAL_MODULE := compatibility-java-scanner_v2
 
 LOCAL_MODULE_TAGS := optional
diff --git a/common/host-side/java-scanner/MANIFEST.mf b/common/host-side/java-scanner/MANIFEST.mf
index a04249a..975b1ef 100644
--- a/common/host-side/java-scanner/MANIFEST.mf
+++ b/common/host-side/java-scanner/MANIFEST.mf
@@ -1,2 +1,3 @@
 Manifest-Version: 1.0
 Main-Class: com.android.compatibility.common.scanner.JavaScanner
+Class-Path: compatibility-common-util-hostsidelib_v2.jar
diff --git a/common/host-side/java-scanner/src/com/android/compatibility/common/scanner/JavaScanner.java b/common/host-side/java-scanner/src/com/android/compatibility/common/scanner/JavaScanner.java
index c423290..f3f8a49 100644
--- a/common/host-side/java-scanner/src/com/android/compatibility/common/scanner/JavaScanner.java
+++ b/common/host-side/java-scanner/src/com/android/compatibility/common/scanner/JavaScanner.java
@@ -16,12 +16,15 @@
 
 package com.android.compatibility.common.scanner;
 
+import com.android.compatibility.common.util.KeyValueArgsParser;
+
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileFilter;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -56,7 +59,7 @@
     }
 
     int scan() throws Exception {
-        final List<String> args = new ArrayList<String>();
+        final ArrayList<String> args = new ArrayList<String>();
         args.add("javadoc");
         args.add("-doclet");
         args.add("com.android.compatibility.common.scanner.JavaScannerDoclet");
@@ -90,7 +93,7 @@
     }
 
     private static String getSourcePath(File sourceDir) {
-        List<String> sourcePath = new ArrayList<String>(Arrays.asList(SOURCE_PATHS));
+        final ArrayList<String> sourcePath = new ArrayList<String>(Arrays.asList(SOURCE_PATHS));
         sourcePath.add(sourceDir.toString());
         return join(sourcePath, ":");
     }
@@ -99,8 +102,8 @@
         return join(Arrays.asList(CLASS_PATHS), ":");
     }
 
-    private static List<String> getSourceFiles(File sourceDir) {
-        List<String> sourceFiles = new ArrayList<String>();
+    private static ArrayList<String> getSourceFiles(File sourceDir) {
+        final ArrayList<String> sourceFiles = new ArrayList<String>();
         final File[] files = sourceDir.listFiles(new FileFilter() {
             public boolean accept(File pathname) {
                 return pathname.isDirectory() || pathname.toString().endsWith(".java");
@@ -117,7 +120,7 @@
     }
 
     private static String join(List<String> list, String delimiter) {
-        StringBuilder builder = new StringBuilder();
+        final StringBuilder builder = new StringBuilder();
         for (String s : list) {
             builder.append(s);
             builder.append(delimiter);
@@ -128,19 +131,18 @@
     }
 
     public static void main(String[] args) throws Exception {
-        String sourcePath = null;
-        String docletPath = null;
-        for (int i = 0; i < args.length; i++) {
-            if (args[i].equals("-s") && ++i < args.length) {
-                sourcePath = args[i];
-            } else if (args[i].equals("-d") && ++i < args.length) {
-                docletPath = args[i];
-            }
-        }
+        final HashMap<String, String> argsMap = KeyValueArgsParser.parse(args);
+        final String sourcePath = argsMap.get("-s");
+        final String docletPath = argsMap.get("-d");
         if (sourcePath == null || docletPath == null) {
-            System.err.println("Usage: javascanner -s SOURCE_DIR -d DOCLET_PATH");
-            System.exit(1);
+            usage(args);
         }
         System.exit(new JavaScanner(new File(sourcePath), new File(docletPath)).scan());
     }
+
+    private static void usage(String[] args) {
+        System.err.println("Arguments: " + Arrays.toString(args));
+        System.err.println("Usage: javascanner -s SOURCE_DIR -d DOCLET_PATH");
+        System.exit(1);
+    }
 }
diff --git a/common/host-side/java-scanner/tests/src/com/android/compatibility/common/scanner/JavaScannerTest.java b/common/host-side/java-scanner/tests/src/com/android/compatibility/common/scanner/JavaScannerTest.java
index e18cf16..4159f0e 100644
--- a/common/host-side/java-scanner/tests/src/com/android/compatibility/common/scanner/JavaScannerTest.java
+++ b/common/host-side/java-scanner/tests/src/com/android/compatibility/common/scanner/JavaScannerTest.java
@@ -21,7 +21,6 @@
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.List;
 
 import junit.framework.TestCase;
 
@@ -100,7 +99,7 @@
             out.print(content);
             out.flush();
             out.close();
-            List<String> args = new ArrayList<String>();
+            ArrayList<String> args = new ArrayList<String>();
             args.add("java");
             args.add("-jar");
             args.add(JAR);
diff --git a/common/host-side/scripts/compatibility-tests_v2 b/common/host-side/scripts/compatibility-tests_v2
index b9e479a..797909e 100755
--- a/common/host-side/scripts/compatibility-tests_v2
+++ b/common/host-side/scripts/compatibility-tests_v2
@@ -22,17 +22,17 @@
 }
 
 HOST_JAR_DIR=${ANDROID_HOST_OUT}/framework
-HOST_JARS="ddmlib-prebuilt.jar tradefed-prebuilt.jar hosttestlib.jar\
-    compatibility-tradefed_v2.jar compatibility-tradefed-tests_v2.jar\
-    compatibility-java-scanner_v2.jar compatibility-java-scanner-tests_v2.jar\
-    compatibility-native-scanner_v2.jar compatibility-native-scanner-tests_v2.jar\
-    compatibility-xml-plan-generator_v2.jar compatibility-xml-plan-generator-tests_v2.jar\
-    compatibility-device-util-tests_v2.jar compatibility-device-setup-tests_v2.jar\
-    compatibility-common-util-hostsidelib_v2.jar compatibility-common-util-tests_v2.jar"
+HOST_JARS="ddmlib-prebuilt tradefed-prebuilt hosttestlib\
+    compatibility-tradefed_v2 compatibility-tradefed-tests_v2\
+    compatibility-java-scanner_v2 compatibility-java-scanner-tests_v2\
+    compatibility-native-scanner_v2 compatibility-native-scanner-tests_v2\
+    compatibility-xml-plan-generator_v2 compatibility-xml-plan-generator-tests_v2\
+    compatibility-device-util-tests_v2 compatibility-device-setup-tests_v2\
+    compatibility-common-util-hostsidelib_v2 compatibility-common-util-tests_v2"
 
 for JAR in ${HOST_JARS}; do
-    checkFile ${HOST_JAR_DIR}/${JAR}
-    JAR_PATH=${JAR_PATH}:${HOST_JAR_DIR}/${JAR}
+    checkFile ${HOST_JAR_DIR}/${JAR}.jar
+    JAR_PATH=${JAR_PATH}:${HOST_JAR_DIR}/${JAR}.jar
 done
 
 DEVICE_LIBS_DIR=${ANDROID_PRODUCT_OUT}/obj/JAVA_LIBRARIES
diff --git a/common/host-side/xml-plan-generator/Android.mk b/common/host-side/xml-plan-generator/Android.mk
index 36491bd..53718e5 100644
--- a/common/host-side/xml-plan-generator/Android.mk
+++ b/common/host-side/xml-plan-generator/Android.mk
@@ -18,6 +18,10 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
+LOCAL_JAVA_LIBRARIES := compatibility-common-util-hostsidelib_v2
+
+LOCAL_STATIC_JAVA_LIBRARIES := vogarexpectlib
+
 LOCAL_JAR_MANIFEST := MANIFEST.mf
 
 LOCAL_CLASSPATH := $(HOST_JDK_TOOLS_JAR)
diff --git a/common/host-side/xml-plan-generator/MANIFEST.mf b/common/host-side/xml-plan-generator/MANIFEST.mf
index c021388..95aee0d 100644
--- a/common/host-side/xml-plan-generator/MANIFEST.mf
+++ b/common/host-side/xml-plan-generator/MANIFEST.mf
@@ -1,2 +1,3 @@
 Manifest-Version: 1.0
 Main-Class: com.android.compatibility.common.xmlgenerator.XmlPlanGenerator
+Class-Path: compatibility-common-util-hostsidelib_v2.jar
diff --git a/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/Test.java b/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/Test.java
new file mode 100644
index 0000000..d3e1d88
--- /dev/null
+++ b/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/Test.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compatibility.common.xmlgenerator;
+
+public class Test {
+
+    private final String mName;
+
+    public Test(String name) {
+        mName = name;
+    }
+
+    public String getName() {
+        return mName;
+    }
+}
diff --git a/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/TestCase.java b/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/TestCase.java
new file mode 100644
index 0000000..65b4aa3
--- /dev/null
+++ b/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/TestCase.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compatibility.common.xmlgenerator;
+
+import java.util.ArrayList;
+
+public class TestCase {
+
+    private final String mName;
+    private final ArrayList<Test> mTests = new ArrayList<Test>();
+
+    public TestCase(String name) {
+        mName = name;
+    }
+
+    public void addTest(Test test) {
+        mTests.add(test);
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public ArrayList<Test> getTests() {
+        return mTests;
+    }
+}
diff --git a/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/TestListParser.java b/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/TestListParser.java
new file mode 100644
index 0000000..6880440
--- /dev/null
+++ b/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/TestListParser.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compatibility.common.xmlgenerator;
+
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Scanner;
+
+/**
+ * Parser of test lists in the form;
+ *
+ * suite:android.sample
+ * case:SampleTest
+ * test:testA
+ * test:testB
+ * suite:android.sample.ui
+ * case:SampleUiTest
+ * test:testA
+ * test:testB
+ */
+public class TestListParser {
+
+    private TestListParser() {}
+
+    public static HashMap<String, TestSuite> parse(InputStream input) {
+        final HashMap<String, TestSuite> suites = new HashMap<String, TestSuite>();
+        TestSuite currentSuite = null;
+        TestCase currentCase = null;
+        Scanner in = null;
+        try {
+            in = new Scanner(input);
+            while (in.hasNextLine()) {
+                final String line = in.nextLine();
+                final String[] parts = line.split(":");
+                if (parts.length != 2) {
+                    throw new RuntimeException("Invalid Format: " + line);
+                }
+                final String key = parts[0];
+                final String value = parts[1];
+                if (currentSuite == null) {
+                    if (!"suite".equals(key)) {
+                        throw new RuntimeException("TestSuite Expected");
+                    }
+                    final String[] names = value.split("\\.");
+                    for (int i = 0; i < names.length; i++) {
+                        final String name = names[i];
+                        if (currentSuite != null) {
+                            if (currentSuite.hasTestSuite(name)) {
+                                currentSuite = currentSuite.getTestSuite(name);
+                            } else {
+                                final TestSuite newSuite = new TestSuite(name);
+                                currentSuite.addTestSuite(newSuite);
+                                currentSuite = newSuite;
+                            }
+                        } else if (suites.containsKey(name)) {
+                            currentSuite = suites.get(name);
+                        } else {
+                            currentSuite = new TestSuite(name);
+                            suites.put(name, currentSuite);
+                        }
+                    }
+                } else if (currentCase == null) {
+                    if (!"case".equals(key)) {
+                        throw new RuntimeException("TestCase Expected");
+                    }
+                    currentCase = new TestCase(value);
+                    currentSuite.addTestCase(currentCase);
+                } else {
+                    if (!"test".equals(key)) {
+                        throw new RuntimeException("Test Expected");
+                    }
+                    currentCase.addTest(new Test(value));
+                }
+            }
+        } finally {
+            if (in != null) {
+                in.close();
+            }
+        }
+        return suites;
+    }
+}
diff --git a/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/TestSuite.java b/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/TestSuite.java
new file mode 100644
index 0000000..db4fd07
--- /dev/null
+++ b/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/TestSuite.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compatibility.common.xmlgenerator;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class TestSuite {
+
+    private final String mName;
+    private final HashMap<String, TestSuite> mTestSuites = new HashMap<String, TestSuite>();
+    private final ArrayList<TestCase> mTestCases = new ArrayList<TestCase>();
+
+    public TestSuite(String name) {
+        mName = name;
+    }
+
+    public boolean hasTestSuite(String name) {
+        return mTestSuites.containsKey(name);
+    }
+
+    public TestSuite getTestSuite(String name) {
+        return mTestSuites.get(name);
+    }
+
+    public void addTestSuite(TestSuite testSuite) {
+        mTestSuites.put(testSuite.getName(), testSuite);
+    }
+
+    public void addTestCase(TestCase testCase) {
+        mTestCases.add(testCase);
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public HashMap<String, TestSuite> getTestSuites() {
+        return mTestSuites;
+    }
+
+    public ArrayList<TestCase> getTestCases() {
+        return mTestCases;
+    }
+}
diff --git a/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/XmlPlanGenerator.java b/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/XmlPlanGenerator.java
index 510c935..d0a3a37 100644
--- a/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/XmlPlanGenerator.java
+++ b/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/XmlPlanGenerator.java
@@ -16,12 +16,202 @@
 
 package com.android.compatibility.common.xmlgenerator;
 
+import com.android.compatibility.common.util.KeyValueArgsParser;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import vogar.Expectation;
+import vogar.ExpectationStore;
+import vogar.ModeId;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+
 /**
  * Passes the scanner output and outputs an xml description of the tests.
  */
 public class XmlPlanGenerator {
 
+    private final ExpectationStore mExpectations;
+    private final String mAppNameSpace;
+    private final String mAppPackageName;
+    private final String mName;
+    private final String mRunner;
+    private final String mTargetBinaryName;
+    private final String mTargetNameSpace;
+    private final String mJarPath;
+    private final String mTestType;
+    private final String mOutput;
+
+    private XmlPlanGenerator(ExpectationStore expectations, String appNameSpace,
+            String appPackageName, String name, String runner, String targetBinaryName,
+            String targetNameSpace, String jarPath, String testType, String output) {
+        mExpectations = expectations;
+        mAppNameSpace = appNameSpace;
+        mAppPackageName = appPackageName;
+        mName = name;
+        mRunner = runner;
+        mTargetBinaryName = targetBinaryName;
+        mTargetNameSpace = targetNameSpace;
+        mJarPath = jarPath;
+        mTestType = testType;
+        mOutput = output;
+    }
+
+    private void writePackageXml() throws IOException {
+        OutputStream out = System.out;
+        if (mOutput != null) {
+            out = new FileOutputStream(mOutput);
+        }
+        PrintWriter writer = null;
+        try {
+            writer = new PrintWriter(out);
+            writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+            writeTestPackage(writer);
+        } finally {
+            if (writer != null) {
+                writer.close();
+            }
+        }
+    }
+
+    private void writeTestPackage(PrintWriter writer) {
+        writer.append("<TestPackage");
+        if (mAppNameSpace != null) {
+            writer.append(" appNameSpace=\"").append(mAppNameSpace).append("\"");
+        }
+
+        writer.append(" appPackageName=\"").append(mAppPackageName).append("\"");
+        writer.append(" name=\"").append(mName).append("\"");
+
+        if (mRunner != null) {
+            writer.append(" runner=\"").append(mRunner).append("\"");
+        }
+
+        if (mAppNameSpace != null && mTargetNameSpace != null
+                && !mAppNameSpace.equals(mTargetNameSpace)) {
+            writer.append(" targetBinaryName=\"").append(mTargetBinaryName).append("\"");
+            writer.append(" targetNameSpace=\"").append(mTargetNameSpace).append("\"");
+        }
+
+        if (mTestType != null && !mTestType.isEmpty()) {
+            writer.append(" testType=\"").append(mTestType).append("\"");
+        }
+
+        if (mJarPath != null) {
+            writer.append(" jarPath=\"").append(mJarPath).append("\"");
+        }
+
+        writer.println(" version=\"1.0\">");
+
+        final HashMap<String, TestSuite> suites = TestListParser.parse(System.in);
+        if (suites.isEmpty()) {
+            throw new RuntimeException("No TestSuites Found");
+        }
+        writeTestSuites(writer, suites, "");
+        writer.println("</TestPackage>");
+    }
+
+    private void writeTestSuites(PrintWriter writer, HashMap<String, TestSuite> suites, String name) {
+        for (String suiteName : suites.keySet()) {
+            final TestSuite suite = suites.get(suiteName);
+            writer.append("<TestSuite name=\"").append(suiteName).println("\">");
+            final String fullname = name + suiteName + ".";
+            writeTestSuites(writer, suite.getTestSuites(), fullname);
+            writeTestCases(writer, suite.getTestCases(), fullname);
+            writer.println("</TestSuite>");
+        }
+    }
+
+    private void writeTestCases(PrintWriter writer, ArrayList<TestCase> cases, String name) {
+        for (TestCase testCase : cases) {
+            final String caseName = testCase.getName();
+            writer.append("<TestCase name=\"").append(caseName).println("\">");
+            final String fullname = name + caseName;
+            writeTests(writer, testCase.getTests(), fullname);
+            writer.println("</TestCase>");
+        }
+    }
+
+    private void writeTests(PrintWriter writer, ArrayList<Test> tests, String name) {
+        if (tests.isEmpty()) {
+            throw new RuntimeException("No Tests Found");
+        }
+        for (Test test : tests) {
+            final String testName = test.getName();
+            writer.append("<Test name=\"").append(testName).append("\"");
+            final String fullname = name + "#" + testName;
+            if (isKnownFailure(mExpectations, fullname)) {
+                writer.append(" expectation=\"failure\"");
+            }
+            writer.println(" />");
+        }
+    }
+
+    public static boolean isKnownFailure(ExpectationStore store, String fullname) {
+        return store != null && store.get(fullname) != Expectation.SUCCESS;
+    }
+
     public static void main(String[] args) throws Exception {
-        // TODO(stuartscott)
+        final HashMap<String, String> argsMap = KeyValueArgsParser.parse(args);
+        final String packageName = argsMap.get("-p");
+        final String name = argsMap.get("-n");
+        final String testType = argsMap.get("-t");
+        final String jarPath = argsMap.get("-j");
+        final String instrumentation = argsMap.get("-i");
+        final String manifest = argsMap.get("-m");
+        final String expectations = argsMap.get("-e");
+        final String output = argsMap.get("-o");
+        String appNameSpace = argsMap.get("-a");
+        String targetNameSpace = argsMap.get("-r");
+        if (packageName == null || name == null) {
+            usage(args);
+        }
+        String runner = null;
+        if (manifest != null) {
+            Document m = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(manifest);
+            Element elem = m.getDocumentElement();
+            appNameSpace = elem.getAttribute("package");
+            runner = getElementAttribute(elem, "instrumentation", "android:name");
+            targetNameSpace = getElementAttribute(elem, "instrumentation", "android:targetPackage");
+        }
+
+        final HashSet<File> expectationFiles = new HashSet<File>();
+        if (expectations != null) {
+            expectationFiles.add(new File(expectations));
+        }
+        final ExpectationStore store = ExpectationStore.parse(expectationFiles, ModeId.DEVICE);
+        XmlPlanGenerator generator = new XmlPlanGenerator(store, appNameSpace, packageName, name,
+            runner, instrumentation, targetNameSpace, jarPath, testType, output);
+        generator.writePackageXml();
+    }
+
+    private static String getElementAttribute(Element parent, String elem, String name) {
+        NodeList nodeList = parent.getElementsByTagName(elem);
+        if (nodeList.getLength() > 0) {
+             Element element = (Element) nodeList.item(0);
+             return element.getAttribute(name);
+        }
+        return null;
+    }
+
+    private static void usage(String[] args) {
+        System.err.println("Arguments: " + Arrays.toString(args));
+        System.err.println("Usage: compatibility-xml-plan-generator -p PACKAGE_NAME -n NAME" +
+            "[-t TEST_TYPE] [-j JAR_PATH] [-i INSTRUMENTATION] [-m MANIFEST] [-e EXPECTATIONS]" +
+            "[-o OUTPUT]");
+        System.exit(1);
     }
 }
diff --git a/common/host-side/xml-plan-generator/tests/src/com/android/compatibility/common/xmlgenerator/XmlPlanGeneratorTest.java b/common/host-side/xml-plan-generator/tests/src/com/android/compatibility/common/xmlgenerator/XmlPlanGeneratorTest.java
index 35bcb02..082af17 100644
--- a/common/host-side/xml-plan-generator/tests/src/com/android/compatibility/common/xmlgenerator/XmlPlanGeneratorTest.java
+++ b/common/host-side/xml-plan-generator/tests/src/com/android/compatibility/common/xmlgenerator/XmlPlanGeneratorTest.java
@@ -18,7 +18,111 @@
 
 import junit.framework.TestCase;
 
+import java.io.ByteArrayInputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Scanner;
+
 public class XmlPlanGeneratorTest extends TestCase {
 
-    // TODO(stuartscott): Add tests when there is something to test.
+    private static final String JAR = "out/host/linux-x86/framework/compatibility-xml-plan-generator_v2.jar";
+    private static final String PACKAGE_NAME = "com.android.test";
+    private static final String NAME = "ValidTest";
+    private static final String VALID_RESULT =
+        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
+        "<TestPackage appPackageName=\"com.android.test\" name=\"ValidTest\" version=\"1.0\">" +
+        "<TestSuite name=\"com\">" +
+        "<TestSuite name=\"android\">" +
+        "<TestSuite name=\"test\">" +
+        "<TestCase name=\"ValidTest\">" +
+        "<Test name=\"testA\" />" +
+        "</TestCase>" +
+        "</TestSuite>" +
+        "</TestSuite>" +
+        "</TestSuite>" +
+        "</TestPackage>";
+
+    private static final String VALID =
+        "suite:com.android.test\n" +
+        "case:ValidTest\n" +
+        "test:testA\n";
+
+    private static final String INVALID_A = "";
+
+    private static final String INVALID_B =
+        "suite:com.android.test\n" +
+        "case:InvalidTest\n";
+
+    private static final String INVALID_C =
+        "uh oh";
+
+    private static final String INVALID_D =
+        "test:testA\n" +
+        "case:InvalidTest\n" +
+        "suite:com.android.test\n";
+
+    private static final String INVALID_E =
+        "suite:com.android.test\n" +
+        "test:testA\n" +
+        "case:InvalidTest\n";
+
+    public void testValid() throws Exception {
+        assertEquals(VALID_RESULT, runGenerator(VALID));
+    }
+
+    public void testInvalidA() throws Exception {
+        assertNull(runGenerator(INVALID_A));
+    }
+
+    public void testInvalidB() throws Exception {
+        assertNull(runGenerator(INVALID_B));
+    }
+
+    public void testTestListParserInvalidFormat() throws Exception {
+        runTestListParser(INVALID_C);
+    }
+
+    public void testTestListParserSuiteExpected() throws Exception {
+        runTestListParser(INVALID_D);
+    }
+
+    public void testTestListParserCaseExpected() throws Exception {
+        runTestListParser(INVALID_E);
+    }
+
+    private static String runGenerator(String input) throws Exception {
+        ArrayList<String> args = new ArrayList<String>();
+        args.add("java");
+        args.add("-jar");
+        args.add(JAR);
+        args.add("-p");
+        args.add(PACKAGE_NAME);
+        args.add("-n");
+        args.add(NAME);
+
+        final Process p = new ProcessBuilder(args).start();
+        final PrintWriter out = new PrintWriter(p.getOutputStream());
+        out.print(input);
+        out.flush();
+        out.close();
+        final StringBuilder output = new StringBuilder();
+        final Scanner in = new Scanner(p.getInputStream());
+        while (in.hasNextLine()) {
+            output.append(in.nextLine());
+        }
+        int ret = p.waitFor();
+        if (ret == 0) {
+            return output.toString();
+        }
+        return null;
+    }
+
+    private static void runTestListParser(String input) throws Exception {
+        try {
+            final ByteArrayInputStream in = new ByteArrayInputStream(input.getBytes());
+            final HashMap<String, TestSuite> suites = TestListParser.parse(in);
+            fail();
+        } catch (RuntimeException e) {}
+    }
 }