Snap for 8564071 from 929ec3da05d8b6b26c9b2708c72512419e1334f7 to mainline-wifi-release

Change-Id: Ia3ec2fc289236d706e79a2e4e1935243fbbf58e6
diff --git a/build/xsdc.go b/build/xsdc.go
index 3a09d71..f92ba28 100644
--- a/build/xsdc.go
+++ b/build/xsdc.go
@@ -17,6 +17,7 @@
 import (
 	"android/soong/android"
 	"android/soong/java"
+	"fmt"
 	"path/filepath"
 	"strings"
 
@@ -80,6 +81,11 @@
 	// Whether getter name of boolean element or attribute is getX or isX.
 	// Default value is false. If the property is true, getter name is isX.
 	Boolean_getter *bool
+	// Generate code that uses libtinyxml2 instead of libxml2. Applies to
+	// C++ only and does not perform the XInclude substitution, or
+	// ENTITY_REFs.
+	// This can improve memory footprint. Default value is false.
+	Tinyxml *bool
 }
 
 type xsdConfig struct {
@@ -95,7 +101,7 @@
 	docsPath android.Path
 
 	xsdConfigPath android.OptionalPath
-	genOutputs    android.Paths
+	genOutputs    android.WritablePaths
 }
 
 var _ android.SourceFileProducer = (*xsdConfig)(nil)
@@ -126,7 +132,10 @@
 }
 
 func (module *xsdConfig) Srcs() android.Paths {
-	return append(module.genOutputs, module.genOutputs_j)
+	var srcs android.WritablePaths
+	srcs = append(srcs, module.genOutputs...)
+	srcs = append(srcs, module.genOutputs_j)
+	return srcs.Paths()
 }
 
 func (module *xsdConfig) GeneratedDeps() android.Paths {
@@ -201,6 +210,10 @@
 		args = args + " -b "
 	}
 
+	if proptools.Bool(module.properties.Tinyxml) {
+		args = args + " -t "
+	}
+
 	module.genOutputs_j = android.PathForModuleGen(ctx, "java", filenameStem+"_xsdcgen.srcjar")
 
 	ctx.Build(pctx, android.BuildParams{
@@ -296,3 +309,25 @@
 
 	return module
 }
+
+func (module *xsdConfig) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		var outputs android.WritablePaths
+		outputs = append(outputs, module.genOutputs_j)
+		outputs = append(outputs, module.genOutputs_c...)
+		outputs = append(outputs, module.genOutputs_h...)
+		outputs = append(outputs, module.genOutputs...)
+		return outputs.Paths(), nil
+	case "java":
+		return android.Paths{module.genOutputs_j}, nil
+	case "cpp":
+		return module.genOutputs_c.Paths(), nil
+	case "h":
+		return module.genOutputs_h.Paths(), nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
+}
+
+var _ android.OutputFileProducer = (*xsdConfig)(nil);
diff --git a/src/com/android/xsdc/Main.java b/src/com/android/xsdc/Main.java
index 0358f08..0f486d0 100644
--- a/src/com/android/xsdc/Main.java
+++ b/src/com/android/xsdc/Main.java
@@ -82,6 +82,13 @@
                 .hasArgs(0)
                 .withDescription("Generate isX() for boolean element or attribute.")
                 .create("b"));
+        options.addOption(OptionBuilder
+                .withLongOpt("tinyxml")
+                .hasArgs(0)
+                .withDescription("Generate code that uses libtinyxml2 instead of libxml2."
+                    + " Smaller binaries, but does not support XInclude substitution, "
+                    + " or ENTITY_REFs.")
+                .create("t"));
         Option genEnumsOnly = OptionBuilder
                 .withLongOpt("genEnumsOnly")
                 .hasArgs(0)
@@ -121,6 +128,7 @@
         boolean enumsOnly = cmd.hasOption('e');
         boolean parserOnly = cmd.hasOption('x');
         boolean booleanGetter = cmd.hasOption('b');
+        boolean useTinyXml = cmd.hasOption('t');
 
         if (xsdFile.length != 1 || packageName == null) {
             System.err.println("Error: no xsd files or package name");
@@ -149,7 +157,8 @@
                     (parserOnly ? CppCodeGenerator.GENERATE_PARSER :
                             CppCodeGenerator.GENERATE_ENUMS | CppCodeGenerator.GENERATE_PARSER);
             CppCodeGenerator cppCodeGenerator =
-                    new CppCodeGenerator(xmlSchema, packageName, writer, generators, booleanGetter);
+                    new CppCodeGenerator(xmlSchema, packageName, writer, generators,
+                            booleanGetter, useTinyXml);
             cppCodeGenerator.print(fs);
         }
     }
diff --git a/src/com/android/xsdc/cpp/CppCodeGenerator.java b/src/com/android/xsdc/cpp/CppCodeGenerator.java
index 3f4b844..e470216 100644
--- a/src/com/android/xsdc/cpp/CppCodeGenerator.java
+++ b/src/com/android/xsdc/cpp/CppCodeGenerator.java
@@ -23,7 +23,9 @@
 import com.android.xsdc.tag.*;
 
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -48,16 +50,18 @@
     private boolean writer;
     private int generators;
     private boolean booleanGetter;
