public static void VerifyConcurrency(AstoriaResponse response) { if (response.Request.ETagHeaderExpected) { if (!response.ETagHeaderFound && !RequestUtil.ExpectEmptyHeaders(response)) { AstoriaTestLog.FailAndThrow("Missing ETag header in response"); } } else if (response.ETagHeaderFound) { if (string.IsNullOrEmpty(response.ETagHeader)) { if (Versioning.Server.BugFixed_NullETagWhenTypeHasNoConcurrency) { AstoriaTestLog.FailAndThrow("Unexpected null ETag header in response"); } else { AstoriaTestLog.WriteLine("Ignoring unexpected null ETag header in response due to known bug"); } } else { AstoriaTestLog.FailAndThrow("Unexpected ETag header in response: '" + response.ETagHeader + "'"); } } }
internal static Workspaces GetWorkspacesFilteredByTestProperties(Action <Workspace> beforeWorkspaceCreate) { if (_workspaceCreationFailed) { throw new TestFailedException("Workspaces failed on creation previously"); } if (_filteredWorkspaces == null) { try { _filteredWorkspaces = CreateFilteredWorkspaces(AstoriaTestProperties.DataLayerProviderKinds, AstoriaTestProperties.MaxPriority, true, beforeWorkspaceCreate); } catch (System.Reflection.ReflectionTypeLoadException typeloadException) { _workspaceCreationFailed = true; AstoriaTestLog.WriteLine("LoaderExceptions:"); foreach (Exception exc in typeloadException.LoaderExceptions) { AstoriaTestLog.FailAndContinue(exc); } throw typeloadException; } catch (Exception exc) { _workspaceCreationFailed = true; throw; } } return(_filteredWorkspaces); }
internal static object GetSingleValueFromIQueryable(IQueryable querable, bool throwOnError) { object val = null; IEnumerator enumerator = querable.GetEnumerator(); bool moved = enumerator.MoveNext(); if (!moved && throwOnError) { throw new TestFailedException("Expected to find one result but none exist in Iqueryable"); } else if (!moved && !throwOnError) { AstoriaTestLog.WriteLine("Error occured, cannot find a result in IQueryable"); AstoriaTestLog.FailureFound = true; } else { val = enumerator.Current; } moved = enumerator.MoveNext(); if (moved && throwOnError) { throw new TestFailedException("Expected to find one result but found 2 exist in Iqueryable"); } else if (moved && !throwOnError) { AstoriaTestLog.WriteLine("Expected to find one result but found 2 exist in Iqueryable"); AstoriaTestLog.FailureFound = true; } return(val); }
public static BatchResponse ParseBatchResponse(BatchRequest batchRequest, StringReader reader, string boundary) { // even if content-id is specified, we may not get it back in error cases. This doesn't feel entirely correct, it seems like there should // always be a 1-1 mapping that explicitly honors content-id's // TODO: responses / requests without content IDs should be matched up in order within changeset only // changesets should have the right number of responses too BatchResponse response = null;// new BatchResponse(batchRequest); List <ResponseFragment> unmatchedFragments = new List <ResponseFragment>(); List <AstoriaRequest> unmatchedRequests = batchRequest.Requests .Union(batchRequest.Changesets.SelectMany(changeset => changeset.AsEnumerable())) .ToList(); foreach (ResponseFragment fragment in ParseBatchResponse(reader, boundary)) { AstoriaRequest request = null; if (fragment.ContentID != null) { request = unmatchedRequests .FirstOrDefault(r => r.Headers.ContainsKey("Content-ID") && r.Headers["Content-ID"].Equals(fragment.ContentID)); } if (request == null) { unmatchedFragments.Add(fragment); } else { unmatchedRequests.Remove(request); response.Responses.Add(FragmentToResponse(request, fragment)); } } if (unmatchedFragments.Any()) { if (unmatchedFragments.Count < unmatchedRequests.Count) { AstoriaTestLog.WriteLine("Warning: recieved fewer batch response fragments than expected"); } else if (unmatchedFragments.Count > unmatchedRequests.Count) { AstoriaTestLog.FailAndThrow("Recieved more batch fragments than expected"); } for (int i = 0; i < unmatchedFragments.Count; i++) { response.Responses.Add(FragmentToResponse(unmatchedRequests[i], unmatchedFragments[i])); } } return(response); }
protected void QueryNavProperty(Func <IEdmEntityType, List <string> > getNavPropsLambda) { ForEachResourceType( (resourceType, resourceContainer, workspace) => { CreateContext(resourceType, workspace); AstoriaTestLog.WriteLine("Querying entityset {0}", resourceContainer.Name); IEdmEntityType entityType = DataServiceMetadata.EntityTypes.FirstOrDefault(eType => eType.Name == resourceType.Name); KeyExpression keyExp = workspace.GetRandomExistingKey(resourceContainer); if (keyExp != null && (!(resourceContainer is ServiceOperation))) { List <KVP> keyExpValues = WebDataCtxWrapper.ConvertKeyExpression(keyExp); foreach (string collNavProperty in getNavPropsLambda(entityType)) { AstoriaTestLog.WriteLine("Querying Properties {0}", collNavProperty); try { DataServiceQuery queryWithExpand = ((DataServiceQuery)CurrentContext.CreateQueryOfT(resourceContainer.Name, resourceType.ClientClrType)).Where(keyExpValues); //.SelectNavigationProperty(collNavProperty) as DataServiceQuery; IEnumerator enumerateQueryResults = ((IQueryable)queryWithExpand).GetEnumerator(); object entity = null; if (enumerateQueryResults.MoveNext()) { entity = enumerateQueryResults.Current; } while (enumerateQueryResults.MoveNext()) { ; } Uri entityUri = null; CurrentContext.UnderlyingContext.TryGetUri(entity, out entityUri); CurrentContext.Detach(entity); entityUri = new Uri(entityUri.OriginalString + "/" + collNavProperty); var qoREsponse = CurrentContext.ExecuteOfT(GetResourceType(collNavProperty, resourceType).ClientClrType, entityUri); IEnumerator enumerator = qoREsponse.GetEnumerator(); while (enumerator.MoveNext()) { ; } if (ChainedFunction != null) { ChainedFunction(); } } catch (System.Reflection.TargetInvocationException tiException) { if (!tiException.ToString().Contains("Sequence Contains")) { throw tiException; } } } } }, false); }
protected override void CreateWebService(bool verify) { #if ClientSKUFramework string serviceHostApplicationName = exeName.Replace("TestServiceHost", "") + ".exe"; AstoriaTestLog.WriteLine("DestinationFolder is {0}", DestinationFolder); AstoriaTestLog.WriteLine("ExecutablePath is {0}", ExecutablePath); Directory.CreateDirectory(DestinationFolder); File.Copy( Path.Combine(Environment.CurrentDirectory, serviceHostApplicationName), Path.Combine(ExecutablePath) , true ); CopyClientSKUFiles(DestinationFolder); #endif CopyServiceFiles(); #if !ClientSKUFramework if (this.Workspace.ServiceModifications != null) { this.Workspace.ServiceModifications.ApplyChanges(DestinationFolder); } string tempDllLocation = Environment.CurrentDirectory; if (ProcessHelper.IsLocalMachine(this.MachineName)) { tempDllLocation = DestinationFolder; } CompileServiceHostExe(tempDllLocation, DestinationFolder, exeName); if (!ProcessHelper.IsLocalMachine(this.MachineName)) { File.Copy(Path.Combine(tempDllLocation, exeName + ".exe"), Path.Combine(DestinationFolder, exeName + ".exe"), true); } #endif DeployAppConfig(); // set up service URIs ServiceRootUri = rootUri + "/" + serviceInstanceName; ServiceUri = ServiceRootUri; Start(); if (verify) { Thread.Sleep(1000); VerifyService(); } }
public static AstoriaRequest BuildDelete(Workspace workspace, KeyExpressions existingKeys, HttpStatusCode expectedStatusCode, SerializationFormatKind format, out KeyExpression deletedKey) { deletedKey = existingKeys.Choose(); if (deletedKey == null) { AstoriaTestLog.WriteLine("Cannot build DELETE request, no existing keys"); return(null); } existingKeys.Remove(deletedKey); return(BuildDelete(workspace, deletedKey, expectedStatusCode, format)); }
protected void QueryLoadProperty(Func <IEdmEntityType, List <string> > getNavPropsLambda, bool workspaceShouldUpdate) { foreach (EntityDescriptor entityDec in CurrentContext.UnderlyingContext.Entities) { IEdmEntityType entityType = DataServiceMetadata.EntityTypes.FirstOrDefault(eType => eType.Name == entityDec.Entity.GetType().Name); foreach (string collNavProperty in getNavPropsLambda(entityType)) { AstoriaTestLog.WriteLine("Loading Properties {0}", collNavProperty); CurrentContext.LoadProperty(entityDec.Entity, collNavProperty); VerifyLoadProperty(entityDec.Entity, collNavProperty, CurrentContext); } } }
protected void QueryExpandProperty(Func <IEdmEntityType, List <string> > getNavPropsLambda) { ForEachResourceType( (resourceType, resourceContainer, workspace) => { CreateContext(resourceType, workspace); AstoriaTestLog.WriteLine("Querying entityset {0}", resourceContainer.Name); IEdmEntityType entityType = DataServiceMetadata.EntityTypes.FirstOrDefault(eType => eType.Name == resourceType.Name); KeyExpression keyExp = workspace.GetRandomExistingKey(resourceContainer); if (keyExp != null) { List <KVP> keyExpValues = WebDataCtxWrapper.ConvertKeyExpression(keyExp); foreach (string collNavProperty in getNavPropsLambda(entityType)) { AstoriaTestLog.WriteLine("Expanding Properties {0}", collNavProperty); try { DataServiceQuery queryWithExpand = ((DataServiceQuery)CurrentContext.CreateQueryOfT(resourceContainer.Name, resourceType.ClientClrType)).Where(keyExpValues).Expand(collNavProperty); IEnumerator enumerateQueryResults = ((IQueryable)queryWithExpand).GetEnumerator(); try { while (enumerateQueryResults.MoveNext()) { ; } } catch (OptimisticConcurrencyException oException) { AstoriaTestLog.WriteLineIgnore("Failed as per Expand causes etags not to be included." + oException.Message); } if (ChainedFunction != null) { ChainedFunction(); } } catch (System.Reflection.TargetInvocationException tiException) { if (!tiException.ToString().Contains("Sequence Contains")) { throw tiException; } } } } }, false); }
public static void Verify(List <APICallLogEntry> expectedCalls, List <APICallLogEntry> observedCalls, Func <APICallLogEntry, APICallLogEntry, bool> compare) { observedCalls = observedCalls.Where(e => !ClassesToIgnore.Any(c => e.MethodName.StartsWith(c + "."))).ToList(); List <int> mismatchedLines = new List <int>(); AstoriaTestLog.WriteLineIgnore("Observed call order: "); for (int i = 0; i < observedCalls.Count; i++) { if (i >= expectedCalls.Count || !compare(expectedCalls[i], observedCalls[i])) { AstoriaTestLog.WriteLine("(Observed) " + i + " - " + observedCalls[i].ToString()); mismatchedLines.Add(i); } else if (!AstoriaTestProperties.IsLabRun) { AstoriaTestLog.WriteLineIgnore("(Observed) " + i + " - " + observedCalls[i].ToString()); } } if (observedCalls.Count < expectedCalls.Count) { for (int i = observedCalls.Count; i < expectedCalls.Count; i++) { mismatchedLines.Add(i); } } if (mismatchedLines.Any()) { AstoriaTestLog.WriteLineIgnore("Expected call order: "); for (int i = 0; i < expectedCalls.Count; i++) { if (mismatchedLines.Contains(i)) { AstoriaTestLog.WriteLine("(Expected) " + i + " - " + expectedCalls[i].ToString()); } else { AstoriaTestLog.WriteLineIgnore("(Expected) " + i + " - " + expectedCalls[i].ToString()); } } AstoriaTestLog.FailAndThrow("Observed call log did not match baseline"); } }
protected void UpdateAction(ResourceType resourceType, ResourceContainer resourceContainer, object entity) { AstoriaTestLog.WriteLine("Updating entity set :{0} , entity type :{1}", resourceContainer.Name, resourceType.Name); //Change any of the properties that make up the Etag #if !ClientSKUFramework IEdmEntityType entityType = DataServiceMetadata.GetEntityType(entity); foreach (string eTagProperty in entityType.EtagProperties()) { Type propertyType = entity.GetType().GetProperty(eTagProperty).PropertyType; object newValue = propertyType.GetTypedValue(eTagProperty, resourceType.Properties[eTagProperty].Facets.MaxSize); //entity.SetPropertyValue(eTagProperty, newValue); } #endif CurrentContext.UpdateObject(entity); ExecuteAndValidate(() => { CurrentContext.SaveChanges(SaveChangeOption); }); }
public DataServiceQueryContinuation GetNextLinkUri(ICollection collection, bool catchException) { DataServiceQueryContinuation nextLink = null; try { nextLink = this._QueryResponse.GetContinuation(collection); } catch (ArgumentNullException arge) { if (catchException) { AstoriaTestLog.WriteLine(arge.Message); nextLink = null; } else { throw; } } return(nextLink); }
protected object InsertAction(ResourceType resourceType, ResourceContainer resourceContainer, Workspace workspace) { AstoriaTestLog.WriteLine("Inserting into entity set :{0} , entity type :{1}", resourceContainer.Name, resourceType.Name); object entity = null; if (resourceType.Name == "AllTypesComplexEntity") { IQueryable results = CurrentContext.ExecuteOfT(resourceType.ClientClrType, new Uri(String.Format("{0}?$top=1", resourceContainer.Name), UriKind.RelativeOrAbsolute)).AsQueryable(); IEnumerator enumerat = results.GetEnumerator(); enumerat.MoveNext(); entity = enumerat.Current; ResourceProperty keyProperty = resourceType.Properties.Cast <ResourceProperty>().First(prop => prop.PrimaryKey != null); entity.GetType().GetProperty(keyProperty.Name).SetValue(entity, null, null); } else { entity = resourceType.CreateInstance(false); } CurrentContext.AddObject(resourceContainer.Name, entity); CurrentContext.EnsureInsert(entity, resourceType); EnsureInsert(CurrentContext.UnderlyingContext, entity, resourceContainer.Name, workspace, ""); ExecuteAndValidate(() => { CurrentContext.SaveChanges(SaveChangeOption); }); return(entity); }
public static void Verify(Workspace w, ExpNode q, IEnumerable results, ResourceType resType, ComplexType cType, bool bCount) { long count = 0; if (bCount) { object[] args = new object[] { results }; Type qorType = typeof(QueryOperationResponseWrapper <>).MakeGenericType(resType.ClientClrType); object qor = Activator.CreateInstance(qorType, args); PropertyInfo pi = qor.GetType().GetProperty("GetTotalCount"); object i = pi.GetValue(qor, new object[] { }); count = (long)i; LinqQueryBuilder lb = new LinqQueryBuilder(w); lb.CountingMode = true; lb.Build(q); object baselineElementsCount = CommonPayload.CreateList(lb.QueryResult).Count; AstoriaTestLog.IsTrue(count == Convert.ToInt64(baselineElementsCount), "Count is different.Count is " + count.ToString() + ". Baseline count is " + baselineElementsCount.ToString()); } LinqQueryBuilder linqBuilder = new LinqQueryBuilder(w); linqBuilder.Build(q); IQueryable baselines = linqBuilder.QueryResult; IEnumerator b = null; IEnumerator r = null; try { b = TrustedMethods.IQueryableGetEnumerator(baselines); r = results.GetEnumerator(); } catch (InvalidOperationException invalidOperation) { if (!AstoriaTestProperties.IsRemoteClient) { throw invalidOperation; } } PropertyInfo propertyInfo = null; Object expectedResult1 = null; Object expectedResult2 = null; try { while (b.MoveNext() && r.MoveNext()) { if (b.Current == null) { return; } if (r.Current == null) { throw new TestFailedException("Less results than expected"); } //skip verification for Binary data type for Linq to Sql if (AstoriaTestProperties.DataLayerProviderKinds[0] == DataLayerProviderKind.LinqToSql && b.Current is System.Byte[]) { return; } if (b.Current is System.Byte[]) { byte[] newBase = (byte[])b.Current; byte[] newAct = (byte[])r.Current; if (newBase.Length != newAct.Length) { throw new TestFailedException("Failed to compare the results!"); } } #if !ClientSKUFramework else if (b.Current is System.Data.Linq.Binary) { System.Data.Linq.Binary newBase = (System.Data.Linq.Binary)b.Current; System.Data.Linq.Binary newAct = new System.Data.Linq.Binary((byte[])r.Current); if (newBase.Length != newAct.Length) { throw new TestFailedException("Failed to compare the results!"); } } #endif else if (b.Current is System.Xml.Linq.XElement) { if (b.Current.ToString() != r.Current.ToString()) { throw new TestFailedException("Failed to compare the results!"); } } else { if (!b.Current.Equals(r.Current)) { if (cType != null) { foreach (ResourceProperty property in cType.Properties) { if (!property.IsNavigation && !property.IsComplexType && !(property.Type is CollectionType)) { propertyInfo = b.Current.GetType().GetProperty(property.Name); expectedResult1 = propertyInfo.GetValue(b.Current, null); PropertyInfo propertyInfo2 = r.Current.GetType().GetProperty(property.Name); expectedResult2 = propertyInfo2.GetValue(r.Current, null); if (expectedResult1 != expectedResult2) { if (expectedResult1 is System.Xml.Linq.XElement) { expectedResult1 = ((System.Xml.Linq.XElement)expectedResult1).ToString(System.Xml.Linq.SaveOptions.None); } if (expectedResult2 is System.Xml.Linq.XElement) { expectedResult2 = ((System.Xml.Linq.XElement)expectedResult2).ToString(System.Xml.Linq.SaveOptions.None); } #if !ClientSKUFramework if (expectedResult1 is byte[]) { expectedResult1 = new System.Data.Linq.Binary((byte[])expectedResult1); } if (expectedResult2 is byte[]) { expectedResult2 = new System.Data.Linq.Binary((byte[])expectedResult2); } #endif AstoriaTestLog.AreEqual(expectedResult1, expectedResult2, String.Format("Resource value for {0} does not match", property.Name), false); } } } } else { foreach (ResourceProperty property in resType.Properties) { if (!property.IsNavigation && !property.IsComplexType && !(property.Type is CollectionType)) { //skip verification for Binary data type for Linq to Sql if (AstoriaTestProperties.DataLayerProviderKinds[0] == DataLayerProviderKind.LinqToSql && property.Type.Name == "LinqToSqlBinary") { return; } propertyInfo = b.Current.GetType().GetProperty(property.Name); expectedResult1 = propertyInfo.GetValue(b.Current, null); PropertyInfo propertyinfo2 = r.Current.GetType().GetProperty(property.Name); expectedResult2 = propertyinfo2.GetValue(r.Current, null); if (expectedResult1 != expectedResult2) { if (expectedResult1 is System.Xml.Linq.XElement) { expectedResult1 = ((System.Xml.Linq.XElement)expectedResult1).ToString(System.Xml.Linq.SaveOptions.None); } if (expectedResult2 is System.Xml.Linq.XElement) { expectedResult2 = ((System.Xml.Linq.XElement)expectedResult2).ToString(System.Xml.Linq.SaveOptions.None); } #if !ClientSKUFramework if (expectedResult1 is byte[]) { expectedResult1 = new System.Data.Linq.Binary((byte[])expectedResult1); } if (expectedResult2 is byte[]) { expectedResult2 = new System.Data.Linq.Binary((byte[])expectedResult2); } #endif AstoriaTestLog.AreEqual(expectedResult1, expectedResult2, String.Format("Resource value for {0} does not match", property.Name), false); } } } } } } } } catch (Exception e) { AstoriaTestLog.WriteLine(e.ToString()); throw; } }
protected override bool SendRequest_Internal(AstoriaRequest request, out AstoriaResponse response) { string uri = request.URI; // workaround bug that same url is always cached. if (request.Verb == RequestVerb.Get) { if (uri.Contains('?')) { uri = String.Format("{0}&bug={1}", uri, DateTime.Now.Ticks.ToString()); } else { uri = String.Format("{0}?bug={1}", uri, DateTime.Now.Ticks.ToString()); } } Exception exc = null; int statusCode = -1; XmlHttpClassComWrapper nativeWebRequest; try { nativeWebRequest = XmlHttpClassComWrapper.CreateXmlHttpClassWrapper(); } catch { AstoriaTestLog.WriteLineIgnore("Could not create a new XmlHttp com wrapper"); AstoriaTestLog.WriteLineIgnore("Time before sleep: " + DateTime.Now); Threading.Thread.Sleep(new TimeSpan(0, 0, 30)); AstoriaTestLog.WriteLineIgnore("Time after sleep: " + DateTime.Now); response = null; return(false); } using (nativeWebRequest) { string userName = null; string password = null; if (AstoriaTestProperties.HostAuthenicationMethod.ToLower().Equals("windows")) { userName = string.Empty; password = string.Empty; } nativeWebRequest.open(request.Verb.ToHttpMethod(), uri, false, userName, password); // this does Accept and Content-Type for us foreach (KeyValuePair <string, string> header in request.Headers) { nativeWebRequest.setRequestHeader(header.Key, header.Value); } byte[] toSend = request.PayloadBytes; try { nativeWebRequest.send(toSend); statusCode = nativeWebRequest.status; } catch (System.Reflection.TargetInvocationException targetInvokationException) { exc = targetInvokationException; if (targetInvokationException.InnerException != null && targetInvokationException.InnerException is System.Runtime.InteropServices.COMException) { //HACK DUE TO XmlHttpIssue // info at http://www.enhanceie.com/ie/bugs.asp if (nativeWebRequest.status == 1223) { statusCode = (int)HttpStatusCode.NoContent; } else { throw targetInvokationException; } } } response = new AstoriaResponse(request); // Assign status code. if (Enum.IsDefined(typeof(HttpStatusCode), statusCode)) { response.ActualStatusCode = (HttpStatusCode)statusCode; } else { response.ActualStatusCode = HttpStatusCode.Ambiguous; } // Assign Content-Type and other headers. string headers = nativeWebRequest.AllResponseHeaders; response.ContentType = null; if (headers != null) { // Parse headers. foreach (string headerLine in headers.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries)) { int valueIndex = headerLine.IndexOf(':'); if (valueIndex > 0 && valueIndex < headerLine.Length - 1) { response.Headers[headerLine.Substring(0, valueIndex)] = headerLine.Substring(valueIndex + 2); } } } else { // No headers. if (statusCode == (int)HttpStatusCode.NoContent) { AstoriaTestLog.WriteLine("Warning: XmlHttp does not return headers when status code is No Content"); } else { AstoriaTestLog.FailAndThrow("Failed to read headers from XmlHttp response"); } } response.ETagHeaderFound = response.Headers.ContainsKey("ETag"); // Assign payload. if (response.ContentType == SerializationFormatKinds.MimeApplicationOctetStream) { response.Bytes = nativeWebRequest.responseBody; } else { response.Payload = nativeWebRequest.responseText; } GC.Collect(); return(true); } }
public static void VerifyGet(AstoriaResponse response) { AstoriaRequest request = response.Request; if (request.Query != null) { LinqQueryBuilder linqBuilder = new LinqQueryBuilder(request.Workspace); linqBuilder.Build(request.Query); if (request.URI.Contains("$value")) { CommonPayload payload; // $value queries will sometimes return unexpected 404s due to null values // if we get a 404 unexpectedly, the underlying result must be null if (response.ActualStatusCode == System.Net.HttpStatusCode.NotFound) { payload = request.CommonPayload; } IEnumerable enumerable = linqBuilder.QueryResult; object expectedResult = CommonPayload.GetSingleValueFromIQueryable(linqBuilder.QueryResult, false); ResourceProperty rp = request.GetPropertyFromQuery(); CommonPayload.ComparePrimitiveValuesObjectAndString(expectedResult, rp.Type.ClrType, response.Payload, true, request.Format, false); } else if (request.URI.Contains("$count") || request.URI.Contains("$inlinecount")) { if (request.URI.Contains("$count")) { // Versioning: make sure the server always returns 2.0. VerifySpecificResponseVersion(response, 2, 0); // PlainText payload. try { VerifyStatusCode(response); if (response.Request.ExpectedStatusCode == System.Net.HttpStatusCode.OK) { int countPayload = Int32.Parse(response.Payload); int countBaseline = CommonPayload.CreateList(linqBuilder.QueryResult).Count; AstoriaTestLog.AreEqual(countPayload, countBaseline, "Counts in payload (" + countPayload + ") and baseline (" + countBaseline + ") differ."); } } catch (TestFailedException tfe) { //When the underlying resource has zero elements, the server now throws a 404 error , causing false negatives ValidateCountException(response, request, tfe); } } else { VerifySpecificResponseVersion(response, 2, 0); try { // JSON, Atom or PlainXml ($ref) payload. VerifyStatusCode(response); if (response.Request.ExpectedStatusCode == System.Net.HttpStatusCode.OK) { CommonPayload payload = response.CommonPayload; LinqQueryBuilder baselineLinqBuilder = new LinqQueryBuilder(request.Workspace); baselineLinqBuilder.CountingMode = true; baselineLinqBuilder.Build(request.Query); object baselineElementsCount = CommonPayload.CreateList(baselineLinqBuilder.QueryResult).Count; payload.CompareCountInline(linqBuilder.QueryResult, baselineElementsCount); AstoriaTestLog.WriteLine(linqBuilder.QueryExpression); if (request.URI.Contains("$ref")) { VerifyLinksPayload(request.Workspace, payload, linqBuilder); } } } catch (TestFailedException tfe) { //When the underlying resource has zero elements, the server now throws a 404 error , causing false negatives ValidateCountException(response, request, tfe); } } } else { if (response.ActualStatusCode == System.Net.HttpStatusCode.NoContent && response.Payload == "") { response.Payload = null; // allow result from Linq query and payload to be compared (NoContent is a get to a null Nav prop) } if (response.ActualStatusCode == System.Net.HttpStatusCode.NotFound) { return; } CommonPayload payload = response.CommonPayload; if (request.URI.Contains("$ref")) { VerifyLinksPayload(request.Workspace, payload, linqBuilder); } else if (payload.Resources == null) { if (request.Format == SerializationFormatKind.JSON) { payload.CompareValue(linqBuilder.QueryResult, true, false); } else { payload.CompareValue(linqBuilder.QueryResult, false, false); } } else { payload.Compare(linqBuilder.QueryResult); } } } }
private static bool HandleWebException(WebException webException, out WebResponse response) { // return whether or not it has been handled // find out if the exception was caused by a socket exception Exception innerException = webException.InnerException; System.Net.Sockets.SocketException sockException = null; while (innerException != null) { if (innerException is System.Net.Sockets.SocketException) { sockException = innerException as System.Net.Sockets.SocketException; break; } else { innerException = innerException.InnerException; } } if (sockException != null) { // Ignore exceptions caused by Fiddler. if (sockException.SocketErrorCode == System.Net.Sockets.SocketError.ConnectionReset) { AstoriaTestLog.WriteLine("Connection was reset. Retrying..."); response = null; return(true); } AstoriaTestLog.WriteLine("A socket exception occurred, waiting 30 seconds before retrying"); AstoriaTestLog.WriteLine("Message: " + sockException.Message); AstoriaTestLog.WriteLine("Error Code: " + sockException.SocketErrorCode); AstoriaTestLog.WriteLineIgnore("Time before sleep: " + DateTime.Now); Threading.Thread.Sleep(new TimeSpan(0, 0, 30)); AstoriaTestLog.WriteLineIgnore("Time after sleep: " + DateTime.Now); response = null; return(true); } if (webException.Status == WebExceptionStatus.Timeout) { AstoriaTestLog.WriteLine("The request timeout out, waiting 30 seconds before retrying"); AstoriaTestLog.WriteLine("Message: " + webException.Message); AstoriaTestLog.WriteLineIgnore("Time before sleep: " + DateTime.Now); Threading.Thread.Sleep(new TimeSpan(0, 0, 30)); AstoriaTestLog.WriteLineIgnore("Time after sleep: " + DateTime.Now); response = null; return(true); } if (webException.Response != null) { response = webException.Response; return(true); } response = null; return(false); }
protected override String Visit(ExpNode caller, ExpNode node) { if (node is ProjectExpression) { ProjectExpression e = (ProjectExpression)node; if (e.Projections.Count == 0) { return(String.Format("{0}", this.Visit(e, e.Input) )); } else if (e.Projections.Count == 1) { if (e.Projections[0] is PropertyExpression) { return(String.Format("{0}/{1}", this.Visit(e, e.Input), this.Visit(e, e.Projections[0]) )); } } else { throw new Exception("More than 1 projection, invalid"); } } else if (node is ServiceOperationExpression) { AstoriaTestLog.WriteLine("Calling Service Operation"); ServiceOperationExpression e = (ServiceOperationExpression)node; string serviceOp = this.Visit(e, e.Input); AstoriaTestLog.WriteLine(serviceOp); StringBuilder sbParametersString = new StringBuilder(); if (!serviceOp.Contains("?")) { sbParametersString.Append("?"); } for (int index = 0; index < e.Arguments.Length; index++) { ServiceOperationParameterExpression parameter = e.Arguments[index]; if (index > 0) { sbParametersString.Append("&"); } string strLiteralString = CreateKeyStringValue(parameter.ParameterValue); sbParametersString.AppendFormat("{0}={1}", parameter.ParameterName, strLiteralString); } return(String.Format("{0}{1}", serviceOp, sbParametersString.ToString())); } else if (node is ScanExpression) { ScanExpression e = (ScanExpression)node; if (e.Input is VariableExpression && ((VariableExpression)e.Input).Variable is ResourceContainer) { return(String.Format("{0}", this.Visit(e, e.Input) )); } throw new Exception("Unsupported on in scan expression"); } else if (node is PredicateExpression) { PredicateExpression e = (PredicateExpression)node; KeyExpression key = e.Predicate as KeyExpression; if (key != null) { return(String.Format("{0}({1})", this.Visit(e, e.Input), this.Visit(e, e.Predicate) )); } else { return(String.Format("{0}?$filter={1}", this.Visit(e, e.Input), this.Visit(e, e.Predicate) )); } } else if (node is CountExpression) { CountExpression e = (CountExpression)node; string sCount = ""; string visit = ""; if (!e.IsInline) { visit = this.Visit(e, e.Input); sCount = string.Format("{0}/$count", visit); } else { visit = this.Visit(e, e.Input); if (visit.IndexOf("?$") == -1) { sCount = string.Format("{0}?$inlinecount={1}", visit, e.CountKind); } else { sCount = string.Format("{0}&$inlinecount={1}", visit, e.CountKind); } } return(sCount); } else if (node is NewExpression) { NewExpression e = (NewExpression)node; string uri = this.Visit(e, e.Input); ExpNode[] pe = new ExpNode[] { }; List <ExpNode> nodes = new List <ExpNode>(); foreach (ExpNode parameter in e.Arguments) { nodes.Add(parameter as ExpNode); } pe = nodes.ToArray(); string _select = String.Format("{0}", this.Visit(e, pe[0])); if (nodes.Count > 1) { for (int j = 1; j < nodes.Count; j++) { _select = String.Format("{0},{1}", _select, this.Visit(e, pe[j])); } } if (uri.IndexOf("?$") == -1) { return(string.Format("{0}?$select={1}", uri, _select)); } else { return(string.Format("{0}&$select={1}", uri, _select)); } } else if (node is MemberBindExpression) { return(this.Visit(node, ((MemberBindExpression)node).SourceProperty)); } else if (node is TopExpression) { TopExpression e = (TopExpression)node; string visit = this.Visit(e, e.Input); if (visit.IndexOf("?$") == -1) { return(string.Format("{0}?$top={1}", visit, e.Predicate)); } else { return(string.Format("{0}&$top={1}", visit, e.Predicate)); } } else if (node is SkipExpression) { SkipExpression e = (SkipExpression)node; string visit = this.Visit(e, e.Input); if (visit.IndexOf("?$") == -1) { return(string.Format("{0}?$skip={1}", visit, e.Predicate)); } else { return(string.Format("{0}&$skip={1}", visit, e.Predicate)); } } else if (node is OrderByExpression) { OrderByExpression e = (OrderByExpression)node; string ordervalue = String.Empty; int i = 0; string visit = this.Visit(e, e.Input); if (e.ExcludeFromUri) { return(visit); } string propVisit = this.Visit(e, e.PropertiesExp[0]); switch (e.AscDesc) { case true: if (visit.IndexOf("?$") == -1) { ordervalue = String.Format("{0}?$orderby={1}", visit, propVisit); } else { ordervalue = String.Format("{0}&$orderby={1}", visit, propVisit); } break; case false: if (visit.IndexOf("?$") == -1) { ordervalue = String.Format("{0}?$orderby={1} desc", visit, propVisit); } else { ordervalue = String.Format("{0}&$orderby={1} desc", visit, propVisit); } break; } ; if (e.PropertiesExp.Length > 0) { for (i++; i < e.PropertiesExp.Length; i++) { String nextValue = this.Visit(e, e.PropertiesExp[i]); nextValue = String.Format("{0}", nextValue); switch (e.AscDesc) { case true: ordervalue = String.Format("{0},{1}", ordervalue, nextValue); break; case false: ordervalue = String.Format("{0},{1} desc", ordervalue, nextValue); break; } } } return(ordervalue); } else if (node is ThenByExpression) { ThenByExpression e = (ThenByExpression)node; string ordervalue = String.Empty; int i = 0; string visit = this.Visit(e, e.Input); if (e.ExcludeFromUri) { return(visit); } switch (e.AscDesc) { case true: ordervalue = String.Format("{0},{1}", visit, e.Properties[0].Name); break; case false: ordervalue = String.Format("{0},{1} desc", visit, e.Properties[0].Name); break; } if (e.Properties.Length > 0) { for (i++; i < e.Properties.Length; i++) { String nextValue = e.Properties[i].Name; nextValue = String.Format("{0}", nextValue); switch (e.AscDesc) { case true: ordervalue = String.Format("{0},{1}", ordervalue, nextValue); break; case false: ordervalue = String.Format("{0},{1} desc", ordervalue, nextValue); break; } } } return(ordervalue); } else if (node is ExpandExpression) { ExpandExpression e = (ExpandExpression)node; string uri = this.Visit(e, e.Input); string expand = String.Format("{0}", this.Visit(e, e.PropertiesExp[0])); if (e.Properties.Length > 1) { for (int i = 1; i < e.Properties.Length; i++) { expand = String.Format("{0},{1}", expand, this.Visit(e, e.PropertiesExp[i])); } } if (uri.IndexOf("?$") == -1) { return(string.Format("{0}?$expand={1}", uri, expand)); } else { return(string.Format("{0}&$expand={1}", uri, expand)); } } else if (node is NavigationExpression) { NavigationExpression e = (NavigationExpression)node; if (!e.IsLink) { return(String.Format("{0}/{1}", this.Visit(e, e.Input), this.Visit(e, e.PropertyExp))); } else { return(String.Format("{0}/{1}/$ref", this.Visit(e, e.Input), this.Visit(e, e.PropertyExp))); } } else if (node is KeyExpression) { KeyExpression key = node as KeyExpression; return(CreateKeyString(key)); } else if (node is NestedPropertyExpression) { NestedPropertyExpression e = (NestedPropertyExpression)node; string enitySetname = e.Name; string nestedProperty = ""; foreach (PropertyExpression p in e.PropertyExpressions) { string interim = this.Visit(e, p); //AstoriaTestLog.WriteLine(interim); if (p.ValueOnly) { nestedProperty = String.Format("{0}/{1}/{2},", nestedProperty, enitySetname, interim + "/$value").TrimStart('/'); } else { nestedProperty = String.Format("{0}/{1}/{2},", nestedProperty, enitySetname, interim).TrimStart('/'); } } return(nestedProperty.TrimEnd(',').Replace(",/", ",")); } else if (node is PropertyExpression) { PropertyExpression e = (PropertyExpression)node; if (e.ValueOnly) { return(e.Property.Name + "/$value"); } else { return(e.Property.Name); } } else if (node is VariableExpression) { VariableExpression e = (VariableExpression)node; return(e.Variable.Name); } if (node is LogicalExpression) { LogicalExpression e = (LogicalExpression)node; string left = this.Visit(e, e.Left); string right = null; if (e.Operator != LogicalOperator.Not) { right = this.Visit(e, e.Right); } string logical; switch (e.Operator) { case LogicalOperator.And: logical = string.Format("{0} and {1}", left, right); break; case LogicalOperator.Or: logical = string.Format("{0} or {1}", left, right); break; case LogicalOperator.Not: logical = string.Format("not {0}", left); break; default: throw new Exception("Unhandled Comparison Type: " + e.Operator); } return(logical); } else if (node is ComparisonExpression) { ComparisonExpression e = (ComparisonExpression)node; String left = this.Visit(e, e.Left); String right = this.Visit(e, e.Right); switch (e.Operator) { case ComparisonOperator.Equal: return(String.Format("{0} eq {1}", left, right)); case ComparisonOperator.NotEqual: return(String.Format("{0} ne {1}", left, right)); case ComparisonOperator.GreaterThan: return(String.Format("{0} gt {1}", left, right)); case ComparisonOperator.GreaterThanOrEqual: return(String.Format("{0} ge {1}", left, right)); case ComparisonOperator.LessThan: return(String.Format("{0} lt {1}", left, right)); case ComparisonOperator.LessThanOrEqual: return(String.Format("{0} le {1}", left, right)); default: throw new Exception("Unhandled Comparison Type: " + e.Operator); } ; } else if (node is IsOfExpression || node is CastExpression) { ExpNode target; NodeType targetType; string operation; if (node is IsOfExpression) { IsOfExpression e = (IsOfExpression)node; operation = "isof"; target = e.Target; targetType = e.TargetType; } else { CastExpression e = (CastExpression)node; operation = "cast"; target = e.Target; targetType = e.TargetType; } string targetTypeName = targetType.FullName; if (targetType is PrimitiveType) { targetTypeName = TypeData.FindForType(targetType.ClrType).GetEdmTypeName(); } if (target == null) { return(String.Format("{0}('{1}')", operation, targetTypeName)); } else { return(String.Format("{0}({1}, '{2}')", operation, this.Visit(node, target), targetTypeName)); } } else if (node is ArithmeticExpression) { ArithmeticExpression e = (ArithmeticExpression)node; String left = this.Visit(e, e.Left); String right = this.Visit(e, e.Right); switch (e.Operator) { case ArithmeticOperator.Add: return(String.Format("{0} add {1}", left, right)); case ArithmeticOperator.Div: return(String.Format("{0} div {1}", left, right)); case ArithmeticOperator.Mod: return(String.Format("{0} mod {1}", left, right)); case ArithmeticOperator.Mult: return(String.Format("{0} mul {1}", left, right)); case ArithmeticOperator.Sub: return(String.Format("{0} sub {1}", left, right)); default: throw new Exception("Unhandled Arithmetic Type: " + e.Operator); } ; } else if (node is MethodExpression) { MethodExpression e = (MethodExpression)node; return(BuildMemberOrMethodExpression(node, e.Caller, e.Arguments, e.Name)); } else if (node is MemberExpression) { MemberExpression e = (MemberExpression)node; return(BuildMemberOrMethodExpression(node, e.Caller, e.Arguments, e.Name)); } else if (node is ConstantExpression) { ConstantExpression e = (ConstantExpression)node; object value = e.Value.ClrValue; string val = TypeData.FormatForKey(value, this.UseSmallCasing, false); if (this.EscapeUriValues) { // FormatForKey already does this for doubles that don't have the D if (!(value is double) || Versioning.Server.SupportsV2Features) { val = Uri.EscapeDataString(val); } } if (value == null) { val = "null"; } else if (!(value is String)) { val = val.Replace("+", "").Replace("%2B", ""); } return(val); } else if (node is NegateExpression) { NegateExpression e = (NegateExpression)node; return("-(" + this.Visit(e, e.Argument) + ")"); } else if (node is FirstExpression) { FirstExpression first = node as FirstExpression; return(this.Visit(first, first.Input)); } else if (node is FirstOrDefaultExpression) { FirstOrDefaultExpression first = node as FirstOrDefaultExpression; return(this.Visit(first, first.Input)); } else if (node is SingleExpression) { SingleExpression first = node as SingleExpression; return(this.Visit(first, first.Input)); } else if (node is SingleOrDefaultExpression) { SingleOrDefaultExpression first = node as SingleOrDefaultExpression; return(this.Visit(first, first.Input)); } else { throw new Exception(this.GetType().Name + " Unhandled Node: " + node.GetType().Name); } }
public TestVariation GenerateVariation(TestCase testcase, MethodInfo method, VariationAttribute testVarAttrib) { //do not generate variations for tests which have KeepInContent set to false and the version is not V2 if (!this.KeepInContent && !Versioning.Server.SupportsV2Features) { return(null); } TestVariation ffTestVariation; MethodInfo generateEpmMappingsMethod = testcase.GetType().GetMethod("GenerateEpmMappings"); FilterResourcesLambda = GenerateLambda(); TestFunc testMethodAction = delegate() { bool thisTestFailed = true; SkipInvalidRuns(); try { generateEpmMappingsMethod.Invoke(testcase, new object[] { KeepInContent, //bool pKeepInContent, RemoveUnEligibleTypes, //bool pRemoveUnEligibleTypes, GenerateClientTypes, //bool pGenerateClientTypes, MapToAtomElements, //bool pMapPropertiesToAtomElements, MapToNonAtomElements, //bool pMapPropertiesToNonAtomElements, GenerateServerEPMMappings, //bool GenerateServerEPMMappings, KeepSameNamespace, //bool KeepSameNamespace, IncludeComplexTypes, //bool IncludeComplexTypes PreDefinedPaths, //string[] PreDefinedPaths FilterResourcesLambda //Func<ResourceType, bool> pFilterResourcesLambda }); method.Invoke(testcase, null); thisTestFailed = AstoriaTestLog.FailureFound; } catch (Microsoft.OData.Client.DataServiceClientException dsException) { if (dsException.InnerException != null) { AstoriaTestLog.WriteLine(dsException.InnerException.Message); } } catch (TestFailedException testException) { AstoriaTestLog.WriteLine("Test failed with :{0}", testException.Message); AstoriaTestLog.WriteLine("Repro Details\r\nClient Code"); } finally { //Disabling this as OOM errors prevent file copying thisTestFailed = false; #region //Clean out all the facets after the test , so that the next test is clear object workspacePointer = null; PropertyInfo testWorkspacesProperty = testcase.Parent.GetType().GetProperty("Workspaces"); workspacePointer = testcase.Parent; if (testWorkspacesProperty == null) { testWorkspacesProperty = testcase.GetType().GetProperty("Workspaces"); workspacePointer = testcase; } Workspaces testWorkspaces = (Workspaces)testWorkspacesProperty.GetValue(workspacePointer, null); foreach (Workspace workspace in testWorkspaces) { if (thisTestFailed) { string testFailureReproPath = String.Format("FF_Failure_{0}", System.Guid.NewGuid().ToString()); testFailureReproPath = System.IO.Path.Combine(Environment.CurrentDirectory, testFailureReproPath); IOUtil.CopyFolder(workspace.DataService.DestinationFolder, testFailureReproPath, true); AstoriaTestLog.WriteLine("Test failed , Repro available at : {0} ", testFailureReproPath); } if (!(workspace is EdmWorkspace)) { workspace.GenerateClientTypes = true; workspace.GenerateServerMappings = false; workspace.ApplyFriendlyFeeds(); } } #endregion } }; ffTestVariation = new TestVariation(testMethodAction); if (testVarAttrib != null) { ffTestVariation.Params = testVarAttrib.Params; ffTestVariation.Param = testVarAttrib.Param; ffTestVariation.Desc = String.Format( "{3} , {0},{1},{2},{4}{5}{6}{7}", GenerateClientTypes ? "Client Mapped" : "", GenerateServerEPMMappings ? "Server Mapped" : "", MapToAtomElements ? "Mapped to Atom" : "Mapped to Non-Atom Elements", testVarAttrib.Desc, InheritanceFilter.ToString(), KeepSameNamespace ? "Same Namespace" : "Different NameSpaces", PreDefinedPaths != null ? ",PredefinedPaths" : "", KeepInContent.ToString()); if (AstoriaTestProperties.MaxPriority == 0) { ffTestVariation.Id = 1; } else { ffTestVariation.Id = idCounter++; } ffTestVariation.Priority = this.Priority; } return(ffTestVariation); }
//--------------------------------------------------------------------- public AstoriaResponse SendAndVerify(object expectedPayload, params string[] headers) { // Set auxiliary header values, if any (like ETag). for (int i = 0; i < headers.Length; i += 2) { base.Headers[headers[i]] = headers[i + 1]; } // Determine lowest possible protocol versions. string requestVersion = "1.0"; string responseVersion = "1.0"; if (base.ExpectedStatusCode == HttpStatusCode.OK) { if (base.URI.Contains("$select=")) { requestVersion = "2.0"; responseVersion = "1.0"; } if (base.URI.Contains("$inlinecount=")) { requestVersion = "2.0"; responseVersion = "2.0"; } if (base.URI.Contains("/$count")) { requestVersion = "2.0"; responseVersion = "2.0"; } } // Set request version headers semi-randomly. switch (base.URI.GetHashCode() & 3) { case 0: base.DataServiceVersion = requestVersion; break; case 1: base.DataServiceVersion = null; break; } switch ((base.URI.GetHashCode() >> 2) & 3) { case 0: base.MaxDataServiceVersion = requestVersion; break; case 1: base.MaxDataServiceVersion = Versioning.Server.DataServiceVersion; break; } // Send request to server. AstoriaResponse response = GetResponse(); // Update last seen MLE or MR ETags. if (response.ETagHeaderFound) { if (base.URI.Contains("$value")) { ETagMRR = response.ETagHeader; } else { ETagMLE = response.ETagHeader; } } if ((AstoriaTestProperties.DataLayerProviderKinds.Contains(DataLayerProviderKind.NonClr)) && (AstoriaTestProperties.UseOpenTypes)) { // // This section of code is handling the DSV values returned from server changes // bool not_3_0 = response.DataServiceVersion.StartsWith("1.0"); not_3_0 = response.DataServiceVersion.StartsWith("2.0") | not_3_0; AstoriaTestLog.Compare(not_3_0, "DataServiceVersion response header was returned with " + response.DataServiceVersion); AstoriaTestLog.TraceInfo("Returned DSV was " + response.DataServiceVersion); } else { // Verify response version header. AstoriaTestLog.Compare(response.DataServiceVersion.StartsWith(responseVersion), "DataServiceVersion response header must be " + responseVersion + " but was " + response.DataServiceVersion); } // Verify content type: xml for Atom, json for JSON. if (!base.Batched && !string.IsNullOrEmpty(response.Payload)) { if (base.Format == SerializationFormatKind.Atom && !response.ContentType.Contains("xml") || (base.Format == SerializationFormatKind.JSON && !response.ContentType.Contains("json"))) { AstoriaTestLog.WriteLine(string.Format("Wrong Content-Type {0} in response to {1} request: ", response.ContentType, base.Format)); AstoriaTestLog.WriteLine("Payload:"); AstoriaTestLog.FailAndThrow(response.Payload ?? "{null}"); } } // Verify MLE (BlobsPayload) or MR response payload. if (expectedPayload is BlobsPayload) { // Compare MLE payloads. BlobsPayload actualPayload = (expectedPayload as BlobsPayload); if (actualPayload.ToString() != expectedPayload.ToString()) { AstoriaTestLog.FailAndThrow( "MLE received:" + TestLog.NewLine + actualPayload + TestLog.NewLine + "MLE expected:" + TestLog.NewLine + expectedPayload); } // Temporarily morph MLE into normal entity and call Verify(). string originalPayload = response.Payload; response.Payload = actualPayload.AdjustedForVerify(); response.Verify(); response.Payload = originalPayload; } else { // Compare MR payloads. string actualPayload = response.Payload; if (expectedPayload != null && actualPayload != expectedPayload as string) { AstoriaTestLog.FailAndThrow( "MR received:" + TestLog.NewLine + actualPayload + TestLog.NewLine + "MR expected:" + TestLog.NewLine + expectedPayload); } response.Verify(); } // Remove auxiliary headers, if any. for (int i = 0; i < headers.Length; i += 2) { base.Headers.Remove(headers[i]); } return(response); }
public static void ParseBatchResponse(BatchResponse response) { string contentType = response.ContentType; string[] pieces = contentType.Split(new string[] { "; boundary=" }, StringSplitOptions.None); if (pieces.Length < 2) { AstoriaTestLog.FailAndThrow(String.Format("Could not retrieve boundary from response's content-type header. Value found was '{0}'", contentType)); } string boundary = pieces[1].Trim(); // even if content-id is specified, we may not get it back in error cases. This doesn't feel entirely correct, it seems like there should // always be a 1-1 mapping that explicitly honors content-id's // TODO: responses / requests without content IDs should be matched up in order within changeset only // changesets should have the right number of responses too BatchRequest batchRequest = response.Request as BatchRequest; List <ResponseFragment> unmatchedFragments = new List <ResponseFragment>(); List <AstoriaRequest> unmatchedRequests = batchRequest.Requests .Union(batchRequest.Changesets.SelectMany(changeset => changeset.AsEnumerable())) .ToList(); using (StringReader reader = new StringReader(response.Payload)) { foreach (ResponseFragment fragment in ParseBatchResponse(reader, boundary)) { AstoriaRequest request = null; if (fragment.ContentID != null) { request = unmatchedRequests .FirstOrDefault(r => r.Headers.ContainsKey("Content-ID") && r.Headers["Content-ID"].Equals(fragment.ContentID)); } if (request == null) { unmatchedFragments.Add(fragment); } else { unmatchedRequests.Remove(request); response.Responses.Add(FragmentToResponse(request, fragment)); } } } if (unmatchedFragments.Any()) { if (unmatchedFragments.Count < unmatchedRequests.Count) { AstoriaTestLog.WriteLine("Warning: recieved fewer batch response fragments than expected"); } else if (unmatchedFragments.Count > unmatchedRequests.Count) { AstoriaTestLog.FailAndThrow("Recieved more batch fragments than expected"); } for (int i = 0; i < unmatchedFragments.Count; i++) { response.Responses.Add(FragmentToResponse(unmatchedRequests[i], unmatchedFragments[i])); } } }