Wednesday, June 27, 2012

iMacros - Logging Errors and Taking ScreenShots

Recently, I got a chance to work on and use a wonderful automated testing tool - iMacros [thanks to my friend Abhinav who kept giving me more and more requirements to program with this tool]. Specifically I used iMacros as a plugin in firefox for testing web pages. I must admit, the folks @iMacros have done a marvellous job and kudos to them for this.
In this blog post I want to specifically talk about one thing that stumped me most of the time using this tool - "How do I log the detailed HTTP error (500, 403 etc....) to a log file & how do I take a screenshot when such a thing happens?". Here's how I did it:

Saving a ScreenShot


iMacros provides for a SAVEAS command, using which you can take and save an image of the page displayed in the browser. I created a new macro, takeScreenShot.iim and here is the implementation


VERSION BUILD=7401110 RECORDER=FX
SAVEAS TYPE=PNG FOLDER={{SCRSHOTFOLDER}} FILE={{SCRSHOTFILENAME}}

Pretty simple huh!! Exactly! The only thing to note here is that I am providing the FOLDER and FILE values from {{SCRSHOTFOLDER}} and {{SCRSHOTFILENAME}} variables respectively. You can hard code the values if required, but I am not sure if it would be useful to hardcode the file name.
To send variables from outside to this macro you can use the iimSet command and set the values appropriately. Also, iMacros can only save files as png, so I always include "png" as an extension in the filename that I pass in to this macro.


To use this macro, simply call it from the place you want to take the screenshot (use iimPlay command).


Logging Errors
This one is a bit tricky. There are two parts to this that I tried to do:
1. Log the faulting Url and the time stamp
2. Log the faulting Url, timestamp and detailed error message

The first one turned out to be easy. Here is some js code to do this
var ret = iimPlay(<your macro name>);
if(ret != 1)
{
        var today = new Date();
      var dd = today.getDate();
      var mth = today.getMonth() + 1;
      var year = today.getFullYear();
      var hrs = today.getHours();
      var mins = today.getMinutes();
      var secs = today.getSeconds();
      var ts = mth + "-" + dd + "-" + year + "  " + hrs + ":" + mins + ":" + secs;

var message = "\r\n" + ts + " FAILED URL: " + window.frames[3].location.href + "\r\n";

writeToFile(logFile, message);

}

I am not going to detail out the writeToFile method here. It is a simple method that takes in a file name and a message string to log and appends the message string to the end of the file.
The key thing to note here is the var ret = iimPlay(....) stuff. The iimPlay command returns a code that signifies the success/failure of the macro that it runs. A detailed list of error messages can be found here.
If the return code from iimPlay is not 1, then we know it's a failure and we can proceed logging stuff.
You might want to add the error code (in ret) to the message string, for a more detailed log.

This brings us to our next question, what if we also wanted to log all the text/html for the erroneous page? Some might argue here that instead of logging the text on the page, won't it be better if we just saved a screenshot instead? I agree, but there might be instances where you would want to send a log file across to the developer so that he can copy paste stuff from the same. Also, there might be some error info that might only be displayed when you expand the section containing that info. These kind of things cannot be easily done using a screenshot. So, here's how I implemented logging the text:

First, we need a way to extract text of a web page. Here is a snippet that can be used to achieve this:

TAG POS=1 TYPE=HTML ATTR=* EXTRACT=TXT

Paste this snippet at the end of the macro that displays the web page you are testing.
Then, in the js code, you can use the iimGetLastExtract() command to get the extracted html of the web page.

var err_string = iimGetLastExtract();
Once we have this, it should be easy to log it to a file, right! But there is a catch.... iimGetLastExtract() does not return back extracts if the web page displayed is the result of an HTTP error (or if iimPlay's return code is something other than 1). To get around this issue, I did a small hack (which apparently I don't like, but did not have any other way of doing it as of this writing), wherein I included the following snippet in my macro that displayed the web page:

SET !ERRORIGNORE YES

Once you do this, iimPlay will always return 1 because it would ignore all the errors. That brings us to the next stumbling block:
our js code if(ret != 1), will not work anymore for detecting errors. To get around this, did the following:


     var err_string = iimGetLastExtract();
     if(err_string.indexOf("Server Error in")> -1)
     {
            //log timestamp and err_string here

     }

Although this does not look very efficient and neat, but for now it gets the job done for me. You can add more conditions to the if (just like the one for "Server Error in") for the errors that you expect to log and it should work the same.

That's it for this post. Hope this helps someone who needs it.

Happy web testing!!
Make Custom Gifts at CafePress

Wednesday, May 9, 2012

C# DateTime utility extension methods

A lot of times we find the need for utility methods to operate on DateTime type in C# .net. Here is a utility class that I implemented which has some of the utility methods I think might come in handy. This class is ready to be consumed as is. Just put it in an appropriate namespace and use that namespace in your class/program. Feel free to post enhancements to this class that you think might come in handy.


