Friday, November 05, 2010

Find the Difference

Ruby is cool, it can write some code like this:
 arrayA = ['a345','v123','765','b666', '333', 'aaaa'];  
 arrayB = ['234','b666','777','v123','c123'];  

 puts arrayA - arrayB;  
 puts arrayB - arrayA;  

Or if you want to practice your head, you can type the code like me :
 def getdiff( array1, array2)  

  array1.sort!  
  array2.sort!  

  a = array1.pop  
  b = array2.pop  

  while (a != nil && b!= nil)  

    if a == b  
     a = array1.pop  
     b = array2.pop  
     next; 
 
    elsif a < b  
     puts b;  
     b = array2.pop();  
     next;  

    elsif a > b  
     puts a;  
     a = array1.pop();  
     next;
  
    end  
  end  

  while(b == nil && a != nil)  
    puts a  
    a = array1.pop();  
  end  

  while(a == nil && b != nil)  
    puts b  
    b = array2.pop();  
  end  

  puts "Mission complete!"  

 end  

 arrayA = ['a345','v123','765','b666', '333', 'aaaa'];  
 arrayB = ['234','b666','777','v123','c123'];  

 getdiff(arrayA, arrayB);  

Thursday, November 04, 2010

Using WebDriver and Dynatrace-Ajax to help your Performance test

I have WebDriver Automation test suite in place, So i do not want to create new scripts like Watir or Selenium to do performance test with dynatrace-ajax, just like this post by Watir : http://blog.dynatrace.com/2009/11/04/5-steps-to-automate-browser-performance-analysis-with-watir-and-dynatrace-ajax-edition/

or This one by Selenium 1.0: http://blog.dynatrace.com/2010/05/21/how-to-use-your-selenium-tests-for-automated-javascriptajax-performance-analysis/

I have created Raft which is a Automation fixture by integrating WebDriver with TestNG to help to Automation Test : https://github.com/joychester/Raft/wiki

So I can use WebDriver scripts to help with Performance sanity test now, which save a lot of effort to find out performance problem in early stage and also reuse the scripts already created by QA team :)
First you need to launch Dynatrace-ajax client from : Start-> All Programs-> dynatrace-> dynatrace Ajax Edition

Then you need to add "DT_IE_XXX" Environment Variables to start your test.

This is my Runnner.bat file to start my WebDriver tests:
 @echo off  
 set DT_IE_AGENT_ACTIVE=true  
 set DT_IE_SESSION_NAME=dynaTrace_Automation
 java -cp ..\Classes;..\lib\*; raft.engine.TestEngine > log.txt  

Here is a Sample test script:
 @Test(priority = 4)  
   public void aTest3() throws InterruptedException{  
     WebDriverPlus driver = WebDriverPlus.newWebDriverPlusInstance("IE");  
     //InternetExplorerDriver driver = new InternetExplorerDriver();  
     Thread.sleep(2000);  
     driver.get("http://www.google.com/");  
     Thread.sleep(2000);  
     driver.findElement(By.name("q")).sendKeys("dynatrace");  
     Thread.sleep(2000);  
     JavascriptExecutor js = (JavascriptExecutor) driver;  
     js.executeScript("_dt_addMark(\"start_search\")");  
     driver.findElement(By.name("btnG")).click();  
     js.executeScript("_dt_addMark(\"end_search\")");  
     System.out.println("Its Test3");  
     driver.quit();  
   }  

PS: Here i am using Raft's own method newWebDriverPlusInstance() which can load different browser Type to do your test.
  WebDriverPlus driver = WebDriverPlus.newWebDriverPlusInstance("IE");   
Of course, you can use InternetExplorerDriver directly in sample code which is commented.

Here is a screen shot of Dynatrace-ajax summary generated by my WebDriver test:

Enjoy!

Wednesday, November 03, 2010

Please help to vote Issue 835 of WebDriver

My teammate Jersey found this bug:"click() does not work when the link is wrapped in two lines"
http://code.google.com/p/selenium/issues/detail?id=941

