Improved android compatibility
diff --git a/src/main/java/com/xtremelabs/robolectric/res/ResourceLoader.java b/src/main/java/com/xtremelabs/robolectric/res/ResourceLoader.java
index ddb63f1..a758b58 100644
--- a/src/main/java/com/xtremelabs/robolectric/res/ResourceLoader.java
+++ b/src/main/java/com/xtremelabs/robolectric/res/ResourceLoader.java
@@ -145,7 +145,7 @@
loadMenuResources( resourceDir );
loadDrawableResources( resourceDir );
loadPreferenceResources( preferenceDir );
- loadXmlFileResources( resourceDir );
+ loadXmlFileResources( preferenceDir );
listNinePatchResources(ninePatchDrawableIds, resourceDir);
} else {
@@ -218,22 +218,11 @@
/**
* All the Xml files should be loaded.
*/
- private void loadXmlFileResources( File resourceDir ) throws Exception {
- if ( resourceDir.exists() ) {
+ private void loadXmlFileResources( File xmlResourceDir ) throws Exception {
+ if ( xmlResourceDir.exists() ) {
DocumentLoader xmlFileDocumentLoader =
new DocumentLoader( xmlFileLoader );
- xmlFileDocumentLoader.loadResourceXmlDir( resourceDir );
-
- FileFilter subfolderFilter = new FileFilter() {
- @Override public boolean accept(File file) {
- return file.isDirectory();
- }
- };
-
- // Load sub-folders
- for (File subfolder: resourceDir.listFiles(subfolderFilter)) {
- xmlFileDocumentLoader.loadResourceXmlDir( subfolder );
- }
+ xmlFileDocumentLoader.loadResourceXmlDir( xmlResourceDir );
}
}
diff --git a/src/main/java/com/xtremelabs/robolectric/res/XmlFileLoader.java b/src/main/java/com/xtremelabs/robolectric/res/XmlFileLoader.java
index 7c68f56..7d167a9 100644
--- a/src/main/java/com/xtremelabs/robolectric/res/XmlFileLoader.java
+++ b/src/main/java/com/xtremelabs/robolectric/res/XmlFileLoader.java
@@ -103,7 +103,7 @@
this.document = document;
}
- private boolean isSupportedFeature(String name) {
+ /*package*/ boolean isSupportedFeature(String name) {
if (name == null) {
return false;
}
@@ -195,11 +195,15 @@
}
public String getText() {
+ if (currentNode == null) {
+ return null;
+ }
return currentNode.getNodeValue();
}
public int getLineNumber() {
- // TODO(msama): The current implementation is unable to return line numbers.
+ // TODO(msama): The current implementation is
+ // unable to return line numbers.
return -1;
}
@@ -208,10 +212,19 @@
return mEventType;
}
+ /*package*/ boolean isWhitespace(String text)
+ throws XmlPullParserException {
+ if (text == null) {
+ return false;
+ }
+ return text.split("\\s").length == 0;
+ }
+
public boolean isWhitespace()
throws XmlPullParserException {
- // Implemented as in android.
- return false;
+ // Note: in android whitespaces are automatically stripped.
+ // Here we have to skip them manually
+ return isWhitespace(getText());
}
public String getPrefix() {
@@ -248,7 +261,7 @@
return currentNode.getNodeName();
}
- private Node getAttributeAt(int index) {
+ /*package*/ Node getAttributeAt(int index) {
if (currentNode == null) {
throw new IndexOutOfBoundsException(String.valueOf(index));
}
@@ -259,12 +272,15 @@
return map.item(index);
}
- private Node getAttribute(String namespaceURI, String name) {
+ /*package*/ Node getAttribute(String namespace, String name) {
if (currentNode == null) {
return null;
}
NamedNodeMap map = currentNode.getAttributes();
- return map.getNamedItemNS(namespaceURI, name);
+ // XXX(msama): getNamedItemNS does not work.
+ // This is an hack to make this implementation working.
+ // return map.getNamedItemNS(namespace, name);
+ return map.getNamedItem(name);
}
public String getAttributeNamespace(int index) {
@@ -287,7 +303,7 @@
public int getAttributeCount() {
if (currentNode == null) {
- return 0;
+ return -1;
}
return currentNode.getAttributes().getLength();
}
@@ -297,10 +313,13 @@
}
public String getAttributeType(int index) {
+ // Android always returns CDATA even if the
+ // node has no attribute.
return "CDATA";
}
public boolean isAttributeDefault(int index) {
+ // The android implementation always returns false
return false;
}
@@ -308,8 +327,12 @@
return next();
}
- public String getAttributeValue(String namespaceURI, String name) {
- return getAttribute(namespaceURI, name).getNodeValue();
+ public String getAttributeValue(String namespace, String name) {
+ Node attr = getAttribute(namespace, name);
+ if (attr == null) {
+ return null;
+ }
+ return attr.getNodeValue();
}
public int next() throws XmlPullParserException,IOException {
@@ -346,8 +369,9 @@
/**
* A twin implementation of the native android nativeNext(status)
+ * @throws XmlPullParserException
*/
- /*package*/ int nativeNext() {
+ /*package*/ int nativeNext() throws XmlPullParserException {
switch(mEventType) {
case(CDSECT): {
throw new IllegalArgumentException("CDSECT");
@@ -397,7 +421,8 @@
"The next event has not been returned.");
}
- /*protected*/ int processNextNodeType(Node node) {
+ /*protected*/ int processNextNodeType(Node node)
+ throws XmlPullParserException {
switch (node.getNodeType()) {
case(Node.ATTRIBUTE_NODE): {
throw new IllegalArgumentException("ATTRIBUTE_NODE");
@@ -434,14 +459,20 @@
throw new IllegalArgumentException("DOCUMENT_TYPE_NODE");
}
case(Node.TEXT_NODE): {
- currentNode = node;
- return TEXT;
+ if (isWhitespace(node.getNodeValue())) {
+ // Skip whitespaces
+ return navigateToNextNode(node);
+ } else {
+ currentNode = node;
+ return TEXT;
+ }
}
}
throw new RuntimeException("The next event has not been returned.");
}
- /*protected*/ int navigateToNextNode(Node node) {
+ /*protected*/ int navigateToNextNode(Node node)
+ throws XmlPullParserException {
Node nextNode = node.getNextSibling();
if (nextNode != null) {
// Move to the next siblings
@@ -449,6 +480,7 @@
} else {
// Goes back to the parent
if (document.getDocumentElement().equals(node)) {
+ currentNode = null;
return END_DOCUMENT;
}
currentNode = node.getParentNode();
@@ -456,7 +488,8 @@
}
}
- public void require(int type, String namespace, String name) throws XmlPullParserException, IOException {
+ public void require(int type, String namespace, String name)
+ throws XmlPullParserException, IOException {
if (type != getEventType()
|| (namespace != null && !namespace.equals( getNamespace () ) )
|| (name != null && !name.equals( getName() ) ) )
@@ -464,33 +497,33 @@
}
public String nextText() throws XmlPullParserException,IOException {
- if(getEventType() != START_TAG) {
- throw new XmlPullParserException(
- getPositionDescription()
- + ": parser must be on START_TAG to read next text", this, null);
+ if (getEventType() != START_TAG) {
+ throw new XmlPullParserException(
+ getPositionDescription()
+ + ": parser must be on START_TAG to read next text", this, null);
}
int eventType = next();
- if(eventType == TEXT) {
- String result = getText();
- eventType = next();
- if(eventType != END_TAG) {
- throw new XmlPullParserException(
- getPositionDescription()
- + ": event TEXT it must be immediately followed by END_TAG", this, null);
+ if (eventType == TEXT) {
+ String result = getText();
+ eventType = next();
+ if(eventType != END_TAG) {
+ throw new XmlPullParserException(
+ getPositionDescription()
+ + ": event TEXT it must be immediately followed by END_TAG", this, null);
}
return result;
} else if(eventType == END_TAG) {
- return "";
+ return "";
} else {
- throw new XmlPullParserException(
- getPositionDescription()
- + ": parser must be on START_TAG or TEXT to read text", this, null);
+ throw new XmlPullParserException(
+ getPositionDescription()
+ + ": parser must be on START_TAG or TEXT to read text", this, null);
}
}
public int nextTag() throws XmlPullParserException,IOException {
int eventType = next();
- if(eventType == TEXT ) { // && isWhitespace()) { // skip whitespace
+ if (eventType == TEXT && isWhitespace()) { // skip whitespace
eventType = next();
}
if (eventType != START_TAG && eventType != END_TAG) {
@@ -550,8 +583,7 @@
{
int value = getAttributeIntValue(namespace, attribute, defaultValue);
if (value < 0) {
- throw new RuntimeException(
- "Expected an unsigned int. Found: " + value + ".");
+ return defaultValue;
}
return value;
}
@@ -583,8 +615,11 @@
public boolean getAttributeBooleanValue(int idx,
boolean defaultValue) {
- String value = getAttributeValue(idx);
- return Boolean.parseBoolean(value);
+ try {
+ return Boolean.parseBoolean(getAttributeValue(idx));
+ } catch (IndexOutOfBoundsException ex) {
+ return defaultValue;
+ }
}
public int getAttributeResourceValue(int idx, int defaultValue) {
@@ -592,46 +627,77 @@
}
public int getAttributeIntValue(int idx, int defaultValue) {
- String value = getAttributeValue(idx);
try {
- return Integer.parseInt(value);
+ return Integer.parseInt(getAttributeValue(idx));
} catch(NumberFormatException ex) {
- throw new RuntimeException("Expected an integer found: " + value + ".");
- }
+ return defaultValue;
+ } catch (IndexOutOfBoundsException ex) {
+ return defaultValue;
+ }
}
public int getAttributeUnsignedIntValue(int idx, int defaultValue) {
- int value = getAttributeIntValue(idx, defaultValue);
- if (value < 0) {
- throw new RuntimeException(
- "Expected an unsigned int. Found: " + value + ".");
- }
- return value;
+ try {
+ int value = getAttributeIntValue(idx, defaultValue);
+ if (value < 0) {
+ return defaultValue;
+ }
+ return value;
+ } catch(NumberFormatException ex) {
+ return defaultValue;
+ } catch (IndexOutOfBoundsException ex) {
+ return defaultValue;
+ }
}
public float getAttributeFloatValue(int idx, float defaultValue) {
- String value = getAttributeValue(idx);
try {
- return Float.parseFloat(value);
+ return Float.parseFloat(getAttributeValue(idx));
} catch(NumberFormatException ex) {
- throw new RuntimeException("Expected a float. Found: " + value + ".");
- }
+ return defaultValue;
+ } catch (IndexOutOfBoundsException ex) {
+ return defaultValue;
+ }
}
public String getIdAttribute() {
- throw new RuntimeException("Not implemented yet");
+ Node attr = getAttribute(null, "id");
+ if (attr == null) {
+ return null;
+ }
+ return attr.getNodeValue();
}
public String getClassAttribute() {
- throw new RuntimeException("Not implemented yet");
+ Node attr = getAttribute(null, "class");
+ if (attr == null) {
+ return null;
+ }
+ return attr.getNodeValue();
}
public int getIdAttributeResourceValue(int defaultValue) {
- throw new RuntimeException("Not implemented yet");
+ String id = getIdAttribute();
+ if (id == null) {
+ return defaultValue;
+ }
+ try {
+ return Integer.parseInt(id);
+ } catch (NumberFormatException ex) {
+ return defaultValue;
+ }
}
public int getStyleAttribute() {
- throw new RuntimeException("Not implemented yet");
+ Node attr = getAttribute(null, "style");
+ if (attr == null) {
+ return 0;
+ }
+ try {
+ return Integer.parseInt(attr.getNodeValue());
+ } catch (NumberFormatException ex) {
+ return 0;
+ }
}
public void close() {
diff --git a/src/test/java/com/xtremelabs/robolectric/res/XmlFileLoaderTest.java b/src/test/java/com/xtremelabs/robolectric/res/XmlFileLoaderTest.java
index fa4cf17..566f665 100644
--- a/src/test/java/com/xtremelabs/robolectric/res/XmlFileLoaderTest.java
+++ b/src/test/java/com/xtremelabs/robolectric/res/XmlFileLoaderTest.java
@@ -11,6 +11,7 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.StringReader;
+import java.util.Stack;
import org.junit.After;
import org.junit.Before;
@@ -36,8 +37,10 @@
*/
public class XmlFileLoaderTest {
+ private static final int ATTRIBUTE_OUT_OF_INDEX = 999;
+
private XmlFileLoader xmlFileLoader;
- private XmlResourceParser parser;
+ private XmlResourceParserImpl parser;
@Before
public void setUp() throws Exception {
@@ -48,7 +51,7 @@
xmlFileLoader = new XmlFileLoader(resourceExtractor);
new DocumentLoader(xmlFileLoader).loadResourceXmlDir(resourceFile("res", "xml"));
- parser = xmlFileLoader.getXml("xml/preferences");
+ parser = (XmlResourceParserImpl)xmlFileLoader.getXml(R.xml.preferences);
}
@After
@@ -56,9 +59,14 @@
parser.close();
}
- private void parseUntilNextStartTag()
+ private void parseUntilNext(int event)
throws XmlPullParserException, IOException {
- while(parser.next() != XmlResourceParser.START_TAG) {};
+ while(parser.next() != event) {
+ if (parser.getEventType() == XmlResourceParser.END_DOCUMENT) {
+ throw new RuntimeException("Impossible to find: " +
+ event + ". End of document reached.");
+ }
+ };
}
/**
@@ -119,6 +127,8 @@
for (String feature: XmlFileLoader.UNAVAILABLE_FEATURES) {
assertThat(parser.getFeature(feature), equalTo(false));
}
+
+ assertThat(parser.getFeature(null), equalTo(false));
}
@Test
@@ -242,15 +252,20 @@
}
@Test
- public void testGetText() {
- fail("Not yet implemented");
+ public void testGetText() throws XmlPullParserException, IOException {
+ assertThat(parser.getText(), equalTo(null));
+ parseUntilNext(XmlResourceParser.START_TAG);
+ assertThat(parser.getText(), equalTo(null));
+ // TODO(msama): Test a node with text
+ // parseUntilNext(XmlResourceParser.TEXT);
+ // assertThat(parser.getText(), notNullValue());
}
@Test
@Ignore("Not implemented yet")
public void testGetLineNumber() throws XmlPullParserException, IOException {
assertThat(parser.getLineNumber(), equalTo(-1));
- parseUntilNextStartTag();
+ parseUntilNext(XmlResourceParser.START_TAG);
assertThat(
"The root element should be at line 1.",
parser.getLineNumber(), equalTo(1));
@@ -280,14 +295,9 @@
}
@Test
- public void testGetTextCharacters() {
- fail("Not yet implemented");
- }
-
- @Test
public void testGetNamespace() throws XmlPullParserException, IOException {
assertThat(parser.getNamespace(), equalTo(""));
- parseUntilNextStartTag();
+ parseUntilNext(XmlResourceParser.START_TAG);
assertThat(parser.getNamespace(), equalTo(""));
}
@@ -307,6 +317,10 @@
int evt;
while ((evt = parser.next()) != XmlResourceParser.END_DOCUMENT) {
switch (evt) {
+ case (XmlResourceParser.START_DOCUMENT): {
+ assertThat(parser.getName(), equalTo(""));
+ break;
+ }
case (XmlResourceParser.START_TAG): {
index ++;
assertThat(parser.getName(), equalTo(expected[index]));
@@ -314,12 +328,25 @@
}
}
}
+ assertThat(
+ "End document name should be empty.",
+ parser.getName(), equalTo(""));
+ }
+
+ @Test
+ public void testGetAttribute() throws XmlPullParserException, IOException {
+ parseUntilNext(XmlResourceParser.START_TAG);
+ assertThat(
+ parser.getAttribute(
+ "http://www.w3.org/2000/xmlns/",
+ "xmlns:android"),
+ notNullValue());
}
@Test
public void testGetAttributeNamespace()
throws XmlPullParserException, IOException {
- parseUntilNextStartTag();
+ parseUntilNext(XmlResourceParser.START_TAG);
assertThat(parser.getAttributeNamespace(0),
equalTo("http://www.w3.org/2000/xmlns/"));
}
@@ -327,15 +354,20 @@
@Test
public void testGetAttributeName()
throws XmlPullParserException, IOException {
- parseUntilNextStartTag();
+ parseUntilNext(XmlResourceParser.START_TAG);
assertThat(parser.getAttributeName(0),
equalTo("xmlns:android"));
+ try {
+ parser.getAttributeName(ATTRIBUTE_OUT_OF_INDEX);
+ } catch (IndexOutOfBoundsException ex) {
+ // pass
+ }
}
@Test
public void testGetAttributePrefix()
throws XmlPullParserException, IOException {
- parseUntilNextStartTag();
+ parseUntilNext(XmlResourceParser.START_TAG);
try {
parser.getAttributePrefix(0);
fail("This method should not be supported");
@@ -351,15 +383,15 @@
"Not START_TAG should return false.",
parser.isEmptyElementTag(),
equalTo(false));
- parseUntilNextStartTag();
+ parseUntilNext(XmlResourceParser.START_TAG);
assertThat(
"Not empty tag should return false.",
parser.isEmptyElementTag(),
equalTo(false));
// Navigate to an empty tag
- parseUntilNextStartTag();
- parseUntilNextStartTag();
- parseUntilNextStartTag();
+ parseUntilNext(XmlResourceParser.START_TAG);
+ parseUntilNext(XmlResourceParser.START_TAG);
+ parseUntilNext(XmlResourceParser.START_TAG);
assertThat(
"Expected CheckBoxPreference.",
parser.getName(),
@@ -373,7 +405,12 @@
@Test
public void testGetAttributeCount()
throws XmlPullParserException, IOException {
- parseUntilNextStartTag();
+ assertThat(
+ "When no node is being explored the number " +
+ "of attributes should be -1.",
+ parser.getAttributeCount(),
+ equalTo(-1));
+ parseUntilNext(XmlResourceParser.START_TAG);
assertThat(
parser.getAttributeCount(),
equalTo(1));
@@ -381,42 +418,112 @@
@Test
public void testGetAttributeValueInt() {
- fail("Not yet implemented");
+ assertThat(
+ parser.getAttributeBooleanValue(ATTRIBUTE_OUT_OF_INDEX, true),
+ equalTo(true));
}
@Test
public void testGetAttributeType() {
- fail("Not yet implemented");
+ assertThat(
+ parser.getAttributeType(ATTRIBUTE_OUT_OF_INDEX),
+ equalTo("CDATA"));
}
@Test
public void testIsAttributeDefault() {
- fail("Not yet implemented");
+ assertThat(
+ parser.isAttributeDefault(ATTRIBUTE_OUT_OF_INDEX),
+ equalTo(false));
}
@Test
- public void testNextToken() {
- fail("Not yet implemented");
+ public void testGetAttributeValueStringString()
+ throws XmlPullParserException, IOException {
+ parseUntilNext(XmlResourceParser.START_TAG);
+ assertThat(parser.getAttributeValue("http://www.w3.org/2000/xmlns/", "xmlns:android"),
+ equalTo("http://schemas.android.com/apk/res/android"));
}
@Test
- public void testGetAttributeValueStringString() {
- fail("Not yet implemented");
+ public void testNext() throws XmlPullParserException, IOException {
+ int lastEvent = -1;
+ int evt = -1;
+ Stack<String> tags = new Stack<String>();
+
+ while ((evt = parser.next()) != XmlResourceParser.END_DOCUMENT) {
+ switch (evt) {
+ case (XmlResourceParser.START_DOCUMENT): {
+ assertThat(lastEvent, equalTo(-1));
+ break;
+ }
+ case (XmlResourceParser.START_TAG): {
+ tags.push(parser.getName());
+ break;
+ }
+ case (XmlResourceParser.END_TAG): {
+ String tag = tags.pop();
+ String current = parser.getName();
+ assertThat(
+ "Closing the wrong tag: found: " +
+ current + ", expected: " + tag + ".",
+ current, equalTo(tag));
+ break;
+ }
+ case (XmlResourceParser.TEXT): {
+ assertThat(lastEvent,
+ anyOf(
+ equalTo(XmlResourceParser.START_TAG),
+ equalTo(XmlResourceParser.END_TAG)));
+ break;
+ }
+ }
+ lastEvent = evt;
+ }
}
@Test
- public void testNext() {
- fail("Not yet implemented");
+ public void testRequire() throws XmlPullParserException, IOException {
+ parseUntilNext(XmlResourceParser.START_TAG);
+ parser.require(XmlResourceParser.START_TAG,
+ parser.getNamespace(), parser.getName());
+
+ try {
+ parser.require(XmlResourceParser.END_TAG,
+ parser.getNamespace(), parser.getName());
+ fail("Require with wrong event should have failed");
+ } catch (XmlPullParserException ex) {
+ // pass
+ }
+
+ try {
+ parser.require(XmlResourceParser.START_TAG,
+ "foo", parser.getName());
+ fail("Require with wrong namespace should have failed");
+ } catch (XmlPullParserException ex) {
+ // pass
+ }
+
+ try {
+ parser.require(XmlResourceParser.START_TAG,
+ parser.getNamespace(), "foo");
+ fail("Require with wrong tag name should have failed");
+ } catch (XmlPullParserException ex) {
+ // pass
+ }
}
@Test
- public void testRequire() {
- fail("Not yet implemented");
- }
-
- @Test
- public void testNextText() {
- fail("Not yet implemented");
+ public void testNextText_noText() throws XmlPullParserException, IOException {
+ parseUntilNext(XmlResourceParser.START_TAG);
+ try {
+ assertThat(parser.nextText(), equalTo(parser.getText()));
+ fail("nextText on a document with no text should have failed");
+ } catch (XmlPullParserException ex) {
+ assertThat(parser.getEventType(),
+ anyOf(equalTo(XmlResourceParser.START_TAG),
+ equalTo(XmlResourceParser.END_DOCUMENT)));
+ }
}
@Test
@@ -485,28 +592,51 @@
}
@Test
- public void testGetAttributeFloatValueIntFloat() {
- fail("Not yet implemented");
+ public void testGetAttributeFloatValueIntFloat()
+ throws XmlPullParserException, IOException {
+ parseUntilNext(XmlResourceParser.START_TAG);
+ assertThat(parser.getAttributeFloatValue(0, 1.0f), equalTo(1.0f));
+ // TODO(msama): test an actual read value
}
@Test
- public void testGetIdAttribute() {
- fail("Not yet implemented");
+ public void testGetIdAttribute() throws XmlPullParserException, IOException {
+ assertThat(
+ "Document should have no id.",
+ parser.getIdAttribute(), equalTo(null));
+ parseUntilNext(XmlResourceParser.START_TAG);
+ assertThat(
+ "Root element have no id.",
+ parser.getIdAttribute(), equalTo(null));
+ // TODO(msama): Test an element with a real ID
+ // None of the preferences elements have id
}
@Test
- public void testGetClassAttribute() {
- fail("Not yet implemented");
+ public void testGetClassAttribute() throws XmlPullParserException, IOException {
+ assertThat(
+ "Document should have no class.",
+ parser.getClassAttribute(), equalTo(null));
+ parseUntilNext(XmlResourceParser.START_TAG);
+ assertThat(
+ "Root element have no id.",
+ parser.getClassAttribute(), equalTo(null));
+ // TODO(msama): Test an element with a class attribute
+ // None of the preferences elements have a class attribute
}
@Test
public void testGetIdAttributeResourceValue() {
- fail("Not yet implemented");
+ assertThat(
+ parser.getIdAttributeResourceValue(12), equalTo(12));
}
@Test
public void testGetStyleAttribute() {
- fail("Not yet implemented");
+ assertThat(
+ parser.getStyleAttribute(), equalTo(0));
+ // TODO(msama): test with an element with style attribute
+ // None of the preferences elements have a style attribute
}
}