public static class DateTimeExt
{

public static bool IsWithinLastXDays(this DateTime dateTime, int days)
{
return
DateTime.Now.AddDays(-days) <= dateTime &&
dateTime <= DateTime.Now;
}

public static bool IsWithinLastXMinutes(this DateTime dateTime, int minutes)
{
return
DateTime.Now.AddMinutes(-minutes) <= dateTime &&
dateTime <= DateTime.Now;
}

public static int DaysSince(this DateTime dateTime)
{
return
new TimeSpan(DateTime.Now.Ticks - dateTime.Ticks).Days;
}


        public static int YearsOld(this DateTime dateTime)
        {
                        DateTime current = DateTime.Now;

int age = current.Year - dateTime.Year;

if (age > 0)
{
age -= Convert.ToInt32(current.Date < dateTime.Date.AddYears(age));
}
else
{
age = 0;
}
return age;

        }

        public static DateTime MinSqlDateTime()
        {
            return new DateTime(1900, 1, 1);
        }


public static DateTime FindFollowingDayOfWeek(this DateTime currentDate, DayOfWeek day)
{
int currentDay = (int)currentDate.DayOfWeek;
int seekDay = (int)day;
int daysToAdd = 0;
if (seekDay <= currentDay)
{
daysToAdd = (7 - currentDay) + seekDay;
}
else
{
daysToAdd = (seekDay - currentDay);
}

return currentDate.AddDays(daysToAdd);
}

public static DateTime FindPreviousDayOfWeek(this DateTime currentDate, DayOfWeek day)
{
int currentDay = (int)currentDate.DayOfWeek;
int seekDay = (int)day;
int daysToAdd = 0;
if (seekDay <= currentDay)
{
daysToAdd = (7 - currentDay) + seekDay;
}
else
{
daysToAdd = (seekDay - currentDay);
}
daysToAdd = daysToAdd - 7;

return currentDate.AddDays(daysToAdd);
}
}

Here is a unit test class (using nUnit) that shows the usage of the class above:
        [TestFixture]
        public class Test
        {
                        [Test]
                        public void IsWithinLastXDaysTest()
                       {
                             Assert.IsFalse(DateTime.Now.AddDays(4).IsWithinLastXDays(5));
                             Assert.IsTrue(DateTime.Now.IsWithinLastXDays(5));
                             Assert.IsTrue(DateTime.Now.AddDays(-4).IsWithinLastXDays(5));
                             Assert.IsTrue(DateTime.Now.AddDays(-5).IsWithinLastXDays(5));
                             Assert.IsFalse(DateTime.Now.AddDays(-6).IsWithinLastXDays(5));
                       }             
        }
Make Custom Gifts at CafePress

Wednesday, March 14, 2012

Using AutoResetEvent for controlling .NET Windows Service Start and Stop

Problem:
You have to implement a polling service that does a certain task every 'n' units of time till it is stopped.

Pretext:
The methods of interest in this problem are OnStart, OnStop and the real worker thread that we implement. So I would be concentrating on these and will skip out on the boiler plate code required to setup the service. I would be demonstrating only the relevant parts of code in this discussion. Also, the example code here would lack exception handling, thread syncing, for the sake of staying on track with explaining the better solution.

Solutions:
1. Implement a worker thread that does the actual work. Inside the worker thread, implement a sleep loop.

Here is some sample code demonstrating this:

public partial class SampleService : ServiceBase
{
           Thread _worker;
           bool _shouldStop;
           //service start
          protected override void OnStart(string[] args)
         {
                    _worker = new Thread(new ThreadStart(DoWork));
                    _worker.Start();
         }

        //service stopped
        protected override void OnStop()
        {
               _shouldStop = true;
        }

        private void DoWork()
        {
               while(!_shouldStop)
               {
                    //do work here
                    Thread.Sleep(1000); //using 1 sec here. but this can be any configurable value.
               }
        }
}

The basic problem with this solution is as follows:
If the sleep time was a larger value (which is the most common case), for example 5 minutes or 300 secs, then the service would stop responding if someone stopped the service while it was in the middle of the sleep statement. Essentially what happens is that the operating system waits for the worker thread to complete before it can dispose off the service process. Because the worker thread is in a sleep mode, and there is no implementation done to interrupt it, it never returns before the sleep timeout is over. Hence, after a certain period of time, the service goes in a not responding state.
This is just a basic explanation of what might happen in this case, there are other scenarios that might happen with different outcomes, but this is the most common one.

2. Use AutoResetEvent to control the service and the worker thread. This is a better approach, as this gives more control on the service lifetime and allows a clean way to stop the service. 