However, there is a duplicate issue there: http://code.google.com/p/selenium/issues/detail?id=835

So please help to vote for this issue and make it get fixed ASAP :)

Monday, October 18, 2010

Asking PageFactory.initElements() for Help

Some people asked me what kind of things are done by PageFactory.initElements()?
Let's see a simple example to see the difference,PS: code just for Demo, not for formal testing:-):

CommonPage.class(PageObject)
 public class CommonPage {  
      private WebDriver driver;  
      @FindBy(how = How.NAME, using = "q")  
   private WebElement searchtext;  
      @FindBy(how = How.NAME, using = "btnG")  
   private WebElement searchbutton;  
      public CommonPage(WebDriver driver) {  
             this.driver = driver;  
             System.out.println("cool stuff!");  
      }  
      public CommonPage getpages(){  
           driver.get("http://www.google.com/");  
           searchtext.sendKeys("hello, common");  
           return this;  
      }  
      public void searchaction(){  
           searchbutton.click();  
      }  
 }  

Version1, using PageFactory.initElements(WebDriver, Class):
Testcode:
 public class PageFactoryTest {  
      /**  
       * @param args  
       */  
      public static void main(String[] args) {  
           // TODO Auto-generated method stub  
                WebDriver driver = new FirefoxDriver();  
                CommonPage searchpage = PageFactory.initElements(driver, CommonPage.class); 
                searchpage.getpages().searchaction();   
                driver.quit();  
           }  
      }  

Version2, just using New:
Testcode:
 public class PageFactoryTest {  
      /**  
       * @param args  
       */  
      public static void main(String[] args) {  
           // TODO Auto-generated method stub  
                WebDriver driver = new FirefoxDriver();  
                CommonPage searchpage = new CommonPage(driver);  
                searchpage.getpages().searchaction();  
                driver.quit();  
           }  
      }  

So both PageFactory.initElements(driver, CommonPage.class); and new CommonPage(driver); are calling the construct method in order to pass the Driver to Commonpage class.

The difference between these 2 versions is to initialize looking up the WebElement (Fields) you declared in Commonpage.
so if you define your WebElements using @FindBy annotation instead of defining it within your each method directly(eg. driver.findElement(By)), then you need to using PageFactory.initElements() for help.

Tuesday, October 12, 2010

Windows OS performance measurements by 'typeperf' in Ruby

I am writing a Windows resource monitoring script with ‘typeperf’ command in Ruby, so that i can centralize to get all my testing server's OS Health status during performance testing.
I simply get measurements like CPU%, Mem utlization, Disk I/O throughput and Network I/O throughput from Perfmon Counters.

perf_mon_util.rb Class Source code:
 class PerfMonUtil  
  @hostIPs = [];  
  def initialize(hostarray)  
   hostarray = hostarray.sub(/\s/, '');  
   @hostIPs = hostarray.split(',');  
   puts "monitor IP(s): " + @hostIPs * ",";  
  end  
  # the unit of duration is set to minutes by default  
  def perfmon(duration = 5,intv = 1, filepath = "C:\\temp_mon\\")  
   puts @hostIPs;  
   puts intv;  
   sampler_count = (duration.to_i * 60) / intv.to_i;  
   @hostIPs.each { |inputIP|  
    machineIP = inputIP.strip;  
    filename = filepath + machineIP.gsub(/\./,'_') + "-" + Time.now.strftime("%Y%m%d-%H%M%S");  
    Thread.new {  
        system("typeperf",  
        "\\processor(_total)\\% processor time", #CPU%  
        "\\processor(_total)\\% user time",  
        "\\processor(_total)\\% privileged time",  
        "\\Memory\\Available Kbytes", #MEM_Utilization  
        "\\physicaldisk(_total)\\% disk read time", #DISK I/O  
        "\\physicaldisk(_total)\\% disk write time",  
        "\\network interface(*#1)\\bytes total/sec", #NetWork I/O  
        "-s", "#{machineIP}","-si","#{intv}", "-sc", "#{sampler_count}", "-o", filename)  
        Process.exit!(1);  
     }  
   }  
  end  
 end  

