Beta.cs 17.3 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
/************************************************************************************************
* All code in this file is under the MS-RL License (https://opensource.org/licenses/MS-RL)      *
* By using the code in this file in any way, you agree to the above license terms.              *
* Copyright (C) LIGHTS IN LINE AB (https://www.lightsinline.se)                                 *
* Repository, Wiki, Issue tracker and more at https://git.lightsinline.se/products/VSTT-Plugins *
*                                                                                               *
* Contributors                                                                                  *
* LIGHTS IN LINE AB                                                                             *
* SWEDBANK AB                                                                                   *
* SKATTEVERKET                                                                                  *
************************************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.WebTesting;
using System.ComponentModel;
using Microsoft.VisualStudio.TestTools.WebTesting.Rules;
using System.Text.RegularExpressions;
using System.IO;
using System.IO.Compression;
using Microsoft.VisualStudio.TestTools.LoadTesting;

namespace LIL_VSTT_Plugins
{
    [DisplayName("Zip File Upload"), Description("Creates an ZIP archive of the file to be uploaded using the files name and adding .zip. Warning, uses %TEMP% for temp storage.")]
    public class ZipFileUploadBeforePost : WebTestRequestPlugin
    {
        String tempDir = null;
        public override void PreRequest(object sender, PreRequestEventArgs e)
        {
            if (e.Request.Body.GetType() == typeof(FormPostHttpBody)) {
                FormPostHttpBody body = (FormPostHttpBody)e.Request.Body;
                foreach (FormPostParameter param in body.FormPostParameters) {
                    if(param.GetType() == typeof(FileUploadParameter))
                    {
                        FileUploadParameter fparam = (FileUploadParameter)param;
                        tempDir = Path.GetTempPath() + "\\" + Guid.NewGuid().ToString();
                        Directory.CreateDirectory(tempDir);
                        Directory.CreateDirectory(tempDir + @"\ZipDir");
                        File.Copy(fparam.FileName, tempDir + @"\ZipDir\" + Path.GetFileName(fparam.FileName));
                        ZipFile.CreateFromDirectory(tempDir + @"\ZipDir", tempDir + "\\" + Path.GetFileName(fparam.FileName) + ".zip");

                        fparam.FileName = tempDir + "\\" + Path.GetFileName(fparam.FileName) + ".zip";
                        fparam.FileUploadName = fparam.FileUploadName + ".zip";
                    }
                }
            }
            base.PreRequest(sender, e);
        }

        public override void PostRequest(object sender, PostRequestEventArgs e)
        {
            if (tempDir != null) Directory.Delete(tempDir, true);
            base.PostRequest(sender, e);
        }
    }

    [DisplayName("Set Request Think Time"), Description("Changes the thinktime on requests with a set thinktime over 0 to the value of the ThinkTime context parameter")]
    public class SetRequestThinkTime : WebTestPlugin
    {
        [DisplayName("Debug"), DefaultValue(false), Description("Debug logging when thinktime is set on requests")]
        public bool DebugMode { get; set; }
        public override void PreRequest(object sender, PreRequestEventArgs e)
        {
            WebTestContext ctx = e.WebTest.Context;

            if (ctx.ContainsKey("ThinkTime"))
            {
                int tt = Int32.Parse(ctx["ThinkTime"].ToString());
                if (e.Request.ThinkTime > 0)
                {
                    if (DebugMode) e.WebTest.AddCommentToResult("Setting Think Time to " + tt);
                    e.Request.ThinkTime = tt;
                }
            }
        }
    }

    [DisplayName("Clear Cookies"), Description("Clears all cookies from previous iterations of this webtest")]
    public class ClearCookies : WebTestPlugin
    {
        public override void PreWebTest(object sender, PreWebTestEventArgs e)
        {
            e.WebTest.Context.CookieContainer = new System.Net.CookieContainer();
        }
    }

    [DisplayName("Think Time Emulator 10/190"), Description("Sets a context parameter named ThinkTime in each starting test to a random value between 10%-190% of the specified value.")]
    public class ThinkTimeEmulator10190 : ILoadTestPlugin
    {
        [DisplayName("Think Time"), DefaultValue(0), Description("The Think Time to be used seconds. Default is 0.")]
        public int ThinkTime { get; set; }

        //store the load test object.  
        LoadTest mLoadTest;
        Random rnd = new Random();

        public void Initialize(LoadTest loadTest)
        {
            mLoadTest = loadTest;

            //connect to the TestStarting event.
            mLoadTest.TestStarting += new EventHandler<TestStartingEventArgs>(mLoadTest_TestStarting);
        }


        void mLoadTest_TestStarting(object sender, TestStartingEventArgs e)
        {
            // Set the think time parameter in the tests context to a new value
            int min = ThinkTime/10;
            int max = ThinkTime*2 - min;
            e.TestContextProperties.Add("ThinkTime", rnd.Next(min, max));
        }
    }

    [DisplayName("Set Cookie from Query String"), Description("Sets a cookie with given name from the value of a given Query String parameter if found in the current request")]
    public class SetCookieFromQueryString : WebTestPlugin
    {
        [DisplayName("Cookie name"), Description("Name of the cookie to set")]
        public String CookieName { get; set; }

        [DisplayName("Query String parameter name"), Description("Name of the query string parameter to look for")]
        public String ParamName { get; set; }

        public override void PreRequest(object sender, PreRequestEventArgs e)
        {
            base.PreRequest(sender, e);
            QueryStringParameterCollection col = e.Request.QueryStringParameters;
            for(int x=0; x < col.Count; x++)
            {
                if (col[x].Name == ParamName)
                {
                    e.Request.Cookies.Add(new System.Net.Cookie(CookieName, col[x].Value));
                    return;
                }
            }
        }

        public override void PostRequest(object sender, PostRequestEventArgs e)
        {
            base.PostRequest(sender, e);
            if (e.Request.HasDependentRequests)
            {
                foreach (WebTestRequest item in e.Request.DependentRequests)
                {
                    QueryStringParameterCollection col = item.QueryStringParameters;
                    for (int x = 0; x < col.Count; x++)
                    {
                        if (col[x].Name == ParamName)
                        {
                            item.Cookies.Add(new System.Net.Cookie(CookieName, col[x].Value));
                        }
                    }
                }
            }
        }
    }

    [DisplayName("Add Header"), Description("Adds the specified header to all requests matching a given URL regex")]
    public class AddHeader : WebTestPlugin
    {
        [DisplayName("Header Name"), Description("Name of the header to set")]
        public String HeaderName { get; set; }

        [DisplayName("Header Value"), Description("Value of the header to set")]
        public String HeaderValue { get; set; }

        [DisplayName("URL RegEx"), Description("Regular Expression to match on the URL. Empty matches all requests.")]
        public String RegEx { get; set; }

        public override void PreRequest(object sender, PreRequestEventArgs e)
        {
            base.PreRequest(sender, e);
            if (Regex.Match(e.Request.Url, RegEx).Success || RegEx.Length == 0)
            {
                e.Request.Headers.Add(HeaderName, HeaderValue);
            }
        }

        public override void PostRequest(object sender, PostRequestEventArgs e)
        {
            base.PostRequest(sender, e);
            if (e.Request.HasDependentRequests)
            {
                foreach (WebTestRequest item in e.Request.DependentRequests)
                {
                    if (Regex.Match(item.Url, RegEx).Success || RegEx.Length == 0)
                    {
                        item.Headers.Add(HeaderName, HeaderValue);
                    }
                }
            }
        }
    }

    [DisplayName("Log Context Arrays to File"), Description("Logs the specified context parameter arrays to file after each test iteration, one row for each array value")]
    public class LogContextArrayToFile : WebTestPlugin
    {
        [DisplayName("Parameter arrays"), Description("Comma separated list of array parameters to log")]
        public String Params { get; set; }

        [DisplayName("File Name"), Description("The file name to use for logging")]
        public String Filename { get; set; }

        private bool header = true;
        private string[] paramlist = null;

        public override void PostWebTest(object sender, PostWebTestEventArgs e)
        {
            if (Params.Length == 0 || Filename.Length == 0) return;
            if (paramlist == null) paramlist = Params.Split(',');
            if (header)
            {
                File.AppendAllText(Filename, Params + "\r\n");
                header = false;
            }
            // Check that the first param array has a count
            int count = 0;
            WebTestContext ctx = e.WebTest.Context;
            if (ctx.ContainsKey(paramlist[0] + "_count")) count = Int32.Parse(ctx[paramlist[0] + "_count"].ToString());
            for (int i = 1; i <= count; i++)
            {
                string row = "";
                foreach (string param in paramlist)
                {
                    if (row.Length > 0) row += ",";
                    if (ctx.ContainsKey(param + "_" + i)) row += ctx[param + "_" + i].ToString();
                }
                File.AppendAllText(Filename, row + "\r\n");
            }
            base.PostWebTest(sender, e);
        }
    }

    [DisplayName("Log Context to File"), Description("Logs the specified context parameters to file after each test iteration")]
    public class LogContextToFile : WebTestPlugin
    {
        [DisplayName("Regular Expression"), Description("The RegEx to use to match context parameter names")]
        public String myRegex { get; set; }

        [DisplayName("File Name"), Description("The file name to use for logging")]
        public String Filename { get; set; }

        [DisplayName("Autogenerate File Name"), Description("Automatically generate a filename using the test name and user id from the load test")]
        public bool autoFilename { get; set; }

        [DisplayName("Write header"), DefaultValue(false), Description("Writes the parameter names as a header. Will write a new header for each user (dont use in loadtest)")]
        public bool useHeader { get; set; }

        [DisplayName("Log if passed"), DefaultValue(true), Description("Logs the parameters if the webtest passed")]
        public bool logPassed { get; set; }

        [DisplayName("Log if failed"), DefaultValue(true), Description("Logs the parameters if the webtest failed")]
        public bool logFailed { get; set; }

        private bool header = true;

        public override void PostWebTest(object sender, PostWebTestEventArgs e)
        {
            String completeFileName = Filename + e.WebTest.Context.WebTestUserId + ".log";
            if (autoFilename)
            {
                completeFileName = e.WebTest.Name + e.WebTest.Context.WebTestUserId + ".log";
            }
            if ((e.WebTest.Outcome == Outcome.Pass && logPassed) || (e.WebTest.Outcome == Outcome.Fail && logFailed)) {
                string row = "";
                string hrow = "";

                foreach (KeyValuePair<string, object> pair in e.WebTest.Context)
                {
                    if (Regex.Match(pair.Key, myRegex).Success)
                    {
                        if (header && useHeader)
                        {
                            if (hrow.Length == 0) hrow = pair.Key.ToString();
                            else hrow += "," + pair.Key.ToString();
                        }
                        if (row.Length == 0) row = pair.Value.ToString();
                        else row += "," + pair.Value.ToString();
                    }
                }
                if (header && useHeader)
                {
                    File.AppendAllText(completeFileName, hrow + "\r\n");
                    header = false;
                }
                File.AppendAllText(completeFileName, row + "\r\n");
                base.PostWebTest(sender, e);
            }
        }
    }

    [DisplayName("Multi Regular Expression"), Description("Saves all matches for a regular expression including a count")]
    public class MultiRegExExtract : ExtractionRule
    {
        [DisplayName("Regular Expression"), Description("The RegEx to use, supports grouping, for example (.+?)")]
        public String Regex { get; set; }

        [DisplayName("Group Name"), DefaultValue("1"), Description("The group to use as the value for the parameter, default is 1")]
        public String GroupName { get; set; }

        [DisplayName("Pass if not found"), DefaultValue(false), Description("Wherever this extration rule should pass even if no matches are found. Default false.")]
        public bool PassIfNotFound { get; set; }

        public override void Extract(object sender, ExtractionEventArgs e)
        {
            MatchCollection matches = System.Text.RegularExpressions.Regex.Matches(
                e.Response.BodyString,
                Regex,
                RegexOptions.IgnoreCase);
            int index = 0;
            foreach (Match match in matches)
            {
                index++;
                e.WebTest.Context[ContextParameterName + "_" + index] = match.Groups[GroupName].Value;
            }
            if (index > 0)
            {
                e.WebTest.Context[ContextParameterName + "_count"] = index.ToString();
                e.Success = true;
                e.Message = "Found " + index + " matches";
            }
            else
            {
                e.WebTest.Context[ContextParameterName + "_count"] = index.ToString();
                e.Success = PassIfNotFound;
                e.Message = "No matches found. Body size is " + e.Response.BodyString.Length;
            }
        }
    }

    [DisplayName("Extract Count"), Description("Counts all matches for a given RegEx and saves the count to the specified parameter")]
    public class ExtractCount : ExtractionRule
    {
        [DisplayName("Regular Expression"), Description("The RegEx to use")]
        public String Regex { get; set; }

        [DisplayName("Fail on zero count"), DefaultValue(false), Description("Wherever this extration rule should fail if no matches are found")]
        public bool FailOnZero { get; set; }

        public override void Extract(object sender, ExtractionEventArgs e)
        {
            MatchCollection matches = System.Text.RegularExpressions.Regex.Matches(
                e.Response.BodyString,
                Regex,
                RegexOptions.IgnoreCase);
            
            e.WebTest.Context[ContextParameterName] = matches.Count.ToString();
            e.Message = "Found " + matches.Count + " matches";
            if (FailOnZero && matches.Count == 0)
            {
                e.Success = false;
            }
        }
    }

    [DisplayName("Regular Expression Loop"), Description("Loop Condition that matches once on each regexp in previous response body")]
    public class RegExpLoop : ConditionalRule
    {
        private Int32 CurrentMatch { get; set; }
        private String LastUrl { get; set; }
        private MatchCollection Matches { get; set; }

        [DisplayName("Context parameter"), IsContextParameterName(true),
        Description("Name of context parameter where the current value should be set in each loop")]
        public string ContextParameterName { get; set; }

        [DisplayName("Regex"), Description("The RegEx to use, supports grouping, for example (.+?)")]
        public String Regex { get; set; }

        [DisplayName("GroupName"), Description("The group to use as the value for the parameter if several are specified, default is 1")]
        public String GroupName { get; set; }

        // Methods
        public override void CheckCondition(object sender, ConditionalEventArgs e)
        {
            if (CurrentMatch < Matches.Count)
            {
                e.WebTest.Context[ContextParameterName] =
                Matches[CurrentMatch].Groups[GroupName].Value;
                e.IsMet = true;
                CurrentMatch++;
                return;
            }

            e.IsMet = false;
        }

        public override void Initialize(object sender, ConditionalEventArgs e)
        {
            CurrentMatch = 0;
            Matches = System.Text.RegularExpressions.Regex.Matches(
            e.WebTest.LastResponse.BodyString,
            Regex,
            RegexOptions.IgnoreCase);
        }

        public override string StringRepresentation()
        {
            return "Regex condition " + Regex;
        }
    }
}