+    private boolean useTinyXml;
 
     private static final String UNKNOWN_ENUM = "UNKNOWN";
 
     public CppCodeGenerator(XmlSchema xmlSchema, String pkgName, boolean writer, int generators,
-            boolean booleanGetter) throws CppCodeGeneratorException {
+            boolean booleanGetter, boolean useTinyXml) throws CppCodeGeneratorException {
         this.xmlSchema = xmlSchema;
         this.pkgName = pkgName;
         this.writer = writer;
         this.generators = generators;
         this.booleanGetter = booleanGetter;
+        this.useTinyXml = useTinyXml;
 
         // class naming validation
         {
@@ -150,17 +154,17 @@
         parserHeaderFile.printf("#include <optional>\n");
         parserHeaderFile.printf("#include <string>\n");
         parserHeaderFile.printf("#include <vector>\n");
+        parserHeaderFile.printf("#include <sstream>\n");
         if (writer) {
             parserHeaderFile.printf("#include <iostream>\n");
         }
         parserHeaderFile.printf("\n");
-        parserHeaderFile.printf("#if __has_include(<libxml/parser.h>)\n");
-        parserHeaderFile.printf("#include <libxml/parser.h>\n");
-        parserHeaderFile.printf("#include <libxml/xinclude.h>\n");
-        parserHeaderFile.printf("#else\n");
-        parserHeaderFile.printf("#error Require libxml2 library. ");
-        parserHeaderFile.printf("Please add libxml2 to shared_libs or static_libs\n");
-        parserHeaderFile.printf("#endif\n");
+        if (useTinyXml) {
+            printGuardedIncludes(parserHeaderFile, "libtinyxml2", "tinyxml2.h");
+        } else {
+            printGuardedIncludes(parserHeaderFile, "libxml2", "libxml/parser.h",
+                    Arrays.asList("libxml/xinclude.h"));
+        }
         if (hasEnums) {
             enumsHeaderFile.printf("#include <xsdc/XsdcSupport.h>\n");
             enumsHeaderFile.printf("\n");
@@ -251,6 +255,23 @@
         enumsHeaderFile.close();
     }
 
+    private void printGuardedIncludes(CodeWriter file, String libName, String mainHeader) {
+        printGuardedIncludes(file, libName, mainHeader, Collections.emptyList());
+    }
+
+    private void printGuardedIncludes(CodeWriter file, String libName, String mainHeader,
+            Collection<String> additionalHeaders) {
+        parserHeaderFile.printf("#if __has_include(<%s>)\n", mainHeader);
+        parserHeaderFile.printf("#include <%s>\n", mainHeader);
+        for (String header : additionalHeaders) {
+            parserHeaderFile.printf("#include <%s>\n", header);
+        }
+        parserHeaderFile.printf("#else\n");
+        parserHeaderFile.printf("#error Require %s library. ", libName);
+        parserHeaderFile.printf("Please add %s to shared_libs or static_libs\n", libName);
+        parserHeaderFile.printf("#endif\n");
+    }
+
     private void printEnum(String name, XsdRestriction restrictionType)
             throws CppCodeGeneratorException {
         enumsHeaderFile.printf("enum class %s {\n", name);
@@ -452,8 +473,9 @@
         }
 
         String fullName = nameScope + name;
-        parserHeaderFile.printf("static %s read(xmlNode *root);\n", fullName, Utils.lowerize(name));
-        parserCppFile.printf("\n%s %s::read(xmlNode *root) {\n", fullName, fullName);
+        String nodeType = getXmlNodeType();
+        parserHeaderFile.printf("static %s read(%s *root);\n", fullName, nodeType);
+        parserCppFile.printf("\n%s %s::read(%s *root) {\n", fullName, fullName, nodeType);
 
         parserCppFile.print("std::string raw;\n");
 
@@ -479,11 +501,7 @@
         }
 
         if (baseValueType != null) {
-            parserCppFile.printf("auto xmlValue = make_xmlUnique(xmlNodeListGetString("
-                    + "root->doc, root->xmlChildrenNode, 1));\n"
-                    + "if (xmlValue != nullptr) {\n"
-                    + "raw = reinterpret_cast<const char*>(xmlValue.get());\n");
-
+            printNodeListGetString("root");
             parserCppFile.print(baseValueType.getParsingExpression());
             parserCppFile.printf("instance.setValue(value);\n");
             parserCppFile.printf("}\n");
@@ -496,8 +514,15 @@
                 parserCppFile.printf("%s %s;\n", Utils.elementTypeName(type.getName(),
                         element.isMultiple() || type instanceof CppComplexType), variableName);
             }
-            parserCppFile.print("for (xmlNode *child = root->xmlChildrenNode; child != nullptr;"
-                    + " child = child->next) {\n");
+            if (useTinyXml) {
+                parserCppFile.print("for (auto *child = root->FirstChildElement();"
+                        + " child != nullptr;"
+                        + " child = child->NextSiblingElement()) {\n");
+            } else {
+                parserCppFile.print("for (auto *child = root->xmlChildrenNode;"
+                        + " child != nullptr;"
+                        + " child = child->next) {\n");
+            }
             for (int i = 0; i < allElements.size(); ++i) {
                 CppType type = allElementTypes.get(i);
                 XsdElement element = allElements.get(i);
@@ -505,15 +530,17 @@
                 String variableName = Utils.toVariableName(getElementName(elementValue));
 
                 if (i != 0) parserCppFile.printf("} else ");
-                parserCppFile.print("if (!xmlStrcmp(child->name, reinterpret_cast<const xmlChar*>");
-                parserCppFile.printf("(\"%s\"))) {\n", elementValue.getName());
+                if (useTinyXml) {
+                    parserCppFile.printf("if (!strcmp(child->Name(), \"%s\")) {\n",
+                            elementValue.getName());
+                } else {
+                    parserCppFile.printf("if (!xmlStrcmp(child->name,"
+                            + " reinterpret_cast<const xmlChar*>(\"%s\"))) {\n",
+                            elementValue.getName());
+                }
 
                 if (type instanceof CppSimpleType) {
-                    parserCppFile.print("auto xmlValue = make_xmlUnique(xmlNodeListGetString(");
-                    parserCppFile.print("child->doc, child->xmlChildrenNode, 1));\n");
-                    parserCppFile.print("if (xmlValue == nullptr) {\nraw = \"\";\n} else {\n");
-                    parserCppFile.print("raw = reinterpret_cast<const char*>(xmlValue.get());\n}");
-                    parserCppFile.print("\n");
+                    printNodeListGetString("child");
                 }
 
                 parserCppFile.print(type.getParsingExpression());
@@ -531,6 +558,31 @@
         parserCppFile.print("return instance;\n}\n");
     }
 
+    private String getXmlNodeType() {
+        return (useTinyXml ? "tinyxml2::XMLElement" : "xmlNode");
+    }
+
+    private void printNodeListGetString(String varName) {
+        if (useTinyXml) {
+            // The tinyxml version, in contrast to xmlNodeListGetString does not deal
+            // with ENTITY_REF nodes
+            parserCppFile.printf("raw = \"\";\n");
+            parserCppFile.printf("for (auto *textNode = %s->FirstChild();"
+                    + " textNode != nullptr;"
+                    + " textNode = textNode->NextSibling()) {\n", varName);
+            parserCppFile.printf("if (textNode->ToText() != nullptr) {\n");
+            parserCppFile.printf("raw.append(textNode->Value());\n");
+            parserCppFile.printf("}\n");
+            parserCppFile.printf("}\n");
+        } else {
+            parserCppFile.printf("auto xmlValue = make_xmlUnique(xmlNodeListGetString(");
+            parserCppFile.printf("%s->doc, %s->xmlChildrenNode, 1));\n", varName, varName);
+            parserCppFile.printf("if (xmlValue == nullptr) {\nraw = \"\";\n} else {\n");
+            parserCppFile.printf("raw = reinterpret_cast<const char*>(xmlValue.get());\n}");
+            parserCppFile.printf("\n");
+        }
+    }
+
     private void printWriter(String name, String nameScope, XsdComplexType complexType)
             throws CppCodeGeneratorException {
         CppSimpleType baseValueType = (complexType instanceof XsdSimpleContent) ?
@@ -766,27 +818,39 @@
     }
 
     private void printXmlParser() throws CppCodeGeneratorException {
-        parserCppFile.printf("template <class T>\n"
-                + "constexpr void (*xmlDeleter)(T* t);\n"
-                + "template <>\nconstexpr auto xmlDeleter<xmlDoc> = xmlFreeDoc;\n"
-                + "template <>\nauto xmlDeleter<xmlChar> = [](xmlChar *s) { xmlFree(s); };\n\n"
-                + "template <class T>\n"
-                + "constexpr auto make_xmlUnique(T *t) {\n"
-                + "auto deleter = [](T *t) { xmlDeleter<T>(t); };\n"
-                + "return std::unique_ptr<T, decltype(deleter)>{t, deleter};\n"
-                + "}\n\n");
+        if (useTinyXml) {
+            // Nothing to do for libtinyxml2
+        } else {
+            parserCppFile.printf("template <class T>\n"
+                    + "constexpr void (*xmlDeleter)(T* t);\n"
+                    + "template <>\nconstexpr auto xmlDeleter<xmlDoc> = xmlFreeDoc;\n"
+                    + "template <>\nauto xmlDeleter<xmlChar> = [](xmlChar *s) { xmlFree(s); };\n\n"
+                    + "template <class T>\n"
+                    + "constexpr auto make_xmlUnique(T *t) {\n"
+                    + "auto deleter = [](T *t) { xmlDeleter<T>(t); };\n"
+                    + "return std::unique_ptr<T, decltype(deleter)>{t, deleter};\n"
+                    + "}\n\n");
+        }
 
         if (hasAttr) {
             parserCppFile.printf("static std::string getXmlAttribute"
-                    + "(const xmlNode *cur, const char *attribute) {\n"
-                    + "auto xmlValue = make_xmlUnique(xmlGetProp(cur, "
-                    + "reinterpret_cast<const xmlChar*>(attribute)));\n"
-                    + "if (xmlValue == nullptr) {\n"
-                    + "return \"\";\n"
-                    + "}\n"
-                    + "std::string value(reinterpret_cast<const char*>(xmlValue.get()));\n"
-                    + "return value;\n"
-                    + "}\n\n");
+                    + "(const %s *cur, const char *attribute) {\n", getXmlNodeType());
+            if (useTinyXml) {
+                parserCppFile.printf("auto attrValue = cur->Attribute(attribute);\n"
+                        + "if(attrValue == nullptr) {\n"
+                        + "return \"\";\n"
+                        + "}\n"
+                        + "return std::string(attrValue);\n");
+            } else {
+                parserCppFile.printf("auto xmlValue = make_xmlUnique(xmlGetProp(cur, "
+                        + "reinterpret_cast<const xmlChar*>(attribute)));\n"
+                        + "if (xmlValue == nullptr) {\n"
+                        + "return \"\";\n"
+                        + "}\n"
+                        + "std::string value(reinterpret_cast<const char*>(xmlValue.get()));\n"
+                        + "return value;\n");
+            }
+            parserCppFile.printf("}\n\n");
         }
 
         String className = Utils.toClassName(pkgName);
@@ -803,20 +867,33 @@
                     typeName, isMultiRootElement ? Utils.capitalize(typeName) : "");
             parserCppFile.printf("std::optional<%s> read%s(const char* configFile) {\n",
                     typeName, isMultiRootElement ? Utils.capitalize(typeName) : "");
