ADT #1844909: Tweak XML schema, add more tests.
Change XML schema to allow for empty <libs>, e.g. one can create and add-on that doesn't declare any extra lib. However enforce that <archives> contains at least one <archive> element to prevent someone from declaring a download element that cannot actually be downloaded. Added a couple tests for validation of empty documents.
This commit is contained in:
@@ -16,15 +16,15 @@
|
||||
|
||||
package com.android.sdklib.repository;
|
||||
|
||||
import com.android.sdklib.repository.SdkRepositoryConstants;
|
||||
|
||||
import org.xml.sax.ErrorHandler;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.SAXParseException;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.StringReader;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.transform.Source;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
@@ -50,34 +50,10 @@ public class TestSdkRepository extends TestCase {
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
public void testValidateLocalRepositoryFile() throws Exception {
|
||||
|
||||
InputStream xsdStream = SdkRepositoryConstants.getXsdStream();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A SAX error handler that captures the errors and warnings.
|
||||
* This allows us to capture *all* errors and just not get an exception on the first one.
|
||||
*/
|
||||
private static class CaptureErrorHandler implements ErrorHandler {
|
||||
|
||||
private String mWarnings = "";
|
||||
@@ -91,6 +67,22 @@ public class TestSdkRepository extends TestCase {
|
||||
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
|
||||
*/
|
||||
@@ -98,9 +90,11 @@ public class TestSdkRepository extends TestCase {
|
||||
mErrors += "Error: " + ex.getMessage() + "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SAXException
|
||||
*/
|
||||
public void fatalError(SAXParseException ex) throws SAXException {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,12 +17,17 @@
|
||||
<sdk:sdk-repository
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
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:version>1.0</sdk:version>
|
||||
<sdk:api-level>1</sdk:api-level>
|
||||
<sdk:revision>3</sdk:revision>
|
||||
<sdk:description>Some optional description</sdk:description>
|
||||
<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:archive os="any">
|
||||
<sdk:size>65536</sdk:size>
|
||||
@@ -31,6 +36,48 @@
|
||||
</sdk:archive>
|
||||
</sdk:archives>
|
||||
</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:version>1.1</sdk:version>
|
||||
<sdk:api-level>2</sdk:api-level>
|
||||
@@ -65,31 +112,7 @@
|
||||
</sdk:archive>
|
||||
</sdk:archives>
|
||||
</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:name>My Second add-on</sdk:name>
|
||||
<sdk:api-level>2</sdk:api-level>
|
||||
@@ -117,19 +140,20 @@
|
||||
</sdk:lib>
|
||||
</sdk:libs>
|
||||
</sdk:add-on>
|
||||
<sdk:doc>
|
||||
<sdk:api-level>1</sdk:api-level>
|
||||
|
||||
<sdk:tool>
|
||||
<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: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/docs/docs1.zip</sdk:url>
|
||||
<sdk:url>http://www.example.com/files/tools1.zip</sdk:url>
|
||||
</sdk:archive>
|
||||
</sdk:archives>
|
||||
</sdk:doc>
|
||||
</sdk:tool>
|
||||
|
||||
<sdk:doc>
|
||||
<sdk:api-level>2</sdk:api-level>
|
||||
<sdk:revision>42</sdk:revision>
|
||||
@@ -151,18 +175,7 @@
|
||||
</sdk:archive>
|
||||
</sdk:archives>
|
||||
</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:revision>42</sdk:revision>
|
||||
<sdk:archives>
|
||||
@@ -183,4 +196,21 @@
|
||||
</sdk:archive>
|
||||
</sdk:archives>
|
||||
</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>
|
||||
|
||||
Reference in New Issue
Block a user