You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ko...@apache.org on 2014/05/16 13:31:05 UTC
[11/13] [OLINGO-238] adopt odata-json-tests.js
http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/0367d2bc/datajs/tests/common/CacheOracle.js
----------------------------------------------------------------------
diff --git a/datajs/tests/common/CacheOracle.js b/datajs/tests/common/CacheOracle.js
new file mode 100644
index 0000000..2130f82
--- /dev/null
+++ b/datajs/tests/common/CacheOracle.js
@@ -0,0 +1,228 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// CacheOracle.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 CacheOracle = function (baseUri, pageSize, total, cacheSize) {
+ /// <summary>Creates a new CacheOracle</summary>
+ /// <param name="baseUri" type="String">The base URI of the collection</param>
+ /// <param name="pageSize" type="Integer">The page size used in the cache</param>
+ /// <param name="total" type="Integer">The total number of items in the collection</param>
+ /// <param name="cacheSize" type="Integer">Cache size in bytes</param>
+ 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;
+ };
+
+ CacheOracle.mechanisms = {
+ memory: "memory",
+ indexeddb: "indexeddb",
+ dom: "dom",
+ best: "best"
+ };
+
+ CacheOracle.isMechanismAvailable = function (mechanism) {
+ /// <summary>Determines if the specified local storage mechanism is available</summary>
+ /// <param name="mechanism">The name of the mechanism</param>
+ /// <returns>Whether the mechanism is available</returns>
+ switch (mechanism) {
+ case CacheOracle.mechanisms.indexeddb:
+ if (window.msIndexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.indexedDB) {
+ return true;
+ }
+ else {
+ return false;
+ }
+ break;
+ case CacheOracle.mechanisms.dom:
+ if (window.localStorage) {
+ return true;
+ }
+ else {
+ return false;
+ }
+ break;
+ case CacheOracle.mechanisms.memory:
+ case CacheOracle.mechanisms.best:
+ case undefined:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ CacheOracle.prototype.clear = function () {
+ /// <summary>Clears the cache in the oracle</summary>
+ this.cachedPages = [];
+ this.actualSize = 0;
+ this.actualCount = 0;
+ this.overflowed = this.cacheSize === 0;
+ }
+
+ CacheOracle.prototype.verifyRequests = function (requests, responses, index, count, description, backwards, isPrefetch) {
+ /// <summary>Verifies the HTTP requests for a single data request, and updates the oracle with cached pages</summary>
+ /// <param name="requests" type="Array">The sequence of request objects (from OData.defaultHttpClient)</param>
+ /// <param name="responses" type="Array">The sequence of response objects (from OData.defaultHttpClient)</param>
+ /// <param name="index" type="Integer">The starting index of the read</param>
+ /// <param name="count" type="Integer">The count of items in the read</param>
+ /// <param name="description" type="String">The description of the requests being verified</param>
+ /// <param name="backwards" type="Boolean">Whether or not filterBack is being verified</param>
+ /// <param name="isPrefetch" type="Boolean">Whether the requests being verified come from the prefetcher</param>
+ var that = this;
+
+ index = (index < 0 ? 0 : index);
+ var pageIndex = function (index) {
+ /// <summary>Returns the page index that the given item index belongs to</summary>
+ /// <param name="index" type="Integer">The item index</param>
+ /// <returns>The page index</returns>
+ return Math.floor(index / that.pageSize);
+ };
+
+ var estimateSize = function (obj) {
+ /// <summary>Estimates the size of an object in bytes.</summary>
+ /// <param name="obj" type="Object">Object to determine the size of.</param>
+ /// <returns type="Number">Estimated size of the object in bytes.</returns>
+
+ 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);
+ };
+
+ CacheOracle.getExpectedFilterResults = function (data, filterIndex, filterCount, predicate, backwards) {
+ /// <summary>Verifies the cache filter returns the correct data</summary>
+ /// <param name="collection" type="Array">Array of items in the collection</param>
+ /// <param name="filterIndex" type="Integer">The index value</param>
+ /// <param name="filterCount" type="Integer">The count value</param>
+ /// <param name="predicate" type="Function">Predicate to be applied in filter, takes an item</param>
+ /// <param name="backwards" type="Boolean">Whether or not filterBackwards is being verified</param>
+ 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.CacheOracle = CacheOracle;
+
+})(this);
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/0367d2bc/datajs/tests/common/Instrument.js
----------------------------------------------------------------------
diff --git a/datajs/tests/common/Instrument.js b/datajs/tests/common/Instrument.js
new file mode 100644
index 0000000..acbb2b0
--- /dev/null
+++ b/datajs/tests/common/Instrument.js
@@ -0,0 +1,48 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// Instrument.js
+// Instrumentation utilities
+
+(function (window, undefined) {
+
+ var warmedUp = false;
+ var getBrowserMemorySize = function (success) {
+ /// <summary>Gets the memory size (in bytes) of the browser process</summary>
+ /// <param name="success" type="Function">The success callback</param>
+ 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/0367d2bc/datajs/tests/common/Instrument.svc
----------------------------------------------------------------------
diff --git a/datajs/tests/common/Instrument.svc b/datajs/tests/common/Instrument.svc
new file mode 100644
index 0000000..3d14b16
--- /dev/null
+++ b/datajs/tests/common/Instrument.svc
@@ -0,0 +1,71 @@
+<%@ ServiceHost Language="C#" Debug="true" Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory"
+ Service="DataJS.Tests.Instrument" %>
+
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+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/0367d2bc/datajs/tests/common/ODataReadOracle.js
----------------------------------------------------------------------
diff --git a/datajs/tests/common/ODataReadOracle.js b/datajs/tests/common/ODataReadOracle.js
new file mode 100644
index 0000000..4114745
--- /dev/null
+++ b/datajs/tests/common/ODataReadOracle.js
@@ -0,0 +1,205 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// Client for the odata.read oracle service
+
+(function (window, undefined) {
+ var jsonMime = "application/json";
+ var universalMime = "*/*";
+ var atomMime = "application/atom+xml";
+
+ var readFeed = function (url, success, mimeType, recognizeDates) {
+ /// <summary>Calls the ReadFeed endpoint with the specified URL</summary>
+ /// <param name="url" type="String">The URL to read the feed from</param>
+ /// <param name="success" type="Function">The success callback function</param>
+ /// <param name="mimeType" type="String">The MIME media type in the Accept header</param>
+ var readMethod = getReadMethod(mimeType, "ReadFeed");
+ oracleRequest("GET", readMethod, typeof url === "string" ? { url: url} : url, mimeType, recognizeDates, function (data) {
+ success(data);
+ });
+ };
+
+ var readEntry = function (url, success, mimeType, recognizeDates) {
+ /// <summary>Calls the ReadEntry endpoint with the specified URL</summary>
+ /// <param name="url" type="String">The URL to read the entry from</param>
+ /// <param name="success" type="Function">The success callback function</param>
+ /// <param name="mimeType" type="String">The MIME media type in the Accept header</param>
+ var readMethod = getReadMethod(mimeType, "ReadEntry");
+ oracleRequest("GET", readMethod, typeof url === "string" ? { url: url} : url, mimeType, recognizeDates, success);
+ };
+
+ var readFeedLoopback = function (atomFeedXml, success, recognizeDates) {
+ /// <summary>Calls the ReadFeedLoopback endpoint with the specified atom feed xml</summary>
+ /// <param name="atomFeedXml" type="String">The atom feed xml</param>
+ /// <param name="success" type="Function">The success callback function</param>
+ oracleRequest("POST", "ReadFeedLoopback", atomFeedXml, atomMime, recognizeDates, success);
+ };
+
+ var readEntryLoopback = function (atomEntryXml, success, recognizeDates) {
+ /// <summary>Calls the ReadEntryLoopback endpoint with the specified atom entry xml</summary>
+ /// <param name="atomEntryXml" type="String">The atom entry xml</param>
+ /// <param name="success" type="Function">The success callback function</param>
+ oracleRequest("POST", "ReadEntryLoopback", atomEntryXml, atomMime, recognizeDates, success);
+ };
+
+ var readLinksEntry = function (url, success) {
+ /// <summary>Calls the ReadMetadata endpoint with the specified URL</summary>
+ /// <param name="url" type="String">The URL to read the metadata from</param>
+ /// <param name="success" type="Function">The success callback function</param>
+ readJson(
+ url,
+ success
+ );
+ };
+
+ var readLinksFeed = function (url, success) {
+ /// <summary>Calls the ReadMetadata endpoint with the specified URL</summary>
+ /// <param name="url" type="String">The URL to read the metadata from</param>
+ /// <param name="success" type="Function">The success callback function</param>
+ readJson(
+ url,
+ function (data) {
+ success(data);
+ }
+ );
+ };
+
+ var readMetadata = function (url, success) {
+ /// <summary>Calls the ReadMetadata endpoint with the specified URL</summary>
+ /// <param name="url" type="String">The URL to read the metadata from</param>
+ /// <param name="success" type="Function">The success callback function</param>
+ oracleRequest("GET", "ReadMetadata", typeof url === "string" ? { url: url} : url, null, null, success);
+ };
+
+ var readServiceDocument = function (url, success, mimeType) {
+ /// <summary>Calls the ReadServiceDocument endpoint with the specified URL</summary>
+ /// <param name="url" type="String">The URL to the service</param>
+ /// <param name="success" type="Function">The success callback function</param>
+ /// <param name="mimeType" type="String">The MIME type being tested</param>
+ var readMethod = getReadMethod(mimeType, "ReadServiceDocument");
+ oracleRequest("GET", readMethod, typeof url === "string" ? { url: url} : url, mimeType, null, success);
+ };
+
+ var readJson = function (url, success) {
+ $.ajax({
+ url: url,
+ accepts: null,
+ dataType: "json",
+ beforeSend: function (xhr) {
+ xhr.setRequestHeader("Accept", jsonMime);
+ xhr.setRequestHeader("OData-MaxVersion", "4.0");
+ },
+ success: function (data) {
+ success(data);
+ }
+ });
+ };
+
+ var readJsonAcrossServerPages = function (url, success) {
+ var data = {};
+ var readPage = function (url) {
+ readJson(url, function (feedData) {
+ var nextLink = feedData["@odata.nextLink"];
+ if (nextLink) {
+ var index = url.indexOf(".svc/", 0);
+ if (index != -1) {
+ nextLink = url.substring(0, index + 5) + nextLink;
+ }
+ }
+
+ if (data.value && feedData.value) {
+ data.value = data.value.concat(feedData.value);
+ }
+ else {
+ for (var property in feedData) {
+ if (property != "@odata.nextLink") {
+ data[property] = feedData[property];
+ }
+ }
+ }
+
+ if (nextLink) {
+ readPage(nextLink);
+ }
+ else {
+ success(data);
+ }
+ });
+ };
+
+ readPage(url);
+ };
+
+ var getReadMethod = function (mimeType, defaultEndpoint) {
+ switch (mimeType) {
+ case atomMime:
+ return defaultEndpoint;
+ case jsonMime:
+ case universalMime:
+ default:
+ return "ReadJson";
+ }
+ };
+
+ var oracleRequest = function (method, endpoint, data, mimeType, recognizeDates, success) {
+ /// <summary>Requests a JSON object from the oracle service, removing WCF-specific artifacts</summary>
+ /// <param name="method" type="String">The HTTP method (GET or POST)</param>
+ /// <param name="endpoint" type="String">The oracle endpoint</param>
+ /// <param name="data" type="Object">The data to send with the request</param>
+ /// <param name="reviver" type="Function">The reviver function to run on each deserialized object</param>
+ /// <param name="success" type="Function">Success callback</param>
+ var url = "./common/ODataReadOracle.svc/" + endpoint;
+ if (mimeType) {
+ data.mimeType = mimeType;
+ }
+
+ $.ajax({
+ type: method,
+ url: url,
+ data: data,
+ dataType: "text",
+ success: function (data) {
+ var json = JSON.parse(data);
+ success(json);
+ }
+ });
+ };
+
+ var removeProperty = function (data, property) {
+ /// <summary>Removes the specified property recursively from the given object</summary>
+ /// <param name="data" type="Object">The object to operate on</param>
+ /// <param name="property" type="String">The name of the property to remove</param>
+ if (typeof data === "object" && data !== null) {
+ if (data[property]) {
+ delete data[property];
+ }
+
+ for (prop in data) {
+ removeProperty(data[prop], property);
+ }
+ }
+ };
+
+ window.ODataReadOracle = {
+ readFeed: readFeed,
+ readEntry: readEntry,
+ readFeedLoopback: readFeedLoopback,
+ readEntryLoopback: readEntryLoopback,
+ readLinksEntry: readLinksEntry,
+ readLinksFeed: readLinksFeed,
+ readJson: readJson,
+ readJsonAcrossServerPages: readJsonAcrossServerPages,
+ readMetadata: readMetadata,
+ readServiceDocument: readServiceDocument
+ };
+})(window);
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/0367d2bc/datajs/tests/common/ODataReadOracle.svc
----------------------------------------------------------------------
diff --git a/datajs/tests/common/ODataReadOracle.svc b/datajs/tests/common/ODataReadOracle.svc
new file mode 100644
index 0000000..51ccd62
--- /dev/null
+++ b/datajs/tests/common/ODataReadOracle.svc
@@ -0,0 +1,189 @@
+<%@ ServiceHost Language="C#" Debug="true" Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory"
+ Service="DataJS.Tests.ODataReadOracle" %>
+
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+//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>
+ /// Oracle for the OData.read library function
+ /// </summary>
+ [ServiceContract]
+ [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
+ [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
+ public class ODataReadOracle
+ {
+ const string jsonlightMediaType = "application/json";
+
+ /// <summary>
+ /// Reads a URI that will return an OData ATOM feed
+ /// </summary>
+ /// <param name="url">The URL to send the request to</param>
+ /// <param name="user">The username for basic authentication</param>
+ /// <param name="password">The password for basic authentication</param>
+ /// <returns>JSON object expected to be returned by OData.read (plus type metadata markers that will need to be removed)</returns>
+ [OperationContract]
+ [WebGet(ResponseFormat = WebMessageFormat.Json)]
+ public JsonObject ReadFeed(string url, string user, string password)
+ {
+ WebResponse response = ReaderUtils.CreateRequest(ResolveUri(url, UriKind.Absolute), user, password).GetResponse();
+ return AtomReader.ReadFeed(new StreamReader(response.GetResponseStream()));
+ }
+
+ /// <summary>
+ /// Reads a URI that will return an OData ATOM feed entry
+ /// </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>JSON object expected to be returned by OData.read</returns>
+ [OperationContract]
+ [WebGet(ResponseFormat = WebMessageFormat.Json)]
+ public JsonObject ReadEntry(string url, string user, string password)
+ {
+ WebResponse response = ReaderUtils.CreateRequest(ResolveUri(url, UriKind.Absolute), user, password).GetResponse();
+ return AtomReader.ReadEntry(new StreamReader(response.GetResponseStream()));
+ }
+
+ /// <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 return a metadata object
+ /// </summary>
+ /// <param name="url">The URL to send the request to</param>
+ /// <param name="mimeType">Mime type being tested to determine base URI</param>
+ /// <returns>JSON object expected to be returned by OData.read (plus type metadata markers that will need to be removed)</returns>
+ [OperationContract]
+ [WebGet(ResponseFormat = WebMessageFormat.Json)]
+ public JsonObject ReadServiceDocument(string url, string mimeType)
+ {
+ WebResponse response = WebRequest.Create(ResolveUri(url, UriKind.Absolute)).GetResponse();
+ string baseUri = string.Empty;
+
+ // With JSON responses only relative path passed to the library is available
+ if (mimeType.Equals(jsonlightMediaType))
+ {
+ baseUri = ResolveUri(url, UriKind.Relative).ToString();
+ }
+ else
+ {
+ baseUri = response.ResponseUri.AbsoluteUri;
+ }
+
+ return AtomReader.ReadServiceDocument(new StreamReader(response.GetResponseStream()), baseUri);
+ }
+
+ /// <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>
+ /// Loops back an ATOM feed passed to the webservice in JSON format.
+ /// </summary>
+ /// <param name="content">The ATOM feed xml stream to loopback as JSON</param>
+ /// <returns>JSON object expected to be returned by OData.read (plus type metadata markers that will need to be removed)</returns>
+ [OperationContract]
+ [WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json)]
+ public JsonObject ReadFeedLoopback(Stream content)
+ {
+ return AtomReader.ReadFeed(new StreamReader(content));
+ }
+
+ /// <summary>
+ /// Loops back an ATOM entry passed to the webservice in JSON format.
+ /// </summary>
+ /// <param name="content">The ATOM entry xml stream to loopback as JSON</param>
+ /// <returns>JSON object expected to be returned by OData.read (plus type metadata markers that will need to be removed)</returns>
+ [OperationContract]
+ [WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json)]
+ public JsonObject ReadEntryLoopback(Stream content)
+ {
+ return AtomReader.ReadEntry(new StreamReader(content));
+ }
+
+ /// <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/0367d2bc/datajs/tests/common/ObservableHttpClient.js
----------------------------------------------------------------------
diff --git a/datajs/tests/common/ObservableHttpClient.js b/datajs/tests/common/ObservableHttpClient.js
new file mode 100644
index 0000000..2b7fe98
--- /dev/null
+++ b/datajs/tests/common/ObservableHttpClient.js
@@ -0,0 +1,78 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// 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 : OData.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
http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/0367d2bc/datajs/tests/common/TestLogger.svc
----------------------------------------------------------------------
diff --git a/datajs/tests/common/TestLogger.svc b/datajs/tests/common/TestLogger.svc
new file mode 100644
index 0000000..d236b24
--- /dev/null
+++ b/datajs/tests/common/TestLogger.svc
@@ -0,0 +1,846 @@
+<%@ ServiceHost Language="C#" Debug="true" Factory="DataJS.Tests.TestSynchronizerFactory" Service="DataJS.Tests.TestSynchronizer" %>
+
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace DataJS.Tests
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Net;
+ using System.ServiceModel;
+ using System.ServiceModel.Activation;
+ using System.ServiceModel.Channels;
+ using System.ServiceModel.Description;
+ using System.ServiceModel.Dispatcher;
+ using System.ServiceModel.Web;
+ using System.Text;
+ using System.Threading;
+ using System.Xml;
+
+ /// <summary>
+ /// This factory supports reconfiguring the service to allow incoming messages
+ /// to be larger than the default.
+ /// </summary>
+ public class TestSynchronizerFactory : WebScriptServiceHostFactory
+ {
+ protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
+ {
+ var result = base.CreateServiceHost(serviceType, baseAddresses);
+ result.Opening += ServiceHostOpening;
+ return result;
+ }
+
+ private static void UpdateService(ServiceDescription description)
+ {
+ const long LargeMaxReceivedMessageSize = 1024 * 1024 * 16;
+ foreach (var endpoint in description.Endpoints)
+ {
+ var basic = endpoint.Binding as BasicHttpBinding;
+ if (basic != null)
+ {
+ basic.MaxReceivedMessageSize = LargeMaxReceivedMessageSize;
+ }
+
+ var http = endpoint.Binding as WebHttpBinding;
+ if (http != null)
+ {
+ http.MaxReceivedMessageSize = LargeMaxReceivedMessageSize;
+ }
+ }
+ }
+
+ private void ServiceHostOpening(object sender, EventArgs e)
+ {
+ UpdateService((sender as ServiceHost).Description);
+ }
+ }
+
+ /// <summary>Use this class to log test activity.</summary>
+ /// <remarks>
+ /// A test run can be created by invoking CreateTestRun. With a test
+ /// run ID, the following operations can be invoked:
+ ///
+ /// - AddTestPages: adds test pages to be made available to future callers (typically to support tests from different files)
+ /// - SetTestNamePrefix: sets a string that will be prefixed to every test name in logs (typically to include a browser name)
+ /// - MarkInProgress: resets the test run to "in-progress" for another variation (typically to run a different browser)
+ /// - IsTestRunInProgress: checks whether it's still in progress
+ /// - GetTestRunResults: returns the test results in TRX format
+ /// - LogAssert: logs a single assertion in the current test for the run
+ /// - LogTestStart: logs a test that has begun execution
+ /// - LogTestDone: logs a test that has ended execution
+ /// - TestCompleted: logs that a test has completed execution; returns the next page with tests or an empty string
+ /// </remarks>
+ [ServiceContract]
+ [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
+ [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
+ public class TestSynchronizer
+ {
+ private static readonly Dictionary<string, TestRunContext> testRuns = new Dictionary<string, TestRunContext>();
+ private const string Inconclusive = "Inconclusive";
+ private const string InProgress = "InProgress";
+ private const string Failed = "Failed";
+ private const string Passed = "Passed";
+ private const string Completed = "Completed";
+
+ /// <summary>
+ /// Adds test pages to the specified runs; replaces existing files (helps with reliablity when something
+ /// fails part-way).
+ /// </summary>
+ /// <remarks>This method is typically called by the test harness.</remarks>
+ [OperationContract]
+ [WebGet(ResponseFormat = WebMessageFormat.Json)]
+ public int AddTestPages(string testRunId, string pages, string filter)
+ {
+ DisableResponseCaching();
+
+ TestRunContext context = GetTestRunContext(testRunId);
+ lock (context)
+ {
+ context.TestPages.Clear();
+ context.TestPages.AddRange(pages.Split(',').Select(page => page + "?testRunId=" + testRunId + (filter == null ? string.Empty : "?filter=" + filter)));
+ return context.TestPages.Count;
+ }
+ }
+
+ /// <remarks>This method is typically called by the test harness.</remarks>
+ [OperationContract]
+ [WebGet(ResponseFormat = WebMessageFormat.Json)]
+ public string CreateTestRun()
+ {
+ DisableResponseCaching();
+
+ Guid value = Guid.NewGuid();
+ string result = value.ToString();
+ TestRunContext context = CreateTestRunContextWithId(value);
+
+ lock (testRuns)
+ {
+ testRuns.Add(result, context);
+ }
+
+ return result;
+ }
+
+ /// <summary>Checks whether the test run is in progress.</summary>
+ /// <remarks>This method is typically called by the test harness.</remarks>
+ [OperationContract]
+ [WebGet(ResponseFormat = WebMessageFormat.Json)]
+ public bool IsTestRunInProgress(string testRunId)
+ {
+ DisableResponseCaching();
+
+ TestRunContext context = GetTestRunContext(testRunId);
+ return context.TestRun.ResultSummary.Outcome == InProgress;
+ }
+
+ /// <summary>Provides a list of all test runs being tracked.</summary>
+ [OperationContract]
+ [WebGet(ResponseFormat=WebMessageFormat.Json)]
+ public IEnumerable<string> GetActiveTestRuns()
+ {
+ DisableResponseCaching();
+
+ List<string> result;
+ lock (testRuns)
+ {
+ result = new List<string>(testRuns.Keys);
+ }
+
+ return result;
+ }
+
+ /// <remarks>This method is typically called by the test harness.</remarks>
+ [OperationContract]
+ [WebGet(ResponseFormat = WebMessageFormat.Xml)]
+ public Message GetTestRunResults(string testRunId)
+ {
+ DisableResponseCaching();
+
+ TestRunContext context = GetTestRunContext(testRunId);
+ lock (context)
+ {
+ TestRun run = context.TestRun;
+ this.CompleteTestRun(run);
+
+ TestRunXmlBodyWriter writer = new TestRunXmlBodyWriter(run);
+ return Message.CreateMessage(
+ MessageVersion.None,
+ OperationContext.Current.OutgoingMessageHeaders.Action,
+ writer);
+ }
+ }
+
+ /// <remarks>This method is typically called by the test case.</remarks>
+ [OperationContract]
+ [WebGet(ResponseFormat = WebMessageFormat.Json)]
+ public void LogAssert(string testRunId, bool pass, string message, string name, string actual, string expected)
+ {
+ DisableResponseCaching();
+
+ TestRunContext context = GetTestRunContext(testRunId);
+ lock (context)
+ {
+ TestRun run = context.TestRun;
+ string prefixedName = context.TestNamePrefix + name;
+ TestResult result = run.TestResults.LastOrDefault(r => r.TestName == prefixedName);
+ if (result == null)
+ {
+ throw new InvalidOperationException("Unable to find test " + prefixedName + " in run " + testRunId);
+ }
+
+ result.DebugTrace.AppendLine(message);
+ if (!pass)
+ {
+ result.ErrorMessages.AppendLine(message);
+ result.ErrorMessages.AppendLine("Expected: " + expected);
+ result.ErrorMessages.AppendLine("Actual: " + actual);
+ }
+ }
+ }
+
+ /// <remarks>This method is typically called by the test case.</remarks>
+ [OperationContract]
+ [WebInvoke(ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
+ public void LogBatch(string[] urls)
+ {
+ DisableResponseCaching();
+
+ foreach (var url in urls)
+ {
+ Uri parsed = new Uri(OperationContext.Current.Channel.LocalAddress.Uri, url);
+ string methodName = parsed.Segments[parsed.Segments.Length - 1];
+ System.Reflection.MethodInfo method = this.GetType().GetMethod(methodName);
+ System.Reflection.ParameterInfo[] parameterInfos = method.GetParameters();
+ object[] parameters = new object[parameterInfos.Length];
+ System.Collections.Specialized.NameValueCollection query = System.Web.HttpUtility.ParseQueryString(parsed.Query);
+ for (int i = 0; i < parameters.Length; i++)
+ {
+ object value = query[parameterInfos[i].Name];
+ parameters[i] = Convert.ChangeType(value, parameterInfos[i].ParameterType, System.Globalization.CultureInfo.InvariantCulture);
+ }
+
+ method.Invoke(this, parameters);
+ }
+ }
+
+ /// <remarks>This method is typically called by the test case.</remarks>
+ [OperationContract]
+ [WebGet(ResponseFormat = WebMessageFormat.Json)]
+ public void LogTestStart(string testRunId, string name, DateTime startTime)
+ {
+ DisableResponseCaching();
+
+ TestRunContext context = GetTestRunContext(testRunId);
+ lock (context)
+ {
+ TestRun run = context.TestRun;
+ string prefixedName = context.TestNamePrefix + name;
+ Guid testId = Guid.NewGuid();
+ Guid executionId = Guid.NewGuid();
+ Guid testListId = run.TestLists.Single().Id;
+ run.TestDefinitions.Add(new TestDefinition()
+ {
+ Id = testId,
+ Name = prefixedName,
+ ExecutionId = executionId,
+ });
+ run.TestEntries.Add(new TestEntry()
+ {
+ TestId = testId,
+ ExecutionId = executionId,
+ TestListId = testListId
+ });
+ run.TestResults.Add(new TestResult()
+ {
+ ExecutionId = executionId,
+ TestId = testId,
+ TestListId = testListId,
+ TestName = prefixedName,
+ ComputerName = Environment.MachineName,
+ StartTime = startTime,
+ EndTime = startTime,
+ TestType = Guid.Parse("13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b"),
+ Outcome = InProgress,
+ // RelativeResultsDirectory?
+ });
+ }
+ }
+
+ /// <remarks>This method is typically called by the test case.</remarks>
+ [OperationContract]
+ [WebGet(ResponseFormat = WebMessageFormat.Json)]
+ public void LogTestDone(string testRunId, string name, int failures, int total, DateTime endTime)
+ {
+ DisableResponseCaching();
+
+ TestRunContext context = GetTestRunContext(testRunId);
+ lock (context)
+ {
+ TestRun run = context.TestRun;
+ string prefixedName = context.TestNamePrefix + name;
+ TestResult result = run.TestResults.LastOrDefault(r => r.TestName == prefixedName);
+ if (failures > 0)
+ {
+ result.Outcome = Failed;
+ }
+ else
+ {
+ result.Outcome = Passed;
+ }
+
+ result.EndTime = endTime;
+ }
+ }
+
+ [OperationContract]
+ [WebGet(ResponseFormat = WebMessageFormat.Json)]
+ public void MarkInProgress(string testRunId)
+ {
+ DisableResponseCaching();
+
+ TestRunContext context = GetTestRunContext(testRunId);
+ lock (context)
+ {
+ context.TestRun.ResultSummary.Outcome = InProgress;
+ }
+ }
+
+ /// <remarks>This method is typically called by the test harness.</remarks>
+ [OperationContract]
+ [WebGet(ResponseFormat = WebMessageFormat.Json)]
+ public void SetTestNamePrefix(string testRunId, string prefix)
+ {
+ DisableResponseCaching();
+
+ TestRunContext context = GetTestRunContext(testRunId);
+ lock (context)
+ {
+ context.TestNamePrefix = prefix;
+ }
+ }
+
+ /// <remarks>This method is typically called by the test case.</remarks>
+ [OperationContract]
+ [WebGet(ResponseFormat = WebMessageFormat.Json)]
+ public string TestCompleted(string testRunId, int failures, int total)
+ {
+ DisableResponseCaching();
+
+ var context = GetTestRunContext(testRunId);
+ lock (context)
+ {
+ string result;
+ if (context.TestPages.Count == 0)
+ {
+ context.TestRun.ResultSummary.Outcome = Completed;
+ result = "";
+ }
+ else
+ {
+ result = context.TestPages[0];
+ context.TestPages.RemoveAt(0);
+ }
+
+ return result;
+ }
+ }
+
+ private static TestRunContext CreateTestRunContextWithId(Guid value)
+ {
+ TestRun run = new TestRun();
+ run.Id = value;
+ run.Name = "Test run";
+ run.TestTimes.Creation = DateTime.Now;
+ run.TestTimes.Queueing = DateTime.Now;
+ run.TestLists.Add(new TestList()
+ {
+ Name = "All Results",
+ Id = Guid.NewGuid()
+ });
+
+ // For the time being, set up a fake test settings.
+ run.TestSettings.Id = Guid.NewGuid();
+
+ run.ResultSummary.Outcome = InProgress;
+
+ TestRunContext context = new TestRunContext();
+ context.TestRun = run;
+
+ return context;
+ }
+
+ private static void DisableResponseCaching()
+ {
+ WebOperationContext.Current.OutgoingResponse.Headers[HttpResponseHeader.CacheControl] = "no-cache";
+ }
+
+ private static TestRunContext GetTestRunContext(string testRunId)
+ {
+ if (testRunId == null)
+ {
+ throw new ArgumentNullException("testRunId");
+ }
+
+ lock (testRuns)
+ {
+ // For an 0-filled GUID, allow create-on-demand to simplify ad-hoc testing.
+ // Something like:
+ // http://localhost:8989/tests/odata-qunit-tests.htm?testRunId=00000000-0000-0000-0000-000000000000
+ if (!testRuns.ContainsKey(testRunId))
+ {
+ Guid value = Guid.Parse(testRunId);
+ if (value == Guid.Empty)
+ {
+ TestRunContext context = CreateTestRunContextWithId(value);
+ testRuns.Add(testRunId, context);
+ }
+ }
+
+ return testRuns[testRunId];
+ }
+ }
+
+ private void CompleteTestRun(TestRun run)
+ {
+ run.TestTimes.Finish = DateTime.Now;
+
+ // Fill counts in result object.
+ var summary = run.ResultSummary;
+ summary.Executed = 0;
+ summary.Error = 0;
+ summary.Failed = 0;
+ summary.Timeout = 0;
+ summary.Aborted = 0;
+ summary.Inconclusive = 0;
+ summary.PassedButRunAborted = 0;
+ summary.NotRunnable = 0;
+ summary.NotExecuted = 0;
+ summary.Disconnected = 0;
+ summary.Warning = 0;
+ summary.Passed = 0;
+ summary.Completed = 0;
+ summary.InProgress = 0;
+ summary.Pending = 0;
+
+ foreach (var testResult in run.TestResults)
+ {
+ string outcome = testResult.Outcome;
+ switch (outcome)
+ {
+ case InProgress:
+ summary.Executed++;
+ summary.InProgress++;
+ break;
+ case Failed:
+ summary.Executed++;
+ summary.Completed++;
+ summary.Failed++;
+ break;
+ case Passed:
+ summary.Executed++;
+ summary.Completed++;
+ summary.Passed++;
+ break;
+ default:
+ summary.Failed++;
+ break;
+ }
+ }
+
+ summary.Total = run.TestResults.Count;
+
+ if (summary.Failed != 0)
+ {
+ summary.Outcome = Failed;
+ }
+ else if (summary.Total <= 0 || summary.Passed < summary.Total)
+ {
+ summary.Outcome = Inconclusive;
+ }
+ else
+ {
+ summary.Outcome = Passed;
+ }
+ }
+ }
+
+ public class TestRunContext
+ {
+ public TestRunContext()
+ {
+ this.TestPages = new List<string>();
+ }
+
+ public TestRun TestRun { get; set; }
+ public string TestNamePrefix { get; set; }
+ public List<string> TestPages { get; set; }
+ }
+
+ public class TestResultWriter
+ {
+ private const string TestNamespace = "http://microsoft.com/schemas/VisualStudio/TeamTest/2010";
+
+ public static void WriteTestRun(TestRun run, string path)
+ {
+ if (run == null)
+ {
+ throw new ArgumentNullException("run");
+ }
+ if (path == null)
+ {
+ throw new ArgumentNullException("path");
+ }
+
+ using (XmlWriter writer = XmlWriter.Create(path))
+ {
+ WriteTestRun(run, path);
+ }
+ }
+
+ public static void WriteTestRun(TestRun run, XmlWriter writer)
+ {
+ if (run == null)
+ {
+ throw new ArgumentNullException("run");
+ }
+ if (writer == null)
+ {
+ throw new ArgumentNullException("writer");
+ }
+
+ writer.WriteStartElement("TestRun", TestNamespace);
+ writer.WriteGuidIfPresent("id", run.Id);
+ writer.WriteAttributeString("name", run.Name);
+ writer.WriteAttributeString("runUser", run.RunUser);
+
+ WriteTestSettings(run.TestSettings, writer);
+ WriteResultSummary(run.ResultSummary, writer);
+
+ // Write test definitions.
+ writer.WriteStartElement("TestDefinitions", TestNamespace);
+ foreach (var definition in run.TestDefinitions)
+ {
+ WriteTestDefinition(definition, writer);
+ }
+
+ writer.WriteEndElement();
+
+ // Write test lists.
+ writer.WriteStartElement("TestLists", TestNamespace);
+ foreach (var list in run.TestLists)
+ {
+ WriteTestList(list, writer);
+ }
+
+ writer.WriteEndElement();
+
+ // Write test entries.
+ writer.WriteStartElement("TestEntries", TestNamespace);
+ foreach (var entry in run.TestEntries)
+ {
+ WriteTestEntry(entry, writer);
+ }
+
+ writer.WriteEndElement();
+
+ // Write test results.
+ writer.WriteStartElement("Results", TestNamespace);
+ foreach (var result in run.TestResults)
+ {
+ WriteTestResults(result, writer);
+ }
+
+ writer.WriteEndElement();
+
+ // Close the test run element.
+ writer.WriteEndElement();
+ }
+
+ private static void WriteTestResults(TestResult result, XmlWriter writer)
+ {
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ writer.WriteStartElement("UnitTestResult", TestNamespace);
+ writer.WriteGuidIfPresent("testId", result.TestId);
+ writer.WriteGuidIfPresent("testListId", result.TestListId);
+ writer.WriteGuidIfPresent("executionId", result.ExecutionId);
+ writer.WriteGuidIfPresent("RelativeResultsDirectory", result.RelativeResultsDirectory);
+
+ writer.WriteAttributeString("testName", result.TestName);
+ writer.WriteAttributeString("computerName", result.ComputerName);
+ writer.WriteAttributeString("duration", result.Duration.ToString());
+ writer.WriteAttributeString("startTime", XmlConvert.ToString(result.StartTime));
+ writer.WriteAttributeString("endTime", XmlConvert.ToString(result.EndTime));
+ writer.WriteAttributeString("outcome", result.Outcome);
+
+ writer.WriteGuidIfPresent("testType", result.TestType);
+
+ if (result.DebugTrace.Length > 0)
+ {
+ writer.WriteStartElement("Output");
+
+ writer.WriteStartElement("DebugTrace");
+ writer.WriteString(result.DebugTrace.ToString());
+ writer.WriteEndElement();
+
+ writer.WriteStartElement("ErrorInfo");
+ writer.WriteStartElement("Message");
+ writer.WriteString(result.ErrorMessages.ToString());
+ writer.WriteEndElement();
+ writer.WriteEndElement();
+
+ writer.WriteEndElement();
+ }
+
+ writer.WriteEndElement();
+ }
+
+ private static void WriteTestEntry(TestEntry entry, XmlWriter writer)
+ {
+ if (entry == null)
+ {
+ throw new ArgumentNullException("entry");
+ }
+
+ writer.WriteStartElement("TestEntry", TestNamespace);
+ writer.WriteGuidIfPresent("testId", entry.TestId);
+ writer.WriteGuidIfPresent("testListId", entry.TestListId);
+ writer.WriteGuidIfPresent("executionId", entry.ExecutionId);
+ writer.WriteEndElement();
+ }
+
+ private static void WriteTestList(TestList list, XmlWriter writer)
+ {
+ if (list == null)
+ {
+ throw new ArgumentNullException("list");
+ }
+
+ writer.WriteStartElement("TestList", TestNamespace);
+ writer.WriteAttributeString("name", list.Name);
+ writer.WriteGuidIfPresent("id", list.Id);
+ writer.WriteEndElement();
+ }
+
+ private static void WriteTestDefinition(TestDefinition definition, XmlWriter writer)
+ {
+ if (definition == null)
+ {
+ throw new ArgumentNullException("definition");
+ }
+
+ writer.WriteStartElement("UnitTest", TestNamespace);
+ writer.WriteAttributeString("name", definition.Name);
+ writer.WriteAttributeString("storage", definition.Storage);
+ writer.WriteGuidIfPresent("id", definition.Id);
+
+ // There are more thing we could write here: DeploymentItems, Execution, TestMethod
+
+ // This is the minimum needed to load the test in the IDE.
+ writer.WriteStartElement("Execution", TestNamespace);
+ writer.WriteGuidIfPresent("id", definition.ExecutionId);
+ writer.WriteEndElement();
+
+ writer.WriteStartElement("TestMethod", TestNamespace);
+ writer.WriteAttributeString("codeBase", "fake-test-file.js");
+ writer.WriteAttributeString("adapterTypeName", "Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestAdapter, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.Adapter, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
+ writer.WriteAttributeString("className", "FakeClassName, TestLogging, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
+ writer.WriteAttributeString("name", definition.Name);
+ writer.WriteEndElement();
+
+ writer.WriteEndElement();
+ }
+
+ private static void WriteResultSummary(ResultSummary resultSummary, XmlWriter writer)
+ {
+ if (resultSummary == null)
+ {
+ throw new ArgumentNullException("resultSummary");
+ }
+
+ writer.WriteStartElement("ResultSummary", TestNamespace);
+ writer.WriteAttributeString("outcome", resultSummary.Outcome);
+
+ writer.WriteStartElement("Counters", TestNamespace);
+
+ foreach (var p in typeof(ResultSummary).GetProperties())
+ {
+ if (p.PropertyType != typeof(int))
+ {
+ continue;
+ }
+
+ int value = (int)p.GetValue(resultSummary, null);
+ string attributeName = p.Name;
+ attributeName = attributeName.Substring(0, 1).ToLowerInvariant() + attributeName.Substring(1);
+ writer.WriteAttributeString(attributeName, value.ToString());
+ }
+
+ writer.WriteEndElement();
+ writer.WriteEndElement();
+ }
+
+ private static void WriteTestSettings(TestSettings testSettings, XmlWriter writer)
+ {
+ if (testSettings == null)
+ {
+ throw new ArgumentNullException("testSettings");
+ }
+
+ writer.WriteStartElement("TestSettings", TestNamespace);
+ writer.WriteAttributeString("name", testSettings.Name);
+ writer.WriteGuidIfPresent("id", testSettings.Id);
+ // There are more things we could write here.
+ writer.WriteEndElement();
+ }
+ }
+
+ public static class XmlWriterExtensions
+ {
+ public static void WriteGuidIfPresent(this XmlWriter writer, string attributeName, Guid value)
+ {
+ if (value != Guid.Empty)
+ {
+ writer.WriteAttributeString(attributeName, value.ToString());
+ }
+ }
+ }
+
+ public class TestRun
+ {
+ public TestRun()
+ {
+ this.TestDefinitions = new List<TestDefinition>();
+ this.TestLists = new List<TestList>();
+ this.TestEntries = new List<TestEntry>();
+ this.TestResults = new List<TestResult>();
+ this.ResultSummary = new ResultSummary();
+ this.TestTimes = new TestTimes();
+ this.TestSettings = new TestSettings();
+
+ this.Id = Guid.NewGuid();
+ this.RunUser = Environment.UserDomainName + "\\" + Environment.UserName;
+ }
+
+ public Guid Id { get; set; }
+ public string Name { get; set; }
+ public string RunUser { get; set; }
+ public TestSettings TestSettings { get; set; }
+ public TestTimes TestTimes { get; set; }
+ public ResultSummary ResultSummary { get; set; }
+ public List<TestDefinition> TestDefinitions { get; set; }
+ public List<TestList> TestLists { get; set; }
+ public List<TestEntry> TestEntries { get; set; }
+ public List<TestResult> TestResults { get; set; }
+ }
+
+ public class TestSettings
+ {
+ public Guid Id { get; set; }
+ public string Name { get; set; }
+ }
+
+ public class TestTimes
+ {
+ public DateTime Creation { get; set; }
+ public DateTime Queueing { get; set; }
+ public DateTime Start { get; set; }
+ public DateTime Finish { get; set; }
+ }
+
+ public class ResultSummary
+ {
+ public string Outcome { get; set; }
+ public int Total { get; set; }
+ public int Executed { get; set; }
+ public int Error { get; set; }
+ public int Failed { get; set; }
+ public int Timeout { get; set; }
+ public int Aborted { get; set; }
+ public int Inconclusive { get; set; }
+ public int PassedButRunAborted { get; set; }
+ public int NotRunnable { get; set; }
+ public int NotExecuted { get; set; }
+ public int Disconnected { get; set; }
+ public int Warning { get; set; }
+ public int Passed { get; set; }
+ public int Completed { get; set; }
+ public int InProgress { get; set; }
+ public int Pending { get; set; }
+ }
+
+ public class TestDefinition
+ {
+ public string Name { get; set; }
+ public string Storage { get; set; }
+ public Guid Id { get; set; }
+ public Guid ExecutionId { get; set; }
+ }
+
+ public class TestList
+ {
+ public string Name { get; set; }
+ public Guid Id { get; set; }
+ }
+
+ public class TestEntry
+ {
+ public Guid TestId { get; set; }
+ public Guid ExecutionId { get; set; }
+ public Guid TestListId { get; set; }
+ }
+
+ public class TestResult
+ {
+ public TestResult()
+ {
+ this.DebugTrace = new StringBuilder();
+ this.ErrorMessages = new StringBuilder();
+ }
+
+ public Guid ExecutionId { get; set; }
+ public Guid TestId { get; set; }
+ public string TestName { get; set; }
+ public string ComputerName { get; set; }
+ public TimeSpan Duration { get { return this.EndTime - this.StartTime; } }
+ public DateTime StartTime { get; set; }
+ public DateTime EndTime { get; set; }
+ public Guid TestType { get; set; }
+ public string Outcome { get; set; }
+ public Guid TestListId { get; set; }
+ public Guid RelativeResultsDirectory { get; set; }
+ public StringBuilder DebugTrace { get; set; }
+ public StringBuilder ErrorMessages { get; set; }
+ }
+
+ class TestRunXmlBodyWriter : BodyWriter
+ {
+ private readonly TestRun run;
+
+ public TestRunXmlBodyWriter(TestRun run)
+ : base(true)
+ {
+ this.run = run;
+ }
+
+ protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
+ {
+ TestResultWriter.WriteTestRun(this.run, writer);
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/0367d2bc/datajs/tests/common/TestSynchronizerClient.js
----------------------------------------------------------------------
diff --git a/datajs/tests/common/TestSynchronizerClient.js b/datajs/tests/common/TestSynchronizerClient.js
new file mode 100644
index 0000000..c758413
--- /dev/null
+++ b/datajs/tests/common/TestSynchronizerClient.js
@@ -0,0 +1,218 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// TestSynchronizer Client
+// Use to log assert pass/fails and notify mstest a test has completed execution
+
+(function (window, undefined) {
+ var testRunId = "";
+ var serviceRoot = "./common/TestLogger.svc/";
+ var recording = null;
+ var recordingLength = 0;
+ var maxStringLength = 8192;
+ var maxPostLength = 2097152;
+
+ var callTestSynchronizer = function (methodName, parameterUrl) {
+ /// <summary>Invokes a function on the test synchronizer.</summary>
+ /// <param name="partialUrl" type="String" optional="true">URL to work with.</param>
+ /// <returns type="String">A response from the server, possibly null.</returns>
+ /// <remarks>
+ /// If the recording variable is assigned, then the call is logged
+ /// but nothing is invoked.
+ /// </remarks>
+
+ var partialUrl;
+ if (testRunId) {
+ partialUrl = methodName + "?testRunId=" + testRunId + "&" + parameterUrl;
+ }
+ else {
+ partialUrl = methodName + "?" + parameterUrl;
+ }
+
+ var url = serviceRoot + partialUrl;
+
+ if (recording) {
+ if (url.length > maxStringLength) {
+ url = url.substr(0, maxStringLength);
+ }
+
+ recordingLength += url.length;
+ if (recordingLength > maxPostLength) {
+ submitRecording();
+ recording = [];
+ recordingLength = url.length;
+ }
+
+ recording.push(url);
+ return null;
+ }
+
+ var xhr;
+ if (window.XMLHttpRequest) {
+ xhr = new window.XMLHttpRequest();
+ } else {
+ xhr = new ActiveXObject("Msxml2.XMLHTTP.6.0");
+ }
+
+ xhr.open("GET", url, false);
+ xhr.send();
+ return xhr.responseText;
+ };
+
+ var getLogPrefix = function (result) {
+ /// <summary>Returns the log prefix for a given result</summary>
+ /// <param name="result" type="Boolean">Whether the result is pass or fail. If null, the log line is assumed to be diagnostic</param>
+ return "[" + getShortDate() + "] " + (result === true ? "[PASS] " : (result === false ? "[FAIL] " : ""));
+ };
+
+ var getShortDate = function () {
+ /// <summary>Returns the current date and time formatted as "yyyy-mm-dd hh:mm:ss.nnn".</summary>
+ var padToLength = function (number, length) {
+ var result = number + "";
+ var lengthDiff = length - result.length;
+ for (var i = 0; i < lengthDiff; i++) {
+ result = "0" + result;
+ }
+
+ return result;
+ }
+
+ var date = new Date();
+ var day = padToLength(date.getDate(), 2);
+ var month = padToLength(date.getMonth() + 1, 2);
+ var year = date.getFullYear();
+
+ var hours = padToLength(date.getHours(), 2);
+ var minutes = padToLength(date.getMinutes(), 2);
+ var seconds = padToLength(date.getSeconds(), 2);
+ var milliseconds = padToLength(date.getMilliseconds(), 3);
+
+ return year + "-" + month + "-" + day + " " + hours + ":" + minutes + ":" + seconds + "." + milliseconds;
+ };
+
+ var submitRecording = function () {
+ var body = { urls: recording };
+ postToUrl("LogBatch", body);
+ };
+
+ var postToUrl = function (methodName, body) {
+ /// <summary>POSTs body to the designated methodName.</summary>
+ var xhr;
+ if (window.XMLHttpRequest) {
+ xhr = new window.XMLHttpRequest();
+ } else {
+ xhr = new ActiveXObject("Msxml2.XMLHTTP.6.0");
+ }
+
+ var url = serviceRoot + methodName;
+ xhr.open("POST", url, false);
+ xhr.setRequestHeader("Content-Type", "application/json");
+ xhr.send(window.JSON.stringify(body));
+ if (xhr.status < 200 || xhr.status > 299) {
+ throw { message: "Unable to POST to url.\r\n" + xhr.responseText };
+ }
+
+ return xhr.responseText;
+ }
+
+ function LogAssert(result, message, name, expected, actual) {
+ var parameterUrl = "pass=" + result + "&message=" + encodeURIComponent(message) + "&name=" + encodeURIComponent(name);
+
+ if (!result) {
+ parameterUrl += "&actual=" + encodeURIComponent(actual) + "&expected=" + encodeURIComponent(expected);
+ }
+
+ callTestSynchronizer("LogAssert", parameterUrl);
+ }
+
+ function LogTestStart(name) {
+ callTestSynchronizer("LogTestStart", "name=" + encodeURIComponent(name) + "&startTime=" + encodeURIComponent(getShortDate()));
+ }
+
+ function LogTestDone(name, failures, total) {
+ callTestSynchronizer("LogTestDone", "name=" + encodeURIComponent(name) + "&failures=" + failures + "&total=" + total + "&endTime=" + encodeURIComponent(getShortDate()));
+ }
+
+ function TestCompleted(failures, total) {
+ return callTestSynchronizer("TestCompleted", "failures=" + failures + "&total=" + total);
+ }
+
+ var extractTestRunId = function () {
+ /// <summary>Extracts the testRunId value from the window query string.</summary>
+ /// <returns type="String">testRunId, possibly empty.</returns>
+ var i, len;
+ var uri = window.location.search;
+ if (uri) {
+ var parameters = uri.split("&");
+ for (i = 0, len = parameters.length; i < len; i++) {
+ var index = parameters[i].indexOf("testRunId=");
+ if (index >= 0) {
+ return parameters[i].substring(index + "testRunId=".length);
+ }
+ }
+ }
+
+ return "";
+ };
+
+ var init = function (qunit) {
+ /// <summary>Initializes the test logger synchronizer.</summary>
+ /// <param name="qunit">Unit testing to hook into.</param>
+ /// <remarks>If there is no testRunId present, the QUnit functions are left as they are.</remarks>
+ var logToConsole = function (context) {
+ if (window.console && window.console.log) {
+ window.console.log(context.result + ' :: ' + context.message);
+ }
+ };
+
+ testRunId = extractTestRunId();
+ if (!testRunId) {
+ qunit.log = logToConsole;
+ } else {
+ recording = [];
+ qunit.log = function (context) {
+ logToConsole(context);
+
+ var name = qunit.config.current.testName;
+ if (!(context.actual && context.expected)) {
+ context.actual = context.result;
+ context.expected = true;
+ }
+ LogAssert(context.result, getLogPrefix(context.result) + context.message, name, window.JSON.stringify(context.expected), window.JSON.stringify(context.actual));
+ };
+
+ qunit.testStart = function (context) {
+ LogTestStart(context.name);
+ };
+
+ qunit.testDone = function (context) {
+ LogTestDone(context.name, context.failed, context.total);
+ }
+
+ qunit.done = function (context) {
+ submitRecording();
+ recording = null;
+
+ var nextUrl = TestCompleted(context.failed, context.total);
+ nextUrl = JSON.parse(nextUrl).d;
+ if (nextUrl) {
+ window.location.href = nextUrl;
+ }
+ }
+ }
+ };
+
+ window.TestSynchronizer = {
+ init: init
+ };
+})(window);
\ No newline at end of file