Here is some sample code demonstrating this approach:

   //Helper class, that wraps the AutoResetEvents.
   //Having this class makes the code a bit cleaner and easier to understand structurally.

    public class ThreadHelper
    {
        public ThreadHelper() { }

        //false in the constructor below signifies not signaled state
        private AutoResetEvent _stopEvent = new AutoResetEvent(false);
        private AutoResetEvent _waitToFinishEvent = new AutoResetEvent(false);
        private Thread _thread = null;

        public AutoResetEvent stopEvent
        {
            set { _stopEvent = value; }
            get { return _stopEvent; }
        }
        public AutoResetEvent waitToFinishEvent
        {
            set { _waitToFinishEvent = value; }
            get { return _waitToFinishEvent; }
        }
        public Thread thread
        {
            set { _thread = value; }
            get { return _thread; }
        }
    }



public partial class SampleService : ServiceBase
{
           ThreadHelper _worker = new ThreadHelper();

           //service start
          protected override void OnStart(string[] args)
         {
                    _worker.thread = new Thread(new ThreadStart(DoWork));
                    _worker.thread.Start();
         }

        //service stopped
        protected override void OnStop()
        {
             if ((_worker.thread != null) && (_worker.thread.IsAlive == true) && (_worker.stopEvent != null))
            {
                 _worker.stopEvent.Set(); //signal DoWork thread to stop
                 //15000 [15 secs] can be changed to any value that you want to let the worker thread finish
                 if (!_worker.waitToFinishEvent.WaitOne(15000, false))
                {
                       //Even after waiting 15000 [15 secs] (or any value that you choose), the worker did not finish
                      //cannot wait any more, so abort the worker thread
                       _worker.thread.Abort();
                }
            }
        }

        private void DoWork()
        {
               bool shouldStop = false;
               while(!shouldStop)
               {
                        if (_worker.stopEvent.WaitOne(/*your polling time*/, false))
                       {
                             shouldStop = true;
                            _worker.waitToFinishEvent.Set();
                       }
                       else
                       {
                             //do work here
                       }
               }
        }
}


An interesting thing to note is that OnStart and OnStop are on the main thread and DoWork is on another thread. Also, the WaitOne method returns true if a signal was received and false if it was not. Specifying a wait time as the first parameter to the WaitOne method causes it to wait for a signal till the wait time is completed. If a signal is not received within the specified wait time, it returns false.

In essence, what happens in this case is that OnStop, signals the stopEvent, which causes the WaitOne (in DoWork thread) to return true (because it was signaled). This causes the shouldStop to be set to true and we come out of the while loop causing the method and hence the thread to complete. On the other hand, after setting shouldStop to true, we signal waitToFinish, which causes the OnStop to complete [because WaitOne in the OnStop returns true and the statements inside if are not evaluated]. In case, if our worker thread was doing the work and it took longer than 15 sec to respond the OnStop WaitOne would return false and it would abort the worker thread.
In any case, this would never cause our service to go in a not responding state.

Although, the explanation here might seem a bit complex, relating with the sample code should help make the concept more clearer.

Happy Coding!!

Make Custom Gifts at CafePress

Friday, March 9, 2012

Calculating DateTime in different TimeZones

Problem:
In a web request, you know the location where the user is coming from, and you have to figure out the current time in the users timezone.

Notes:
To keep this example simple, I've made an assumption that we'll be calculating times only in US time zones. You can also use IP geocoding to figure out the location where the request is coming from and modify the code in this example accordingly.

Solution:
.net framework base class library provides for System.TimeZoneInfo class that can be used for doing timezone based calculations. The idea is as follows:
1. Convert the given time to a Utctime
2. Figure out users location. This can be done by
             a. Geocoding the IP
             b. If the users location is already known (through some profile data already available in the system)
3. Figure out the timezone applicable as per the users location
4. Convert UTC time from step 1 above to the time in users timezone by using TimezoneInfo.ConvertTimeFromUtc method.
The ConvertTimeFromUtc method is intelligent enough to apply daylight savings if applicable to the converted time.

Sample Code:

public class TimezoneComputation
{
          public TimezoneComputation(){}
       
        public virtual DateTime ConvertDateTimeToLocalDateTime(DateTime input,
                                                                                                    string timezoneShortName)
         {
                if (string.IsNullOrEmpty(timezone) == false)
                {
                    var tz = TimeZoneInfo.FindSystemTimeZoneById( timezoneShortName .ToFullTimeZoneName());
                    var tempDt = TimeZoneInfo.ConvertTimeToUtc(input);
                    return TimeZoneInfo.ConvertTimeFromUtc(tempDt, tz);
                }
                //by default return the input without any conversions
                return input;                  
         }
}
    //this helper class adds an extension method to string which returns the full timezone name
   //that can be used by the TimeZoneInfo.FindSystemTimeZoneById method
   //right now this has only the US time zones, but can be extended to include other timezones also

    internal static class TimeZoneMapper
    {
        public static string ToFullTimeZoneName(this string shortName)
        {
            switch (shortName.ToLower())
            {
                case "cst":
                    return "Central Standard Time";
                case "pst":
                    return "Pacific Standard Time";
                case "akst":
                    return "Alaskan Standard Time";
                case "mst":
                    return "Mountain Standard Time";
                case "est":
                    return "Eastern Standard Time";
                case "hst":
                    return "Hawaiian Standard Time";
                case "asst":
                    return "Samoa Standard Time";
                case "ast":
                    return "Atlantic Standard Time";
            }

            return "";
        }
    }