Perf_mon_runner.rb source code:
 require 'perf_mon_util'  
 case ARGV.length  
 when 0  
  puts "Please assign monitored machines' name, you can access help by type Perf_mon_runner.rb -help"  
  puts "Process exiting..."  
  Process.exit!(1);  
 when 1  
  if ARGV[0] == "-help"  
   puts "usage samples listed: Perf_mon_runner.rb MonitredIPs Duration Interval FilePath"  
   puts "Perf_mon_runner.rb \"ip1,ip2,ip3\" 1 20 \"D:\\\\temp\\\\\""  
   puts "Monitoring object list(Measurements including):"  
   puts " \\processor(_total)\\% processor time"  
   puts " \\processor(_total)\\% user time"  
   puts " \\processor(_total)\\% privileged time"  
   puts " \\Memory\\Available Kbytes"  
   puts " \\physicaldisk(_total)\\% disk read time"  
   puts " \\physicaldisk(_total)\\% disk write time"  
   puts " \\network interface(*#1)\\bytes total/sec"  
   Process.exit!(1);  
  end  
  puts "put your monitoring files in C:\\temp_mon\\"  
  CPU_Utilization= PerfMonUtil.new(ARGV[0]);  
  CPU_Utilization.perfmon();  
 when 2  
  puts "put your monitoring files in C:\\temp_mon\\"  
  CPU_Utilization= PerfMonUtil.new(ARGV[0]);  
  CPU_Utilization.perfmon(ARGV[1]);  
 when 3  
  puts "put your monitoring files in C:\\temp_mon\\"  
  CPU_Utilization= PerfMonUtil.new(ARGV[0]);  
  CPU_Utilization.perfmon(ARGV[1],ARGV[2]);  
 when 4  
  puts "put your monitoring files in " + ARGV[3]  
  CPU_Utilization= PerfMonUtil.new(ARGV[0]);  
  CPU_Utilization.perfmon(ARGV[1],ARGV[2],ARGV[3]);  
 end  

How to use it in command-line:
Perf_mon_runner.rb ["YourIP1,YourIP2"] [Monitor_Duration] [Sample_Interval] [FilePath]

Real Usage sample:
Perf_mon_runner.rb "YourIP1,YourIP2" 1 20 "D:\\temp\\"

PS: The Monitoring result will write in the file path as .CSV format by default.

i am using Gruff to draw the graph of monitoring result:

the code is pretty straightforward:
 require 'csv'  
 require 'gruff'  
 def parserperfmon(csvfile,targetfile)  
  arraycpu = Array.new;  
  i = 0;  
  CSV.foreach(csvfile) do |rec|  
    # CPU% data is put into arraycpu
    arraycpu[i] = rec[1]  
    i = i+1;  
  end  
  #column title is removed
  arraycpu.shift  
  #String need to be converted to float type in arraycpu
  arraycpu.collect! { |item|  
   item.to_f  
  }  
  g = Gruff::Line.new  
  g.title = "CPU% Monitoring Graph";  
  g.data("CPU%", arraycpu)  
  g.write(targetfile)
 end  

 filename = "D:\\TEMP\\10_201_10_173-20101125-153102\.csv"  
 targetname ="D:\\TEMP\\perf_mon.png"
 parserperfmon(filename,targetname);  

will consider to add : \\System\\Processor Queue Length to monitor the Queue length:
An instantaneous count of threads that are in the processor queue.

Saturday, October 09, 2010

A Hangzhou Trip















A bird's eye-view of West LakeOn the top of the hill













I like the smile face in the middle :-) The question is who did that...













 "Taking look at my tail,  i am changing the color"






















































Friday, October 08, 2010

Raft Flow Chart

I Draw my Raft automation framework flow chart using "TheBrain" Application, here it is:

http://webbrain.com/u/11J0

Sunday, September 26, 2010

Create WaterFall Chart for Performance (Duration)