-            parserCppFile.printf("auto doc = make_xmlUnique(xmlParseFile(configFile));\n"
-                    + "if (doc == nullptr) {\n"
-                    + "return std::nullopt;\n"
-                    + "}\n"
-                    + "xmlNodePtr child = xmlDocGetRootElement(doc.get());\n"
-                    + "if (child == nullptr) {\n"
-                    + "return std::nullopt;\n"
-                    + "}\n"
-                    + "if (xmlXIncludeProcess(doc.get()) < 0) {\n"
-                    + "return std::nullopt;\n"
-                    + "}\n\n"
-                    + "if (!xmlStrcmp(child->name, reinterpret_cast<const xmlChar*>"
-                    + "(\"%s\"))) {\n",
-                    elementName);
+            if (useTinyXml) {
+                parserCppFile.printf("tinyxml2::XMLDocument doc;\n"
+                        + "if (doc.LoadFile(configFile) != tinyxml2::XML_SUCCESS) {\n"
+                        + "return std::nullopt;\n"
+                        + "}\n"
+                        + "auto child = doc.FirstChildElement();\n"
+                        + "if (child == nullptr) {\n"
+                        + "return std::nullopt;\n"
+                        + "}\n\n"
+                        + "if (strcmp(child->Name(), \"%s\") == 0) {\n",
+                        elementName);
+            } else {
+                parserCppFile.printf("auto doc = make_xmlUnique(xmlParseFile(configFile));\n"
+                        + "if (doc == nullptr) {\n"
+                        + "return std::nullopt;\n"
+                        + "}\n"
+                        + "xmlNodePtr child = xmlDocGetRootElement(doc.get());\n"
+                        + "if (child == nullptr) {\n"
+                        + "return std::nullopt;\n"
+                        + "}\n"
+                        + "if (xmlXIncludeProcess(doc.get()) < 0) {\n"
+                        + "return std::nullopt;\n"
+                        + "}\n\n"
+                        + "if (!xmlStrcmp(child->name, reinterpret_cast<const xmlChar*>"
+                        + "(\"%s\"))) {\n",
+                        elementName);
+            }
 
             if (cppType instanceof CppSimpleType) {
                 parserCppFile.printf("%s value = getXmlAttribute(child, \"%s\");\n",
diff --git a/src/com/android/xsdc/cpp/CppSimpleType.java b/src/com/android/xsdc/cpp/CppSimpleType.java
index 282855f..d063ebe 100644
--- a/src/com/android/xsdc/cpp/CppSimpleType.java
+++ b/src/com/android/xsdc/cpp/CppSimpleType.java
@@ -63,15 +63,12 @@
         if (list) {
             expression.append(
                     String.format("%s value;\n", getName()));
-            expression.append(String.format("{\nint base = 0;\n"
-                    + "int found;\n"
-                    + "while(true) {\n"
-                    + "found = raw.find_first_of(\" \", base);\n"
-                    + "value.push_back(%s);\n"
-                    + "if (found == raw.npos) break;\n"
-                    + "base = found + 1;\n"
+            expression.append(String.format("{\n"
+                    + "std::istringstream stream(raw);\n"
+                    + "for(std::string str; stream >> str; ) {\n"
+                    + "    value.push_back(%s);\n"
                     + "}\n",
-                    String.format(rawParsingExpression, "raw.substr(base, found - base)")));
+                    String.format(rawParsingExpression, "str")));
             expression.append("}\n");
         } else {
             expression.append(
diff --git a/tests/Android.bp b/tests/Android.bp
index ab44220..6ef14ee 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -34,6 +34,7 @@
     srcs: [
         "simple_type.cpp",
         "tests.cpp",
+        "test_group.cpp",
         "main.cpp",
     ],
     test_options: {
@@ -70,6 +71,51 @@
     test_suites: ["general-tests"],
 }
 
+// These tests verify generated sources with libtinyxml2.
+cc_test_host {
+    name: "xsdc-tinyxml-cpp-tests",
+    srcs: [
+        "simple_type.cpp",
+        "tests.cpp",
+        "main.cpp",
+        // Since tinyxml2 doesn't support XInclude, skip the
+        // group test
+        // "test_group.cpp",
+    ],
+    test_options: {
+        unit_test: true,
+    },
+    generated_sources: [
+        "xsdc_attr_group_simple_tinyxml_tests",
+        "xsdc_attr_enumtype_tinyxml_tests",
+        "xsdc_group_tinyxml_tests",
+        "xsdc_nested_type_tinyxml_tests",
+        "xsdc_predefined_types_tinyxml_tests",
+        "xsdc_purchase_simple_tinyxml_tests",
+        "xsdc_reference_tinyxml_tests",
+        "xsdc_simple_complex_content_tinyxml_tests",
+        "xsdc_simple_type_tinyxml_tests",
+    ],
+    generated_headers: [
+        "xsdc_attr_group_simple_tinyxml_tests",
+        "xsdc_attr_enumtype_tinyxml_tests",
+        "xsdc_group_tinyxml_tests",
+        "xsdc_nested_type_tinyxml_tests",
+        "xsdc_predefined_types_tinyxml_tests",
+        "xsdc_purchase_simple_tinyxml_tests",
+        "xsdc_reference_tinyxml_tests",
+        "xsdc_simple_complex_content_tinyxml_tests",
+        "xsdc_simple_type_tinyxml_tests",
+    ],
+    header_libs: ["libxsdc-utils"],
+    shared_libs: [
+        "libbase",
+        "libtinyxml2",
+    ],
+    data: ["resources/*.xml"],
+    test_suites: ["general-tests"],
+}
+
 // These tests verify that enums-only and parser-only
 // modules can be combined later.
 cc_test_host {
@@ -98,6 +144,36 @@
     test_suites: ["general-tests"],
 }
 
+
+// These tests verify that enums-only and parser-only
+// modules can be combined later. The tests use libtinyxml2
+// in the generated sources.
+cc_test_host {
+    name: "xsdc-tinyxml-cpp-tests-split",
+    srcs: [
+        "simple_type.cpp",
+        "main.cpp",
+    ],
+    test_options: {
+        unit_test: true,
+    },
+    generated_sources: [
+        "xsdc_simple_type_tinyxml_tests_enums",
+        "xsdc_simple_type_tinyxml_tests_parser",
+    ],
+    generated_headers: [
+        "xsdc_simple_type_tinyxml_tests_enums",
+        "xsdc_simple_type_tinyxml_tests_parser",
+    ],
+    header_libs: ["libxsdc-utils"],
+    shared_libs: [
+        "libbase",
+        "libtinyxml2",
+    ],
+    data: ["resources/*.xml"],
+    test_suites: ["general-tests"],
+}
+
 // These tests verify that enums-only module can be used on
 // its own and it does not depend on libxml2.
 cc_test_host {
@@ -122,3 +198,57 @@
     data: ["resources/*.xml"],
     test_suites: ["general-tests"],
 }
+
+// These tests verify that enums-only module can be used on
+// its own and it does not depend on libtinyxml2.
+cc_test_host {
+    name: "xsdc-tinyxml-cpp-tests-enums",
+    srcs: [
+        "simple_type_enumsonly.cpp",
+        "main.cpp",
+    ],
+    test_options: {
+        unit_test: true,
+    },
+    generated_sources: [
+        "xsdc_simple_type_tinyxml_tests_enums",
+    ],
+    generated_headers: [
+        "xsdc_simple_type_tinyxml_tests_enums",
+    ],
+    header_libs: ["libxsdc-utils"],
+    shared_libs: [
+        "libbase",
+    ],
+    data: ["resources/*.xml"],
+    test_suites: ["general-tests"],
+}
+
+// These tests verify that documents generated with the
+// libxml2 variant can be parsed by the tinyxml variant and
+// vice-versa.
+cc_test_host {
+    name: "xsdc-cross-generate-parse-cpp-tests",
+    srcs: [
+        "cross_generate_parse.cpp",
+        "main.cpp",
+    ],
+    test_options: {
+        unit_test: true,
+    },
+    generated_sources: [
+        "xsdc_cross_generate_parse_libxml_tests",
+        "xsdc_cross_generate_parse_tinyxml_tests",
+    ],
+    generated_headers: [
+        "xsdc_cross_generate_parse_libxml_tests",
+        "xsdc_cross_generate_parse_tinyxml_tests",
+    ],
+    header_libs: ["libxsdc-utils"],
+    shared_libs: [
+        "libbase",
+        "libtinyxml2",
+        "libxml2",
+    ],
+    test_suites: ["general-tests"],
+}
diff --git a/tests/cross_generate_parse.cpp b/tests/cross_generate_parse.cpp
new file mode 100644
index 0000000..b5cb07f
--- /dev/null
+++ b/tests/cross_generate_parse.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <optional>
+
+#include <android-base/macros.h>
+#include <gtest/gtest.h>
+#include "cross_libxml.h"
+#include "cross_tinyxml.h"
+
+#include "xmltest.h"
+
+using namespace std;
+namespace libxml = cross::libxml;
+namespace tinyxml = cross::tinyxml;
+
+TEST_F(XmlTest, LibXmlGenerateTinyXmlParse) {
+  std::vector<libxml::USAddress> shipTo;
+  shipTo.emplace_back("name1", "street1", "city1", "state1", 1, "US");
+  shipTo.emplace_back("name2", "street2", "city2", "state2", 7922816251426433759, "US");
+
+  std::vector<libxml::USAddress> billTo;
+  billTo.emplace_back("billName", "billStree", "billCity", "billState", 1, "US");
+  billTo.emplace_back("billName2", std::nullopt, std::nullopt,
+      std::nullopt, std::nullopt, std::nullopt);
+
+  libxml::PurchaseOrderType libXmlDoc(std::move(shipTo), std::move(billTo), "1900-01-01");
+
+  ofstream out("libxml_generated.xml");
+  write(out, libXmlDoc);
+
+  tinyxml::PurchaseOrderType tinyXmlDoc = *tinyxml::read("libxml_generated.xml");
+
+  EXPECT_EQ(libXmlDoc.getOrderDate(), tinyXmlDoc.getOrderDate());
+
+  EXPECT_EQ(libXmlDoc.getShipTo().size(), tinyXmlDoc.getShipTo().size());
+  EXPECT_EQ(libXmlDoc.getShipTo()[0].getName(), tinyXmlDoc.getShipTo()[0].getName());
+  EXPECT_EQ(libXmlDoc.getShipTo()[0].getStreet(), tinyXmlDoc.getShipTo()[0].getStreet());
+  EXPECT_EQ(libXmlDoc.getShipTo()[0].getCity(), tinyXmlDoc.getShipTo()[0].getCity());
+  EXPECT_EQ(libXmlDoc.getShipTo()[0].getState(), tinyXmlDoc.getShipTo()[0].getState());
+  EXPECT_EQ(libXmlDoc.getShipTo()[0].getZip(), tinyXmlDoc.getShipTo()[0].getZip());
+  EXPECT_EQ(libXmlDoc.getShipTo()[0].getCountry(), tinyXmlDoc.getShipTo()[0].getCountry());
+  EXPECT_EQ(libXmlDoc.getShipTo()[1].getName(), tinyXmlDoc.getShipTo()[1].getName());
+  EXPECT_EQ(libXmlDoc.getShipTo()[1].getStreet(), tinyXmlDoc.getShipTo()[1].getStreet());
+  EXPECT_EQ(libXmlDoc.getShipTo()[1].getCity(), tinyXmlDoc.getShipTo()[1].getCity());
+  EXPECT_EQ(libXmlDoc.getShipTo()[1].getState(), tinyXmlDoc.getShipTo()[1].getState());
+  EXPECT_EQ(libXmlDoc.getShipTo()[1].getZip(), tinyXmlDoc.getShipTo()[1].getZip());
+  EXPECT_EQ(libXmlDoc.getShipTo()[1].getCountry(), tinyXmlDoc.getShipTo()[1].getCountry());
+
+  EXPECT_EQ(libXmlDoc.getBillTo().size(), tinyXmlDoc.getBillTo().size());
+  EXPECT_EQ(libXmlDoc.getBillTo()[0].getName(), tinyXmlDoc.getBillTo()[0].getName());
+  EXPECT_EQ(libXmlDoc.getBillTo()[0].getStreet(), tinyXmlDoc.getBillTo()[0].getStreet());
+  EXPECT_EQ(libXmlDoc.getBillTo()[0].getCity(), tinyXmlDoc.getBillTo()[0].getCity());
+  EXPECT_EQ(libXmlDoc.getBillTo()[0].getState(), tinyXmlDoc.getBillTo()[0].getState());
+  EXPECT_EQ(libXmlDoc.getBillTo()[0].getZip(), tinyXmlDoc.getBillTo()[0].getZip());
+  EXPECT_EQ(libXmlDoc.getBillTo()[0].getCountry(), tinyXmlDoc.getBillTo()[0].getCountry());
+  EXPECT_EQ(libXmlDoc.getBillTo()[1].getName(), tinyXmlDoc.getBillTo()[1].getName());
+  EXPECT_EQ(libXmlDoc.getBillTo()[1].hasStreet(), tinyXmlDoc.getBillTo()[1].hasStreet());
+  EXPECT_EQ(libXmlDoc.getBillTo()[1].hasCity(), tinyXmlDoc.getBillTo()[1].hasCity());
+  EXPECT_EQ(libXmlDoc.getBillTo()[1].hasState(), tinyXmlDoc.getBillTo()[1].hasState());
+  EXPECT_EQ(libXmlDoc.getBillTo()[1].hasZip(), tinyXmlDoc.getBillTo()[1].hasZip());
+  EXPECT_EQ(libXmlDoc.getBillTo()[1].hasCountry(), tinyXmlDoc.getBillTo()[1].hasCountry());
+}
+
+TEST_F(XmlTest, TinyXmlGenerateLibXmlParse) {
+  std::vector<tinyxml::USAddress> shipTo;
+  shipTo.emplace_back("name1", "street1", "city1", "state1", 1, "US");
+  shipTo.emplace_back("name2", "street2", "city2", "state2", 7922816251426433759, "US");
+
+  std::vector<tinyxml::USAddress> billTo;
+  billTo.emplace_back("billName", "billStree", "billCity", "billState", 1, "US");
+  billTo.emplace_back("billName2", std::nullopt, std::nullopt,
+      std::nullopt, std::nullopt, std::nullopt);
+
+  tinyxml::PurchaseOrderType tinyXmlDoc(std::move(shipTo), std::move(billTo), "1900-01-01");
+
+  ofstream out("tinyxml_generated.xml");
+  write(out, tinyXmlDoc);
+
+  libxml::PurchaseOrderType libXmlDoc = *libxml::read("tinyxml_generated.xml");
+
+  EXPECT_EQ(tinyXmlDoc.getOrderDate(), libXmlDoc.getOrderDate());
+
+  EXPECT_EQ(tinyXmlDoc.getShipTo().size(), libXmlDoc.getShipTo().size());
+  EXPECT_EQ(tinyXmlDoc.getShipTo()[0].getName(), libXmlDoc.getShipTo()[0].getName());
+  EXPECT_EQ(tinyXmlDoc.getShipTo()[0].getStreet(), libXmlDoc.getShipTo()[0].getStreet());
+  EXPECT_EQ(tinyXmlDoc.getShipTo()[0].getCity(), libXmlDoc.getShipTo()[0].getCity());
+  EXPECT_EQ(tinyXmlDoc.getShipTo()[0].getState(), libXmlDoc.getShipTo()[0].getState());
+  EXPECT_EQ(tinyXmlDoc.getShipTo()[0].getZip(), libXmlDoc.getShipTo()[0].getZip());
+  EXPECT_EQ(tinyXmlDoc.getShipTo()[0].getCountry(), libXmlDoc.getShipTo()[0].getCountry());
+  EXPECT_EQ(tinyXmlDoc.getShipTo()[1].getName(), libXmlDoc.getShipTo()[1].getName());
+  EXPECT_EQ(tinyXmlDoc.getShipTo()[1].getStreet(), libXmlDoc.getShipTo()[1].getStreet());
+  EXPECT_EQ(tinyXmlDoc.getShipTo()[1].getCity(), libXmlDoc.getShipTo()[1].getCity());
+  EXPECT_EQ(tinyXmlDoc.getShipTo()[1].getState(), libXmlDoc.getShipTo()[1].getState());
+  EXPECT_EQ(tinyXmlDoc.getShipTo()[1].getZip(), libXmlDoc.getShipTo()[1].getZip());
+  EXPECT_EQ(tinyXmlDoc.getShipTo()[1].getCountry(), libXmlDoc.getShipTo()[1].getCountry());
+
+  EXPECT_EQ(tinyXmlDoc.getBillTo().size(), libXmlDoc.getBillTo().size());
+  EXPECT_EQ(tinyXmlDoc.getBillTo()[0].getName(), libXmlDoc.getBillTo()[0].getName());
+  EXPECT_EQ(tinyXmlDoc.getBillTo()[0].getStreet(), libXmlDoc.getBillTo()[0].getStreet());
+  EXPECT_EQ(tinyXmlDoc.getBillTo()[0].getCity(), libXmlDoc.getBillTo()[0].getCity());
+  EXPECT_EQ(tinyXmlDoc.getBillTo()[0].getState(), libXmlDoc.getBillTo()[0].getState());
+  EXPECT_EQ(tinyXmlDoc.getBillTo()[0].getZip(), libXmlDoc.getBillTo()[0].getZip());
+  EXPECT_EQ(tinyXmlDoc.getBillTo()[0].getCountry(), libXmlDoc.getBillTo()[0].getCountry());
+  EXPECT_EQ(tinyXmlDoc.getBillTo()[1].getName(), libXmlDoc.getBillTo()[1].getName());
+  EXPECT_EQ(tinyXmlDoc.getBillTo()[1].hasStreet(), libXmlDoc.getBillTo()[1].hasStreet());
+  EXPECT_EQ(tinyXmlDoc.getBillTo()[1].hasCity(), libXmlDoc.getBillTo()[1].hasCity());
+  EXPECT_EQ(tinyXmlDoc.getBillTo()[1].hasState(), libXmlDoc.getBillTo()[1].hasState());
+  EXPECT_EQ(tinyXmlDoc.getBillTo()[1].hasZip(), libXmlDoc.getBillTo()[1].hasZip());
+  EXPECT_EQ(tinyXmlDoc.getBillTo()[1].hasCountry(), libXmlDoc.getBillTo()[1].hasCountry());
+}
diff --git a/tests/resources/attr_group_simple.xml b/tests/resources/attr_group_simple.xml
index 0a14222..fa400f9 100644
--- a/tests/resources/attr_group_simple.xml
+++ b/tests/resources/attr_group_simple.xml
@@ -1,4 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Student State="CA" city="Mountain View" road="Street 101" list="1 2 3">
+<Student State="CA" city="Mountain View" road="Street 101" list="1  2
+                                                                 3">
     <Name>Jun</Name>
 </Student>
diff --git a/tests/resources/attr_group_simple/Android.bp b/tests/resources/attr_group_simple/Android.bp
index ef3136d..a8ca07e 100644
--- a/tests/resources/attr_group_simple/Android.bp
+++ b/tests/resources/attr_group_simple/Android.bp
@@ -9,3 +9,11 @@
     package_name: "attr.group.simple",
     gen_writer: true,
 }
+
+xsd_config {
+    name: "xsdc_attr_group_simple_tinyxml_tests",
+    srcs: ["attr_group_simple.xsd"],
+    package_name: "attr.group.simple",
+    gen_writer: true,
+    tinyxml: true,
+}
diff --git a/tests/resources/cross_generate_parse_libxml/Android.bp b/tests/resources/cross_generate_parse_libxml/Android.bp
new file mode 100644
index 0000000..f881a07
--- /dev/null
+++ b/tests/resources/cross_generate_parse_libxml/Android.bp
@@ -0,0 +1,11 @@
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+xsd_config {
+    name: "xsdc_cross_generate_parse_libxml_tests",
+    srcs: ["cross_generate_parse.xsd"],
+    package_name: "cross.libxml",
+    gen_writer: true,
+}
diff --git a/tests/resources/cross_generate_parse_libxml/api/current.txt b/tests/resources/cross_generate_parse_libxml/api/current.txt
new file mode 100644
index 0000000..9a9a170
--- /dev/null
+++ b/tests/resources/cross_generate_parse_libxml/api/current.txt
@@ -0,0 +1,43 @@
+// Signature format: 2.0
+package cross.libxml {
+
+  public class PurchaseOrderType {
+    ctor public PurchaseOrderType();
+    method public cross.libxml.USAddress getBillTo();
+    method public javax.xml.datatype.XMLGregorianCalendar getOrderDate();
+    method public java.util.List<cross.libxml.USAddress> getShipTo();
+    method public void setBillTo(cross.libxml.USAddress);
+    method public void setOrderDate(javax.xml.datatype.XMLGregorianCalendar);
+  }
+
+  public class USAddress {
+    ctor public USAddress();
+    method public String getCity();
+    method public String getCountry();
+    method public String getName();
+    method public String getState();
+    method public String getStreet();
+    method public java.math.BigInteger getZip();
+    method public void setCity(String);
+    method public void setCountry(String);
+    method public void setName(String);
+    method public void setState(String);
+    method public void setStreet(String);
+    method public void setZip(java.math.BigInteger);
+  }
+
+  public class XmlParser {
+    ctor public XmlParser();
+    method public static cross.libxml.PurchaseOrderType read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+  }
+
+  public class XmlWriter implements java.io.Closeable {
+    ctor public XmlWriter(java.io.PrintWriter);
+    method public void close();
+    method public static void write(cross.libxml.XmlWriter, cross.libxml.PurchaseOrderType) throws java.io.IOException;
+  }
+
+}
+
diff --git a/tests/resources/cross_generate_parse_libxml/api/last_current.txt b/tests/resources/cross_generate_parse_libxml/api/last_current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/cross_generate_parse_libxml/api/last_current.txt
diff --git a/tests/resources/cross_generate_parse_libxml/api/last_removed.txt b/tests/resources/cross_generate_parse_libxml/api/last_removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/cross_generate_parse_libxml/api/last_removed.txt
diff --git a/tests/resources/cross_generate_parse_libxml/api/removed.txt b/tests/resources/cross_generate_parse_libxml/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/tests/resources/cross_generate_parse_libxml/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/tests/resources/cross_generate_parse_libxml/cross_generate_parse.xsd b/tests/resources/cross_generate_parse_libxml/cross_generate_parse.xsd
new file mode 100644
index 0000000..7898a18
--- /dev/null
+++ b/tests/resources/cross_generate_parse_libxml/cross_generate_parse.xsd
@@ -0,0 +1,25 @@
+<!-- https://msdn.microsoft.com/ko-kr/library/dd489283.aspx -->
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tempuri.org/PurchaseOrderSchema.xsd"
+            targetNamespace="http://tempuri.org/PurchaseOrderSchema.xsd"
+            elementFormDefault="qualified">
+    <xsd:element name="PurchaseOrder" type="tns:PurchaseOrderType"/>
+    <xsd:complexType name="PurchaseOrderType">
+        <xsd:sequence>
+            <xsd:element name="ShipTo" type="tns:USAddress" maxOccurs="2"/>
+            <xsd:element name="BillTo" type="tns:USAddress"/>
+        </xsd:sequence>
+        <xsd:attribute name="OrderDate" type="xsd:date"/>
+    </xsd:complexType>
+
+    <xsd:complexType name="USAddress">
+        <xsd:sequence>
+            <xsd:element name="name"   type="xsd:string"/>
+            <xsd:element name="street" type="xsd:string"/>
+            <xsd:element name="city"   type="xsd:string"/>
+            <xsd:element name="state"  type="xsd:string"/>
+            <xsd:element name="zip"    type="xsd:integer"/>
+        </xsd:sequence>
+        <xsd:attribute name="country" type="xsd:NMTOKEN" fixed="US"/>
+    </xsd:complexType>
+</xsd:schema>
diff --git a/tests/resources/cross_generate_parse_tinyxml/Android.bp b/tests/resources/cross_generate_parse_tinyxml/Android.bp
new file mode 100644
index 0000000..3a0256a
--- /dev/null
+++ b/tests/resources/cross_generate_parse_tinyxml/Android.bp
@@ -0,0 +1,12 @@
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+xsd_config {
+    name: "xsdc_cross_generate_parse_tinyxml_tests",
+    srcs: ["cross_generate_parse.xsd"],
+    package_name: "cross.tinyxml",
+    gen_writer: true,
+    tinyxml: true,
+}
diff --git a/tests/resources/cross_generate_parse_tinyxml/api/current.txt b/tests/resources/cross_generate_parse_tinyxml/api/current.txt
new file mode 100644
index 0000000..6daf0dd
--- /dev/null
+++ b/tests/resources/cross_generate_parse_tinyxml/api/current.txt
@@ -0,0 +1,43 @@
+// Signature format: 2.0
+package cross.tinyxml {
+
+  public class PurchaseOrderType {
+    ctor public PurchaseOrderType();
+    method public cross.tinyxml.USAddress getBillTo();
+    method public javax.xml.datatype.XMLGregorianCalendar getOrderDate();
+    method public java.util.List<cross.tinyxml.USAddress> getShipTo();
+    method public void setBillTo(cross.tinyxml.USAddress);
+    method public void setOrderDate(javax.xml.datatype.XMLGregorianCalendar);
+  }
+
+  public class USAddress {
+    ctor public USAddress();
+    method public String getCity();
+    method public String getCountry();
+    method public String getName();
+    method public String getState();
+    method public String getStreet();
+    method public java.math.BigInteger getZip();
+    method public void setCity(String);
+    method public void setCountry(String);
+    method public void setName(String);
+    method public void setState(String);
+    method public void setStreet(String);
+    method public void setZip(java.math.BigInteger);
+  }
+
+  public class XmlParser {
+    ctor public XmlParser();
+    method public static cross.tinyxml.PurchaseOrderType read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+  }
+
+  public class XmlWriter implements java.io.Closeable {
+    ctor public XmlWriter(java.io.PrintWriter);
+    method public void close();
+    method public static void write(cross.tinyxml.XmlWriter, cross.tinyxml.PurchaseOrderType) throws java.io.IOException;
+  }
+
+}
+
diff --git a/tests/resources/cross_generate_parse_tinyxml/api/last_current.txt b/tests/resources/cross_generate_parse_tinyxml/api/last_current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/cross_generate_parse_tinyxml/api/last_current.txt
diff --git a/tests/resources/cross_generate_parse_tinyxml/api/last_removed.txt b/tests/resources/cross_generate_parse_tinyxml/api/last_removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/cross_generate_parse_tinyxml/api/last_removed.txt
diff --git a/tests/resources/cross_generate_parse_tinyxml/api/removed.txt b/tests/resources/cross_generate_parse_tinyxml/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/tests/resources/cross_generate_parse_tinyxml/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/tests/resources/cross_generate_parse_tinyxml/cross_generate_parse.xsd b/tests/resources/cross_generate_parse_tinyxml/cross_generate_parse.xsd
new file mode 100644
index 0000000..7898a18
--- /dev/null
+++ b/tests/resources/cross_generate_parse_tinyxml/cross_generate_parse.xsd
@@ -0,0 +1,25 @@
+<!-- https://msdn.microsoft.com/ko-kr/library/dd489283.aspx -->
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tempuri.org/PurchaseOrderSchema.xsd"
+            targetNamespace="http://tempuri.org/PurchaseOrderSchema.xsd"
+            elementFormDefault="qualified">
+    <xsd:element name="PurchaseOrder" type="tns:PurchaseOrderType"/>
+    <xsd:complexType name="PurchaseOrderType">
+        <xsd:sequence>
+            <xsd:element name="ShipTo" type="tns:USAddress" maxOccurs="2"/>
+            <xsd:element name="BillTo" type="tns:USAddress"/>
+        </xsd:sequence>
+        <xsd:attribute name="OrderDate" type="xsd:date"/>
+    </xsd:complexType>
+
+    <xsd:complexType name="USAddress">
+        <xsd:sequence>
+            <xsd:element name="name"   type="xsd:string"/>
+            <xsd:element name="street" type="xsd:string"/>
+            <xsd:element name="city"   type="xsd:string"/>
+            <xsd:element name="state"  type="xsd:string"/>
+            <xsd:element name="zip"    type="xsd:integer"/>
+        </xsd:sequence>
+        <xsd:attribute name="country" type="xsd:NMTOKEN" fixed="US"/>
+    </xsd:complexType>
+</xsd:schema>
diff --git a/tests/resources/enum_type/Android.bp b/tests/resources/enum_type/Android.bp
index 979ae6d..a788b3b 100644
--- a/tests/resources/enum_type/Android.bp
+++ b/tests/resources/enum_type/Android.bp
@@ -7,3 +7,10 @@
     srcs: ["attr_enumtype.xsd"],
     package_name: "attr.enumtype",
 }
+
+xsd_config {
+    name: "xsdc_attr_enumtype_tinyxml_tests",
+    srcs: ["attr_enumtype.xsd"],
+    package_name: "attr.enumtype",
+    tinyxml: true,
+}
diff --git a/tests/resources/group/Android.bp b/tests/resources/group/Android.bp
index 6560ffd..6e4abc1 100644
--- a/tests/resources/group/Android.bp
+++ b/tests/resources/group/Android.bp
@@ -9,3 +9,11 @@
     package_name: "group",
     gen_writer: true,
 }
+
+xsd_config {
+    name: "xsdc_group_tinyxml_tests",
+    srcs: ["group.xsd"],
+    package_name: "group",
+    gen_writer: true,
+    tinyxml: true,
+}
diff --git a/tests/resources/nested_type/Android.bp b/tests/resources/nested_type/Android.bp
index 15140fb..1963a12 100644
--- a/tests/resources/nested_type/Android.bp
+++ b/tests/resources/nested_type/Android.bp
@@ -9,3 +9,11 @@
     package_name: "nested.type",
     gen_writer: true,
 }
