从 TestResult(.trx) 文件中提取 Visual Studio Webtest 结果
从 TestResult(.trx) 文件中提取 Visual Studio Webtest 结果
背景
在 Visual Studio Webtest 中,您可以设置浏览器的 User-Agent。
使用此功能,您可以模拟各种浏览器(IE、FireFox、Opera、Safari...)来测试您的网站。
但是,我们只能在 Visual Studio 测试结果窗口中查看结果,该窗口使用 IE 作为渲染引擎。
要在其他浏览器中查看,您需要从 TestResult 文件中提取网站响应。
TestResult 文件结构
响应信息存储在 WebRequestResult 元素中,如下所示。
<WebRequestResult exceptionMessage="" run="0" submitted="true" cached="false"
isRedirectFollow="false" requestBodyBytesHandle="-2" responseBytesHandle="0">
<Request url="http://doda.jp/">http://doda.jp/" command="GET / HTTP/1.1" encoding="utf-8">
<Headers>User-Agent : Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)
Accept : */*
Accept-Language : ja,en-us;q=0.5
Accept-Encoding : GZIP
Host : doda.jp
Connection : Keep-Alive
</Headers>
</Request>
<Response url="http://doda.jp/index.html">http://doda.jp/index.html" contentType="text/html; charset=Shift_JIS"
statusLine="HTTP/1.1 200 OK" pageTime="0.995" time="0.104" statusCodeString="200 OK" contentLength="45922">
<Headers>Content-Location : http://doda.jp/index.html
Vary : Accept-Encoding
Accept-Ranges : bytes
Content-Length : 45922
Content-Type : text/html
Date : Wed, 05 Dec 2007 15:27:03 GMT
ETag : "092f3fd3b35c81:23fe"
Last-Modified : Sun, 02 Dec 2007 23:35:16 GMT
Set-Cookie : BIGipServerwww_80=220795052.20480.0000; path=/
Server : Microsoft-IIS/6.0
</Headers>
</Response>
响应主体存储在 Entry 元素中,如下所示。
<Entry handle="0" bytes="H4sIAAAAAAAEAO29B2AcSZYlJi9tyn
WebRequestResult 通过 responseBytesHandle 属性和 handle 属性引用 Entry。
Entry 元素的 bytes 属性经过 GZip 压缩,然后通过 Base64 编码。
概述
1. 从 TestResult 文件中提取响应主体。
2. 将响应主体写入文件。
3. 重写链接 URL。
代码
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.IO.Compression;
using System.Xml;
namespace Rei
{
public class Converter
{
private Encoding defaultEncoding = Encoding.GetEncoding("Shift-JIS");
private Regex srcRegex = new Regex("src=\"(?<url>[^\"]+)\"", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private Regex hrefRegex = new Regex("href=\"(?<url>[^\"]+)\"", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public void Convert(string inputFileName,string outputDirName)
{
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(inputFileName);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmldoc.NameTable);
nsmgr.AddNamespace("a", "http://microsoft.com/schemas/VisualStudio/TeamTest/2006">http://microsoft.com/schemas/VisualStudio/TeamTest/2006");
List<WebRequestResult> reqResults = new List<WebRequestResult>();
foreach (XmlNode node in xmldoc.SelectNodes("//a:WebRequestResult",nsmgr))
{
string responseHandle = node.Attributes["responseBytesHandle"].Value;
XmlNode childNode = node.SelectSingleNode("a:Request",nsmgr);
string url = childNode.Attributes["url"].Value;
WebRequestResult reqresult = new WebRequestResult();
reqresult.ResponseHandle = responseHandle;
reqresult.Url = url;
XmlNode responseNode = node.SelectSingleNode("a:Response", nsmgr);
reqresult.ContentType = responseNode.Attributes["contentType"].Value;
reqResults.Add(reqresult);
}
foreach (WebRequestResult reqresult in reqResults)
{
string handle = reqresult.ResponseHandle;
XmlNode entryNode = xmldoc.SelectSingleNode("//a:Entry[@handle =\"" + handle + "\"]",nsmgr);
if (entryNode != null)
{
reqresult.EncodedResponse = entryNode.Attributes["bytes"].Value;
}
}
foreach (WebRequestResult reqresult in reqResults)
{
string outFileName = outputDirName + "\\" + reqresult.fileName;
using(FileStream fs = new FileStream(outFileName,FileMode.Create))
{
if (!reqresult.ContentType.StartsWith("text"))
{
byte[] bytes = reqresult.Response;
fs.Write(bytes, 0, bytes.Length);
fs.Close();
}
else
{
Encoding enc = null;
if (reqresult.Charset != string.Empty)
{
enc = Encoding.GetEncoding(reqresult.Charset);
}
else
{
enc = defaultEncoding;
}
string body = enc.GetString(reqresult.Response, 0, reqresult.Response.Length);
Dictionary<string, string> urlMap = new Dictionary<string, string>();
MatchCollection srcMatches = srcRegex.Matches(body);
foreach (Match match in srcMatches)
{
string src = match.Groups["url"].Value;
if (!urlMap.ContainsKey(src))
{
for (int i = 0; i < reqResults.Count; i++)
{
if (reqResults[i].Url.EndsWith(src))
{
urlMap.Add(src, reqResults[i].fileName);
break;
}
}
}
if (src.StartsWith("/") || src.StartsWith("."))
{
Uri uri = new Uri(new Uri(reqresult.Url), src);
if (!urlMap.ContainsKey(src))
{
for (int i = 0; i < reqResults.Count; i++)
{
if (reqResults[i].Url.EndsWith(uri.AbsoluteUri))
{
urlMap.Add(src, reqResults[i].fileName);
break;
}
}
}
}
}
MatchCollection hrefMatches = hrefRegex.Matches(body);
foreach (Match match in hrefMatches)
{
string src = match.Groups["url"].Value;
if (!urlMap.ContainsKey(src))
{
for (int i = 0; i < reqResults.Count; i++)
{
if (reqResults[i].Url.EndsWith(src))
{
urlMap.Add(src, reqResults[i].fileName);
break;
}
}
}
if (src.StartsWith("/") || src.StartsWith("."))
{
Uri uri = new Uri(new Uri(reqresult.Url), src);
if (!urlMap.ContainsKey(src))
{
for (int i = 0; i < reqResults.Count; i++)
{
if (reqResults[i].Url.EndsWith(uri.AbsoluteUri))
{
urlMap.Add(src, reqResults[i].fileName);
break;
}
}
}
}
}
foreach (string beforeUrl in urlMap.Keys)
{
Regex regex1 = new Regex("src=\"" + beforeUrl + "\"", RegexOptions.IgnoreCase);
Regex regex2 = new Regex("href=\"" + beforeUrl + "\"", RegexOptions.IgnoreCase);
body = regex1.Replace(body, "src=\"" + urlMap[beforeUrl] + "\"");
body = regex2.Replace(body, "href=\"" + urlMap[beforeUrl] + "\"");
}
byte[] converted = enc.GetBytes(body);
fs.Write(converted, 0, converted.Length);
fs.Close();
}
}
}
}
}
internal class WebRequestResult
{
private static Regex charsetRegex = new Regex(@"charset=(?<charsetName>.+)", RegexOptions.Compiled);
public string Charset
{
get {
MatchCollection matches = charsetRegex.Matches(this.contentType);
if (matches.Count > 0)
{
return matches[0].Groups["charsetName"].Value;
}
return string.Empty;
}
}
private string contentType;
public string ContentType
{
get { return this.contentType; }
set {this.contentType = value;}
}
private string url;
public string Url
{
get { return this.url; }
set { this.url = value; }
}
private string guid = Guid.NewGuid().ToString();
public string fileName
{
get
{
string name = Url;
name = name.Replace("//", "_");
name = name.Replace('/', '_');
name = name.Replace(':', '_');
name = name.Replace('*', '_');
name = name.Replace('"', '_');
name = name.Replace('<', '_');
name = name.Replace('>', '_');
name = name.Replace('|', '_');
name = name.Replace('?', '_');
if (name.EndsWith("\\"))
{
name = name.Substring(0, name.Length - 1);
}
if (name.Length > 100)
{
return this.guid;
}
return name;
}
}
private string responseHandle;
public string ResponseHandle
{
get { return this.responseHandle; }
set { this.responseHandle = value; }
}
private string encodedResponse;
public string EncodedResponse
{
get { return this.encodedResponse; }
set { this.encodedResponse = value; }
}
public byte[] Response
{
get {
if (this.encodedResponse == null || this.encodedResponse == string.Empty)
{
return new byte[0];
}
byte[] compressed = Convert.FromBase64String(this.encodedResponse);
MemoryStream ms = new MemoryStream();
ms.Write(compressed, 0, compressed.Length);
ms.Position = 0;
GZipStream gzipStream = new GZipStream(ms, CompressionMode.Decompress,true);
MemoryStream msOut = new MemoryStream();
int uncompressedByte = -1;
while ((uncompressedByte = gzipStream.ReadByte()) != -1)
{
msOut.WriteByte((byte)uncompressedByte);
}
return msOut.ToArray();
}
}
}
}