You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by mi...@apache.org on 2014/05/09 14:47:19 UTC

[6/6] git commit: [OLINGO-231] Several refactorings

[OLINGO-231] Several refactorings


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata2/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata2/commit/cb1ba468
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata2/tree/cb1ba468
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata2/diff/cb1ba468

Branch: refs/heads/OLINGO-231_PocForAndroid
Commit: cb1ba46855a469d7fc81d5a7e2f9b1b15187fcfa
Parents: 696288d
Author: Michael Bolz <mi...@sap.com>
Authored: Thu May 8 08:35:19 2014 +0200
Committer: Michael Bolz <mi...@sap.com>
Committed: Fri May 9 14:38:52 2014 +0200

----------------------------------------------------------------------
 odata2-android/pom.xml                          |     9 +-
 .../odata2/android/xml/AndroidXmlFactory.java   |    17 +-
 .../odata2/android/xml/AndroidXmlReader.java    |    26 +-
 .../odata2/android/xml/AndroidTestBase.java     |   257 +
 .../android/xml/AndroidXmlFactoryTest.java      |     3 +-
 .../android/xml/AndroidXmlReaderTest.java       |     8 +-
 .../android/xml/AtomEntryProducerTest.java      |   849 ++
 .../android/xml/AtomFeedProducerTest.java       |   215 +
 .../android/xml/XmlEntityConsumerTest.java      |  2420 ++++
 .../odata2/android/xml/XmlFeedConsumerTest.java |   155 +
 .../src/test/resources/LargeEmployeeFeed.xml    | 12556 +++++++++++++++++
 .../src/test/resources/double_expanded_team.xml |   264 +
 .../src/test/resources/expandedBuilding.xml     |    61 +
 .../src/test/resources/expanded_team.xml        |   146 +
 .../src/test/resources/feed_employees.xml       |   247 +
 .../src/test/resources/feed_employees_full.xml  |   249 +
 .../resources/feed_with_deleted_entries.xml     |    40 +
 .../src/test/resources/feed_with_delta_link.xml |    69 +
 odata2-lib/odata-core/pom.xml                   |     5 -
 .../olingo/odata2/core/commons/Base64.java      |   589 +
 .../olingo/odata2/core/commons/BaseNCodec.java  |   350 +
 .../apache/olingo/odata2/core/commons/Hex.java  |   124 +
 .../olingo/odata2/core/debug/DebugInfoBody.java |     2 +-
 .../olingo/odata2/core/edm/EdmBinary.java       |     9 +-
 .../provider/EdmServiceMetadataImplProv.java    |    25 +-
 .../odata2/core/ep/BasicEntityProvider.java     |    29 +-
 .../odata2/core/debug/DebugInfoBodyTest.java    |     2 +-
 .../core/uri/expression/TestTokenizer.java      |     9 +-
 .../core/xml/JavaxStaxStreamFactoryTest.java    |    13 +-
 pom.xml                                         |     1 -
 30 files changed, 18668 insertions(+), 81 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/cb1ba468/odata2-android/pom.xml
----------------------------------------------------------------------
diff --git a/odata2-android/pom.xml b/odata2-android/pom.xml
index 8a8e1c3..614f558 100644
--- a/odata2-android/pom.xml
+++ b/odata2-android/pom.xml
@@ -34,7 +34,7 @@
 
 <properties>
   <version.android>4.1.1.4</version.android>
-  <version.robolectric>2.2</version.robolectric>
+  <version.robolectric>2.3-SNAPSHOT</version.robolectric>
 </properties>
 
   <build>
@@ -96,6 +96,7 @@
       <artifactId>olingo-odata2-core</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <!-- Test dependencies -->
     <dependency>
       <groupId>org.apache.olingo</groupId>
       <artifactId>olingo-odata2-testutil</artifactId>
@@ -112,6 +113,12 @@
       <artifactId>robolectric</artifactId>
       <version>${version.robolectric}</version>
       <scope>test</scope>
+      <exclusions>
+        <exclusion>
+          <groupId>com.android.support</groupId>
+          <artifactId>support-v4</artifactId>
+        </exclusion>
+      </exclusions>
     </dependency>
     <dependency>
       <groupId>xmlunit</groupId>

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/cb1ba468/odata2-android/src/main/java/org/apache/olingo/odata2/android/xml/AndroidXmlFactory.java
----------------------------------------------------------------------
diff --git a/odata2-android/src/main/java/org/apache/olingo/odata2/android/xml/AndroidXmlFactory.java b/odata2-android/src/main/java/org/apache/olingo/odata2/android/xml/AndroidXmlFactory.java
index f2888a9..85eb024 100644
--- a/odata2-android/src/main/java/org/apache/olingo/odata2/android/xml/AndroidXmlFactory.java
+++ b/odata2-android/src/main/java/org/apache/olingo/odata2/android/xml/AndroidXmlFactory.java
@@ -18,13 +18,26 @@
  ******************************************************************************/
 package org.apache.olingo.odata2.android.xml;
 
