am 17c674d1: ADT #1844909: Tweak XML schema, add more tests.

Merge commit '17c674d1057aeb3946747ca3b1d67a8468e1efb4'

* commit '17c674d1057aeb3946747ca3b1d67a8468e1efb4':
  ADT #1844909: Tweak XML schema, add more tests.
This commit is contained in:
Raphael
2009-05-15 13:31:48 -07:00
committed by The Android Open Source Project
3 changed files with 244 additions and 76 deletions

View File

@@ -86,9 +86,11 @@
<xsd:element name="desc-url" type="xsd:token" minOccurs="0" /> <xsd:element name="desc-url" type="xsd:token" minOccurs="0" />
<xsd:element name="archives" type="sdk:archivesType" /> <xsd:element name="archives" type="sdk:archivesType" />
<!-- An add-on can declare 0 or more libraries. -->
<xsd:element name="libs"> <xsd:element name="libs">
<xsd:complexType> <xsd:complexType>
<xsd:sequence maxOccurs="unbounded"> <xsd:sequence minOccurs="0" maxOccurs="unbounded">
<xsd:element name="lib"> <xsd:element name="lib">
<xsd:complexType> <xsd:complexType>
<xsd:all> <xsd:all>
@@ -135,13 +137,16 @@
</xsd:complexType> </xsd:complexType>
</xsd:element> </xsd:element>
<!-- A collection of files that can be downloaded for a given architectures --> <!-- A collection of files that can be downloaded for a given architecture.
The <archives> node is mandatory in the repository elements and the
collection must have at least one <archive> declared.
-->
<xsd:complexType name="archivesType"> <xsd:complexType name="archivesType">
<xsd:annotation> <xsd:annotation>
<xsd:documentation>A collection of architecture-dependent archives.</xsd:documentation> <xsd:documentation>A collection of architecture-dependent archives.</xsd:documentation>
</xsd:annotation> </xsd:annotation>
<xsd:sequence maxOccurs="unbounded"> <xsd:sequence minOccurs="1" maxOccurs="unbounded">
<!-- One archive file --> <!-- One archive file -->
<xsd:element name="archive"> <xsd:element name="archive">
<xsd:complexType> <xsd:complexType>

View File