usually, I wanna know how much load created at certain point of time on my async system, and see how much impact the concurrent load bring to it. So I made a simple Create WaterFall Chart to help me to understand these more clearly.
This is written in Ruby,sorry java guys, it is faster for me to write ruby code than java, but i like java gradually :-)

I am using Google Chart API, Googlecharts, TinyURL services, Thanks you for providing these!

A quick snapshot for waterfall chart(click to see big):




Test code:

 require 'gchart'  
 require 'time'  
 require 'open-uri'  
 require 'uri'  
 require 'accessDB'  
 #Get start and end time stamp from DB  
 startArray = ["2010-09-26 16:58:37.693","2010-09-26 16:58:38.223","2010-09-26 16:58:38.833",  
 "2010-09-26 16:58:39.600","2010-09-26 16:58:40.210","2010-09-26 16:58:40.833","2010-09-26 16:58:55.740"];  
 endArray = ["2010-09-26 16:59:56.543","2010-09-26 16:59:58.183","2010-09-26 17:00:04.217",  
 "2010-09-26 17:00:09.370","2010-09-26 17:00:10.217","2010-09-26 17:00:19.323","2010-09-26 17:00:25.027"];  
 Gchart_string = prepareData(startArray, endArray )  
 Gchart_url = waterFallUrl(Gchart_string)  
 puts minifyUrl(Gchart_url)  

Prepare Google chart Data and para:
 def prepareData(starttime, endtime )  
  if starttime.length > 400  
   puts "\nExceed the maxium number(400) of records."  
   puts "You can try to split them to display.Press Enter to exit..."; $stdout.flush  
   gets  
   break;  
  end  
  # convert Time Object string to Time object Int  
  starttime.collect! { |time1|  
  Time.parse("#{time1}").to_i;  
  }  
  starttime.sort!  
  endtime.collect! { |time2|  
   Time.parse("#{time2}").to_i;  
  }  
  endtime.sort!  
  # prepare data in array, convert to string used by Gchart  
  flag = starttime[0]  
  starttime.collect! { |time1|  
   time1-flag;  
  }  
  endtime.collect! { |time2|  
   time2-flag;  
  }  
  if endtime.last > 3600  
   puts "\nWarning: Duration is too long to display well for Chart(3600s is recommended).";  
   puts "You can split the data to display."  
  end  
  line_xy_string = "";  
  scale_num = 1;  
  cata = 10;  
  while endtime.last > cata  
   cata = cata + 10 ;  
  end  
  scale_num = cata/starttime.length  
  starttime.length.times { |i|   
   line_xy_string << starttime[i].to_s;  
   line_xy_string << ",";  
   line_xy_string << endtime[i].to_s;  
   line_xy_string << "|";  
   line_xy_string << ((i+1)*scale_num).to_s + "," + ((i+1)*scale_num).to_s;  
   # ignore the last "|"  
   if ((i+1) < starttime.length)  
    line_xy_string << "|";  
   end  
  }  
  line_xy_string << "&chds=";  
  line_xy_string << "0,#{cata}";  
  line_xy_string << "&chxt=x,x,y"  
  line_xy_string << "&chxr=0,0,#{cata}"  
  line_xy_string << "&chxl=1:|Duration(sec)"  
  return line_xy_string;  
 end  


Get Google Chart URL:
 def waterFallUrl(chart_string)  
  output_url = Gchart.line_xy(:size => '700x400',  
       :title => "WaterFall Charts",  
       :custom => "chd=t:#{chart_string}")  
  #"&chxr=0,0" is automatic added to the end of string,  
  #remove it due to already identify one in chart_string  
  output_url.chomp!("&chxr=0,0");  
  return output_url  
 end  

Minify URL using tinyURL:
 def minifyUrl(gchartUrl)  
  # convert URL to escaped one  
  escaped_url = URI.escape(gchartUrl, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))  
  short_url = open("http://tinyurl.com/api-create.php?url=#{escaped_url}").read  
  return short_url;  
 end  