+import org.apache.olingo.odata2.api.ep.EntityProviderException;
 import org.apache.olingo.odata2.api.xml.*;
 import org.apache.olingo.odata2.core.xml.AbstractXmlStreamFactory;
 
 public class AndroidXmlFactory extends AbstractXmlStreamFactory {
+  private static final String DEFAULT_CHARSET = "UTF-8";
+
   @Override
-  public XMLStreamReader createXMLStreamReader(Object content) throws XMLStreamException {
-    return new AndroidXmlReader(content).setProperties(readProperties);
+  public XMLStreamReader createXMLStreamReader(Object content) throws XMLStreamException, EntityProviderException {
+    AndroidXmlReader reader = new AndroidXmlReader(content).setProperties(readProperties);
+
+    // verify charset encoding set in content is supported (if not set UTF-8 is used as defined in
+    // 'http://www.w3.org/TR/2008/REC-xml-20081126/')
+    String characterEncodingInContent = reader.getCharacterEncodingScheme();
+    if (characterEncodingInContent != null && !DEFAULT_CHARSET.equalsIgnoreCase(characterEncodingInContent)) {
+      throw new EntityProviderException(EntityProviderException
+              .UNSUPPORTED_CHARACTER_ENCODING.addContent(characterEncodingInContent));
+    }
+
+    return reader;
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/cb1ba468/odata2-android/src/main/java/org/apache/olingo/odata2/android/xml/AndroidXmlReader.java
----------------------------------------------------------------------
diff --git a/odata2-android/src/main/java/org/apache/olingo/odata2/android/xml/AndroidXmlReader.java b/odata2-android/src/main/java/org/apache/olingo/odata2/android/xml/AndroidXmlReader.java
index d793fe6..0bc1881 100644
--- a/odata2-android/src/main/java/org/apache/olingo/odata2/android/xml/AndroidXmlReader.java
+++ b/odata2-android/src/main/java/org/apache/olingo/odata2/android/xml/AndroidXmlReader.java
@@ -20,6 +20,7 @@ package org.apache.olingo.odata2.android.xml;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.olingo.odata2.api.xml.NamespaceContext;
@@ -69,6 +70,10 @@ public class AndroidXmlReader implements XMLStreamReader {
     return this;
   }
 
+  public String getCharacterEncodingScheme() {
+    return parser.getInputEncoding();
+  }
+
   @Override
   public void close() throws XMLStreamException {
   }
@@ -114,7 +119,6 @@ public class AndroidXmlReader implements XMLStreamReader {
       text = parser.getText();
       parser.next();
     } catch (Exception e) {
-      e.printStackTrace();
       throw new XMLStreamException("Failure during step forward after 'getText'.", e);
     }
     return text;
@@ -137,18 +141,24 @@ public class AndroidXmlReader implements XMLStreamReader {
   }
 
   @Override
-  public NamespaceContext getNamespaceContext() {
-    String tmp = null;
+  public NamespaceContext getNamespaceContext() throws XMLStreamException {
+    final Map<String, String> prefix2Namespace;
     try {
       int depth = parser.getDepth();
-      tmp = parser.getNamespacePrefix(depth);
+      int nsStart = parser.getNamespaceCount(depth-1);
+      int nsEnd = parser.getNamespaceCount(depth);
+      prefix2Namespace = new HashMap<String, String>(nsEnd-nsStart+1);
+      for (int i = nsStart; i < nsEnd; i++) {
+        String prefix = parser.getNamespacePrefix(i);
+        String namespace = parser.getNamespaceUri(i);
+        prefix2Namespace.put(namespace, prefix);
+      }
     } catch (XmlPullParserException e) {
-      e.printStackTrace();
+      throw new XMLStreamException("Got XmlPullParserException with message: " + e.getMessage(), e);
     }
-    final String prefix = tmp;
     return new NamespaceContext() {
-      public String getPrefix(String index) {
-        return prefix;
+      public String getPrefix(String namespaceUri) {
+        return prefix2Namespace.get(namespaceUri);
       }
     };
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/cb1ba468/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AndroidTestBase.java
----------------------------------------------------------------------
diff --git a/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AndroidTestBase.java b/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AndroidTestBase.java
new file mode 100644
index 0000000..fad1c12
--- /dev/null
+++ b/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AndroidTestBase.java
@@ -0,0 +1,257 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.olingo.odata2.android.xml;
+
+import org.apache.olingo.odata2.api.edm.Edm;
+import org.apache.olingo.odata2.api.ep.EntityProviderException;
+import org.apache.olingo.odata2.api.ep.EntityProviderWriteProperties;
+import org.apache.olingo.odata2.api.ep.callback.TombstoneCallback;
+import org.apache.olingo.odata2.core.ep.AtomEntityProvider;
+import org.custommonkey.xmlunit.SimpleNamespaceContext;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.*;
+
+import static org.apache.olingo.odata2.api.xml.XMLStreamReaderFactory.XML_STREAM_READER_FACTORY_CLASS;
+import static org.apache.olingo.odata2.api.xml.XMLStreamWriterFactory.XML_STREAM_WRITER_FACTORY_CLASS;
+
+/**
+ *  
+*/
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest=Config.NONE)
+public abstract class AndroidTestBase {
+
+  protected static final URI BASE_URI;
+
+  static {
+    try {
+      BASE_URI = new URI("http://host:80/service/");
+    } catch (URISyntaxException e) {
+      throw new RuntimeException(e);
+    }
+  }
+  protected static final EntityProviderWriteProperties DEFAULT_PROPERTIES = EntityProviderWriteProperties.serviceRoot(
+      BASE_URI).build();
+
+  protected Map<String, Object> employeeData;
+
+  protected ArrayList<Map<String, Object>> employeesData;
+
+  protected Map<String, Object> photoData;
+
+  protected Map<String, Object> roomData;
+
+  protected Map<String, Object> buildingData;
+
+  protected ArrayList<Map<String, Object>> roomsData;
+
+  {
+    employeeData = new HashMap<String, Object>();
+
+    Calendar date = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+    date.clear();
+    date.set(1999, 0, 1);
+
+    employeeData.put("EmployeeId", "1");
+    employeeData.put("ImmageUrl", null);
+    employeeData.put("ManagerId", "1");
+    employeeData.put("Age", new Integer(52));
+    employeeData.put("RoomId", "1");
+    employeeData.put("EntryDate", date);
+    employeeData.put("TeamId", "42");
+    employeeData.put("EmployeeName", "Walter Winter");
+
+    Map<String, Object> locationData = new HashMap<String, Object>();
+    Map<String, Object> cityData = new HashMap<String, Object>();
+    cityData.put("PostalCode", "33470");
+    cityData.put("CityName", "Duckburg");
+    locationData.put("City", cityData);
+    locationData.put("Country", "Calisota");
+
+    employeeData.put("Location", locationData);
+
+    Map<String, Object> employeeData2 = new HashMap<String, Object>();
+    employeeData2.put("EmployeeId", "1");
+    employeeData2.put("ImmageUrl", null);
+    employeeData2.put("ManagerId", "1");
+    employeeData2.put("Age", new Integer(52));
+    employeeData2.put("RoomId", "1");
+    employeeData2.put("EntryDate", date);
+    employeeData2.put("TeamId", "42");
+    employeeData2.put("EmployeeName", "Walter Winter");
+
+    Map<String, Object> locationData2 = new HashMap<String, Object>();
+    Map<String, Object> cityData2 = new HashMap<String, Object>();
+    cityData2.put("PostalCode", "33470");
+    cityData2.put("CityName", "Duckburg");
+    locationData2.put("City", cityData2);
+    locationData2.put("Country", "Calisota");
+
+    employeeData2.put("Location", locationData2);
+
+    employeesData = new ArrayList<Map<String, Object>>();
+    employeesData.add(employeeData);
+    employeesData.add(employeeData2);
+
+    photoData = new HashMap<String, Object>();
+    photoData.put("Id", Integer.valueOf(1));
+    photoData.put("Name", "Mona Lisa");
+    photoData.put("Type", "image/png");
+    photoData.put(
+                "ImageUrl",
+                "http://www.mopo.de/image/view/2012/6/4/16548086,13385561,medRes,maxh,234,maxw,234," +
+                        "Parodia_Mona_Lisa_Lego_Hamburger_Morgenpost.jpg");
+    Map<String, Object> imageData = new HashMap<String, Object>();
+    imageData.put("Image", new byte[] { 1, 2, 3, 4 });
+    imageData.put("getImageType", "image/png");
+    photoData.put("Image", imageData);
+    photoData.put("BinaryData", new byte[] { -1, -2, -3, -4 });
+    photoData.put("Содержание", "В лесу шумит водопад. Если он не торопится просп воды");
+
+    roomData = new HashMap<String, Object>();
+    roomData.put("Id", "1");
+    roomData.put("Name", "Neu Schwanstein");
+    roomData.put("Seats", new Integer(20));
+    roomData.put("Version", new Integer(3));
+
+    buildingData = new HashMap<String, Object>();
+    buildingData.put("Id", "1");
+    buildingData.put("Name", "WDF03");
+    buildingData.put("Image", "image");
+  }
+
+  protected void initializeRoomData(final int count) {
+    roomsData = new ArrayList<Map<String, Object>>();
+    for (int i = 1; i <= count; i++) {
+      HashMap<String, Object> tmp = new HashMap<String, Object>();
+      tmp.put("Id", "" + i);
+      tmp.put("Name", "Neu Schwanstein" + i);
+      tmp.put("Seats", new Integer(20));
+      tmp.put("Version", new Integer(3));
+      roomsData.add(tmp);
+    }
+  }
+
+  // CHECKSTYLE:OFF
+  @Before
+  public void setXmlFactory() throws Exception {
+    //
+    System.setProperty(XML_STREAM_WRITER_FACTORY_CLASS, AndroidXmlFactory.class.getName()); // NOSONAR
+    System.setProperty(XML_STREAM_READER_FACTORY_CLASS, AndroidXmlFactory.class.getName()); // NOSONAR
+  }
+  // CHECKSTYLE:ON
+
+  @Before
+  public void setXmlNamespacePrefixes() throws Exception {
+    //
+    Map<String, String> prefixMap = new HashMap<String, String>();
+    prefixMap.put("a", Edm.NAMESPACE_ATOM_2005);
+    prefixMap.put("d", Edm.NAMESPACE_D_2007_08);
+    prefixMap.put("m", Edm.NAMESPACE_M_2007_08);
+    prefixMap.put("xml", Edm.NAMESPACE_XML_1998);
+    prefixMap.put("ру", "http://localhost");
+    prefixMap.put("custom", "http://localhost");
+    prefixMap.put("at", TombstoneCallback.NAMESPACE_TOMBSTONE);
+    XMLUnit.setXpathNamespaceContext(new SimpleNamespaceContext(prefixMap));
+  }
+
+  protected AtomEntityProvider createAtomEntityProvider() throws EntityProviderException {
+    return new AtomEntityProvider();
+  }
+
+  protected String readFile(final String filename) throws IOException {
+    InputStream in = getFileAsStream(filename);
+
+    byte[] tmp = new byte[8192];
+    int count = in.read(tmp);
+    StringBuilder b = new StringBuilder();
+    while (count >= 0) {
+      b.append(new String(tmp, 0, count));
+      count = in.read(tmp);
+    }
+
+    return b.toString();
+  }
+
+  protected InputStream getFileAsStream(final String filename) throws IOException {
+    InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(filename);
+    if (in == null) {
+      throw new IOException("Requested file '" + filename + "' was not found.");
+    }
+    return in;
+  }
+
+  protected InputStream createContentAsStream(final String content) throws UnsupportedEncodingException {
+    return new ByteArrayInputStream(content.getBytes("UTF-8"));
+  }
+
+  /**
+   *
+   * @param content
+   * @param replaceWhitespaces if <code>true</code> all XML not necessary whitespaces between tags are
+   * @return
+   * @throws UnsupportedEncodingException
+   */
+  protected InputStream createContentAsStream(final String content, final boolean replaceWhitespaces)
+          throws UnsupportedEncodingException {
+    String contentForStream = content;
+    if (replaceWhitespaces) {
+      contentForStream = content.replaceAll(">\\s.<", "><");
+    }
+
+    return new ByteArrayInputStream(contentForStream.getBytes("UTF-8"));
+  }
+
+  /**
+   * Create a map with a 'String' to 'Class<?>' mapping based on given parameters.
+   * Therefore parameters MUST be a set of such pairs.
+   * As example an correct method call would be:
+   * <p>
+   * <code>
+   * createTypeMappings("someKey", Integer.class, "anotherKey", Long.class);
+   * </code>
+   * </p>
+   *
+   * @param firstKeyThenMappingClass
+   * @return
+   */
+  protected Map<String, Object> createTypeMappings(final Object... firstKeyThenMappingClass) {
+    Map<String, Object> typeMappings = new HashMap<String, Object>();
+    if (firstKeyThenMappingClass.length % 2 != 0) {
+      throw new IllegalArgumentException("Got odd number of parameters. Please read javadoc.");
+    }
+    for (int i = 0; i < firstKeyThenMappingClass.length; i += 2) {
+      String key = (String) firstKeyThenMappingClass[i];
+      Class<?> mappingClass = (Class<?>) firstKeyThenMappingClass[i + 1];
+      typeMappings.put(key, mappingClass);
+    }
+    return typeMappings;
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/cb1ba468/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AndroidXmlFactoryTest.java
----------------------------------------------------------------------
diff --git a/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AndroidXmlFactoryTest.java b/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AndroidXmlFactoryTest.java
index e51c345..17364cd 100644
--- a/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AndroidXmlFactoryTest.java
+++ b/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AndroidXmlFactoryTest.java
@@ -24,7 +24,6 @@ import org.apache.olingo.odata2.api.xml.XMLStreamReader;
 import org.apache.olingo.odata2.api.xml.XMLStreamWriter;
 import org.apache.olingo.odata2.testutil.helper.StringHelper;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
@@ -62,7 +61,7 @@ public class AndroidXmlFactoryTest {
 
 
   @Test
-  @Ignore("Will work with robolectric version 2.3")
+//  @Ignore("Will work with robolectric version 2.3")
   public void createReader() throws Exception {
     InputStream stream = StringHelper.encapsulate(BASIC_CONTENT);
     XMLStreamReader xmlReader = streamFactory.createXMLStreamReader(stream);

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/cb1ba468/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AndroidXmlReaderTest.java
----------------------------------------------------------------------
diff --git a/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AndroidXmlReaderTest.java b/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AndroidXmlReaderTest.java
index 51cebbb..af0d6a0 100644
--- a/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AndroidXmlReaderTest.java
+++ b/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AndroidXmlReaderTest.java
@@ -30,7 +30,6 @@ import org.apache.olingo.odata2.testutil.mock.MockFacade;
 import org.custommonkey.xmlunit.SimpleNamespaceContext;
 import org.custommonkey.xmlunit.XMLUnit;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
@@ -47,7 +46,7 @@ import static org.apache.olingo.odata2.api.xml.XMLStreamReaderFactory.XML_STREAM
  */
 @RunWith(RobolectricTestRunner.class)
 @Config(manifest=Config.NONE)
-@Ignore("Will work with robolectric version 2.3")
+//  @Ignore("Will work with robolectric version 2.3")
 public class AndroidXmlReaderTest {
 
   private static final String BASIC_RESULT =
@@ -117,7 +116,6 @@ public class AndroidXmlReaderTest {
     AndroidXmlReader xmlReader = new AndroidXmlReader(stream.asStream());
 
     int[] expected = new int[]{
-            XMLStreamConstants.START_DOCUMENT,
             XMLStreamConstants.START_ELEMENT,
             XMLStreamConstants.START_ELEMENT,
             XMLStreamConstants.END_ELEMENT,
@@ -126,8 +124,8 @@ public class AndroidXmlReaderTest {
     int pos = 0;
     while(xmlReader.hasNext()) {
       int elementId = xmlReader.next();
-//      System.out.println("E: " + elementId);
-      assertEquals(expected[pos++], elementId);
+      assertEquals("Unexpected type at position: " + pos,
+              expected[pos++], elementId);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/cb1ba468/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AtomEntryProducerTest.java
----------------------------------------------------------------------
diff --git a/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AtomEntryProducerTest.java b/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AtomEntryProducerTest.java
new file mode 100644
index 0000000..5f1ee53
--- /dev/null
+++ b/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AtomEntryProducerTest.java
@@ -0,0 +1,849 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.olingo.odata2.android.xml;
+
+import org.apache.olingo.odata2.api.edm.*;
+import org.apache.olingo.odata2.api.ep.EntityProviderException;
+import org.apache.olingo.odata2.api.ep.EntityProviderWriteProperties;
+import org.apache.olingo.odata2.api.exception.ODataException;
+import org.apache.olingo.odata2.api.exception.ODataMessageException;
+import org.apache.olingo.odata2.api.processor.ODataResponse;
+import org.apache.olingo.odata2.api.xml.XMLStreamException;
+import org.apache.olingo.odata2.core.commons.ContentType;
+import org.apache.olingo.odata2.core.ep.AtomEntityProvider;
+import org.apache.olingo.odata2.testutil.helper.StringHelper;
+import org.apache.olingo.odata2.testutil.helper.XMLUnitHelper;
+import org.apache.olingo.odata2.testutil.mock.MockFacade;
+import org.custommonkey.xmlunit.SimpleNamespaceContext;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.custommonkey.xmlunit.exceptions.XpathException;
+import org.junit.Test;
+import org.robolectric.annotation.Config;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.custommonkey.xmlunit.XMLAssert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ *  
+ */
+@Config(manifest=Config.NONE)
+public class AtomEntryProducerTest extends AndroidTestBase {
+
+  @Test
+  public void noneSyndicationKeepInContentFalseMustNotShowInProperties() throws Exception {
+    // prepare Mock
+    EdmEntitySet employeesSet = MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Employees");
+    EdmCustomizableFeedMappings employeeCustomPropertyMapping = mock(EdmCustomizableFeedMappings.class);
+    when(employeeCustomPropertyMapping.isFcKeepInContent()).thenReturn(Boolean.FALSE);
+    when(employeeCustomPropertyMapping.getFcNsPrefix()).thenReturn("customPre");
+    when(employeeCustomPropertyMapping.getFcNsUri()).thenReturn("http://customUri.com");
+    EdmTyped employeeEntryDateProperty = employeesSet.getEntityType().getProperty("EmployeeName");
+    when(((EdmProperty) employeeEntryDateProperty).getCustomizableFeedMappings()).thenReturn(
+        employeeCustomPropertyMapping);
+
+    Map<String, String> prefixMap = new HashMap<String, String>();
+    prefixMap.put("a", Edm.NAMESPACE_ATOM_2005);
+    prefixMap.put("d", Edm.NAMESPACE_D_2007_08);
+    prefixMap.put("m", Edm.NAMESPACE_M_2007_08);
+    prefixMap.put("xml", Edm.NAMESPACE_XML_1998);
+    prefixMap.put("customPre", "http://customUri.com");
+    XMLUnit.setXpathNamespaceContext(new SimpleNamespaceContext(prefixMap));
+
+    AtomEntityProvider ser = createAtomEntityProvider();
+    ODataResponse response = ser.writeEntry(employeesSet, employeeData, DEFAULT_PROPERTIES);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry/customPre:EmployeeName", xmlString);
+    assertXpathNotExists("/a:entry/m:properties/d:EmployeeName", xmlString);
+  }
+
+  @Test
+  public void noneSyndicationKeepInContentTrueMustShowInProperties() throws Exception {
+    // prepare Mock
+    EdmEntitySet employeesSet = MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Employees");
+    EdmCustomizableFeedMappings employeeCustomPropertyMapping = mock(EdmCustomizableFeedMappings.class);
+    when(employeeCustomPropertyMapping.isFcKeepInContent()).thenReturn(Boolean.TRUE);
+    when(employeeCustomPropertyMapping.getFcNsPrefix()).thenReturn("customPre");
+    when(employeeCustomPropertyMapping.getFcNsUri()).thenReturn("http://customUri.com");
+    EdmTyped employeeEntryDateProperty = employeesSet.getEntityType().getProperty("EmployeeName");
+    when(((EdmProperty) employeeEntryDateProperty).getCustomizableFeedMappings()).thenReturn(
+        employeeCustomPropertyMapping);
+
+    Map<String, String> prefixMap = new HashMap<String, String>();
+    prefixMap.put("a", Edm.NAMESPACE_ATOM_2005);
+    prefixMap.put("d", Edm.NAMESPACE_D_2007_08);
+    prefixMap.put("m", Edm.NAMESPACE_M_2007_08);
+    prefixMap.put("xml", Edm.NAMESPACE_XML_1998);
+    prefixMap.put("customPre", "http://customUri.com");
+    XMLUnit.setXpathNamespaceContext(new SimpleNamespaceContext(prefixMap));
+
+    AtomEntityProvider ser = createAtomEntityProvider();
+    ODataResponse response = ser.writeEntry(employeesSet, employeeData, DEFAULT_PROPERTIES);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry/customPre:EmployeeName", xmlString);
+    assertXpathExists("/a:entry/m:properties/d:EmployeeName", xmlString);
+  }
+
+  @Test
+  public void noneSyndicationWithNullPrefix() throws Exception {
+    // prepare Mock
+    EdmEntitySet employeesSet = MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Employees");
+    EdmCustomizableFeedMappings employeeCustomPropertyMapping = mock(EdmCustomizableFeedMappings.class);
+    when(employeeCustomPropertyMapping.isFcKeepInContent()).thenReturn(Boolean.TRUE);
+    when(employeeCustomPropertyMapping.getFcNsUri()).thenReturn("http://customUri.com");
+    EdmTyped employeeEntryDateProperty = employeesSet.getEntityType().getProperty("EmployeeName");
+    when(((EdmProperty) employeeEntryDateProperty).getCustomizableFeedMappings()).thenReturn(
+        employeeCustomPropertyMapping);
+
+    Map<String, String> prefixMap = new HashMap<String, String>();
+    prefixMap.put("a", Edm.NAMESPACE_ATOM_2005);
+    prefixMap.put("d", Edm.NAMESPACE_D_2007_08);
+    prefixMap.put("m", Edm.NAMESPACE_M_2007_08);
+    prefixMap.put("xml", Edm.NAMESPACE_XML_1998);
+    prefixMap.put("customPre", "http://customUri.com");
+    XMLUnit.setXpathNamespaceContext(new SimpleNamespaceContext(prefixMap));
+
+    AtomEntityProvider ser = createAtomEntityProvider();
+    boolean thrown = false;
+    try {
+      ser.writeEntry(employeesSet, employeeData, DEFAULT_PROPERTIES);
+    } catch (EntityProviderException e) {
+      verifyRootCause(EntityProviderException.class, EntityProviderException.INVALID_NAMESPACE.getKey(), e);
+      thrown = true;
+    }
+    if (!thrown) {
+      fail("Exception should have been thrown");
+    }
+  }
+
+  @Test
+  public void noneSyndicationWithNullUri() throws Exception {
+    // prepare Mock
+    EdmEntitySet employeesSet = MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Employees");
+    EdmCustomizableFeedMappings employeeCustomPropertyMapping = mock(EdmCustomizableFeedMappings.class);
+    when(employeeCustomPropertyMapping.isFcKeepInContent()).thenReturn(Boolean.TRUE);
+    when(employeeCustomPropertyMapping.getFcNsPrefix()).thenReturn("customPre");
+    EdmTyped employeeEntryDateProperty = employeesSet.getEntityType().getProperty("EmployeeName");
+    when(((EdmProperty) employeeEntryDateProperty).getCustomizableFeedMappings()).thenReturn(
+        employeeCustomPropertyMapping);
+
+    Map<String, String> prefixMap = new HashMap<String, String>();
+    prefixMap.put("a", Edm.NAMESPACE_ATOM_2005);
+    prefixMap.put("d", Edm.NAMESPACE_D_2007_08);
+    prefixMap.put("m", Edm.NAMESPACE_M_2007_08);
+    prefixMap.put("xml", Edm.NAMESPACE_XML_1998);
+    prefixMap.put("customPre", "http://customUri.com");
+    XMLUnit.setXpathNamespaceContext(new SimpleNamespaceContext(prefixMap));
+
+    AtomEntityProvider ser = createAtomEntityProvider();
+    boolean thrown = false;
+    try {
+      ser.writeEntry(employeesSet, employeeData, DEFAULT_PROPERTIES);
+    } catch (EntityProviderException e) {
+      verifyRootCause(EntityProviderException.class, EntityProviderException.INVALID_NAMESPACE.getKey(), e);
+      thrown = true;
+    }
+    if (!thrown) {
+      fail("Exception should have been thrown");
+    }
+  }
+
+  @Test
+  public void noneSyndicationWithNullUriAndNullPrefix() throws Exception {
+    // prepare Mock
+    EdmEntitySet employeesSet = MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Employees");
+    EdmCustomizableFeedMappings employeeCustomPropertyMapping = mock(EdmCustomizableFeedMappings.class);
+    when(employeeCustomPropertyMapping.isFcKeepInContent()).thenReturn(Boolean.TRUE);
+    EdmTyped employeeEntryDateProperty = employeesSet.getEntityType().getProperty("EmployeeName");
+    when(((EdmProperty) employeeEntryDateProperty).getCustomizableFeedMappings()).thenReturn(
+        employeeCustomPropertyMapping);
+
+    Map<String, String> prefixMap = new HashMap<String, String>();
+    prefixMap.put("a", Edm.NAMESPACE_ATOM_2005);
+    prefixMap.put("d", Edm.NAMESPACE_D_2007_08);
+    prefixMap.put("m", Edm.NAMESPACE_M_2007_08);
+    prefixMap.put("xml", Edm.NAMESPACE_XML_1998);
+    prefixMap.put("f", "http://customUri.com");
+    XMLUnit.setXpathNamespaceContext(new SimpleNamespaceContext(prefixMap));
+
+    AtomEntityProvider ser = createAtomEntityProvider();
+    boolean thrown = false;
+    try {
+      ser.writeEntry(employeesSet, employeeData, DEFAULT_PROPERTIES);
+    } catch (EntityProviderException e) {
+      verifyRootCause(EntityProviderException.class, EntityProviderException.INVALID_NAMESPACE.getKey(), e);
+      thrown = true;
+    }
+    if (!thrown) {
+      fail("Exception should have been thrown");
+    }
+  }
+
+  @Test
+  public void syndicationWithComplexProperty() throws Exception {
+    // prepare Mock
+    EdmEntitySet employeesSet = MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Employees");
+    EdmCustomizableFeedMappings employeeCustomPropertyMapping = mock(EdmCustomizableFeedMappings.class);
+    when(employeeCustomPropertyMapping.isFcKeepInContent()).thenReturn(Boolean.TRUE);
+    when(employeeCustomPropertyMapping.getFcNsPrefix()).thenReturn("customPre");
+    when(employeeCustomPropertyMapping.getFcNsUri()).thenReturn("http://customUri.com");
+    EdmTyped employeeLocationProperty = employeesSet.getEntityType().getProperty("Location");
+    when(((EdmProperty) employeeLocationProperty).getCustomizableFeedMappings()).thenReturn(
+        employeeCustomPropertyMapping);
+
+    Map<String, String> prefixMap = new HashMap<String, String>();
+    prefixMap.put("a", Edm.NAMESPACE_ATOM_2005);
+    prefixMap.put("d", Edm.NAMESPACE_D_2007_08);
+    prefixMap.put("m", Edm.NAMESPACE_M_2007_08);
+    prefixMap.put("xml", Edm.NAMESPACE_XML_1998);
+    prefixMap.put("customPre", "http://customUri.com");
+    XMLUnit.setXpathNamespaceContext(new SimpleNamespaceContext(prefixMap));
+
+    AtomEntityProvider ser = createAtomEntityProvider();
+    ODataResponse response = ser.writeEntry(employeesSet, employeeData, DEFAULT_PROPERTIES);
+    String xmlString = verifyResponse(response);
+
+    assertXpathNotExists("/a:entry/customPre:Location", xmlString);
+    assertXpathExists("/a:entry/m:properties/d:Location", xmlString);
+  }
+
+  private void verifyRootCause(final Class<?> class1, final String key, final ODataMessageException e) {
+
+    Throwable thrownException = e;
+    Throwable lastFoundException = null;
+    if (e.getClass().equals(class1)) {
+      lastFoundException = e;
+    }
+
+    while (thrownException.getCause() != null) {
+      thrownException = thrownException.getCause();
+      if (thrownException.getClass().equals(class1)) {
+        lastFoundException = thrownException;
+      }
+    }
+
+    if (lastFoundException != null) {
+      ODataMessageException msgException = (ODataMessageException) lastFoundException;
+      assertEquals(key, msgException.getMessageReference().getKey());
+    } else {
+      fail("Exception of class: " + class1.getCanonicalName() + " in stacktrace not found.");
+    }
+  }
+
+  @Test
+  public void serializeAtomMediaResource() throws IOException, XpathException, SAXException, XMLStreamException,
+      ODataException {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    ODataResponse response =
+        ser.writeEntry(MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Employees"), employeeData,
+            DEFAULT_PROPERTIES);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry", xmlString);
+    assertXpathEvaluatesTo(BASE_URI.toASCIIString(), "/a:entry/@xml:base", xmlString);
+
+    assertXpathExists("/a:entry/a:content", xmlString);
+    assertXpathEvaluatesTo(ContentType.APPLICATION_OCTET_STREAM.toString(), "/a:entry/a:content/@type", xmlString);
+    assertXpathEvaluatesTo("Employees('1')/$value", "/a:entry/a:content/@src", xmlString);
+    assertXpathExists("/a:entry/m:properties", xmlString);
+
+    assertXpathExists("/a:entry/a:link[@href=\"Employees('1')/$value\"]", xmlString);
+    assertXpathExists("/a:entry/a:link[@rel='edit-media']", xmlString);
+    assertXpathExists("/a:entry/a:link[@type='application/octet-stream']", xmlString);
+
+    assertXpathExists("/a:entry/a:link[@href=\"Employees('1')\"]", xmlString);
+    assertXpathExists("/a:entry/a:link[@rel='edit']", xmlString);
+    assertXpathExists("/a:entry/a:link[@title='Employee']", xmlString);
+
+    // assert navigation link order
+    verifyTagOrdering(xmlString,
+        "link((?:(?!link).)*?)edit",
+        "link((?:(?!link).)*?)edit-media",
+        "link((?:(?!link).)*?)ne_Manager",
+        "link((?:(?!link).)*?)ne_Team",
+        "link((?:(?!link).)*?)ne_Room");
+  }
+
+  private String verifyResponse(final ODataResponse response) throws IOException {
+    assertNotNull(response);
+    assertNotNull(response.getEntity());
+    assertNull("EntityProvider should not set content header", response.getContentHeader());
+    String xmlString = StringHelper.inputStreamToString((InputStream) response.getEntity());
+    return xmlString;
+  }
+
+  @Test
+  public void serializeAtomMediaResourceWithMimeType() throws IOException, XpathException, SAXException,
+      XMLStreamException, ODataException {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    EntityProviderWriteProperties properties =
+        EntityProviderWriteProperties.serviceRoot(BASE_URI).mediaResourceMimeType("abc").build();
+    ODataResponse response =
+        ser.writeEntry(MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Employees"), employeeData,
+            properties);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry", xmlString);
+    assertXpathEvaluatesTo(BASE_URI.toASCIIString(), "/a:entry/@xml:base", xmlString);
+
+    assertXpathExists("/a:entry/a:content", xmlString);
+    assertXpathEvaluatesTo("abc", "/a:entry/a:content/@type", xmlString);
+    assertXpathEvaluatesTo("Employees('1')/$value", "/a:entry/a:content/@src", xmlString);
+    assertXpathExists("/a:entry/m:properties", xmlString);
+  }
+
+  /**
+   * Test serialization of empty syndication title property. EmployeeName is set to NULL after the update (which is
+   * allowed because EmployeeName has default Nullable behavior which is true).
+   * Write of an empty atom title tag is allowed within RFC4287 (http://tools.ietf.org/html/rfc4287#section-4.2.14).
+   */
+  @Test
+  public void serializeEmployeeWithNullSyndicationTitleProperty() throws IOException, XpathException, SAXException,
+      XMLStreamException, ODataException {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    EntityProviderWriteProperties properties = EntityProviderWriteProperties.serviceRoot(BASE_URI).build();
+    employeeData.put("EmployeeName", null);
+    ODataResponse response =
+        ser.writeEntry(MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Employees"), employeeData,
+            properties);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry/a:title", xmlString);
+    assertXpathEvaluatesTo("", "/a:entry/a:title", xmlString);
+
+    assertXpathExists("/a:entry", xmlString);
+    assertXpathEvaluatesTo(BASE_URI.toASCIIString(), "/a:entry/@xml:base", xmlString);
+
+    assertXpathExists("/a:entry/a:content", xmlString);
+    assertXpathEvaluatesTo("Employees('1')/$value", "/a:entry/a:content/@src", xmlString);
+    assertXpathExists("/a:entry/m:properties", xmlString);
+  }
+
+  @Test
+  public void serializeEmployeeAndCheckOrderOfTags() throws IOException, XpathException, SAXException,
+      XMLStreamException, ODataException {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    EntityProviderWriteProperties properties =
+        EntityProviderWriteProperties.serviceRoot(BASE_URI).mediaResourceMimeType("abc").build();
+    ODataResponse response =
+        ser.writeEntry(MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Employees"), employeeData,
+            properties);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry", xmlString);
+    assertXpathExists("/a:entry/a:content", xmlString);
+    // verify self link
+    assertXpathExists("/a:entry/a:link[@href=\"Employees('1')\"]", xmlString);
+    // verify content media link
+    assertXpathExists("/a:entry/a:link[@href=\"Employees('1')/$value\"]", xmlString);
+    // verify one navigation link
+    assertXpathExists("/a:entry/a:link[@title='ne_Manager']", xmlString);
+
+    // verify content
+    assertXpathExists("/a:entry/a:content[@type='abc']", xmlString);
+    // verify properties
+    assertXpathExists("/a:entry/m:properties", xmlString);
+    assertXpathEvaluatesTo("9", "count(/a:entry/m:properties/*)", xmlString);
+
+    // verify order of tags
+    verifyTagOrdering(xmlString, "id", "title", "updated", "category",
+        "link((?:(?!link).)*?)edit",
+        "link((?:(?!link).)*?)edit-media",
+        "link((?:(?!link).)*?)ne_Manager",
+        "content", "properties");
+  }
+
+  @Test
+  public void serializeEmployeeAndCheckOrderOfPropertyTags() throws IOException, XpathException, SAXException,
+      XMLStreamException, ODataException {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    EntityProviderWriteProperties properties =
+        EntityProviderWriteProperties.serviceRoot(BASE_URI).mediaResourceMimeType("abc").build();
+    EdmEntitySet employeeEntitySet = MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Employees");
+    ODataResponse response = ser.writeEntry(employeeEntitySet, employeeData, properties);
+    String xmlString = verifyResponse(response);
+
+    // log.debug(xmlString);
+
+    assertXpathExists("/a:entry", xmlString);
+    assertXpathExists("/a:entry/a:content", xmlString);
+    // verify properties
+    assertXpathExists("/a:entry/m:properties", xmlString);
+    assertXpathEvaluatesTo("9", "count(/a:entry/m:properties/*)", xmlString);
+
+    // verify order of tags
+    List<String> expectedPropertyNamesFromEdm = employeeEntitySet.getEntityType().getPropertyNames();
+    verifyTagOrdering(xmlString, expectedPropertyNamesFromEdm.toArray(new String[0]));
+  }
+
+  @Test
+  public void serializeEmployeeAndCheckKeepInContentFalse() throws IOException, XpathException, SAXException,
+      XMLStreamException, ODataException {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    EntityProviderWriteProperties properties =
+        EntityProviderWriteProperties.serviceRoot(BASE_URI).mediaResourceMimeType("abc").build();
+    EdmEntitySet employeeEntitySet = MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Employees");
+
+    // set "keepInContent" to false for EntryDate
+    EdmCustomizableFeedMappings employeeUpdatedMappings = mock(EdmCustomizableFeedMappings.class);
+    when(employeeUpdatedMappings.getFcTargetPath()).thenReturn(EdmTargetPath.SYNDICATION_UPDATED);
+    when(employeeUpdatedMappings.isFcKeepInContent()).thenReturn(Boolean.FALSE);
+    EdmTyped employeeEntryDateProperty = employeeEntitySet.getEntityType().getProperty("EntryDate");
+    when(((EdmProperty) employeeEntryDateProperty).getCustomizableFeedMappings()).thenReturn(employeeUpdatedMappings);
+
+    ODataResponse response = ser.writeEntry(employeeEntitySet, employeeData, properties);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry", xmlString);
+    assertXpathExists("/a:entry/a:content", xmlString);
+    // verify properties
+    assertXpathExists("/a:entry/m:properties", xmlString);
+    assertXpathEvaluatesTo("8", "count(/a:entry/m:properties/*)", xmlString);
+    //
+    assertXpathNotExists("/a:entry/m:properties/d:EntryDate", xmlString);
+
+    // verify order of tags
+    List<String> expectedPropertyNamesFromEdm =
+        new ArrayList<String>(employeeEntitySet.getEntityType().getPropertyNames());
+    expectedPropertyNamesFromEdm.remove(String.valueOf("EntryDate"));
+    verifyTagOrdering(xmlString, expectedPropertyNamesFromEdm.toArray(new String[0]));
+  }
+
+  @Test(expected = EntityProviderException.class)
+  public void serializeAtomEntryWithNullData() throws IOException, XpathException, SAXException, XMLStreamException,
+      ODataException {
+    final EntityProviderWriteProperties properties = EntityProviderWriteProperties.serviceRoot(BASE_URI).build();
+    AtomEntityProvider ser = createAtomEntityProvider();
+    ser.writeEntry(MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Rooms"), null, properties);
+  }
+
+  @Test(expected = EntityProviderException.class)
+  public void serializeAtomEntryWithEmptyHashMap() throws IOException, XpathException, SAXException,
+      XMLStreamException, ODataException {
+    final EntityProviderWriteProperties properties = EntityProviderWriteProperties.serviceRoot(BASE_URI).build();
+    AtomEntityProvider ser = createAtomEntityProvider();
+    ser.writeEntry(MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Rooms"),
+        new HashMap<String, Object>(), properties);
+  }
+
+  @Test
+  public void serializeAtomEntry() throws IOException, XpathException, SAXException, XMLStreamException,
+      ODataException {
+    final EntityProviderWriteProperties properties = EntityProviderWriteProperties.serviceRoot(BASE_URI).build();
+    AtomEntityProvider ser = createAtomEntityProvider();
+    ODataResponse response =
+        ser.writeEntry(MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Rooms"), roomData, properties);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry", xmlString);
+    assertXpathEvaluatesTo(BASE_URI.toASCIIString(), "/a:entry/@xml:base", xmlString);
+
+    assertXpathExists("/a:entry/a:content", xmlString);
+    assertXpathEvaluatesTo(ContentType.APPLICATION_XML.toString(), "/a:entry/a:content/@type", xmlString);
+
+    assertXpathExists("/a:entry/a:content/m:properties", xmlString);
+  }
+
+  @Test
+  public void serializeAtomEntryWithSimplePropertyTypeInformation() throws Exception {
+    final EntityProviderWriteProperties properties =
+        EntityProviderWriteProperties.serviceRoot(BASE_URI).includeSimplePropertyType(true).build();
+    AtomEntityProvider ser = createAtomEntityProvider();
+    ODataResponse response =
+        ser.writeEntry(MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Rooms"), roomData, properties);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry/a:content/m:properties", xmlString);
+    assertXpathExists("/a:entry/a:content/m:properties/d:Id[@m:type=\"Edm.String\"]", xmlString);
+    assertXpathExists("/a:entry/a:content/m:properties/d:Name[@m:type=\"Edm.String\"]", xmlString);
+    assertXpathExists("/a:entry/a:content/m:properties/d:Seats[@m:type=\"Edm.Int16\"]", xmlString);
+    assertXpathExists("/a:entry/a:content/m:properties/d:Version[@m:type=\"Edm.Int16\"]", xmlString);
+  }
+
+  @Test
+  public void serializeEntryId() throws IOException, XpathException, SAXException, XMLStreamException,
+      ODataException {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    ODataResponse response =
+        ser.writeEntry(MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Employees"), employeeData,
+            DEFAULT_PROPERTIES);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry", xmlString);
+    assertXpathEvaluatesTo(BASE_URI.toASCIIString(), "/a:entry/@xml:base", xmlString);
+    assertXpathExists("/a:entry/a:id", xmlString);
+    assertXpathEvaluatesTo(BASE_URI.toASCIIString() + "Employees('1')", "/a:entry/a:id/text()", xmlString);
+  }
+
+  @Test
+  public void serializeEntryTitle() throws Exception {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    ODataResponse response =
+        ser.writeEntry(MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Employees"), employeeData,
+            DEFAULT_PROPERTIES);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry/a:title", xmlString);
+    assertXpathEvaluatesTo("text", "/a:entry/a:title/@type", xmlString);
+    assertXpathEvaluatesTo((String) employeeData.get("EmployeeName"), "/a:entry/a:title/text()", xmlString);
+  }
+
+  @Test
+  public void serializeEntryUpdated() throws Exception {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    ODataResponse response =
+        ser.writeEntry(MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Employees"), employeeData,
+            DEFAULT_PROPERTIES);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry/a:updated", xmlString);
+    assertXpathEvaluatesTo("1999-01-01T00:00:00Z", "/a:entry/a:updated/text()", xmlString);
+  }
+
+  @Test
+  public void serializeIds() throws IOException, XpathException, SAXException, XMLStreamException,
+      ODataException {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    ODataResponse response =
+        ser.writeEntry(MockFacade.getMockEdm().getEntityContainer("Container2").getEntitySet("Photos"), photoData,
+            DEFAULT_PROPERTIES);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry", xmlString);
+    assertXpathEvaluatesTo(BASE_URI.toASCIIString(), "/a:entry/@xml:base", xmlString);
+    assertXpathExists("/a:entry/a:id", xmlString);
+    assertXpathEvaluatesTo(BASE_URI.toASCIIString() + "Container2.Photos(Id=1,Type='image%2Fpng')",
+        "/a:entry/a:id/text()", xmlString);
+  }
+
+  @Test
+  public void serializeProperties() throws IOException, XpathException, SAXException, XMLStreamException,
+      ODataException {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    ODataResponse response =
+        ser.writeEntry(MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Employees"), employeeData,
+            DEFAULT_PROPERTIES);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry/m:properties", xmlString);
+    assertXpathEvaluatesTo((String) employeeData.get("RoomId"), "/a:entry/m:properties/d:RoomId/text()", xmlString);
+    assertXpathEvaluatesTo((String) employeeData.get("TeamId"), "/a:entry/m:properties/d:TeamId/text()", xmlString);
+  }
+
+  @Test
+  public void serializeWithValueEncoding() throws IOException, XpathException, SAXException, XMLStreamException,
+      ODataException {
+    photoData.put("Type", "< Ö >");
+
+    AtomEntityProvider ser = createAtomEntityProvider();
+    ODataResponse response =
+        ser.writeEntry(MockFacade.getMockEdm().getEntityContainer("Container2").getEntitySet("Photos"), photoData,
+            DEFAULT_PROPERTIES);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry", xmlString);
+    assertXpathEvaluatesTo(BASE_URI.toASCIIString(), "/a:entry/@xml:base", xmlString);
+    assertXpathExists("/a:entry/a:id", xmlString);
+    assertXpathEvaluatesTo(BASE_URI.toASCIIString() + "Container2.Photos(Id=1,Type='%3C%20%C3%96%20%3E')",
+        "/a:entry/a:id/text()", xmlString);
+    assertXpathEvaluatesTo("Container2.Photos(Id=1,Type='%3C%20%C3%96%20%3E')", "/a:entry/a:link/@href", xmlString);
+  }
+
+  @Test
+  public void serializeCategory() throws IOException, XpathException, SAXException, XMLStreamException,
+      ODataException {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    ODataResponse response =
+        ser.writeEntry(MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Employees"), employeeData,
+            DEFAULT_PROPERTIES);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry/a:category", xmlString);
+    assertXpathExists("/a:entry/a:category/@term", xmlString);
+    assertXpathExists("/a:entry/a:category/@scheme", xmlString);
+    assertXpathEvaluatesTo("RefScenario.Employee", "/a:entry/a:category/@term", xmlString);
+    assertXpathEvaluatesTo(Edm.NAMESPACE_SCHEME_2007_08, "/a:entry/a:category/@scheme", xmlString);
+  }
+
+  @Test
+  public void serializeETag() throws IOException, XpathException, SAXException, XMLStreamException,
+      ODataException {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    ODataResponse response =
+        ser.writeEntry(MockFacade.getMockEdm().getEntityContainer("Container2").getEntitySet("Photos"), photoData,
+            DEFAULT_PROPERTIES);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry", xmlString);
+    assertXpathExists("/a:entry/@m:etag", xmlString);
+    assertXpathEvaluatesTo("W/\"1\"", "/a:entry/@m:etag", xmlString);
+    assertEquals("W/\"1\"", response.getETag());
+  }
+
+  @Test
+  public void serializeETagEncoding() throws IOException, XpathException, SAXException, XMLStreamException,
+      ODataException {
+    Edm edm = MockFacade.getMockEdm();
+    EdmTyped roomIdProperty = edm.getEntityType("RefScenario", "Room").getProperty("Id");
+    EdmFacets facets = mock(EdmFacets.class);
+    when(facets.getConcurrencyMode()).thenReturn(EdmConcurrencyMode.Fixed);
+    when(facets.getMaxLength()).thenReturn(3);
+    when(((EdmProperty) roomIdProperty).getFacets()).thenReturn(facets);
+
+    roomData.put("Id", "<\">");
+    AtomEntityProvider ser = createAtomEntityProvider();
+    ODataResponse response =
+        ser.writeEntry(edm.getDefaultEntityContainer().getEntitySet("Rooms"), roomData, DEFAULT_PROPERTIES);
+
+    assertNotNull(response);
+    assertNotNull(response.getEntity());
+    assertNull("EntityProvider should not set content header", response.getContentHeader());
+    assertEquals("W/\"<\">.3\"", response.getETag());
+
+    String xmlString = StringHelper.inputStreamToString((InputStream) response.getEntity());
+
+    assertXpathExists("/a:entry", xmlString);
+    assertXpathExists("/a:entry/@m:etag", xmlString);
+    assertXpathEvaluatesTo("W/\"<\">.3\"", "/a:entry/@m:etag", xmlString);
+  }
+
+  @Test
+  public void serializeCustomMapping() throws IOException, XpathException, SAXException, XMLStreamException,
+      ODataException {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    ODataResponse response =
+        ser.writeEntry(MockFacade.getMockEdm().getEntityContainer("Container2").getEntitySet("Photos"), photoData,
+            DEFAULT_PROPERTIES);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry", xmlString);
+    assertXpathExists("/a:entry/ру:Содержание", xmlString);
+    assertXpathEvaluatesTo((String) photoData.get("Содержание"), "/a:entry/ру:Содержание/text()", xmlString);
+    verifyTagOrdering(xmlString, "category", "Содержание", "content", "properties");
+  }
+
+  @Test
+  public void testCustomProperties() throws Exception {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    EdmEntitySet entitySet = MockFacade.getMockEdm().getEntityContainer("Container2").getEntitySet("Photos");
+
+    ODataResponse response = ser.writeEntry(entitySet, photoData, DEFAULT_PROPERTIES);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry", xmlString);
+    assertXpathExists("/a:entry/custom:CustomProperty", xmlString);
+    assertXpathNotExists("/a:entry/custom:CustomProperty/text()", xmlString);
+    assertXpathEvaluatesTo("true", "/a:entry/custom:CustomProperty/@m:null", xmlString);
+    verifyTagOrdering(xmlString, "category", "Содержание", "CustomProperty", "content", "properties");
+  }
+
+  @Test
+  public void testKeepInContentNull() throws Exception {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    EdmEntitySet entitySet = MockFacade.getMockEdm().getEntityContainer("Container2").getEntitySet("Photos");
+
+    EdmProperty customProperty = (EdmProperty) entitySet.getEntityType().getProperty("CustomProperty");
+    when(customProperty.getCustomizableFeedMappings().isFcKeepInContent()).thenReturn(null);
+
+    ODataResponse response = ser.writeEntry(entitySet, photoData, DEFAULT_PROPERTIES);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry", xmlString);
+    assertXpathExists("/a:entry/custom:CustomProperty", xmlString);
+    assertXpathNotExists("/a:entry/custom:CustomProperty/text()", xmlString);
+    assertXpathEvaluatesTo("true", "/a:entry/custom:CustomProperty/@m:null", xmlString);
+    assertXpathExists("/a:entry/m:properties/d:CustomProperty", xmlString);
+    verifyTagOrdering(xmlString, "category", "Содержание", "CustomProperty", "content", "properties");
+  }
+
+  @Test
+  public void serializeAtomMediaResourceLinks() throws IOException, XpathException, SAXException, XMLStreamException,
+      ODataException {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    ODataResponse response =
+        ser.writeEntry(MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Employees"), employeeData,
+            DEFAULT_PROPERTIES);
+    String xmlString = verifyResponse(response);
+
+    String rel = Edm.NAMESPACE_REL_2007_08 + "ne_Manager";
+
+    assertXpathExists("/a:entry/a:link[@href=\"Employees('1')/ne_Manager\"]", xmlString);
+    assertXpathExists("/a:entry/a:link[@rel='" + rel + "']", xmlString);
+    assertXpathExists("/a:entry/a:link[@type='application/atom+xml;type=entry']", xmlString);
+    assertXpathExists("/a:entry/a:link[@title='ne_Manager']", xmlString);
+  }
+
+  @Test
+  public void additionalLink() throws Exception {
+    Map<String, Map<String, Object>> links = new HashMap<String, Map<String, Object>>();
+    links.put("nr_Building", buildingData);
+    final ODataResponse response = createAtomEntityProvider().writeEntry(
+        MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Rooms"), roomData,
+        EntityProviderWriteProperties.serviceRoot(BASE_URI).additionalLinks(links).build());
+    final String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry/a:link[@title='nr_Building']", xmlString);
+    assertXpathNotExists("/a:entry/a:link[@href=\"Rooms('1')/nr_Building\"]", xmlString);
+    assertXpathExists("/a:entry/a:link[@href=\"Buildings('1')\"]", xmlString);
+    assertXpathNotExists("/a:entry/a:link[@type='application/atom+xml;type=entry']", xmlString);
+  }
+
+  @Test
+  public void additionalLinkToOneOfMany() throws Exception {
+    Map<String, Map<String, Object>> links = new HashMap<String, Map<String, Object>>();
+    links.put("nr_Employees", employeeData);
+    final ODataResponse response = createAtomEntityProvider().writeEntry(
+        MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Rooms"), roomData,
+        EntityProviderWriteProperties.serviceRoot(BASE_URI).additionalLinks(links).build());
+    final String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry/a:link[@title='nr_Employees']", xmlString);
+    assertXpathNotExists("/a:entry/a:link[@href=\"Rooms('1')/nr_Employees\"]", xmlString);
+    assertXpathExists("/a:entry/a:link[@href=\"Employees('1')\"]", xmlString);
+    assertXpathNotExists("/a:entry/a:link[@type='application/atom+xml;type=feed']", xmlString);
+  }
+
+  @Test
+  public void serializeWithCustomSrcAttributeOnEmployee() throws Exception {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    Map<String, Object> localEmployeeData = new HashMap<String, Object>(employeeData);
+    String mediaResourceSourceKey = "~src";
+    localEmployeeData.put(mediaResourceSourceKey, "http://localhost:8080/images/image1");
+    EdmEntitySet employeesSet = MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Employees");
+    EdmMapping mapping = employeesSet.getEntityType().getMapping();
+    when(mapping.getMediaResourceSourceKey()).thenReturn(mediaResourceSourceKey);
+
+    ODataResponse response = ser.writeEntry(employeesSet, localEmployeeData, DEFAULT_PROPERTIES);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists(
+        "/a:entry/a:link[@href=\"Employees('1')/$value\" and" +
+            " @rel=\"edit-media\" and @type=\"application/octet-stream\"]", xmlString);
+    assertXpathExists("/a:entry/a:content[@type=\"application/octet-stream\"]", xmlString);
+    assertXpathExists("/a:entry/a:content[@src=\"http://localhost:8080/images/image1\"]", xmlString);
+  }
+
+  @Test
+  public void serializeWithCustomSrcAndTypeAttributeOnEmployee() throws Exception {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    Map<String, Object> localEmployeeData = new HashMap<String, Object>(employeeData);
+    String mediaResourceSourceKey = "~src";
+    localEmployeeData.put(mediaResourceSourceKey, "http://localhost:8080/images/image1");
+    String mediaResourceMimeTypeKey = "~type";
+    localEmployeeData.put(mediaResourceMimeTypeKey, "image/jpeg");
+    EdmEntitySet employeesSet = MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Employees");
+    EdmMapping mapping = employeesSet.getEntityType().getMapping();
+    when(mapping.getMediaResourceSourceKey()).thenReturn(mediaResourceSourceKey);
+    when(mapping.getMediaResourceMimeTypeKey()).thenReturn(mediaResourceMimeTypeKey);
+    when(mapping.getMimeType()).thenReturn(null);
+    ODataResponse response = ser.writeEntry(employeesSet, localEmployeeData, DEFAULT_PROPERTIES);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists(
+        "/a:entry/a:link[@href=\"Employees('1')/$value\" and" +
+            " @rel=\"edit-media\" and @type=\"image/jpeg\"]", xmlString);
+    assertXpathExists("/a:entry/a:content[@type=\"image/jpeg\"]", xmlString);
+    assertXpathExists("/a:entry/a:content[@src=\"http://localhost:8080/images/image1\"]", xmlString);
+  }
+
+  @Test
+  public void serializeWithCustomSrcAttributeOnRoom() throws Exception {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    Map<String, Object> localRoomData = new HashMap<String, Object>(roomData);
+    String mediaResourceSourceKey = "~src";
+    localRoomData.put(mediaResourceSourceKey, "http://localhost:8080/images/image1");
+    EdmEntitySet roomsSet = MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Rooms");
+    EdmEntityType roomType = roomsSet.getEntityType();
+    EdmMapping mapping = mock(EdmMapping.class);
+    when(roomType.getMapping()).thenReturn(mapping);
+    when(mapping.getMediaResourceSourceKey()).thenReturn(mediaResourceSourceKey);
+
+    ODataResponse response = ser.writeEntry(roomsSet, localRoomData, DEFAULT_PROPERTIES);
+    String xmlString = verifyResponse(response);
+
+    assertXpathNotExists(
+        "/a:entry/a:link[@href=\"Rooms('1')/$value\" and" +
+            " @rel=\"edit-media\" and @type=\"application/octet-stream\"]", xmlString);
+    assertXpathNotExists("/a:entry/a:content[@type=\"application/octet-stream\"]", xmlString);
+    assertXpathNotExists("/a:entry/a:content[@src=\"http://localhost:8080/images/image1\"]", xmlString);
+  }
+
+  @Test
+  public void serializeWithCustomSrcAndTypeAttributeOnRoom() throws Exception {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    Map<String, Object> localRoomData = new HashMap<String, Object>(roomData);
+    String mediaResourceSourceKey = "~src";
+    localRoomData.put(mediaResourceSourceKey, "http://localhost:8080/images/image1");
+    String mediaResourceMimeTypeKey = "~type";
+    localRoomData.put(mediaResourceMimeTypeKey, "image/jpeg");
+    EdmEntitySet roomsSet = MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Rooms");
+    EdmEntityType roomType = roomsSet.getEntityType();
+    EdmMapping mapping = mock(EdmMapping.class);
+    when(roomType.getMapping()).thenReturn(mapping);
+    when(mapping.getMediaResourceSourceKey()).thenReturn(mediaResourceSourceKey);
+    when(mapping.getMediaResourceMimeTypeKey()).thenReturn(mediaResourceMimeTypeKey);
+
+    ODataResponse response = ser.writeEntry(roomsSet, localRoomData, DEFAULT_PROPERTIES);
+    String xmlString = verifyResponse(response);
+
+    assertXpathNotExists(
+        "/a:entry/a:link[@href=\"Rooms('1')/$value\" and" +
+            " @rel=\"edit-media\" and @type=\"image/jpeg\"]", xmlString);
+    assertXpathNotExists("/a:entry/a:content[@type=\"image/jpeg\"]", xmlString);
+    assertXpathNotExists("/a:entry/a:content[@src=\"http://localhost:8080/images/image1\"]", xmlString);
+  }
+
+  @Test
+  public void assureGetMimeTypeWinsOverGetMediaResourceMimeTypeKey() throws Exception {
+    // Keep this test till version 1.2
+    AtomEntityProvider ser = createAtomEntityProvider();
+    Map<String, Object> localEmployeeData = new HashMap<String, Object>(employeeData);
+    String mediaResourceMimeTypeKey = "~type";
+    localEmployeeData.put(mediaResourceMimeTypeKey, "wrong");
+    String originalMimeTypeKey = "~originalType";
+    localEmployeeData.put(originalMimeTypeKey, "right");
+    EdmEntitySet employeesSet = MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Employees");
+    EdmMapping mapping = employeesSet.getEntityType().getMapping();
+    when(mapping.getMediaResourceMimeTypeKey()).thenReturn(mediaResourceMimeTypeKey);
+    when(mapping.getMimeType()).thenReturn(originalMimeTypeKey);
+    ODataResponse response = ser.writeEntry(employeesSet, localEmployeeData, DEFAULT_PROPERTIES);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:entry/a:content[@type=\"right\"]", xmlString);
+    assertXpathNotExists("/a:entry/a:content[@type=\"wrong\"]", xmlString);
+  }
+
+  private void verifyTagOrdering(final String xmlString, final String... toCheckTags) {
+    XMLUnitHelper.verifyTagOrdering(xmlString, toCheckTags);
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/cb1ba468/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AtomFeedProducerTest.java
----------------------------------------------------------------------
diff --git a/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AtomFeedProducerTest.java b/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AtomFeedProducerTest.java
new file mode 100644
index 0000000..eaef473
--- /dev/null
+++ b/odata2-android/src/test/java/org/apache/olingo/odata2/android/xml/AtomFeedProducerTest.java
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.olingo.odata2.android.xml;
+
+import org.apache.olingo.odata2.api.commons.InlineCount;
+import org.apache.olingo.odata2.api.edm.EdmEntitySet;
+import org.apache.olingo.odata2.api.ep.EntityProviderException;
+import org.apache.olingo.odata2.api.ep.EntityProviderWriteProperties;
+import org.apache.olingo.odata2.api.processor.ODataResponse;
+import org.apache.olingo.odata2.api.uri.info.GetEntitySetUriInfo;
+import org.apache.olingo.odata2.core.ep.AtomEntityProvider;
+import org.apache.olingo.odata2.testutil.helper.StringHelper;
+import org.apache.olingo.odata2.testutil.mock.MockFacade;
+import org.junit.Before;
+import org.junit.Test;
+import org.robolectric.annotation.Config;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+
+import static org.custommonkey.xmlunit.XMLAssert.*;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ *  
+ */
+@Config(manifest=Config.NONE)
+public class AtomFeedProducerTest extends AndroidTestBase {
+
+  private GetEntitySetUriInfo view;
+
+  @Before
+  public void before() throws Exception {
+    initializeRoomData(1);
+
+    view = mock(GetEntitySetUriInfo.class);
+
+    EdmEntitySet set = MockFacade.getMockEdm().getDefaultEntityContainer().getEntitySet("Rooms");
+    when(view.getTargetEntitySet()).thenReturn(set);
+  }
+
+  @Test
+  public void testWithIncludeSimplePropertyTypes() throws Exception {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    EntityProviderWriteProperties properties =
+        EntityProviderWriteProperties.serviceRoot(BASE_URI).includeSimplePropertyType(true).build();
+    ODataResponse response = ser.writeFeed(view.getTargetEntitySet(), roomsData, properties);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:feed", xmlString);
+    assertXpathExists("/a:feed/a:entry/a:content/m:properties", xmlString);
+    assertXpathExists("/a:feed/a:entry/a:content/m:properties/d:Id[@m:type=\"Edm.String\"]", xmlString);
+    assertXpathExists("/a:feed/a:entry/a:content/m:properties/d:Name[@m:type=\"Edm.String\"]", xmlString);
+    assertXpathExists("/a:feed/a:entry/a:content/m:properties/d:Seats[@m:type=\"Edm.Int16\"]", xmlString);
+    assertXpathExists("/a:feed/a:entry/a:content/m:properties/d:Version[@m:type=\"Edm.Int16\"]", xmlString);
+  }
+
+  @Test
+  public void testFeedNamespaces() throws Exception {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    EntityProviderWriteProperties properties =
+        EntityProviderWriteProperties.serviceRoot(BASE_URI).mediaResourceMimeType("mediatype").build();
+    ODataResponse response = ser.writeFeed(view.getTargetEntitySet(), roomsData, properties);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:feed", xmlString);
+    assertXpathEvaluatesTo(BASE_URI.toASCIIString(), "/a:feed/@xml:base", xmlString);
+  }
+
+  @Test
+  public void testSelfLink() throws Exception {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    EntityProviderWriteProperties properties =
+        EntityProviderWriteProperties.serviceRoot(BASE_URI).mediaResourceMimeType("mediatype").build();
+    ODataResponse response = ser.writeFeed(view.getTargetEntitySet(), roomsData, properties);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:feed/a:link[@rel='self']", xmlString);
+    assertXpathEvaluatesTo("Rooms", "/a:feed/a:link[@rel='self']/@href", xmlString);
+    assertXpathEvaluatesTo("Rooms", "/a:feed/a:link[@rel='self']/@title", xmlString);
+  }
+
+  @Test
+  public void testCustomSelfLink() throws Exception {
+    String customLink = "Test";
+    AtomEntityProvider ser = createAtomEntityProvider();
+    EntityProviderWriteProperties properties =
+        EntityProviderWriteProperties.serviceRoot(BASE_URI).mediaResourceMimeType("mediatype").selfLink(
+            new URI(customLink)).build();
+    ODataResponse response = ser.writeFeed(view.getTargetEntitySet(), roomsData, properties);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:feed/a:link[@rel='self']", xmlString);
+    assertXpathEvaluatesTo(customLink, "/a:feed/a:link[@rel='self']/@href", xmlString);
+    assertXpathEvaluatesTo("Rooms", "/a:feed/a:link[@rel='self']/@title", xmlString);
+  }
+
+  @Test
+  public void testFeedMandatoryParts() throws Exception {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    EntityProviderWriteProperties properties =
+        EntityProviderWriteProperties.serviceRoot(BASE_URI).mediaResourceMimeType("mediatype").build();
+    ODataResponse response = ser.writeFeed(view.getTargetEntitySet(), roomsData, properties);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:feed/a:id", xmlString);
+    assertXpathEvaluatesTo(BASE_URI.toASCIIString() + "Rooms", "/a:feed/a:id/text()", xmlString);
+
+    assertXpathExists("/a:feed/a:title", xmlString);
+    assertXpathEvaluatesTo("Rooms", "/a:feed/a:title/text()", xmlString);
+
+    assertXpathExists("/a:feed/a:updated", xmlString);
+    assertXpathExists("/a:feed/a:author", xmlString);
+    assertXpathExists("/a:feed/a:author/a:name", xmlString);
+  }
+
+  private String verifyResponse(final ODataResponse response) throws IOException {
+    assertNotNull(response);
+    assertNotNull(response.getEntity());
+    assertNull("EntityProvider should not set content header", response.getContentHeader());
+    String xmlString = StringHelper.inputStreamToString((InputStream) response.getEntity());
+    return xmlString;
+  }
+
+  @Test
+  public void testInlineCountAllpages() throws Exception {
+    initializeRoomData(20);
+
+    AtomEntityProvider ser = createAtomEntityProvider();
+    EntityProviderWriteProperties properties = EntityProviderWriteProperties.serviceRoot(BASE_URI)
+        .mediaResourceMimeType("mediatype")
+        .inlineCount(Integer.valueOf(103))
+        .inlineCountType(InlineCount.ALLPAGES)
+        .build();
+    ODataResponse response = ser.writeFeed(view.getTargetEntitySet(), roomsData, properties);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:feed/m:count", xmlString);
+    assertXpathEvaluatesTo("103", "/a:feed/m:count/text()", xmlString);
+  }
+
+  @Test
+  public void testInlineCountNone() throws Exception {
+    when(view.getInlineCount()).thenReturn(InlineCount.NONE);
+
+    AtomEntityProvider ser = createAtomEntityProvider();
+    EntityProviderWriteProperties properties =
+        EntityProviderWriteProperties.serviceRoot(BASE_URI).mediaResourceMimeType("mediatype").build();
+    ODataResponse response = ser.writeFeed(view.getTargetEntitySet(), roomsData, properties);
+    String xmlString = verifyResponse(response);
+
+    assertXpathNotExists("/a:feed/m:count", xmlString);
+  }
+
+  @Test
+  public void testNextLink() throws Exception {
+    when(view.getInlineCount()).thenReturn(InlineCount.NONE);
+
+    AtomEntityProvider ser = createAtomEntityProvider();
+    EntityProviderWriteProperties properties = EntityProviderWriteProperties.serviceRoot(BASE_URI)
+        .mediaResourceMimeType("mediatype")
+        .nextLink("http://thisisanextlink")
+        .build();
+    ODataResponse response = ser.writeFeed(view.getTargetEntitySet(), roomsData, properties);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:feed/a:link[@rel='next']", xmlString);
+    assertXpathEvaluatesTo("http://thisisanextlink", "/a:feed/a:link[@rel='next']/@href", xmlString);
+  }
+
+  @Test(expected = EntityProviderException.class)
+  public void testInlineCountInvalid() throws Exception {
+    AtomEntityProvider ser = createAtomEntityProvider();
+    EntityProviderWriteProperties properties =
+        EntityProviderWriteProperties.serviceRoot(BASE_URI).mediaResourceMimeType("mediatype").inlineCountType(
+            InlineCount.ALLPAGES).build();
+    ser.writeFeed(view.getTargetEntitySet(), roomsData, properties);
+  }
+
+  @Test
+  public void testEntries() throws Exception {
+    initializeRoomData(103);
+
+    AtomEntityProvider ser = createAtomEntityProvider();
+    EntityProviderWriteProperties properties =
+        EntityProviderWriteProperties.serviceRoot(BASE_URI).mediaResourceMimeType("mediatype").build();
+    ODataResponse response = ser.writeFeed(view.getTargetEntitySet(), roomsData, properties);
+    String xmlString = verifyResponse(response);
+
+    assertXpathExists("/a:feed/a:entry[1]", xmlString);
+    assertXpathExists("/a:feed/a:entry[2]", xmlString);
+    assertXpathExists("/a:feed/a:entry[103]", xmlString);
+  }
+
+}