Further Remarks:
In the TimeZoneMapper helper class above, I return the long names (system identifiers) of different timezones. You can find this list here.

Let me know if you want to see how I figured out what to pass in the timezoneShortName parameter of TimezoneComputation.ConvertDateTimeToLocalDateTime method.

The sample code shown above does need some more error checking, for example for edge cases when input is mindatetime, which can be easily implemented.

Here is a sample unit test class (I am using nUnit here):

    [TestFixture]
    public class TimezoneComputationTests
    {

        #region ConvertDateTimeToLocalDateTime Tests

        [Test]
        public void ConvertDateTimeToLocalDateTimeReturnsTheCorrectTimeByTimeZoneShortName()
        {
            //check EST. 2/29/2012 11:10:00 CST should be 2/29/2012 12:10:00 EST
            DateTime cstTime = new DateTime(2012, 2, 29, 11, 10, 0);
            DateTime dt = _timezoneComputation.ConvertDateTimeToLocalDateTime(cstTime, "est");
            Assert.AreEqual(cstTime.Date, dt.Date);
            Assert.AreEqual(12, dt.Hour);
            Assert.AreEqual(10, dt.Minute);
            Assert.AreEqual(0, dt.Second);

            //check CST. 2/29/2012 11:10:00 CST should be 2/29/2012 11:10:00 CST
            dt = _timezoneComputation.ConvertDateTimeToLocalDateTime(cstTime, "cst");
            Assert.AreEqual(cstTime.Date, dt.Date);
            Assert.AreEqual(11, dt.Hour);
            Assert.AreEqual(10, dt.Minute);
            Assert.AreEqual(0, dt.Second);

            //check PST. 2/29/2012 11:10:00 CST should be 2/29/2012 9:10:00 PST
            dt = _timezoneComputation.ConvertDateTimeToLocalDateTime(cstTime, "pst");
            Assert.AreEqual(cstTime.Date, dt.Date);
            Assert.AreEqual(9, dt.Hour);
            Assert.AreEqual(10, dt.Minute);
            Assert.AreEqual(0, dt.Second);

            //check MST. 2/29/2012 11:10:00 CST should be 2/29/2012 10:10:00 MST
            dt = _timezoneComputation.ConvertDateTimeToLocalDateTime(cstTime, "mst");
            Assert.AreEqual(cstTime.Date, dt.Date);
            Assert.AreEqual(10, dt.Hour);
            Assert.AreEqual(10, dt.Minute);
            Assert.AreEqual(0, dt.Second);

            //check AKST. 2/29/2012 11:10:00 CST should be 2/29/2012 8:10:00 AKST
            dt = _timezoneComputation.ConvertDateTimeToLocalDateTime(cstTime, "akst");
            Assert.AreEqual(cstTime.Date, dt.Date);
            Assert.AreEqual(8, dt.Hour);
            Assert.AreEqual(10, dt.Minute);
            Assert.AreEqual(0, dt.Second);

            //check HST. 2/29/2012 11:10:00 CST should be 2/29/2012 7:10:00 HST
            dt = _timezoneComputation.ConvertDateTimeToLocalDateTime(cstTime, "hst");
            Assert.AreEqual(cstTime.Date, dt.Date);
            Assert.AreEqual(7, dt.Hour);
            Assert.AreEqual(10, dt.Minute);
            Assert.AreEqual(0, dt.Second);


            //check empty timezoneshortname. Should return back the input unmodified
            dt = _timezoneComputation.ConvertDateTimeToLocalDateTime(cstTime, "");
            Assert.AreEqual(cstTime.Date, dt.Date);
            Assert.AreEqual(11, dt.Hour);
            Assert.AreEqual(10, dt.Minute);
            Assert.AreEqual(0, dt.Second);

            //check null timezoneshortname. Should return back the input unmodified
            dt = _timezoneComputation.ConvertDateTimeToLocalDateTime(cstTime, null);
            Assert.AreEqual(cstTime.Date, dt.Date);
            Assert.AreEqual(11, dt.Hour);
            Assert.AreEqual(10, dt.Minute);
            Assert.AreEqual(0, dt.Second);
        }

        #endregion

    }


Make Custom Gifts at CafePress