@@ -16,15 +16,15 @@
package com.android.sdklib.repository; package com.android.sdklib.repository;
import com.android.sdklib.repository.SdkRepositoryConstants;
import org.xml.sax.ErrorHandler; import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException; import org.xml.sax.SAXParseException;
import java.io.InputStream; import java.io.InputStream;
import java.io.StringReader;
import javax.xml.XMLConstants; import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource; import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema; import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory; import javax.xml.validation.SchemaFactory;
@@ -50,34 +50,10 @@ public class TestSdkRepository extends TestCase {
super.tearDown(); super.tearDown();
} }
public void testValidateLocalRepositoryFile() throws Exception { /**
* A SAX error handler that captures the errors and warnings.
InputStream xsdStream = SdkRepositoryConstants.getXsdStream(); * This allows us to capture *all* errors and just not get an exception on the first one.
InputStream xmlStream = TestSdkRepository.class.getResourceAsStream("repository_sample.xml"); */
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(new StreamSource(xsdStream));
Validator validator = schema.newValidator();
CaptureErrorHandler handler = new CaptureErrorHandler();
validator.setErrorHandler(handler);
validator.validate(new StreamSource(xmlStream));
String warnings = handler.getWarnings();
if (warnings.length() > 0) {
System.err.println(warnings);
}
String errors = handler.getErrors();
if (errors.length() > 0) {
System.err.println(errors);
fail(errors);
}
}
private static class CaptureErrorHandler implements ErrorHandler { private static class CaptureErrorHandler implements ErrorHandler {
private String mWarnings = ""; private String mWarnings = "";
@@ -91,6 +67,22 @@ public class TestSdkRepository extends TestCase {
return mWarnings; return mWarnings;
} }
/**
* Verifies if the handler captures some errors or warnings.
* Prints them on stderr.
* Also fails the unit test if any error was generated.
*/
public void verify() {
if (mWarnings.length() > 0) {
System.err.println(mWarnings);
}
if (mErrors.length() > 0) {
System.err.println(mErrors);
fail(mErrors);
}
}
/** /**
* @throws SAXException * @throws SAXException
*/ */
@@ -98,9 +90,11 @@ public class TestSdkRepository extends TestCase {
mErrors += "Error: " + ex.getMessage() + "\n"; mErrors += "Error: " + ex.getMessage() + "\n";
} }
/**
* @throws SAXException
*/
public void fatalError(SAXParseException ex) throws SAXException { public void fatalError(SAXParseException ex) throws SAXException {
mErrors += "Fatal Error: " + ex.getMessage() + "\n"; mErrors += "Fatal Error: " + ex.getMessage() + "\n";
throw ex;
} }
/** /**
@@ -112,4 +106,143 @@ public class TestSdkRepository extends TestCase {
} }
// --- Helpers ------------
/** Helper method that returns a validator for our XSD */
private Validator getValidator(CaptureErrorHandler handler) throws SAXException {
InputStream xsdStream = SdkRepositoryConstants.getXsdStream();
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(new StreamSource(xsdStream));
Validator validator = schema.newValidator();
if (handler != null) {
validator.setErrorHandler(handler);
}
return validator;
}
/** Validate a valid sample using an InputStream */
public void testValidateLocalRepositoryFile() throws Exception {
InputStream xmlStream =
TestSdkRepository.class.getResourceAsStream("repository_sample.xml");
Source source = new StreamSource(xmlStream);
CaptureErrorHandler handler = new CaptureErrorHandler();
Validator validator = getValidator(handler);
validator.validate(source);
handler.verify();
}
/** An helper that validates a string against an expected regexp. */
private void assertRegex(String expectedRegexp, String actualString) {
assertNotNull(actualString);
assertTrue(
String.format("Regexp Assertion Failed:\nExpected: %s\nActual: %s\n",
expectedRegexp, actualString),
actualString.matches(expectedRegexp));
}
// --- Tests ------------
/** A document should at least have a root to be valid */
public void testEmptyXml() throws Exception {
String document = "<?xml version=\"1.0\"?>";
Source source = new StreamSource(new StringReader(document));
CaptureErrorHandler handler = new CaptureErrorHandler();
Validator validator = getValidator(handler);
try {
validator.validate(source);
} catch (SAXParseException e) {
// We expect to get this specific exception message
assertRegex("Premature end of file.*", e.getMessage());
return;
}
// We shouldn't get here
handler.verify();
fail();
}
/** A document with a root element containing no platform, addon, etc., is valid. */
public void testEmptyRootXml() throws Exception {
String document = "<?xml version=\"1.0\"?>" +
"<r:sdk-repository xmlns:r=\"http://schemas.android.com/sdk/android/repository/1\" />";
Source source = new StreamSource(new StringReader(document));
CaptureErrorHandler handler = new CaptureErrorHandler();
Validator validator = getValidator(handler);
validator.validate(source);
handler.verify();
}
/** A document with an unknown element. */
public void testUnknownContentXml() throws Exception {
String document = "<?xml version=\"1.0\"?>" +
"<r:sdk-repository xmlns:r=\"http://schemas.android.com/sdk/android/repository/1\" >" +
"<r:unknown />" +
"</r:sdk-repository>";
Source source = new StreamSource(new StringReader(document));
// don't capture the validator errors, we want it to fail and catch the exception
Validator validator = getValidator(null);
try {
validator.validate(source);
} catch (SAXParseException e) {
// We expect a parse expression referring to this grammar rule
assertRegex("cvc-complex-type.2.4.a: Invalid content was found.*", e.getMessage());
return;
}
// If we get here, the validator has not failed as we expected it to.
fail();
}
/** A document with an incomplete element. */
public void testIncompleteContentXml() throws Exception {
String document = "<?xml version=\"1.0\"?>" +
"<r:sdk-repository xmlns:r=\"http://schemas.android.com/sdk/android/repository/1\" >" +
"<r:platform> <r:api-level>1</r:api-level> <r:libs /> </r:platform>" +
"</r:sdk-repository>";
Source source = new StreamSource(new StringReader(document));
// don't capture the validator errors, we want it to fail and catch the exception
Validator validator = getValidator(null);
try {
validator.validate(source);
} catch (SAXParseException e) {
// We expect a parse error referring to this grammar rule
assertRegex("cvc-complex-type.2.4.a: Invalid content was found.*", e.getMessage());
return;
}
// If we get here, the validator has not failed as we expected it to.
fail();
}
/** A document with a wrong type element. */
public void testWrongTypeContentXml() throws Exception {
String document = "<?xml version=\"1.0\"?>" +
"<r:sdk-repository xmlns:r=\"http://schemas.android.com/sdk/android/repository/1\" >" +
"<r:platform> <r:api-level>NotAnInteger</r:api-level> <r:libs /> </r:platform>" +
"</r:sdk-repository>";
Source source = new StreamSource(new StringReader(document));
// don't capture the validator errors, we want it to fail and catch the exception
Validator validator = getValidator(null);
try {
validator.validate(source);
} catch (SAXParseException e) {
// We expect a parse error referring to this grammar rule
assertRegex("cvc-datatype-valid.1.2.1: 'NotAnInteger' is not a valid value.*",
e.getMessage());
return;
}
// If we get here, the validator has not failed as we expected it to.
fail();
}
} }

