A response message specifies the format used to contain sub-responses matching sub-requests.
Example #1
0
        /// <summary>
        /// Get EditorsTable from server response.
        /// </summary>
        /// <param name="subResponse">The sub response from server.</param>
        /// <param name="site">Transfer ITestSite into this operation, for this operation to use ITestSite's function.</param>
        /// <returns>The instance of EditorsTable.</returns>
        public static EditorsTable GetEditorsTableFromResponse(FsshttpbResponse subResponse, ITestSite site)
        {
            if (subResponse == null || subResponse.DataElementPackage == null || subResponse.DataElementPackage.DataElements == null)
            {
                site.Assert.Fail("The parameter CellResponse is not valid, check whether the CellResponse::DataElementPackage or CellResponse::DataElementPackage::DataElements is null.");
            }

            foreach (DataElement de in subResponse.DataElementPackage.DataElements)
            {
                if (de.Data.GetType() == typeof(ObjectGroupDataElementData))
                {
                    ObjectGroupDataElementData ogde = de.Data as ObjectGroupDataElementData;

                    if (ogde.ObjectGroupData == null || ogde.ObjectGroupData.ObjectGroupObjectDataList.Count == 0)
                    {
                        continue;
                    }

                    for (int i = 0; i < ogde.ObjectGroupData.ObjectGroupObjectDataList.Count; i++)
                    {
                        if (IsEditorsTableHeader(ogde.ObjectGroupData.ObjectGroupObjectDataList[i].Data.Content.ToArray()))
                        {
                            string editorsTableXml = null;

                            // If the current object group object data is the header byte array 0x1a, 0x5a, 0x3a, 0x30, 0, 0, 0, 0, then the immediate following object group object data will contain the Editor table xml.
                            byte[] buffer             = ogde.ObjectGroupData.ObjectGroupObjectDataList[i + 1].Data.Content.ToArray();
                            System.IO.MemoryStream ms = null;
                            try
                            {
                                ms = new System.IO.MemoryStream();
                                ms.Write(buffer, 0, buffer.Length);
                                ms.Position = 0;
                                using (DeflateStream stream = new DeflateStream(ms, CompressionMode.Decompress))
                                {
                                    stream.Flush();
                                    byte[] decompressBuffer = new byte[buffer.Length * 3];
                                    stream.Read(decompressBuffer, 0, buffer.Length * 3);
                                    stream.Close();
                                    editorsTableXml = System.Text.Encoding.UTF8.GetString(decompressBuffer);
                                }

                                ms.Close();
                            }
                            finally
                            {
                                if (ms != null)
                                {
                                    ms.Dispose();
                                }
                            }

                            return(GetEditorsTable(editorsTableXml));
                        }
                    }
                }
            }

            throw new InvalidOperationException("Cannot find any data group object data contain editor tables information.");
        }
        /// <summary>
        /// Get EditorsTable from server response.
        /// </summary>
        /// <param name="subResponse">The sub response from server.</param>
        /// <param name="site">Transfer ITestSite into this operation, for this operation to use ITestSite's function.</param>
        /// <returns>The instance of EditorsTable.</returns>
        public static EditorsTable GetEditorsTableFromResponse(FsshttpbResponse subResponse, ITestSite site)
        {
            if (subResponse == null || subResponse.DataElementPackage == null || subResponse.DataElementPackage.DataElements == null)
            {
                site.Assert.Fail("The parameter CellResponse is not valid, check whether the CellResponse::DataElementPackage or CellResponse::DataElementPackage::DataElements is null.");
            }

            foreach (DataElement de in subResponse.DataElementPackage.DataElements)
            {
                if (de.Data.GetType() == typeof(ObjectGroupDataElementData))
                {
                    ObjectGroupDataElementData ogde = de.Data as ObjectGroupDataElementData;

                    if (ogde.ObjectGroupData == null || ogde.ObjectGroupData.ObjectGroupObjectDataList.Count == 0)
                    {
                        continue;
                    }

                    for (int i = 0; i < ogde.ObjectGroupData.ObjectGroupObjectDataList.Count; i++)
                    {
                        if (IsEditorsTableHeader(ogde.ObjectGroupData.ObjectGroupObjectDataList[i].Data.Content.ToArray()))
                        {
                            string editorsTableXml = null;

                            // If the current object group object data is the header byte array 0x1a, 0x5a, 0x3a, 0x30, 0, 0, 0, 0, then the immediate following object group object data will contain the Editor table xml. 
                            byte[] buffer = ogde.ObjectGroupData.ObjectGroupObjectDataList[i + 1].Data.Content.ToArray();
                            System.IO.MemoryStream ms = null;
                            try
                            {
                                ms = new System.IO.MemoryStream();
                                ms.Write(buffer, 0, buffer.Length);
                                ms.Position = 0;
                                using (DeflateStream stream = new DeflateStream(ms, CompressionMode.Decompress))
                                {
                                    stream.Flush();
                                    byte[] decompressBuffer = new byte[buffer.Length * 3];
                                    stream.Read(decompressBuffer, 0, buffer.Length * 3);
                                    stream.Close();
                                    editorsTableXml = System.Text.Encoding.UTF8.GetString(decompressBuffer);
                                }

                                ms.Close();
                            }
                            finally
                            {
                                if (ms != null)
                                {
                                    ms.Dispose();
                                }
                            }

                            return GetEditorsTable(editorsTableXml);
                        }
                    }
                }
            }

            throw new InvalidOperationException("Cannot find any data group object data contain editor tables information.");
        }
        /// <summary>
        /// This method is used to test Response Message Syntax related adapter requirements.
        /// </summary>
        /// <param name="instance">Specify the instance which need to be verified.</param>
        /// <param name="site">Specify the ITestSite instance.</param>
        public void VerifyFsshttpbResponse(FsshttpbResponse instance, ITestSite site)
        {
            // If the instance is not null and there are no parsing errors, then the Response Message Syntax related adapter requirements can be directly captured.
            if (null == instance)
            {
                site.Assert.Fail("The instance of type FsshttpbResponse is null due to parsing error or type casting error.");
            }

            // Directly capture requirement MS-FSSHTTPB_R534, if there are no parsing errors. 
            site.CaptureRequirement(
                     "MS-FSSHTTPB",
                     534,
                     @"[In Response Message Syntax]  Protocol Version (2bytes): An unsigned integer that specifies the protocol schema version number used in this request.");

            // Capture requirement MS-FSSHTTPB_R535, if the protocol version number equals to 12. 
            site.CaptureRequirementIfAreEqual<int>(
                     12,
                     instance.ProtocolVersion,
                     "MS-FSSHTTPB",
                     535,
                     @"[In Response Message Syntax] Protocol Version (2bytes): This value[Protocol Version] MUST be 12.");

            // Directly capture requirement MS-FSSHTTPB_R536, if there are no parsing errors. 
            site.CaptureRequirement(
                     "MS-FSSHTTPB",
                     536,
                     @"[In Response Message Syntax] Minimum Version (2 bytes): An unsigned integer that specifies the oldest version of the protocol schema that this schema is compatible with.");

            // Capture requirement MS-FSSHTTPB_R537, if the minimum version number equals to 11. 
            site.CaptureRequirementIfAreEqual<int>(
                     11,
                     instance.MinimumVersion,
                     "MS-FSSHTTPB",
                     537,
                     @"[In Response Message Syntax] Minimum Version (2 bytes): This value[Minimum Version] MUST be 11.");

            // Directly capture requirement MS-FSSHTTPB_R538, if there are no parsing errors. 
            site.CaptureRequirement(
                     "MS-FSSHTTPB",
                     538,
                     @"[In Response Message Syntax] Signature (8 bytes): An unsigned integer that specifies a constant signature, to identify this as a response.");

            // Capture requirement MS-FSSHTTPB_R539, if the signature number equals to 0x9B069439F329CF9D.
            site.CaptureRequirementIfAreEqual<ulong>(
                     0x9B069439F329CF9D,
                     instance.Signature,
                     "MS-FSSHTTPB",
                     539,
                     @"[In Response Message Syntax] Signature (8 bytes): This[Signature] MUST be set to 0x9B069439F329CF9D.");

            // Verify the stream object header related requirements.
            this.ExpectStreamObjectHeaderStart(instance.ResponseStart, instance.GetType(), site);

            // Capture requirement MS-FSSHTTPB_R540, if the response header equals to StreamObjectHeaderStart32bit.
            site.CaptureRequirementIfAreEqual<Type>(
                     typeof(StreamObjectHeaderStart32bit),
                     instance.ResponseStart.GetType(),
                     "MS-FSSHTTPB",
                     540,
                     @"[In Response Message Syntax] Response Start (4 bytes): A 32-bit stream object header (section 2.2.1.5.2) that specifies a response start.");

            if (instance.Status == true)
            {
                site.Log.Add(
                        LogEntryKind.Debug,
                        "When the status is set, the response error value {0}.",
                        instance.ResponseError.ErrorData.ErrorDetail);
            }
            else
            {
                if (instance.DataElementPackage != null)
                {
                    // Directly capture requirement MS-FSSHTTPB_R2191, if there are no parsing errors. 
                    site.CaptureRequirement(
                             "MS-FSSHTTPB",
                             2191,
                             @"[In Response Message Syntax] Data Element Package (variable): An optional data element package structure (section 2.2.1.12) that specifies data elements corresponding to the sub-responses (section 2.2.3.1).");
                }

                if (instance.CellSubResponses != null && instance.CellSubResponses.Count != 0)
                {
                    // Directly capture requirement MS-FSSHTTPB_R2192, if there are no parsing errors. 
                    site.CaptureRequirement(
                             "MS-FSSHTTPB",
                             2192,
                             @"[In Response Message Syntax] Sub-response (variable): Specifies an array of sub-responses corresponding to the sub-requests as specified in section 2.2.2.1.");

                    // Directly capture requirement MS-FSSHTTPB_R851, if there are no parsing errors.
                    site.CaptureRequirement(
                             "MS-FSSHTTPB",
                             851,
                             @"[In Message Processing Events and Sequencing Rules] The server MUST reply to a well-formed request with a response, as specified in section 2.2.3, which includes a sub-response for each sub-request.");
                }

                // Directly capture requirement MS-FSSHTTPB_R2190, if there are no parsing errors.
                site.CaptureRequirement(
                         "MS-FSSHTTPB",
                         2190,
                         @"[In Response Message Syntax] Response Data (variable): If the request did not fail, the response data is specified by the following structure [Data Element Package, Sub Responses].");
            }

            // Capture requirement MS-FSSHTTPB_R543, if the reserved value equals to 0.
            site.CaptureRequirementIfAreEqual<int>(
                     0,
                     instance.Reserved,
                     "MS-FSSHTTPB",
                     543,
                     @"[In Response Message Syntax] Reserved (7 bits): A 7-bit reserved field that MUST be set to zero.");

            // Verify the stream object header end related requirements.
            this.ExpectStreamObjectHeaderEnd(instance.ResponseEnd, instance.GetType(), site);
            this.ExpectCompoundObject(instance.ResponseStart, site);

            // Capture requirement MS-FSSHTTPB_R546, if the response header equals to StreamObjectHeaderEnd16bit.
            site.CaptureRequirementIfAreEqual<Type>(
                     typeof(StreamObjectHeaderEnd16bit),
                     instance.ResponseEnd.GetType(),
                     "MS-FSSHTTPB",
                     546,
                     @"[In Response Message Syntax] Response End (2 bytes): A 16-bit stream object header (section 2.2.1.5.4) that specifies a response end.");
        }
        /// <summary>
        /// Deserialize response from byte array.
        /// </summary>
        /// <param name="byteArray">Server returned message.</param>
        /// <param name="startIndex">The index special where start.</param>
        /// <returns>The instance of CellResponse.</returns>
        public static FsshttpbResponse DeserializeResponseFromByteArray(byte[] byteArray, int startIndex)
        {
            int index = startIndex;

            FsshttpbResponse response = new FsshttpbResponse();

            response.ProtocolVersion = LittleEndianBitConverter.ToUInt16(byteArray, index);
            index += 2;

            response.MinimumVersion = LittleEndianBitConverter.ToUInt16(byteArray, index);
            index += 2;

            response.Signature = LittleEndianBitConverter.ToUInt64(byteArray, index);
            index += 8;

            int length = 0;
            StreamObjectHeaderStart streamObjectHeader;

            if ((length = StreamObjectHeaderStart.TryParse(byteArray, index, out streamObjectHeader)) == 0)
            {
                throw new ResponseParseErrorException(index, "Failed to parse the response header", null);
            }

            if (!(streamObjectHeader is StreamObjectHeaderStart32bit))
            {
                throw new ResponseParseErrorException(index, "Unexpected 16-bit response stream object header, expect 32-bit stream object header for Response", null);
            }

            if (streamObjectHeader.Type != StreamObjectTypeHeaderStart.FsshttpbResponse)
            {
                throw new ResponseParseErrorException(index, "Failed to extract the response header type, unexpected value " + streamObjectHeader.Type, null);
            }

            if (streamObjectHeader.Length != 1)
            {
                throw new ResponseParseErrorException(index, "Response object over-parse error", null);
            }

            index += length;
            response.ResponseStart = streamObjectHeader as StreamObjectHeaderStart32bit;

            response.Status   = (byteArray[index] & 0x1) == 0x1 ? true : false;
            response.Reserved = (byte)(byteArray[index] >> 1);
            index            += 1;

            try
            {
                if (response.Status)
                {
                    response.ResponseError      = StreamObject.GetCurrent <ResponseError>(byteArray, ref index);
                    response.DataElementPackage = null;
                    response.CellSubResponses   = null;
                }
                else
                {
                    DataElementPackage package;
                    if (StreamObject.TryGetCurrent <DataElementPackage>(byteArray, ref index, out package))
                    {
                        response.DataElementPackage = package;
                    }

                    response.CellSubResponses = new List <FsshttpbSubResponse>();
                    FsshttpbSubResponse subResponse;
                    while (StreamObject.TryGetCurrent <FsshttpbSubResponse>(byteArray, ref index, out subResponse))
                    {
                        response.CellSubResponses.Add(subResponse);
                    }
                }

                response.ResponseEnd = BasicObject.Parse <StreamObjectHeaderEnd16bit>(byteArray, ref index);
            }
            catch (StreamObjectParseErrorException streamObjectException)
            {
                throw new ResponseParseErrorException(index, streamObjectException);
            }
            catch (DataElementParseErrorException dataElementException)
            {
                throw new ResponseParseErrorException(index, dataElementException);
            }
            catch (KnowledgeParseErrorException knowledgeException)
            {
                throw new ResponseParseErrorException(index, knowledgeException);
            }

            if (index != byteArray.Length)
            {
                throw new ResponseParseErrorException(index, "Failed to pass the whole response, not reach the end of the byte array", null);
            }

            return(response);
        }
        /// <summary>
        /// This method is used to validate the sub response according to the current record sub request token and sub request type.
        /// </summary>
        /// <param name="rawResponse">Specify the raw XML string response returned by the protocol server.</param>
        /// <param name="site">An object provides logging, assertions, and SUT adapters for test code onto its execution context.</param>
        public void Validate(string rawResponse, ITestSite site)
        {
            // Extract the sub response whose token equals the SubToken value.
            XmlDocument subResponseDocument = this.ExtractSubResponseNode(rawResponse);

            // De-serialize the sub response to instance
            object subResponse = this.SerializeSubResponse(subResponseDocument, site);

            // Try to parse the MS-FSSHTTPB structure
            if (subResponse is CellSubResponseType)
            {
                // If the sub request type is CellSubRequestType, then indicating that there is one MS-FSSHTTPB response embedded. Try parse this an capture all the related requirements.
                CellSubResponseType cellSubResponse = subResponse as CellSubResponseType;
                if (cellSubResponse.SubResponseData != null && cellSubResponse.SubResponseData.Text.Length == 1)
                {
                    string           subResponseBase64 = cellSubResponse.SubResponseData.Text[0];
                    byte[]           subResponseBinary = Convert.FromBase64String(subResponseBase64);
                    FsshttpbResponse fsshttpbResponse  = FsshttpbResponse.DeserializeResponseFromByteArray(subResponseBinary, 0);

                    if (fsshttpbResponse.DataElementPackage != null && fsshttpbResponse.DataElementPackage.DataElements != null)
                    {
                        // If the response data elements is complete, then try to verify the requirements related in the MS-FSSHTPD
                        foreach (DataElement storageIndex in fsshttpbResponse.DataElementPackage.DataElements.Where(dataElement => dataElement.DataElementType == DataElementType.StorageIndexDataElementData))
                        {
                            // Just build the root node to try to parse the signature related requirements, no need to restore the result.
                            new RootNodeObject.RootNodeObjectBuilder().Build(
                                fsshttpbResponse.DataElementPackage.DataElements,
                                storageIndex.DataElementExtendedGUID);
                        }
                    }

                    if (SharedContext.Current.IsMsFsshttpRequirementsCaptured)
                    {
                        new MsfsshttpbAdapterCapture().VerifyTransport(site);

                        // Capture the response related requirements
                        new MsfsshttpbAdapterCapture().VerifyFsshttpbResponse(fsshttpbResponse, site);
                    }
                }
            }

            // Validating the fragment of the sub response
            // Record the validation errors and warnings.
            ValidationResult result = SchemaValidation.ValidateXml(subResponseDocument.OuterXml);

            if (!SharedContext.Current.IsMsFsshttpRequirementsCaptured)
            {
                if (result != ValidationResult.Success)
                {
                    // Add error log
                    site.Assert.Fail("Schema validation fails, the reason is " + SchemaValidation.GenerateValidationResult());
                }

                // No need to run the capture code, just return.
                return;
            }

            if (result == ValidationResult.Success)
            {
                // Capture the requirement related to the sub response token.
                MsfsshttpAdapterCapture.ValidateSubResponseToken(site);

                // Call corresponding sub response capture code.
                this.InvokeCaptureCode(subResponse, site);
            }
            else
            {
                // Add error log
                site.Assert.Fail("Schema validation fails, the reason is " + SchemaValidation.GenerateValidationResult());
            }
        }
        /// <summary>
        /// Deserialize response from byte array.
        /// </summary>
        /// <param name="byteArray">Server returned message.</param>
        /// <param name="startIndex">The index special where start.</param>
        /// <returns>The instance of CellResponse.</returns>
        public static FsshttpbResponse DeserializeResponseFromByteArray(byte[] byteArray, int startIndex)
        {
            int index = startIndex;

            FsshttpbResponse response = new FsshttpbResponse();

            response.ProtocolVersion = LittleEndianBitConverter.ToUInt16(byteArray, index);
            index += 2;

            response.MinimumVersion = LittleEndianBitConverter.ToUInt16(byteArray, index);
            index += 2;

            response.Signature = LittleEndianBitConverter.ToUInt64(byteArray, index);
            index += 8;

            int length = 0;
            StreamObjectHeaderStart streamObjectHeader;
            if ((length = StreamObjectHeaderStart.TryParse(byteArray, index, out streamObjectHeader)) == 0)
            {
                throw new ResponseParseErrorException(index, "Failed to parse the response header", null);
            }

            if (!(streamObjectHeader is StreamObjectHeaderStart32bit))
            {
                throw new ResponseParseErrorException(index, "Unexpected 16-bit response stream object header, expect 32-bit stream object header for Response", null);
            }

            if (streamObjectHeader.Type != StreamObjectTypeHeaderStart.FsshttpbResponse)
            {
                throw new ResponseParseErrorException(index, "Failed to extract the response header type, unexpected value " + streamObjectHeader.Type, null);
            }

            if (streamObjectHeader.Length != 1)
            {
                throw new ResponseParseErrorException(index, "Response object over-parse error", null);
            }

            index += length;
            response.ResponseStart = streamObjectHeader as StreamObjectHeaderStart32bit;

            response.Status = (byteArray[index] & 0x1) == 0x1 ? true : false;
            response.Reserved = (byte)(byteArray[index] >> 1);
            index += 1;

            try
            {
                if (response.Status)
                {
                    response.ResponseError = StreamObject.GetCurrent<ResponseError>(byteArray, ref index);
                    response.DataElementPackage = null;
                    response.CellSubResponses = null;
                }
                else
                {
                    DataElementPackage package;
                    if (StreamObject.TryGetCurrent<DataElementPackage>(byteArray, ref index, out package))
                    {
                        response.DataElementPackage = package;
                    }

                    response.CellSubResponses = new List<FsshttpbSubResponse>();
                    FsshttpbSubResponse subResponse;
                    while (StreamObject.TryGetCurrent<FsshttpbSubResponse>(byteArray, ref index, out subResponse))
                    {
                        response.CellSubResponses.Add(subResponse);
                    }
                }

                response.ResponseEnd = BasicObject.Parse<StreamObjectHeaderEnd16bit>(byteArray, ref index);
            }
            catch (StreamObjectParseErrorException streamObjectException)
            {
                throw new ResponseParseErrorException(index, streamObjectException);
            }
            catch (DataElementParseErrorException dataElementException)
            {
                throw new ResponseParseErrorException(index, dataElementException);
            }
            catch (KnowledgeParseErrorException knowledgeException)
            {
                throw new ResponseParseErrorException(index, knowledgeException);
            }

            if (index != byteArray.Length)
            {
                throw new ResponseParseErrorException(index, "Failed to pass the whole response, not reach the end of the byte array", null);
            }

            return response;
        }