Also if you have written a data query method to get start and end time from DB, then it is easy to get startArray and endArray directly by T-SQL. i am using this in practice: http://snippets.dzone.com/posts/show/3906

or you may take look at this, FYI:
 require 'win32ole'  
 class SQLServer  
   # This class manages database connection and queries  
   attr_accessor :connection, :data, :fields  
   attr_writer :username, :password  
   def initialize(host, username = 'sa', password='sa')  
     @connection = nil  
     @data = nil  
     @host = host  
     @username = username  
     @password = password  
   end  
   def open(database)  
     # Open ADO connection to the SQL Server database  
     connection_string = "Provider=SQLOLEDB.1;" #SQLOLEDB.1,SQLNCLI  
     connection_string << "Persist Security Info=False;"  
     connection_string << "User ID=#{@username};"  
     connection_string << "password=#{@password};"  
     connection_string << "Initial Catalog=#{database};"  
     connection_string << "Data Source=#{@host};"  
     connection_string << "Network Library=dbmssocn"  
     @connection = WIN32OLE.new('ADODB.Connection')  
     @connection.Open(connection_string)  
   end  
   def query(sql)  
     # Create an instance of an ADO Recordset  
     recordset = WIN32OLE.new('ADODB.Recordset')  
     # Open the recordset, using an SQL statement and the  
     # existing ADO connection  
     recordset.Open(sql, @connection)  
     # Create and populate an array of field names  
     @fields = []  
     recordset.Fields.each do |field|  
       @fields << field.Name  
     end  
     begin  
       # Move to the first record/row, if any exist  
       recordset.MoveFirst  
       # Grab all records  
       @data = recordset.GetRows  
     rescue  
       @data = []  
     end  
     recordset.Close  
     # An ADO Recordset's GetRows method returns an array  
     # of columns, so we'll use the transpose method to  
     # convert it to an array of rows  
     @data = @data.transpose  
   end  
   def close  
     @connection.Close  
   end  
 end  

Usage sample:
 db = SQLServer.new('YourDBIP', 'sa', 'sa')  
 sp = db.open('DBName')  
 startArray = db.query("select * from somewhere where ...")  
 endArray = db.query("sselect * from somewhere where ...")  
 db.close  

Monday, September 20, 2010

Handle Datepicker using WebDriver simple sample

Someone asked me: "How to identify a datepicker on the webpage, which will display after clicking the datepicker icon?"
Here is a simple sample, you can customize your own function for reading your web page source and make a api for picking any dates freely :-)

//Find all the Calendar on the Web Page
List<WebElement> Datepickers = driver.findElements(By.className("CalendarIcon"));

//Trigger the calendar of StartDate
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
Datepickers.get(0).click();


WebElement table = driver.findElement(By.className("ui-datepicker-calendar"));

List<WebElement> tds = table.findElements(By.tagName("td"));
for (WebElement td: tds){
//Select 20th Date of current month
if (td.getText().equals("20")){
td.findElement(By.linkText("20")).click();
break;
}

}

//Trigger the calendar of EndDate
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
Datepickers.get(1).click();

WebElement table2 = driver.findElement(By.className("ui-datepicker-calendar"));

List<WebElement> tds2 = table2.findElements(By.tagName("td"));
for (WebElement td2: tds2){
//Select 22th Date of current month
if (td2.getText().equals("22")){
td2.findElement(By.linkText("22")).click();
break;
}

}

Wednesday, September 15, 2010

Table walk through in WebDriver

Very Simple sample for dealing with table elements on webPage in WebDriver:


WebElement table = driver.findElement(By.name("meetingRoomTable"));
//findout all checkboxes within the table
List<WebElement> checkboxes = table.findElements(By.cssSelector("input[type=\"checkbox\"]"));
if (!checkboxes.isEmpty()){
for (WebElement checkbox : checkboxes){

if(!checkbox.isSelected()){
checkbox.toggle();
}
}
System.out.println("all checkboxes elements checked now");
}

else
System.out.println("no checkbox elements exsit on this table");