+
+xsd_config {
+    name: "xsdc_nested_type_tinyxml_tests",
+    srcs: ["nested_type.xsd"],
+    package_name: "nested.type",
+    gen_writer: true,
+    tinyxml: true,
+}
diff --git a/tests/resources/predefined_types/Android.bp b/tests/resources/predefined_types/Android.bp
index 05f2ae0..bf814f2 100644
--- a/tests/resources/predefined_types/Android.bp
+++ b/tests/resources/predefined_types/Android.bp
@@ -9,3 +9,11 @@
     package_name: "predefined.types",
     gen_writer: true,
 }
+
+xsd_config {
+    name: "xsdc_predefined_types_tinyxml_tests",
+    srcs: ["predefined_types.xsd"],
+    package_name: "predefined.types",
+    gen_writer: true,
+    tinyxml: true,
+}
diff --git a/tests/resources/purchase_simple/Android.bp b/tests/resources/purchase_simple/Android.bp
index 0773cb8..4026c8a 100644
--- a/tests/resources/purchase_simple/Android.bp
+++ b/tests/resources/purchase_simple/Android.bp
@@ -9,3 +9,11 @@
     package_name: "purchase.simple",
     gen_writer: true,
 }
+
+xsd_config {
+    name: "xsdc_purchase_simple_tinyxml_tests",
+    srcs: ["purchase_simple.xsd"],
+    package_name: "purchase.simple",
+    gen_writer: true,
+    tinyxml: true,
+}
diff --git a/tests/resources/reference/Android.bp b/tests/resources/reference/Android.bp
index b0e7bca..1f99c03 100644
--- a/tests/resources/reference/Android.bp
+++ b/tests/resources/reference/Android.bp
@@ -10,3 +10,12 @@
     gen_writer: true,
     nullability: true,
 }