View File

@@ -17,12 +17,17 @@
<sdk:sdk-repository <sdk:sdk-repository
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sdk="http://schemas.android.com/sdk/android/repository/1"> xmlns:sdk="http://schemas.android.com/sdk/android/repository/1">
<!-- Inner elements must be either platform, add-on, doc or tool.
There can be 0 or more of each, in any order. -->
<sdk:platform> <sdk:platform>
<sdk:version>1.0</sdk:version> <sdk:version>1.0</sdk:version>
<sdk:api-level>1</sdk:api-level> <sdk:api-level>1</sdk:api-level>
<sdk:revision>3</sdk:revision> <sdk:revision>3</sdk:revision>
<sdk:description>Some optional description</sdk:description> <sdk:description>Some optional description</sdk:description>
<sdk:desc-url>http://www.example.com/platform1.html</sdk:desc-url> <sdk:desc-url>http://www.example.com/platform1.html</sdk:desc-url>
<!-- The archives node is mandatory and it cannot be empty. -->
<sdk:archives> <sdk:archives>
<sdk:archive os="any"> <sdk:archive os="any">
<sdk:size>65536</sdk:size> <sdk:size>65536</sdk:size>
@@ -31,6 +36,48 @@
</sdk:archive> </sdk:archive>
</sdk:archives> </sdk:archives>
</sdk:platform> </sdk:platform>
<sdk:doc>
<sdk:api-level>1</sdk:api-level>
<sdk:revision>1</sdk:revision>
<sdk:description>Some optional description</sdk:description>
<sdk:desc-url>http://www.example.com/docs.html</sdk:desc-url>
<sdk:archives>
<sdk:archive os="any">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>http://www.example.com/docs/docs1.zip</sdk:url>
</sdk:archive>
</sdk:archives>
</sdk:doc>
<sdk:add-on>
<sdk:name>My First add-on</sdk:name>
<sdk:api-level>1</sdk:api-level>
<sdk:vendor>John Doe</sdk:vendor>
<sdk:revision>1</sdk:revision>
<sdk:description>Some optional description</sdk:description>
<sdk:desc-url>http://www.example.com/myfirstaddon</sdk:desc-url>
<sdk:archives>
<sdk:archive os="any">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>http://www.example.com/add-ons/first.zip</sdk:url>
</sdk:archive>
</sdk:archives>
<!-- The libs node is mandatory, however it can be empty. -->
<sdk:libs>
<sdk:lib>
<sdk:name>android.blah.somelib</sdk:name>
<sdk:description>The description for this library.</sdk:description>
</sdk:lib>
<sdk:lib>
<!-- sdk:description is optional, name is not -->
<sdk:name>com.android.mymaps</sdk:name>
</sdk:lib>
</sdk:libs>
</sdk:add-on>
<sdk:platform> <sdk:platform>
<sdk:version>1.1</sdk:version> <sdk:version>1.1</sdk:version>
<sdk:api-level>2</sdk:api-level> <sdk:api-level>2</sdk:api-level>
@@ -65,31 +112,7 @@
</sdk:archive> </sdk:archive>
</sdk:archives> </sdk:archives>
</sdk:platform> </sdk:platform>
<sdk:add-on>
<sdk:name>My First add-on</sdk:name>
<sdk:api-level>1</sdk:api-level>
<sdk:vendor>John Doe</sdk:vendor>
<sdk:revision>1</sdk:revision>
<sdk:description>Some optional description</sdk:description>
<sdk:desc-url>http://www.example.com/myfirstaddon</sdk:desc-url>
<sdk:archives>
<sdk:archive os="any">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>http://www.example.com/add-ons/first.zip</sdk:url>
</sdk:archive>
</sdk:archives>
<sdk:libs>
<sdk:lib>
<sdk:name>android.blah.somelib</sdk:name>
<sdk:description>The description for this library.</sdk:description>
</sdk:lib>
<sdk:lib>
<!-- sdk:description is optional, name is not -->
<sdk:name>com.android.mymaps</sdk:name>
</sdk:lib>
</sdk:libs>
</sdk:add-on>
<sdk:add-on> <sdk:add-on>
<sdk:name>My Second add-on</sdk:name> <sdk:name>My Second add-on</sdk:name>
<sdk:api-level>2</sdk:api-level> <sdk:api-level>2</sdk:api-level>
@@ -117,19 +140,20 @@
</sdk:lib> </sdk:lib>
</sdk:libs> </sdk:libs>
</sdk:add-on> </sdk:add-on>
<sdk:doc>
<sdk:api-level>1</sdk:api-level> <sdk:tool>
<sdk:revision>1</sdk:revision> <sdk:revision>1</sdk:revision>
<sdk:description>Some optional description</sdk:description> <sdk:description>Some optional description</sdk:description>
<sdk:desc-url>http://www.example.com/docs.html</sdk:desc-url> <sdk:desc-url>http://www.example.com/tools.html</sdk:desc-url>
<sdk:archives> <sdk:archives>
<sdk:archive os="any"> <sdk:archive os="any">
<sdk:size>65536</sdk:size> <sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum> <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>http://www.example.com/docs/docs1.zip</sdk:url> <sdk:url>http://www.example.com/files/tools1.zip</sdk:url>
</sdk:archive> </sdk:archive>
</sdk:archives> </sdk:archives>
</sdk:doc> </sdk:tool>
<sdk:doc> <sdk:doc>
<sdk:api-level>2</sdk:api-level> <sdk:api-level>2</sdk:api-level>
<sdk:revision>42</sdk:revision> <sdk:revision>42</sdk:revision>
@@ -151,18 +175,7 @@
</sdk:archive> </sdk:archive>
</sdk:archives> </sdk:archives>
</sdk:doc> </sdk:doc>
<sdk:tool>
<sdk:revision>1</sdk:revision>
<sdk:description>Some optional description</sdk:description>
<sdk:desc-url>http://www.example.com/tools.html</sdk:desc-url>
<sdk:archives>
<sdk:archive os="any">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>http://www.example.com/files/tools1.zip</sdk:url>
</sdk:archive>
</sdk:archives>
</sdk:tool>
<sdk:tool> <sdk:tool>
<sdk:revision>42</sdk:revision> <sdk:revision>42</sdk:revision>
<sdk:archives> <sdk:archives>
@@ -183,4 +196,21 @@
</sdk:archive> </sdk:archive>
</sdk:archives> </sdk:archives>
</sdk:tool> </sdk:tool>
<sdk:add-on>
<sdk:name>This add-on has no libraries</sdk:name>
<sdk:api-level>4</sdk:api-level>
<sdk:vendor>Joe Bar</sdk:vendor>
<sdk:revision>3</sdk:revision>
<sdk:archives>
<sdk:archive os="any" arch="any">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>distrib/imnotanarchiveimadoctorjim.zip</sdk:url>
</sdk:archive>
</sdk:archives>
<!-- The libs node is mandatory, however it can be empty. -->
<sdk:libs />
</sdk:add-on>
</sdk:sdk-repository> </sdk:sdk-repository>