http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/503b4417/tests/code/ReflectionDataContext.cs
----------------------------------------------------------------------
diff --git a/tests/code/ReflectionDataContext.cs 
b/tests/code/ReflectionDataContext.cs
new file mode 100644
index 0000000..1635d54
--- /dev/null
+++ b/tests/code/ReflectionDataContext.cs
@@ -0,0 +1,743 @@
+/*
+ * 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.
+ */
+
+
+namespace DataJS.Tests
+{
+    using System;
+    using System.Collections;
+    using System.Collections.Generic;
+    using System.Collections.ObjectModel;
+    using Microsoft.OData.Service;
+    using System.Globalization;
+    using System.Linq;
+    using System.Reflection;
+
+    /// <summary>
+    /// Provides a reflection-based, updatable data context.
+    /// </summary>
+    public abstract class ReflectionDataContext
+    {
+        // Fields
+        private List<object> deletedObjects = new List<object>();
+        private List<Action> pendingChanges;
+        private static Dictionary<Type, Dictionary<string, IList>> 
resourceSetsByContextTypeStorage = new Dictionary<Type, Dictionary<string, 
IList>>();
+
+        // Methods
+        protected ReflectionDataContext()
+        {
+            this.MetadataHelper = new ReflectionMetadataHelper(this);
+            this.pendingChanges = new List<Action>();
+            if (!resourceSetsByContextTypeStorage.ContainsKey(base.GetType()))
+            {
+                resourceSetsByContextTypeStorage.Add(base.GetType(), new 
Dictionary<string, IList>());
+                foreach (string resourceSetName in 
this.MetadataHelper.GetResourceSetNames())
+                {
+                    Type resourceType = 
this.MetadataHelper.GetResourceTypeOfSet(resourceSetName);
+                    IList listOfTInstance = 
Activator.CreateInstance(typeof(List<>).MakeGenericType(new Type[] { 
resourceType })) as IList;
+                    this.ResourceSetsStorage.Add(resourceSetName, 
listOfTInstance);
+                }
+            }
+            this.EnsureDataIsInitialized();
+        }
+
+        public virtual void AddReferenceToCollection(object targetResource, 
string propertyName, object resourceToBeAdded)
+        {
+            ExceptionUtilities.CheckArgumentNotNull(targetResource, 
"targetResource");
+            ExceptionUtilities.CheckArgumentNotNull(propertyName, 
"propertyName");
+            ExceptionUtilities.CheckArgumentNotNull(resourceToBeAdded, 
"resourceToBeAdded");
+            UpdatableToken targetToken = 
UpdatableToken.AssertIsToken(targetResource, "targetResource");
+            targetResource = targetToken.Resource;
+            resourceToBeAdded = 
UpdatableToken.AssertIsTokenAndResolve(resourceToBeAdded, "resourceToBeAdded");
+            IList list = this.GetValue(targetToken, propertyName) as IList;
+            ExceptionUtilities.CheckObjectNotNull(list, "Property '{0}' on 
type '{1}' was not a list", new object[] { propertyName, 
targetResource.GetType().Name });
+            this.pendingChanges.Add(delegate {
+                list.Add(resourceToBeAdded);
+            });
+        }
+
+        public virtual void ClearChanges()
+        {
+            this.pendingChanges.Clear();
+        }
+
+        public void ClearData()
+        {
+            this.ResourceSetsStorage.Clear();
+        }
+
+        private static bool CompareETagValues(Dictionary<string, object> 
resourceCookieValues, IEnumerable<KeyValuePair<string, object>> 
concurrencyValues)
+        {
+            if (concurrencyValues.Count<KeyValuePair<string, object>>() != 
resourceCookieValues.Count)
+            {
+                return false;
+            }
+            foreach (KeyValuePair<string, object> keyValuePair in 
concurrencyValues)
+            {
+                if (!resourceCookieValues.ContainsKey(keyValuePair.Key))
+                {
+                    return false;
+                }
+                if (keyValuePair.Value == null)
+                {
+                    return (resourceCookieValues[keyValuePair.Key] == null);
+                }
+                if 
(!keyValuePair.Value.Equals(resourceCookieValues[keyValuePair.Key]))
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public virtual object CreateResource(string containerName, string 
fullTypeName)
+        {
+            ExceptionUtilities.CheckArgumentNotNull(fullTypeName, 
"fullTypeName");
+            UpdatableToken token = this.InstantiateResourceType(fullTypeName);
+            if (containerName != null)
+            {
+                this.pendingChanges.Add(delegate {
+                    
this.GetResourceSetEntities(containerName).Add(token.Resource);
+                });
+            }
+            return token;
+        }
+
+        private void DeleteAllReferences(object targetResource)
+        {
+            foreach (string currentSetName in 
this.MetadataHelper.GetResourceSetNames())
+            {
+                Type currentEntityType = 
this.MetadataHelper.GetResourceTypeOfSet(currentSetName);
+                IList entitySetList = 
this.GetResourceSetEntities(currentSetName);
+                foreach (NavigationPropertyInfo navigationProperty in 
this.MetadataHelper.GetNavigationProperties(GetResourceTypeFullName(currentEntityType)))
+                {
+                    if (navigationProperty.CollectionElementType != null)
+                    {
+                        foreach (object currentEntityInstance in entitySetList)
+                        {
+                            
this.RemoveResourceFromCollectionOnTargetResourceMatch(targetResource, 
navigationProperty, currentEntityInstance);
+                        }
+                    }
+                    else
+                    {
+                        
ExceptionUtilities.CheckObjectNotNull(navigationProperty.PropertyInfo, "Invalid 
navigation property info", new object[0]);
+                        foreach (object currentEntityInstance in entitySetList)
+                        {
+                            
this.SetEntityReferenceToNullOnTargetResourceMatch(targetResource, 
navigationProperty, currentEntityInstance);
+                        }
+                    }
+                }
+            }
+        }
+
+        public virtual void DeleteResource(object targetResource)
+        {
+            ExceptionUtilities.CheckArgumentNotNull(targetResource, 
"targetResource");
+            targetResource = 
UpdatableToken.AssertIsTokenAndResolve(targetResource, "targetResource");
+            string resourceSetName = 
this.GetResourceSetOfTargetResource(targetResource);
+            ExceptionUtilities.CheckObjectNotNull(resourceSetName, "Unable to 
find set of the resource to delete", new object[0]);
+            this.deletedObjects.Add(targetResource);
+            IList resourceSetList = 
this.GetResourceSetEntities(resourceSetName);
+            this.DeleteAllReferences(targetResource);
+            this.pendingChanges.Add(delegate {
+                resourceSetList.Remove(targetResource);
+            });
+        }
+
+        protected abstract void EnsureDataIsInitialized();
+
+        protected virtual Type GetCollectionPropertyType(string fullTypeName, 
string propertyName)
+        {
+            Type type = 
this.MetadataHelper.FindClrTypeByFullName(fullTypeName);
+            Type collectionType = null;
+            if (type != null)
+            {
+                PropertyInfo property = type.GetProperty(propertyName);
+                if (property != null)
+                {
+                    collectionType = property.PropertyType;
+                }
+            }
+            return collectionType;
+        }
+
+        private Dictionary<string, object> GetConcurrencyValues(object 
targetResource)
+        {
+            Dictionary<string, object> etagValues = new Dictionary<string, 
object>();
+            foreach (string etagProperty in 
this.MetadataHelper.GetETagPropertiesOfType(GetResourceTypeFullName(targetResource.GetType())))
+            {
+                etagValues.Add(etagProperty, 
targetResource.GetType().GetProperty(etagProperty).GetValue(targetResource, 
null));
+            }
+            return etagValues;
+        }
+
+        public virtual object GetResource(IQueryable query, string 
fullTypeName)
+        {
+            ExceptionUtilities.CheckArgumentNotNull(query, "query");
+            object resource = null;
+            foreach (object r in query)
+            {
+                ExceptionUtilities.Assert(resource == null, "Invalid Uri 
specified. The query '{0}' must refer to a single resource", new object[] { 
query.ToString() });
+                resource = r;
+            }
+            if (resource == null)
+            {
+                return null;
+            }
+            if (fullTypeName != null)
+            {
+                this.ValidateResourceType(resource, fullTypeName);
+            }
+            return new UpdatableToken(resource);
+        }
+
+        public IList<T> GetResourceSetEntities<T>(string resourceSetName)
+        {
+            return (IList<T>) this.GetResourceSetEntities(resourceSetName);
+        }
+
+        internal IList GetResourceSetEntities(string resourceSetName)
+        {
+            IList entities;
+            if (!this.ResourceSetsStorage.TryGetValue(resourceSetName, out 
entities))
+            {
+                Type elementType = 
this.MetadataHelper.GetResourceTypeOfSet(resourceSetName);
+                entities = (IList) 
Activator.CreateInstance(typeof(List<>).MakeGenericType(new Type[] { 
elementType }));
+                this.ResourceSetsStorage[resourceSetName] = entities;
+            }
+            return entities;
+        }
+
+        private string GetResourceSetOfTargetResource(object targetResource)
+        {
+            foreach (string currentResourceSetName in 
this.MetadataHelper.GetResourceSetNames())
+            {
+                if 
(this.GetResourceSetEntities(currentResourceSetName).Contains(targetResource))
+                {
+                    return currentResourceSetName;
+                }
+            }
+            return null;
+        }
+
+        public static string GetResourceTypeFullName(Type type)
+        {
+            return type.FullName.Replace('+', '_');
+        }
+
+        public virtual object GetValue(object targetResource, string 
propertyName)
+        {
+            ExceptionUtilities.CheckArgumentNotNull(targetResource, 
"targetResource");
+            ExceptionUtilities.CheckArgumentNotNull(propertyName, 
"propertyName");
+            UpdatableToken token = 
UpdatableToken.AssertIsToken(targetResource, "targetResource");
+            if (token.PendingPropertyUpdates.ContainsKey(propertyName))
+            {
+                return token.PendingPropertyUpdates[propertyName];
+            }
+            targetResource = token.Resource;
+            PropertyInfo pi = 
targetResource.GetType().GetProperty(propertyName);
+            ExceptionUtilities.CheckObjectNotNull(pi, "Cannot find the 
property '{0}' on type '{1}'", new object[] { propertyName, 
targetResource.GetType().Name });
+            object value = pi.GetValue(targetResource, null);
+            if ((value != null) && (pi.PropertyType.Assembly == 
base.GetType().Assembly))
+            {
+                
ExceptionUtilities.Assert(!this.MetadataHelper.IsTypeAnEntityType(pi.PropertyType),
 "GetValue should never be called for reference properties. Type was '{0}', 
property was '{1}'", new object[] { pi.PropertyType.FullName, propertyName });
+                value = new UpdatableToken(value);
+            }
+            return value;
+        }
+
+        private UpdatableToken InstantiateResourceType(string fullTypeName)
+        {
+            Type t = this.MetadataHelper.FindClrTypeByFullName(fullTypeName);
+            object instance = Activator.CreateInstance(t);
+            UpdatableToken token = new UpdatableToken(instance);
+            foreach (PropertyInfo p in t.GetProperties())
+            {
+                object generatedValue;
+                PropertyInfo property = p;
+                if (this.IsCollectionProperty(property))
+                {
+                    Type collectionType = 
this.GetCollectionPropertyType(GetResourceTypeFullName(t), property.Name);
+                    if (collectionType != null)
+                    {
+                        object newCollection = 
Activator.CreateInstance(collectionType);
+                        token.PendingPropertyUpdates[property.Name] = 
newCollection;
+                        this.pendingChanges.Add(delegate {
+                            property.SetValue(instance, newCollection, null);
+                        });
+                    }
+                }
+                if (this.TryGetStoreGeneratedValue(fullTypeName, 
property.Name, out generatedValue))
+                {
+                    token.PendingPropertyUpdates[property.Name] = 
generatedValue;
+                    this.pendingChanges.Add(delegate {
+                        property.SetValue(instance, generatedValue, null);
+                    });
+                }
+            }
+            return token;
+        }
+
+        protected virtual bool IsCollectionProperty(PropertyInfo propertyInfo)
+        {
+            return 
((typeof(IEnumerable).IsAssignableFrom(propertyInfo.PropertyType) && 
(propertyInfo.PropertyType != typeof(string))) && (propertyInfo.PropertyType != 
typeof(byte[])));
+        }
+
+        public virtual void RemoveReferenceFromCollection(object 
targetResource, string propertyName, object resourceToBeRemoved)
+        {
+            ExceptionUtilities.CheckArgumentNotNull(targetResource, 
"targetResource");
+            ExceptionUtilities.CheckArgumentNotNull(propertyName, 
"propertyName");
+            ExceptionUtilities.CheckArgumentNotNull(resourceToBeRemoved, 
"resourceToBeRemoved");
+            UpdatableToken.AssertIsToken(targetResource, "targetResource");
+            resourceToBeRemoved = 
UpdatableToken.AssertIsTokenAndResolve(resourceToBeRemoved, 
"resourceToBeRemoved");
+            IList list = this.GetValue(targetResource, propertyName) as IList;
+            ExceptionUtilities.CheckObjectNotNull(list, "Property '{0}' on 
type '{1}' was not a list", new object[] { propertyName, 
targetResource.GetType().Name });
+            this.pendingChanges.Add(delegate {
+                list.Remove(resourceToBeRemoved);
+            });
+        }
+
+        private void RemoveResourceFromCollectionOnTargetResourceMatch(object 
targetResource, NavigationPropertyInfo navigationPropertyInfo, object 
currentEntityInstance)
+        {
+            IEnumerable childCollectionObject = 
navigationPropertyInfo.PropertyInfo.GetValue(currentEntityInstance, null) as 
IEnumerable;
+            if (childCollectionObject.Cast<object>().Any<object>(delegate 
(object o) {
+                return o == targetResource;
+            }))
+            {
+                MethodInfo removeMethod = 
navigationPropertyInfo.PropertyInfo.PropertyType.GetMethod("Remove");
+                this.pendingChanges.Add(delegate {
+                    removeMethod.Invoke(childCollectionObject, new object[] { 
targetResource });
+                });
+            }
+        }
+
+        public virtual object ResetResource(object resource)
+        {
+            ExceptionUtilities.CheckArgumentNotNull(resource, "resource");
+            UpdatableToken token = UpdatableToken.AssertIsToken(resource, 
"resource");
+            resource = token.Resource;
+            token = new UpdatableToken(resource);
+            object newInstance = Activator.CreateInstance(resource.GetType());
+            ExceptionUtilities.CheckObjectNotNull(newInstance, "Cannot reset 
resource because unable to creating new instance of type '{0}' returns null", 
new object[] { resource.GetType().Name });
+            foreach (string propertyToReset in 
this.MetadataHelper.GetPropertiesToReset(GetResourceTypeFullName(resource.GetType())))
+            {
+                PropertyInfo pi = 
newInstance.GetType().GetProperty(propertyToReset);
+                ExceptionUtilities.CheckObjectNotNull(pi, "Cannot reset 
resource because unable to find property '{0}'", new object[] { propertyToReset 
});
+                object newValue = pi.GetValue(newInstance, null);
+                this.pendingChanges.Add(delegate {
+                    pi.SetValue(resource, newValue, null);
+                });
+                token.PendingPropertyUpdates[propertyToReset] = newValue;
+            }
+            return token;
+        }
+
+        public virtual object ResolveResource(object resource)
+        {
+            ExceptionUtilities.CheckArgumentNotNull(resource, "resource");
+            return UpdatableToken.AssertIsTokenAndResolve(resource, 
"resource");
+        }
+
+        public virtual void SaveChanges()
+        {
+            foreach (Action pendingChange in this.pendingChanges)
+            {
+                pendingChange();
+            }
+            this.pendingChanges.Clear();
+            foreach (object deleted in this.deletedObjects)
+            {
+                foreach (object entity in 
this.ResourceSetsStorage.SelectMany<KeyValuePair<string, IList>, 
object>(delegate (KeyValuePair<string, IList> p) {
+                    return p.Value.Cast<object>();
+                }))
+                {
+                    ExceptionUtilities.Assert(!object.ReferenceEquals(deleted, 
entity), "Found deleted entity!", new object[0]);
+                    foreach (PropertyInfo propertyInfo in 
entity.GetType().GetProperties())
+                    {
+                        object value = propertyInfo.GetValue(entity, null);
+                        
ExceptionUtilities.Assert(!object.ReferenceEquals(deleted, value), "Found 
deleted entity!", new object[0]);
+                        IEnumerable enumerable = value as IEnumerable;
+                        if (enumerable != null)
+                        {
+                            foreach (object valueElement in 
enumerable.Cast<object>())
+                            {
+                                
ExceptionUtilities.Assert(!object.ReferenceEquals(deleted, valueElement), 
"Found deleted entity!", new object[0]);
+                            }
+                        }
+                    }
+                }
+            }
+            this.deletedObjects.Clear();
+        }
+
+        protected virtual void SetCollectionPropertyValue(object 
targetResource, PropertyInfo propertyInfo, IEnumerable propertyValue)
+        {
+            object collection;
+            ExceptionUtilities.CheckArgumentNotNull(targetResource, 
"targetResource");
+            ExceptionUtilities.CheckArgumentNotNull(propertyInfo, 
"propertyInfo");
+            ExceptionUtilities.CheckArgumentNotNull(propertyValue, 
"propertyValue");
+            Type collectionType = 
this.GetCollectionPropertyType(GetResourceTypeFullName(propertyInfo.ReflectedType),
 propertyInfo.Name);
+            ExceptionUtilities.CheckObjectNotNull(collectionType, "Could not 
infer collection type for property", new object[0]);
+            propertyValue = propertyValue.Cast<object>().Select<object, 
object>(delegate (object o) {
+                return UpdatableToken.ResolveIfToken(o);
+            });
+            ConstructorInfo enumerableConstructor = 
collectionType.GetConstructor(new Type[] { typeof(IEnumerable) });
+            if (enumerableConstructor != null)
+            {
+                collection = enumerableConstructor.Invoke(new object[] { 
propertyValue });
+            }
+            else if (collectionType.IsGenericType && 
(collectionType.GetGenericArguments().Count<Type>() == 1))
+            {
+                Type typeArgument = 
collectionType.GetGenericArguments().Single<Type>();
+                ConstructorInfo typedEnumerableConstructor = 
collectionType.GetConstructor(new Type[] { 
typeof(IEnumerable<>).MakeGenericType(new Type[] { typeArgument }) });
+                if (typedEnumerableConstructor != null)
+                {
+                    object typedEnumerable = 
typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(new Type[] { 
typeArgument }).Invoke(null, new object[] { propertyValue });
+                    collection = typedEnumerableConstructor.Invoke(new 
object[] { typedEnumerable });
+                }
+                else
+                {
+                    MethodInfo typedAddMethod = 
collectionType.GetMethod("Add", new Type[] { typeArgument });
+                    ExceptionUtilities.CheckObjectNotNull(typedAddMethod, 
"Could not find constructor or add method for type: " + 
collectionType.FullName, new object[0]);
+                    collection = Activator.CreateInstance(collectionType);
+                    foreach (object element in propertyValue)
+                    {
+                        typedAddMethod.Invoke(collection, new object[] { 
element });
+                    }
+                }
+            }
+            else
+            {
+                MethodInfo addMethod = collectionType.GetMethod("Add");
+                ExceptionUtilities.CheckObjectNotNull(addMethod, "Could not 
find constructor or add method for type: " + collectionType.FullName, new 
object[0]);
+                collection = Activator.CreateInstance(collectionType);
+                foreach (object element in propertyValue)
+                {
+                    addMethod.Invoke(collection, new object[] { element });
+                }
+            }
+            propertyInfo.SetValue(targetResource, collection, null);
+        }
+
+        public virtual void SetConcurrencyValues(object resourceCookie, bool? 
checkForEquality, IEnumerable<KeyValuePair<string, object>> concurrencyValues)
+        {
+            ExceptionUtilities.CheckArgumentNotNull(resourceCookie, 
"resourceCookie");
+            
ExceptionUtilities.ThrowDataServiceExceptionIfFalse(checkForEquality.HasValue, 
0x1a1, "Missing concurrency token for update operation", new object[0]);
+            ExceptionUtilities.Assert(checkForEquality.Value, "Should not be 
called with check for equality parameter equal to false", new object[0]);
+            ExceptionUtilities.CheckArgumentNotNull(concurrencyValues, 
"concurrencyValues");
+            if (concurrencyValues.Any<KeyValuePair<string, object>>())
+            {
+                resourceCookie = 
UpdatableToken.AssertIsTokenAndResolve(resourceCookie, "resourceCookie");
+                
ExceptionUtilities.ThrowDataServiceExceptionIfFalse(CompareETagValues(this.GetConcurrencyValues(resourceCookie),
 concurrencyValues), 0x19c, "Concurrency tokens do not match", new object[0]);
+            }
+        }
+
+        private void SetEntityReferenceToNullOnTargetResourceMatch(object 
targetResource, NavigationPropertyInfo navigationPropertyInfo, object 
currentEntityInstance)
+        {
+            if 
(navigationPropertyInfo.PropertyInfo.GetValue(currentEntityInstance, null) == 
targetResource)
+            {
+                this.pendingChanges.Add(delegate {
+                    
navigationPropertyInfo.PropertyInfo.SetValue(currentEntityInstance, null, null);
+                });
+            }
+        }
+
+        public virtual void SetReference(object targetResource, string 
propertyName, object propertyValue)
+        {
+            ExceptionUtilities.CheckArgumentNotNull(targetResource, 
"targetResource");
+            ExceptionUtilities.CheckArgumentNotNull(propertyName, 
"propertyName");
+            if (propertyValue != null)
+            {
+                UpdatableToken.AssertIsToken(propertyValue, "propertyValue");
+            }
+            this.SetValue(targetResource, propertyName, propertyValue);
+        }
+
+        public virtual void SetValue(object targetResource, string 
propertyName, object propertyValue)
+        {
+            ExceptionUtilities.CheckArgumentNotNull(targetResource, 
"targetResource");
+            ExceptionUtilities.CheckArgumentNotNull(propertyName, 
"propertyName");
+            UpdatableToken token = 
UpdatableToken.AssertIsToken(targetResource, "targetResource");
+            targetResource = token.Resource;
+            token.PendingPropertyUpdates[propertyName] = propertyValue;
+            this.pendingChanges.Add(delegate {
+                object generatedValue;
+                Type t = targetResource.GetType();
+                PropertyInfo pi = t.GetProperty(propertyName);
+                ExceptionUtilities.CheckObjectNotNull(pi, "Unable to find 
property '{0}' on type '{1}'", new object[] { propertyName, 
targetResource.GetType().Name });
+                if (this.TryGetStoreGeneratedValue(GetResourceTypeFullName(t), 
propertyName, out generatedValue))
+                {
+                    propertyValue = generatedValue;
+                }
+                if (this.IsCollectionProperty(pi))
+                {
+                    ExceptionUtilities.CheckObjectNotNull(propertyValue, 
"Collection property value was null", new object[0]);
+                    IEnumerable enumerable = propertyValue as IEnumerable;
+                    ExceptionUtilities.CheckObjectNotNull(enumerable, 
"Collection property value was not an enumerable", new object[0]);
+                    this.SetCollectionPropertyValue(targetResource, pi, 
enumerable);
+                }
+                else
+                {
+                    propertyValue = 
UpdatableToken.ResolveIfToken(propertyValue);
+                    pi.SetValue(targetResource, propertyValue, null);
+                }
+            });
+        }
+
+        protected virtual bool TryGetStoreGeneratedValue(string fullTypeName, 
string propertyName, out object propertyValue)
+        {
+            propertyValue = null;
+            return false;
+        }
+
+        private void ValidateResourceType(object targetResource, string 
fullTypeName)
+        {
+            
ExceptionUtilities.Assert(this.MetadataHelper.FindClrTypeByFullName(fullTypeName).IsAssignableFrom(targetResource.GetType()),
 "Invalid uri specified. expected type: '{0}', actual type: '{1}'", new 
object[] { fullTypeName, targetResource.GetType().FullName });
+        }
+
+        // Properties
+        internal ReflectionMetadataHelper MetadataHelper { get; set; }
+
+        internal Dictionary<string, IList> ResourceSetsStorage
+        {
+            get
+            {
+                Dictionary<string, IList> resourceSetsLookup = null;
+                Type currentContextType = base.GetType();
+                
ExceptionUtilities.Assert(resourceSetsByContextTypeStorage.TryGetValue(currentContextType,
 out resourceSetsLookup), "Cannot find resource sets by the context type 
'{0}'", new object[] { currentContextType });
+                return resourceSetsLookup;
+            }
+        }
+
+        #region Inner types.
+    
+        internal class ReflectionMetadataHelper
+        {
+            // Fields
+            private ReflectionDataContext reflectionDataContext;
+
+            // Methods
+            public ReflectionMetadataHelper(ReflectionDataContext 
reflectionDataContext)
+            {
+                this.reflectionDataContext = reflectionDataContext;
+            }
+
+            public Type FindClrTypeByFullName(string resourceTypeFullName)
+            {
+                Type type = 
this.reflectionDataContext.GetType().Assembly.GetTypes().Where<Type>(delegate 
(Type t) {
+                    return (ReflectionDataContext.GetResourceTypeFullName(t) 
== resourceTypeFullName);
+                }).FirstOrDefault<Type>();
+                ExceptionUtilities.CheckObjectNotNull(type, "Unable to find 
type '{0}'", new object[] { resourceTypeFullName });
+                return type;
+            }
+
+            public string[] GetETagPropertiesOfType(string fullTypeName)
+            {
+                Type type = this.FindClrTypeByFullName(fullTypeName);
+                List<string> etags = new List<string>();
+                foreach (ETagAttribute customAttribute in 
type.GetCustomAttributes(typeof(ETagAttribute), true))
+                {
+                    etags.AddRange(customAttribute.PropertyNames);
+                }
+                
+                return etags.ToArray();
+            }
+
+            public string[] GetKeyProperties(string fullTypeName)
+            {
+                Type type = this.FindClrTypeByFullName(fullTypeName);
+                List<string> keyPropertyList = new List<string>();
+                foreach (PropertyInfo keyProperty in 
type.GetProperties().Where(pi => pi.Name.Contains("ID")))
+                {
+                    keyPropertyList.Add(keyProperty.Name);
+                }
+                
+                return keyPropertyList.ToArray();
+            }
+
+            public NavigationPropertyInfo[] GetNavigationProperties(string 
fullTypeName)
+            {
+                Type type = this.FindClrTypeByFullName(fullTypeName);
+                var navigationProperties = new List<NavigationPropertyInfo>();
+                var keyProperties = new 
List<string>(this.GetKeyProperties(fullTypeName));
+                foreach (PropertyInfo pi in type.GetProperties())
+                {
+                    if (!keyProperties.Contains(pi.Name))
+                    {
+                        if (this.IsTypeAnEntityType(pi.PropertyType))
+                        {
+                            navigationProperties.Add(new 
NavigationPropertyInfo(pi, null));
+                        }
+
+                        if (pi.PropertyType.IsGenericType && 
((pi.PropertyType.GetGenericTypeDefinition() == typeof(List<>)) || 
(pi.PropertyType.GetGenericTypeDefinition() == typeof(Collection<>))))
+                        {
+                            Type elementType = 
pi.PropertyType.GetGenericArguments()[0];
+                            if (this.IsTypeAnEntityType(elementType))
+                            {
+                                navigationProperties.Add(new 
NavigationPropertyInfo(pi, elementType));
+                            }
+                        }
+                    }
+                }
+
+                return navigationProperties.ToArray();
+            }
+
+            public string[] GetPropertiesToReset(string fullTypeName)
+            {
+                Type type = this.FindClrTypeByFullName(fullTypeName);
+                var keyProperties = new 
List<string>(this.GetKeyProperties(fullTypeName));
+                var navigationProperties = new 
List<string>(this.GetNavigationProperties(fullTypeName).Select(ni 
=>ni.PropertyInfo.Name));
+                return type.GetProperties().Where(
+                    pi => !keyProperties.Contains(pi.Name) && 
!navigationProperties.Contains(pi.Name)
+                ).Select(pi => pi.Name).ToArray();
+            }
+
+            public string[] GetResourceSetNames()
+            {
+                return 
this.reflectionDataContext.GetType().GetProperties().Where(
+                    pi => pi.PropertyType.IsGenericType && 
(pi.PropertyType.GetGenericTypeDefinition() == typeof(IQueryable<>))
+                ).Select(pi => pi.Name).ToArray();
+            }
+
+            public Type GetResourceTypeOfSet(string resourceSetName)
+            {
+                PropertyInfo resourceSetPropertyInfo = 
this.reflectionDataContext.GetType().GetProperties().Where(pi => pi.Name == 
resourceSetName).FirstOrDefault();
+                ExceptionUtilities.CheckObjectNotNull(resourceSetPropertyInfo, 
"Error finding type of set '{0}'", new object[] { resourceSetName });
+                return 
resourceSetPropertyInfo.PropertyType.GetGenericArguments()[0];
+            }
+
+            public bool IsTypeAnEntityType(Type t)
+            {
+                foreach (string setName in this.GetResourceSetNames())
+                {
+                    if (this.GetResourceTypeOfSet(setName).IsAssignableFrom(t))
+                    {
+                        return true;
+                    }
+                }
+
+                return false;
+            }
+        }
+
+        internal static class ExceptionUtilities
+        {
+            // Methods
+            public static void Assert(bool condition, string errorMessage, 
params object[] messageArguments)
+            {
+                if (!condition)
+                {
+                    throw new InvalidOperationException("Assertion failed: " + 
string.Format(CultureInfo.InvariantCulture, errorMessage, messageArguments));
+                }
+            }
+
+            public static void CheckArgumentNotNull(object argument, string 
argumentName)
+            {
+                if (argument == null)
+                {
+                    throw new ArgumentNullException(argumentName);
+                }
+            }
+
+            public static void 
CheckCollectionNotEmpty<TElement>(IEnumerable<TElement> argument, string 
argumentName)
+            {
+                CheckArgumentNotNull(argument, argumentName);
+                if (!argument.Any<TElement>())
+                {
+                    throw new 
ArgumentException(string.Format(CultureInfo.InvariantCulture, "Collection 
argument '{0}' must have at least one element.", new object[] { argumentName 
}));
+                }
+            }
+
+            public static void CheckObjectNotNull(object value, string 
exceptionMessageFormatText, params object[] messageArguments)
+            {
+                Assert(exceptionMessageFormatText != null, "message cannnot be 
null", new object[0]);
+                Assert(messageArguments != null, "messageArguments cannnot be 
null", new object[0]);
+                if (value == null)
+                {
+                    throw new 
InvalidOperationException(string.Format(CultureInfo.InvariantCulture, 
exceptionMessageFormatText, messageArguments));
+                }
+            }
+
+            public static void ThrowDataServiceExceptionIfFalse(bool 
condition, int statusCode, string errorMessage, params object[] 
messageArguments)
+            {
+                if (!condition)
+                {
+                    throw new DataServiceException(statusCode, 
string.Format(CultureInfo.InvariantCulture, errorMessage, messageArguments));
+                }
+            }
+        }
+
+        public class UpdatableToken
+        {
+            // Methods
+            public UpdatableToken(object resource)
+            {
+                ExceptionUtilities.CheckArgumentNotNull(resource, "resource");
+                this.Resource = resource;
+                this.PendingPropertyUpdates = new Dictionary<string, object>();
+            }
+
+            public static UpdatableToken AssertIsToken(object resource, string 
name)
+            {
+                ExceptionUtilities.CheckArgumentNotNull(resource, "resource");
+                UpdatableToken token = resource as UpdatableToken;
+                ExceptionUtilities.CheckObjectNotNull(token, "{0} was not a 
token. Type was: '{1}'", new object[] { name, resource.GetType() });
+                return token;
+            }
+
+            public static object AssertIsTokenAndResolve(object resource, 
string name)
+            {
+                return AssertIsToken(resource, name).Resource;
+            }
+
+            public static object ResolveIfToken(object resource)
+            {
+                UpdatableToken token = resource as UpdatableToken;
+                if (token != null)
+                {
+                    resource = token.Resource;
+                }
+                return resource;
+            }
+
+            // Properties
+            public IDictionary<string, object> PendingPropertyUpdates { get; 
set; }
+
+            public object Resource { get; set; }
+        }
+
+        internal class NavigationPropertyInfo
+        {
+            // Methods
+            internal NavigationPropertyInfo(PropertyInfo pi, Type 
collectionElementType)
+            {
+                this.PropertyInfo = pi;
+                this.CollectionElementType = collectionElementType;
+            }
+
+            // Properties
+            public Type CollectionElementType { get; set; }
+
+            public PropertyInfo PropertyInfo { get; set; }
+        }
+
+        #endregion Inner types.
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/503b4417/tests/code/csdlreader.cs
----------------------------------------------------------------------
diff --git a/tests/code/csdlreader.cs b/tests/code/csdlreader.cs
new file mode 100644
index 0000000..c88333e
--- /dev/null
+++ b/tests/code/csdlreader.cs
@@ -0,0 +1,205 @@
+/*
+ * 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.
+ */
+
+/// <summary>
+/// Class used to parse csdl to create the metatdata object
+/// </summary>
+/// 
+
+namespace DataJS.Tests
+{
+    using System;
+    using System.Collections.Generic;
+    using System.IO;
+    using System.Linq;
+    using System.Xml.Linq;
+
+
+    public static class CsdlReader
+    {
+        static readonly string knownNamespace = "http://docs.oasis-open.org";;
+        static readonly string[] repeatingElements = 
+            {
+                "Action",
+                "ActionImport",
+                "Annotation",
+                "Annotations",
+                "Apply",
+                "Binary",
+                "Bool",
+                "Cast",
+                "Collection",
+                "ComplexType",
+                "Date",
+                "DateTimeOffset",
+                "Decimal",
+                "Duration",
+                "EntitySet",
+                "EntityType",
+                "EnumMember",
+                "EnumType",
+                "Float",
+                "Function",
+                "FunctionImport",
+                "Guid",
+                "If",
+                "Int",
+                "IsOf",
+                "Key",
+                "LabeledElement",
+                "LabeledElementReference",
+                "Member",
+                "NavigationProperty",
+                "NavigationPropertyBinding",
+                "NavigationPropertyPath",
+                "Null",
+                "OnDelete",
+                "Path",
+                "Parameter",
+                "Property",
+                "PropertyPath",
+                "PropertyRef",
+                "PropertyValue",
+                "Record",
+                "ReferentialConstraint",
+                "String",
+                "Schema",
+                "Singleton",
+                "Term",
+                "TimeOfDay",
+                "TypeDefinition",
+                "UrlRef",
+                "Reference",
+                "Include",
+                "IncludeAnnotations"
+            };
+
+        public static Dictionary<string, object> ReadCsdl(TextReader payload)
+        {
+            return BuildElementJsonObject(XElement.Load(payload));
+        }
+
+        /// <summary>
+        /// Build the attribute object 
+        /// </summary>
+        /// <param name="xmlAttributes">IEnumberable of XAttributes to build 
the attribute object</param>
+        /// <returns>The JsonObject containing the name-value pairs for an 
element's attributes</returns>
+        static Dictionary<string, object> 
BuildAttributeJsonObject(IEnumerable<XAttribute> xmlAttributes)
+        {
+            Dictionary<string, object> jsonObject = new Dictionary<string, 
object>();
+
+            foreach (XAttribute attribute in xmlAttributes)
+            {
+                if (!attribute.IsNamespaceDeclaration)
+                {
+                    string attributeNamespace = 
attribute.Name.Namespace.ToString();
+                    if (string.IsNullOrEmpty(attributeNamespace) ||
+                        attributeNamespace.StartsWith(knownNamespace, 
StringComparison.InvariantCultureIgnoreCase))
+                    {
+                        
jsonObject[MakeFirstLetterLowercase(attribute.Name.LocalName)] = 
attribute.Value;
+                    }
+                }
+            }
+
+            return jsonObject;
+        }
+
+        /// <summary>
+        /// Creates a JsonObject from an XML container element with each 
attribute or subelement as a property
+        /// </summary>
+        /// <param name="container">The XML container</param>
+        /// <param name="buildValue">Function that builds a value from a 
property element</param>
+        /// <returns>The JsonObject containing the name-value pairs</returns>
+        public static Dictionary<string, object> 
BuildElementJsonObject(XElement container)
+        {
+            if (container == null)
+            {
+                return null;
+            }
+
+            Dictionary<string, object> jsonObject = new Dictionary<string, 
object>();
+            string keyName = 
MakeFirstLetterLowercase(container.Name.LocalName);
+
+            if (container.HasAttributes || container.HasElements)
+            {
+                Dictionary<string, List<Dictionary<string, object>>> 
repeatingObjectArrays = new Dictionary<string, List<Dictionary<string, 
object>>>();
+
+                jsonObject = BuildAttributeJsonObject(container.Attributes());
+
+                foreach (XElement propertyElement in container.Elements())
+                {
+                    string propertyName = 
MakeFirstLetterLowercase(propertyElement.Name.LocalName);
+                    string properyNamespace = 
propertyElement.Name.Namespace.ToString();
+
+                    if (string.IsNullOrEmpty(properyNamespace) || 
properyNamespace.StartsWith(knownNamespace, 
StringComparison.InvariantCultureIgnoreCase))
+                    {
+                        // Check to see if the element is repeating and needs 
to be an array
+                        if 
(repeatingElements.Contains(propertyElement.Name.LocalName))
+                        {
+                            // See if property was already created as an 
array, if not then create it
+                            if 
(!repeatingObjectArrays.ContainsKey(propertyName))
+                            {
+                                repeatingObjectArrays.Add(propertyName, new 
List<Dictionary<string, object>>());
+                            }
+
+                            
repeatingObjectArrays[propertyName].Add(BuildElementJsonObject(propertyElement));
+                        }
+                        else
+                        {
+                            jsonObject[propertyName] = 
BuildElementJsonObject(propertyElement);
+                        }
+                    }
+                }
+
+                foreach (string key in repeatingObjectArrays.Keys)
+                {
+                    jsonObject[key] = repeatingObjectArrays[key].ToArray();
+                }
+            }
+            else
+            {
+                jsonObject[MakeFirstLetterLowercase(container.Name.LocalName)] 
= container.Value;
+            }
+
+            return jsonObject;
+        }
+
+        /// <summary>
+        /// Makes the first letter of a string lowercase
+        /// </summary>
+        /// <param name="name">The string to be modified</param>
+        /// <returns>Modified string</returns>
+        private static string MakeFirstLetterLowercase(string str)
+        {
+            if (!string.IsNullOrWhiteSpace(str))
+            {
+                if (str.Length > 1 && !(str[1].ToString() == 
str[1].ToString().ToUpper()))
+                {
+                    return str[0].ToString().ToLower() + str.Substring(1);
+                }
+                else
+                {
+                    return str;
+                }
+            }
+
+            return str;
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/503b4417/tests/code/jsdate.cs
----------------------------------------------------------------------
diff --git a/tests/code/jsdate.cs b/tests/code/jsdate.cs
new file mode 100644
index 0000000..4439a3a
--- /dev/null
+++ b/tests/code/jsdate.cs
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+/// <summary>
+/// The verifiers's representation of a Javascript date object as deserialized 
by the library
+/// </summary>
+
+namespace DataJS.Tests
+{
+    using System;
+    using System.Collections.Generic;
+    using System.IO;
+    using System.Linq;
+    using System.Net;
+    using System.Runtime.Serialization;
+    using System.ServiceModel;
+    using System.ServiceModel.Activation;
+    using System.ServiceModel.Syndication;
+    using System.ServiceModel.Web;
+    using System.Xml;
+    using System.Xml.Linq;
+    using Microsoft.Spatial;
+    using Microsoft.OData.Core;
+
+    [Serializable]
+    public class JsDate : JsonObject
+    {
+        private static readonly DateTime JsEpoch = new DateTime(1970, 1, 1, 0, 
0, 0, DateTimeKind.Utc);
+
+        public JsDate(DateTime dateTime)
+            : base()
+        {
+            this["milliseconds"] = 
dateTime.Subtract(JsEpoch).TotalMilliseconds;
+        }
+
+        public JsDate(DateTimeOffset dateTimeOffset)
+            : this(dateTimeOffset.UtcDateTime)
+        {
+            this["__edmType"] = "Edm.DateTimeOffset";
+            this["__offset"] = (dateTimeOffset.Offset < TimeSpan.Zero ? "-" : 
"+") + dateTimeOffset.Offset.ToString("hh':'mm");
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/503b4417/tests/code/jsonobject.cs
----------------------------------------------------------------------
diff --git a/tests/code/jsonobject.cs b/tests/code/jsonobject.cs
new file mode 100644
index 0000000..27f4b9b
--- /dev/null
+++ b/tests/code/jsonobject.cs
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+/// <summary>
+/// A weakly typed representation of a JSON object using a dictionary 
implementation
+/// </summary>
+/// <typeparam name="T">The CLR type of the values of the 
properties</typeparam>
+
+namespace DataJS.Tests
+{
+    using System;
+    using System.Collections;
+    using System.Collections.Generic;
+    using System.Linq;
+    using System.Runtime.Serialization;
+
+    [Serializable]
+    [KnownType(typeof(JsonObject))]
+    [KnownType(typeof(JsonObject[]))]
+    [KnownType(typeof(JsDate))]
+    [KnownType(typeof(List<object>))]
+    public class JsonObject : ISerializable, IEnumerable<KeyValuePair<string, 
object>>
+    {
+        Dictionary<string, object> dictionary = new Dictionary<string, 
object>();
+
+        public void Remove(string key)
+        {
+            dictionary.Remove(key);
+        }
+
+        public object this[string key]
+        {
+            get
+            {
+                return this.dictionary[key];
+            }
+            set
+            {
+                this.dictionary[key] = value;
+            }
+        }
+
+        public bool ContainsKey(string key)
+        {
+            return this.dictionary.ContainsKey(key);
+        }
+
+        public static JsonObject Merge(JsonObject first, JsonObject second)
+        {
+            if (first == null)
+            {
+                return second;
+            }
+
+            if (second != null)
+            {
+                JsonObject merged = new JsonObject();
+                merged.dictionary = new Dictionary<string, 
object>(first.dictionary);
+                foreach (var pair in second.dictionary)
+                {
+                    merged.dictionary[pair.Key] = pair.Value;
+                }
+                return merged;
+            }
+            return first;
+        }
+
+        public void GetObjectData(SerializationInfo info, StreamingContext 
context)
+        {
+            this.dictionary.ToList().ForEach(pair => info.AddValue(pair.Key, 
pair.Value));
+        }
+
+        public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
+        {
+            return this.dictionary.GetEnumerator();
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return this.dictionary.GetEnumerator();
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/503b4417/tests/code/readerutils.cs
----------------------------------------------------------------------
diff --git a/tests/code/readerutils.cs b/tests/code/readerutils.cs
new file mode 100644
index 0000000..094a70c
--- /dev/null
+++ b/tests/code/readerutils.cs
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Web.Script.Serialization;
+
+namespace DataJS.Tests
+{
+    public static class ReaderUtils
+    {
+        public static JsonObject CreateEntryPropertyMetadata(string type)
+        {
+            return CreateEntryPropertyMetadata(type, true);
+        }
+
+        public static JsonObject CreateEntryPropertyMetadata(string type, bool 
withExtensions)
+        {
+            JsonObject json = new JsonObject();
+            json["type"] = type;
+
+
+            // TODO: add proper support for property extensions
+            if (withExtensions)
+            {
+                json["extensions"] = new JsonObject[] { };
+            }
+
+            return json;
+        }
+
+        public static JsonObject CreateExtension(string name, string 
nameSpace, string value)
+        {
+            JsonObject json = new JsonObject();
+            json["name"] = name;
+            json["namespaceURI"] = nameSpace;
+            json["value"] = value;
+            return json;
+        }
+
+        public static WebRequest CreateRequest(string url, string user = null, 
string password = null)
+        {
+            WebRequest request = WebRequest.Create(url);
+            if (user != null || password != null)
+            {
+                request.Credentials = new NetworkCredential(user, password);
+                request.PreAuthenticate = true;
+            }
+
+            return request;
+        }
+
+        public static Stream 
ConvertDictionarytoJsonlightStream(Dictionary<string, object> dict)
+        {
+            MemoryStream stream = new MemoryStream();
+            if (dict == null)
+            {
+                return stream;
+            }
+
+            string jsonString = new JavaScriptSerializer().Serialize(dict);
+            StreamWriter writer = new StreamWriter(stream);
+            writer.Write(jsonString);
+            writer.Flush();
+            stream.Position = 0;
+            return stream;
+        }
+
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/503b4417/tests/common/CacheVerifier.js
----------------------------------------------------------------------
diff --git a/tests/common/CacheVerifier.js b/tests/common/CacheVerifier.js
new file mode 100644
index 0000000..1e9f0d4
--- /dev/null
+++ b/tests/common/CacheVerifier.js
@@ -0,0 +1,241 @@
+/*
+ * 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.
+ */
+
+// CacheVerifier.js
+// This object verifies the operation of the cache.
+// Internally it maintains a simple model of the cache implemented using a 
lookup array of the expected cached pages.
+
+(function (window, undefined) {
+
+    var CacheVerifier = function (baseUri, pageSize, total, cacheSize) {
+        /** Creates a new CacheVerifier
+         * @param {String} baseUri - The base URI of the collection
+         * @param {Integer} pageSize - The page size used in the cache
+         * @param {Integer} total - The total number of items in the collection
+         * @param {Integer} cacheSize - Cache size in bytes
+         */
+        this.baseUri = baseUri;
+        this.pageSize = pageSize;
+        this.total = total;
+        this.cacheSize = (cacheSize !== undefined) ? cacheSize : 1024 * 1024;
+        this.actualSize = 0;
+        this.actualCount = 0;
+        this.cachedPages = [];
+        this.exactPageCount = (total % pageSize === 0);
+        this.maxPage = Math.floor(total / pageSize);
+        this.overflowed = this.cacheSize === 0;
+    };
+
+    CacheVerifier.mechanisms = {
+        memory: "memory",
+        indexeddb: "indexeddb",
+        dom: "dom",
+        best: "best"
+    };
+
+    CacheVerifier.isMechanismAvailable = function (mechanism) {
+        /** Determines if the specified local storage mechanism is available
+         * @param mechanism - The name of the mechanism
+         * @returns Whether the mechanism is available
+         */
+        switch (mechanism) {
+            case CacheVerifier.mechanisms.indexeddb:
+                if (window.msIndexedDB || window.mozIndexedDB || 
window.webkitIndexedDB || window.indexedDB) {
+                    return true;
+                }
+                else {
+                    return false;
+                }
+                break;
+            case CacheVerifier.mechanisms.dom:
+                if (window.localStorage) {
+                    return true;
+                }
+                else {
+                    return false;
+                }
+                break;
+            case CacheVerifier.mechanisms.memory:
+            case CacheVerifier.mechanisms.best:
+            case undefined:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    CacheVerifier.prototype.clear = function () {
+        /** Clears the cache in the verifier
+        */
+        this.cachedPages = [];
+        this.actualSize = 0;
+        this.actualCount = 0;
+        this.overflowed = this.cacheSize === 0;
+    }
+
+    CacheVerifier.prototype.verifyRequests = function (requests, responses, 
index, count, description, backwards, isPrefetch) {
+        /** Verifies the HTTP requests for a single data request, and updates 
the verifier with cached pages
+         * @param {Array} requests - The sequence of request objects (from 
OData.defaultHttpClient)
+         * @param {Array} responses - The sequence of response objects (from 
OData.defaultHttpClient)
+         * @param {Integer} index - The starting index of the read
+         * @param {Integer} count - The count of items in the read
+         * @param {String} description - The description of the requests being 
verified
+         * @param {Boolean} backwards - Whether or not filterBack is being 
verified
+         * @param {Boolean} isPrefetch - Whether the requests being verified 
come from the prefetcher
+         */
+        var that = this;
+
+        index = (index < 0 ? 0 : index);
+        var pageIndex = function (index) {
+            /** Returns the page index that the given item index belongs to
+             * @param {Integer} index - The item index
+             * @returns The page index
+             */
+            return Math.floor(index / that.pageSize);
+        };
+
+        var estimateSize = function (obj) {
+            /** Estimates the size of an object in bytes.
+             * @param {Object} obj - Object to determine the size of.
+             * @returns {Number} Estimated size of the object in bytes.
+             */
+
+            var size = 0;
+            var type = typeof obj;
+
+            if (type === "object" && obj) {
+                for (var name in obj) {
+                    size += name.length * 2 + estimateSize(obj[name]);
+                }
+            } else if (type === "string") {
+                size = obj.length * 2;
+            } else {
+                size = 8;
+            }
+            return size;
+        };
+
+        var expectedUris = [];
+        var responseIndex = 0;
+        if (count >= 0) {
+            var minPage = pageIndex(index);
+            var maxPage = Math.min(pageIndex(index + count - 1), 
pageIndex(this.total));
+
+            // In the case that the index is outside the range of the 
collection the minPage will be greater than the maxPage  
+            maxPage = Math.max(minPage, maxPage);
+
+            if (!(isPrefetch && !this.exactPageCount && minPage > 
this.maxPage)) {
+                for (var page = minPage; page <= maxPage && this.actualCount 
<= this.total && !(isPrefetch && this.overflowed); page++) {
+                    if (!this.cachedPages[page]) {
+
+                        expectedUris.push(that.baseUri + "?$skip=" + page * 
this.pageSize + "&$top=" + (this.pageSize));
+
+                        var actualPageSize = 0;
+                        var actualPageCount = 0;
+                        if (responses[responseIndex] && 
responses[responseIndex].data) {
+                            actualPageSize += 
estimateSize(responses[responseIndex].data);
+                            actualPageCount += 
responses[responseIndex].data.value.length;
+                            // Handle server paging skipToken requests
+                            while 
(responses[responseIndex].data["@odata.nextLink"]) {
+                                var nextLink = 
responses[responseIndex].data["@odata.nextLink"];
+                                if (nextLink) {
+                                    var index = that.baseUri.indexOf(".svc/", 
0);
+                                    if (index != -1) {
+                                        nextLink = that.baseUri.substring(0, 
index + 5) + nextLink;
+                                    }
+                                }
+
+                                expectedUris.push(nextLink);
+                                responseIndex++;
+                                actualPageSize += 
estimateSize(responses[responseIndex].data);
+                                actualPageCount += 
responses[responseIndex].data.value.length;
+                            }
+
+                            actualPageSize += 24; // 24 byte overhead for the 
pages (i)ndex, and (c)ount fields
+                        }
+
+                        responseIndex++;
+
+                        this.overflowed = this.cacheSize >= 0 && 
this.actualSize + actualPageSize > this.cacheSize;
+                        if (!this.overflowed) {
+                            this.cachedPages[page] = true;
+                            this.actualSize += actualPageSize;
+                            this.actualCount += actualPageCount;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (backwards) {
+            expectedUris.reverse();
+        }
+
+        var actualUris = $.map(requests, function (r) { return r.requestUri; 
});
+        djstest.assertAreEqualDeep(actualUris, expectedUris, description);
+    };
+
+    CacheVerifier.getExpectedFilterResults = function (data, filterIndex, 
filterCount, predicate, backwards) {
+        /** Verifies the cache filter returns the correct data
+         * @param {Array} collection - Array of items in the collection
+         * @param {Integer} filterIndex - The index value
+         * @param {Integer} filterCount - The count value
+         * @param {Function} predicate - Predicate to be applied in filter, 
takes an item
+         * @param {Boolean} backwards - Whether or not filterBackwards is 
being verified
+         */
+        if (!data || !data.value) {
+            return data;
+        }
+
+        var value = [];
+        if (filterCount !== 0) {
+            // Convert [item0, item1, ...] into [{ index: 0, item: item0 }, { 
index: 1, item: item1 }, ...]
+            var indexedCollection = $.map(data.value, function (item, index) {
+                return { index: index, item: item };
+            });
+
+            var grepPredicate = function (element, index) {
+                return predicate(element.item);
+            };
+
+            var index = filterIndex < 0 ? 0 : filterIndex;
+            var count = filterCount < 0 ? indexedCollection.length : 
filterCount;
+
+            value = backwards ?
+            // Slice up to 'index', filter, then slice 'count' number of items 
from the end
+                $.grep(indexedCollection.slice(0, index + 1), 
grepPredicate).slice(-count) :
+            // Slice from 'index' to the end, filter, then slice 'count' 
number of items from the beginning
+                $.grep(indexedCollection.slice(index), grepPredicate).slice(0, 
count);
+        }
+
+        var expectedResults = {};
+        for (var property in data) {
+            if (property == "value") {
+                expectedResults[property] = value;
+            } else {
+                expectedResults[property] = data[property];
+            }
+        }
+
+        return expectedResults;
+    };
+
+    window.CacheVerifier = CacheVerifier;
+
+})(this);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/503b4417/tests/common/Instrument.js
----------------------------------------------------------------------
diff --git a/tests/common/Instrument.js b/tests/common/Instrument.js
new file mode 100644
index 0000000..fab583a
--- /dev/null
+++ b/tests/common/Instrument.js
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+// Instrument.js
+// Instrumentation utilities
+
+(function (window, undefined) {
+
+    var warmedUp = false;
+    var getBrowserMemorySize = function (success) {
+        /** Gets the memory size (in bytes) of the browser process
+         * @param {Function} success - The success callback
+         */
+        var makeRequest = function (success) {
+            $.get("./common/Instrument.svc/GetBrowserMemorySize", function 
(data) {
+                success(parseInt(data));
+            }, "text");
+        };
+
+        if (window.CollectGarbage) {
+            window.CollectGarbage();
+        }
+
+        if (!warmedUp) {
+            // Make a dummy request to warm it up
+            makeRequest(function () {
+                warmedUp = true;
+                makeRequest(success);
+            });
+        } else {
+            makeRequest(success);
+        }
+    }
+
+    window.Instrument = {
+        getBrowserMemorySize: getBrowserMemorySize
+    };
+
+})(this);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/503b4417/tests/common/Instrument.svc
----------------------------------------------------------------------
diff --git a/tests/common/Instrument.svc b/tests/common/Instrument.svc
new file mode 100644
index 0000000..111020f
--- /dev/null
+++ b/tests/common/Instrument.svc
@@ -0,0 +1,77 @@
+/*
+ * 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.
+*/
+
+<%@ ServiceHost Language="C#" Debug="true" 
Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory"
+    Service="DataJS.Tests.Instrument" %>
+
+namespace DataJS.Tests
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Diagnostics;
+    using System.IO;
+    using System.Linq;
+    using System.Runtime.Serialization;
+    using System.ServiceModel;
+    using System.ServiceModel.Activation;
+    using System.ServiceModel.Syndication;
+    using System.ServiceModel.Web;
+    using System.Text;
+
+    /// <summary>
+    /// Instrumentation utilities
+    /// </summary>
+    [ServiceContract]
+    [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
+    [AspNetCompatibilityRequirements(RequirementsMode = 
AspNetCompatibilityRequirementsMode.Allowed)]
+    public class Instrument
+    {
+        static readonly Dictionary<string, string> userAgents = new 
Dictionary<string,string>
+        {
+            { "MSIE", "iexplore" },
+            { "Firefox", "firefox" },
+            { "Chrome", "chrome" },
+            { "Safari", "safari" }
+        };
+        
+        /// <summary>
+        /// Gets the memory size used by the browser
+        /// </summary>
+        /// <returns>The memory size used by the browser (in bytes), or zero 
if browser is not supported</returns>
+        [OperationContract]
+        [WebGet]
+        public Stream GetBrowserMemorySize()
+        {
+            string userAgentString = 
WebOperationContext.Current.IncomingRequest.UserAgent;
+            string userAgentKey = Instrument.userAgents.Keys.FirstOrDefault(ua 
=> userAgentString.Contains(ua));
+
+            if (userAgentKey != null)
+            {
+                string processName = userAgents[userAgentKey];
+                long totalMemory = 
Process.GetProcessesByName(processName).Select(p => p.WorkingSet64).Sum();
+                
+                return new 
MemoryStream(Encoding.UTF8.GetBytes(totalMemory.ToString()));
+            }
+            else
+            {
+                return new MemoryStream(Encoding.UTF8.GetBytes("0"));
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/503b4417/tests/common/ODataVerifyReader.svc
----------------------------------------------------------------------
diff --git a/tests/common/ODataVerifyReader.svc 
b/tests/common/ODataVerifyReader.svc
new file mode 100644
index 0000000..6b71acf
--- /dev/null
+++ b/tests/common/ODataVerifyReader.svc
@@ -0,0 +1,114 @@
+/*
+ * 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.
+*/
+
+<%@ ServiceHost Language="C#" Debug="true" 
Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory"
+    Service="DataJS.Tests.ODataVerifyReader" %>
+
+//uncomment this line to debug JSON serialization.
+//#define DEBUG_SERIALIZATION
+
+namespace DataJS.Tests
+{
+    using System;
+    using System.Collections.Generic;
+    using System.IO;
+    using System.Linq;
+    using System.Net;
+    using System.Runtime.Serialization;
+    using System.ServiceModel;
+    using System.ServiceModel.Activation;
+    using System.ServiceModel.Syndication;
+    using System.ServiceModel.Web;
+    using System.Xml;
+    using System.Xml.Linq;
+    using Microsoft.Spatial;
+    using Microsoft.OData.Core;
+    using System.Web.Script.Serialization;
+
+    /// <summary>
+    /// Verifier for the OData.read library function
+    /// </summary>
+    [ServiceContract]
+    [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
+    [AspNetCompatibilityRequirements(RequirementsMode = 
AspNetCompatibilityRequirementsMode.Allowed)]
+    public class ODataVerifyReader
+    {
+        const string jsonlightMediaType = "application/json";
+
+        /// <summary>
+        /// Reads a URI that will return a metadata object
+        /// </summary>
+        /// <param name="url">The URL to send the request to</param>
+        /// <returns>Stream of metadata in json light format</returns>
+        [OperationContract]
+        [WebGet]
+        public Stream ReadMetadata(string url)
+        {
+            WebResponse response = WebRequest.Create(ResolveUri(url, 
UriKind.Absolute)).GetResponse();
+            Dictionary<string, object> jsonObject = CsdlReader.ReadCsdl(new 
StreamReader(response.GetResponseStream()));
+            return ReaderUtils.ConvertDictionarytoJsonlightStream(jsonObject);
+        }
+        
+        /// <summary>
+        /// Reads a URI that will get the Json response and return the stream
+        /// </summary>
+        /// <param name="url">URL of the entry</param>
+        /// <param name="user">The username for basic authentication</param>
+        /// <param name="password">The password for basic 
authentication</param>
+        /// <returns>Stream of the Json response expected to be returned by 
OData.read</returns>
+        [OperationContract]
+        [WebGet(ResponseFormat = WebMessageFormat.Json)]
+        public Stream ReadJson(string url, string mimeType, string user, 
string password)
+        {
+            if (mimeType == null)
+            {
+                mimeType = jsonlightMediaType + 
";odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8";
+            }
+            
+            HttpWebRequest request = 
(HttpWebRequest)ReaderUtils.CreateRequest(ResolveUri(url, UriKind.Absolute), 
user, password);
+            request.Accept = mimeType;
+            WebResponse response = request.GetResponse();
+
+            return response.GetResponseStream();
+        }
+
+        /// <summary>
+        /// Resolves the given url string to a URI
+        /// </summary>
+        /// <param name="url">The given URL string</param>
+        /// <param name="urlKind">URI kind to resolve to</param>
+        /// <returns>The resolved URI</returns>
+        private static string ResolveUri(string url, UriKind uriKind)
+        {
+            Uri resolvedUri = new Uri(url, UriKind.RelativeOrAbsolute);
+            if (!resolvedUri.IsAbsoluteUri)
+            {
+                // If the given URI is relative, then base it on the Referer 
URI
+                Uri baseUri = new 
Uri(WebOperationContext.Current.IncomingRequest.Headers["Referer"]);
+                resolvedUri = new Uri(baseUri, resolvedUri);
+                if (uriKind == UriKind.Relative)
+                {
+                    resolvedUri = baseUri.MakeRelativeUri(resolvedUri);
+                }
+            }
+
+            return resolvedUri.ToString();
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/503b4417/tests/common/ObservableHttpClient.js
----------------------------------------------------------------------
diff --git a/tests/common/ObservableHttpClient.js 
b/tests/common/ObservableHttpClient.js
new file mode 100644
index 0000000..9be75f8
--- /dev/null
+++ b/tests/common/ObservableHttpClient.js
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+// ObservableHttpClient.js
+// This object extends OData's default httpClient by supporting request and 
response recording sessions, and firing a custom
+// JQuery event for each request/response.
+//
+// The events fired by this object are:
+//      request: Before a request is made
+//      success: Before the primary success handler is called
+//
+// To bind to an event, JQuery event attachers can be used on the object, e.g.
+//      $(observableHttpClient).bind("request", function (request) { ... });
+//
+// To begin a new recording session, use:
+//      var session = observableHttpClient.newSession();
+//
+// Requests and responses are then recorded in session.requests and 
session.responses. Session can be ended by session.end().
+// Multiple simultaneous sessions are supported.
+
+(function (window, undefined) {
+
+    var ObservableHttpClient = function (provider) {
+        this.provider = provider ? provider : 
window.odatajs.oData.net.defaultHttpClient;
+    };
+
+    ObservableHttpClient.prototype.newSession = function () {
+        return new Session(this);
+    };
+
+    ObservableHttpClient.prototype.request = function (request, success, 
error) {
+        var that = this;
+
+        $(this).triggerHandler("request", request);
+        return this.provider.request(request, function (response) {
+            $(that).triggerHandler("success", response);
+            success(response);
+        }, error);
+    };
+
+
+    var Session = function (client) {
+        var that = this;
+
+        this.client = client;
+        this.clear();
+
+        this.requestHandler = function (event, request) { 
that.requests.push(request); };
+        $(client).bind("request", this.requestHandler);
+
+        this.successHandler = function (event, response) { 
that.responses.push(response); };
+        $(client).bind("success", this.successHandler);
+    };
+
+    Session.prototype.clear = function () {
+        this.requests = [];
+        this.responses = [];
+    }
+
+    Session.prototype.end = function () {
+        $(this.client).unbind("request", this.requestHandler);
+        $(this.client).unbind("success", this.successHandler);
+    };
+
+    window.ObservableHttpClient = ObservableHttpClient;
+    window.Session = Session;
+
+})(this);
\ No newline at end of file

Reply via email to