+
+xsd_config {
+    name: "xsdc_reference_tinyxml_tests",
+    srcs: ["reference.xsd"],
+    package_name: "reference",
+    gen_writer: true,
+    nullability: true,
+    tinyxml: true,
+}
diff --git a/tests/resources/simple_complex_content/Android.bp b/tests/resources/simple_complex_content/Android.bp
index 00502cb..5e73010 100644
--- a/tests/resources/simple_complex_content/Android.bp
+++ b/tests/resources/simple_complex_content/Android.bp
@@ -10,3 +10,12 @@
     gen_writer: true,
     gen_has: true,
 }
+
+xsd_config {
+    name: "xsdc_simple_complex_content_tinyxml_tests",
+    srcs: ["simple_complex_content.xsd"],
+    package_name: "simple.complex.content",
+    gen_writer: true,
+    gen_has: true,
+    tinyxml: true,
+}
diff --git a/tests/resources/simple_type/Android.bp b/tests/resources/simple_type/Android.bp
index b4be8d4..c782418 100644
--- a/tests/resources/simple_type/Android.bp
+++ b/tests/resources/simple_type/Android.bp
@@ -12,6 +12,15 @@
 }
 
 xsd_config {
+    name: "xsdc_simple_type_tinyxml_tests",
+    srcs: ["simple_type.xsd"],
+    package_name: "simple.type",
+    gen_writer: true,
+    boolean_getter: true,
+    tinyxml: true,
+}
+
+xsd_config {
     name: "xsdc_simple_type_tests_enums",
     srcs: ["simple_type.xsd"],
     package_name: "simple.type",
@@ -21,6 +30,16 @@
 }
 
 xsd_config {
+    name: "xsdc_simple_type_tinyxml_tests_enums",
+    srcs: ["simple_type.xsd"],
+    package_name: "simple.type",
+    enums_only: true,
+    gen_writer: true,
+    boolean_getter: true,
+    tinyxml: true,
+}
+
+xsd_config {
     name: "xsdc_simple_type_tests_parser",
     srcs: ["simple_type.xsd"],
     package_name: "simple.type",
@@ -28,3 +47,13 @@
     gen_writer: true,
     boolean_getter: true,
 }
+
+xsd_config {
+    name: "xsdc_simple_type_tinyxml_tests_parser",
+    srcs: ["simple_type.xsd"],
+    package_name: "simple.type",
+    parser_only: true,
+    gen_writer: true,
+    boolean_getter: true,
+    tinyxml: true,
+}
diff --git a/tests/test_group.cpp b/tests/test_group.cpp
new file mode 100644
index 0000000..bb8d065
--- /dev/null
+++ b/tests/test_group.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <optional>
+
+#include <android-base/macros.h>
+#include <gtest/gtest.h>
+
+#include "group.h"
+#include "xmltest.h"
+
+using namespace std;
+
+TEST_F(XmlTest, Group) {
+  using namespace group;
+  Student student = *read(Resource("group.xml").c_str());
+
+  EXPECT_EQ(student.getCity(), "Mountain View");
+  EXPECT_EQ(student.getState(), "CA");
+  EXPECT_EQ(student.getRoad(), "Street 101");
+
+  ofstream out("old_group.xml");
+  write(out, student);
+}
diff --git a/tests/tests.cpp b/tests/tests.cpp
index c143f4e..1ed99ea 100644
--- a/tests/tests.cpp
+++ b/tests/tests.cpp
@@ -268,15 +268,3 @@
   ofstream out("old_attr_group_simple.xml");
   write(out, student);
 }
-
-TEST_F(XmlTest, Group) {
-  using namespace group;
-  Student student = *read(Resource("group.xml").c_str());
-
-  EXPECT_EQ(student.getCity(), "Mountain View");
-  EXPECT_EQ(student.getState(), "CA");
-  EXPECT_EQ(student.getRoad(), "Street 101");
-
-  ofstream out("old_group.xml");
-  write(out, student);
-}