Monday, December 20, 2010

WOW,Continual Full GC Detected!

'Continual Full GC',I will KILL you...

Updated:
Root Cause: when using Axis as an implementation of the SOAP, when Web Service request coming with the attachment over 1MB, then it will make trouble with Heap Memory.
Lot of OOM issues after searching on the web, and no ideal solution with that right now on Apache Axis:

1. SAX2EventRecorder causes Out of memory during Deserialization https://issues.apache.org/jira/browse/AXIS-2698

2. SAX2EventRecorder throws Out of Memory exception during Deserialization https://issues.apache.org/jira/browse/AXIS-2749

Solution:Just Replace the Apache Axis which project has been suspended for quite long time since 2006.

Take Away:
1. When you find Memory Heap Issues, you need to get HeapDump as possible as you can, the other info can be useless... We use SendSingal.exe(for Windows) to trigger the JDK5 to get the Heapdump, when we noticed the Continual Full GC
2. Analysis HeapDump file together with Functional QA, they can give some idea from business perspective, in order to reproduce your issue on local much easier.
After reproducing the issue, then you will solve your pain sooner or later, there will be something wrong with your code :)

Tuesday, December 14, 2010

Track my Performance Data with WebDriver test by MongoDB

In my WebDriver test, I will always track performance data during automation test on daily bases, it helps to reuse automation scripts which QA team donated to track slowness cases, and guarantee the test coverage very well.
Here is a sample tracking diagram (X-axis: Date, Y-axis: Response time):


Meanwhile, the data is End-End response time,not only back-end html generation time, which load testing tool usually measures.

Testing Code:
MongoDBReader conn = new MongoDBReader();  
WebDriver driver = new FirefoxDriver();  

long starttime = new Date().getTime();  
driver.get("http://www.google.com/");  
conn.InstertPermLog(starttime, "Landing_Page");  

driver.close();  

MongoDBReader source code:
 import java.net.UnknownHostException;  
 import java.util.Date;  
 import java.text.DateFormat;  
 import java.text.SimpleDateFormat;  
 import com.mongodb.BasicDBObject;  
 import com.mongodb.DB;  
 import com.mongodb.DBCollection;  
 import com.mongodb.Mongo;  
 import com.mongodb.MongoException;  
 /* sample usage  
  * MongoDBReader conn = new MongoDBReader();  
  * ...  
  * long starttime = new Date().getTime();  
  * driver.get("http://www.google.com/");  
  * conn.InstertPermLog(starttime, "Landing_Page");  
  */  
 public class MongoDBReader {  
      private DB db;  
      private DBCollection coll;  
      public MongoDBReader(String hostname) throws UnknownHostException, MongoException{  
           // Connect to Mongo DB server  
           Mongo m = new Mongo( hostname );  
           // Get Mongo DB Connection  
           db = m.getDB("perfdb");  
           // Prepare Collection  
           coll = db.getCollection("perfTestColl");  
      }  
      public MongoDBReader() throws UnknownHostException, MongoException {  
           //Connect to the localhost MongoDB by Default  
           String hostname = "localhost";  
           // Connect to Mongo DB server  
           Mongo m = new Mongo( hostname );  
           // Get Mongo DB Connection  
           db = m.getDB("perfdb");  
           // Prepare Collection  
           coll = db.getCollection("perfTestColl");  
      }  
      public DB getDB(){  
           return db;  
      }  
      public DBCollection getCollection(){  
           return coll;  
      }  
      public void InstertPermLog(long startTime, String transactionName){  
           long endtime = new java.util.Date().getTime();  
           long responsetime = endtime - startTime;  
           DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.S");  
        Date date = new Date();  
           BasicDBObject doc = new BasicDBObject();  
           doc.put("TimeStamp", dateFormat.format(date));  
           doc.put("Rtime", responsetime);  
           doc.put("transaction", transactionName);  
           coll.insert(doc);  
      }  
 }  

MongoDB Shell console:
> db.perfTestColl.find();
{ "_id" : ObjectId("4d078fbb3599030f8a412be1"), "TimeStamp" : "2010/12/14 23:39:
39.546", "Rtime" : NumberLong(4609), "transaction" : "Landing_Page" }
{ "_id" : ObjectId("4d078fc33599030f8b412be1"), "TimeStamp" : "2010/12/14 23:39:
47.953", "Rtime" : NumberLong(1641), "transaction" : "Landing_Page" }
{ "_id" : ObjectId("4d078fcb3599030f8c412be1"), "TimeStamp" : "2010/12/14 23:39:
55.578", "Rtime" : NumberLong(1360), "transaction" : "Landing_Page" }

Wednesday, December 08, 2010

O'Reilly Velocity China 2010

I am starting to do Performance Testing since 2006. And I have followed Steve about Front-end Performance optimization from 2007, although i am mainly focus on the testing and back-end tuning at most of time.
A big Fan of him, because always can get many fresh ideas and best practices after reading his blog, if you haven't read his blog, suggest to catch up with it :) (Seems twitter has become his major updated place right now...)


Thursday, November 25, 2010

Sorting algorithm in Perl

My Girl Friend likes the language that easy to write but hard to read, just like Perl which makes me crazy :)

Here are 3 simple examples which is illustrating her algorithm basic skills(BTW, she is a math talented person i ever met during college)

Insertion Sorting
 @arry1 = (12,32,3,25,67,24,56);  
 @arry2 = shift @arry1;  
 $j=$#arry2;  
 foreach $arry (@arry1){  
  if ($arry2[$j]<= $arry) {  
   push @arry2, $arry;  
  }  
  elsif ($j==0 ) {  
   unshift @arry2, $arry;  
  }  
  else {  
   foreach (reverse(@arry2)){  
    if ($_ > $arry){  
    $j-=1;  
    }  
   }  
   if ($j >=0){  
   $i = $j+1;  
   @arry2= (@arry2[0..$j], $arry, @arry2[$i..$#arry2]);  
   }  
   else {  
    unshift @arry2, $arry;   
   }  
  }  
  $j=$#arry2;  
 }  

 foreach (@arry2){  
   print "$_ ";   
  }  
Quick Sorting:

 @arry1=(12,32,3,25,67,24,56);  
 $i=0;  
 $j=$#arry1;  
 &quick_sorting($i,$j);  
  sub quick_sorting {  
   my($m,$n)= @_;  
   while ($m <$n){  
    if ($arry1[$m]<= $arry1[$n]) {  
     $n-=1;  
     }  
    else {  
      ($arry1[$m], $arry1[$n]) = ($arry1[$n], $arry1[$m]);  
      $m+=1;  
     }  
    }  
   if ($m - $_[0]> 1) {  
    &quick_sorting($_[0], $m-1);  
    }  
   if ($_[1] -$n > 1){  
    &quick_sorting($n+1, $_[1]);  
    }     
  }  
 foreach $arry (@arry1){  
  print "$arry\n";   
 }  

Bubble Sorting
 @arry1 = (12,32,3,25,67,24,56);  
 $i=0;  
 $j=$#arry1;  
  for ( ; $j >=0; $j-=1){  
   for ($i=0; $i<$j; $i+=1 ){  
   if ($arry1[$i]-$arry1[$i+1] gt 0) {  
     ($arry1[$i], $arry1[$i+1])=($arry1[$i+1], $arry1[$i]);  
     }  
   }  
  }  
  foreach $arry (@arry1){  
   print "$arry\n";   
  }  

Wednesday, November 24, 2010

Aggregate Jmeter Raw data into CSV file

I am doing daily automated Jmeter load test to track performance trend using command line, due to only raw data available , so i need to write some code to parser and Aggregate it.
PS: I am using Ruby1.9 to handle this, Ruby1.8.7 and below do not implement the append row to CSV file yet.

You need to Modify the settings for Results file configuration in jmeter.properties in advance:

 jmeter.save.saveservice.output_format=csv  
 jmeter.save.saveservice.assertion_results=none  
 jmeter.save.saveservice.data_type=false  
 jmeter.save.saveservice.label=true  
 jmeter.save.saveservice.response_code=true  
 jmeter.save.saveservice.response_data=false   
 jmeter.save.saveservice.response_data.on_error=false  
 jmeter.save.saveservice.response_message=false  
 jmeter.save.saveservice.successful=false  
 jmeter.save.saveservice.thread_name=true  
 jmeter.save.saveservice.time=true  
 jmeter.save.saveservice.subresults=false  
 jmeter.save.saveservice.assertions=false  
 jmeter.save.saveservice.latency=true  
 jmeter.save.saveservice.samplerData=false  
 jmeter.save.saveservice.responseHeaders=false  
 jmeter.save.saveservice.requestHeaders=false  
 jmeter.save.saveservice.encoding=false  
 jmeter.save.saveservice.bytes=true  
 jmeter.save.saveservice.url=false  
 jmeter.save.saveservice.filename=false  
 jmeter.save.saveservice.hostname=false  
 jmeter.save.saveservice.thread_counts=false  
 jmeter.save.saveservice.sample_count=false  
 jmeter.save.saveservice.idle_time=false
 jmeter.save.saveservice.print_field_names=false

Ruby Source code:

 require 'csv'
 require 'time'
 #Generate agg result from Jmeter raw data
 def parserCSVperfdata(filename)  
  h = {};  
  h_count = {}  
  result = [];  
  i =0;  
  CSV.foreach(filename) do |rec|  
   if (h.has_key?(rec[2]))  
    h[rec[2]] = h[rec[2]].to_i + rec[1].to_i  
    h_count[rec[2]] = h_count[rec[2]].to_i + 1  
    next;  
   else  
    h[rec[2]] = rec[1]  
    h_count[rec[2]] = h_count[rec[2]].to_i + 1  
   end  
  end  
  h.each_key { |key|  
   h[key] = h[key].to_i/h_count[key].to_i  
   result[i] = key;  
   result[i+1] = ",";  
   result[i+2] = h[key];  
   result[i+3] = "\n";  
   i=i+4  
  }  
  return h.sort;  
 end 

 #insert agg result into target CSV file
 def insertlog(src,dest)  
  # get current date and collect the data today  
  t = Time.now  
  cdate = t.strftime("%Y-%m-%d")  
  #append row to CSV file, using Ruby1.9 here for mode = 'a'  
  csvwriter = CSV.open(dest,'a')  
  #append array of string, need to convert date string to array  
  csvwriter << cdate.split  
  src.each do |row|  
   csvwriter << row  
  end  
 end  

Testing code:

 t = Time.now  
 mdate = t.strftime("%Y%m%d")  
 aggresultfile = "D://Perf//loadtest.csv";  
 singlethreadresultfile = "D://Perf//singleuser.csv"  
 # Generate JMeter agg result on daily bases  
 arraydir = Dir.entries("D://tools//jakarta-jmeter-2.4//bin");  
 arraydir.each { |item|  
       item.scan(/#{mdate}_[0-9]+_rawdata\.csv/) { |match|  
           aggdata = parserCSVperfdata("D://tools//jakarta-jmeter-2.4//bin//#{match}");  
           insertlog(aggdata,aggresultfile);  
      }  
 }  

Enjoy!

Monday, November 22, 2010

Set current date and time variable in Batch file

Check your system date format at first:
The current date is: 11/23/2010 Tue
so the format is mm/dd/yyyy weekday

It might be different from other computer, some of are put weekday in the front of the date:
The current date is: Tue 11/23/2010

Here we take 11/23/2010 Tue as an example:
@echo off  
SET inputdate=%date:~6,4%%date:~0,2%%date:~3,2%  
SET inputtime=%time:~0,2%%time:~3,2%%time:~6,2%  
echo %inputdate%_%inputtime%_Rawdata.jtl

 Output:20101122_234425_Rawdata.jtl  

PS: no space between ‘=’ and ‘%’, it costs me 10 mins around this...

Thursday, November 11, 2010

How to attach add-ons to your FirefoxDirver

A Typical sample like this, in case i forget how to do :)
 //Pre-condition: Right Clicking the button to save the add-on to some location  
final String firebugPath = "C:\\addon-1843-latest.xpi";  
FirefoxProfile profile = new FirefoxProfile();      
profile.addExtension(new File(firebugPath));  

WebDriver driver = new FirefoxDriver(profile);  

Another Sample is to set proxy for the FirefoxDriver:
FirefoxProfile profile = new FirefoxProfile();  
profile.setPreference("network.proxy.http", "YourDomain");  
profile.setPreference("network.proxy.http_port", port);  
profile.setPreference("network.proxy.type", 1);   
profile.setPreference("network.proxy.share_proxy_settings", true);  
WebDriver driver = new FirefoxDriver(profile);  

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");

Sunday, September 12, 2010

Distribute your test suites by STAF(STAX)

STAF is a good Open source "test automation" framework which developed by IBM. I am currently using STAF(STAX) to distribute my test Suites which are organized by TestNG, it is improve your efficiency.

Here is a simple sample how I zip, transfer and execute My Test on 2 machines(please format this XML by yourself, if you need to look at it):

 <?xml version="1.0" encoding="UTF-8" standalone="no"?>  
 <!DOCTYPE stax SYSTEM "stax.dtd">  
 <!-- New document created with EditiX at Fri Jul 09 15:35:49 CST 2010 -->  
 <stax>  
 <script>machinelist = ['Machinename1','Machinename2']</script>  
 <defaultcall function="ZipTest"></defaultcall>  
 <function name="ZipTest">  
 <sequence>  
 <stafcmd>  
 <location>'localhost'</location>  
 <service>'zip'</service>  
 <request>'ADD ZIPFILE D:/Raft_Project/TestPlan.zip DIRECTORY D:/Raft_Project/TestPlan RECURSE RELATIVETO D:/Raft_Project' </request>  
 </stafcmd>  
 <call function="'sleep'"></call>  
 <call function="'DistributionTest'"></call>  
 <call function="'sleep'"></call>  
 <call function="'CleanupTest'"></call>  
 </sequence>  
 </function>  
 <function name="DistributionTest">  
 <paralleliterate var="machinename" in="machinelist">  
 <sequence>  
 <stafcmd>  
 <location>machinename</location>  
 <service>'fs'</service>  
 <request>'DELETE ENTRY D:/Raft_Project/TestPlan RECURSE CONFIRM' </request>  
 </stafcmd>  
 <call function="'sleep'"></call>  
 <stafcmd>  
 <location>'local'</location>  
 <service>'fs'</service>  
 <request>'COPY FILE D:/Raft_Project/TestPlan.zip TODIRECTORY D:/Raft_Project/ TOMACHINE %s' % machinename </request>  
 </stafcmd>  
 <call function="'sleep'"></call>  
 <stafcmd>  
 <location>machinename</location>  
 <service>'zip'</service>  
 <request>'UNZIP ZIPFILE D:/Raft_Project/TestPlan.zip TODIRECTORY D:/Raft_Project/'</request>  
 </stafcmd>  
 <call function="'sleep'"></call>  
 <stafcmd>  
 <location>machinename</location>  
 <service>'fs'</service>  
 <request>'DELETE ENTRY D:/Raft_Project/TestPlan.zip RECURSE CONFIRM'</request>  
 </stafcmd>  
 <process>  
 <location>machinename</location>  
 <command mode='"shell"'>'Runner.bat'</command>  
 <workdir>R'D:\Raft_Project\TestPlan\TestRunner_Module1'</workdir>  
 <returnstdout/>  
 <returnstderr/>  
 </process>  
 </sequence>  
 </paralleliterate>  
 </function>  
 <function name="CleanupTest">  
 <stafcmd>  
 <location>'localhost'</location>  
 <service>'fs'</service>  
 <request>'DELETE ENTRY D:/Raft_Project/TestPlan.zip RECURSE CONFIRM'</request>  
 </stafcmd>  
 </function>  
 <function name="sleep">  
 <stafcmd>  
 <location>'localhost'</location>  
 <service>'delay'</service>  
 <request>'delay 6000'</request>  
 </stafcmd>  
 </function>  
 </stax>  
hope it helps! More detail instruction, please go to STAF official website:http://staf.sourceforge.net/

PS: you may need to modify your under STAF/bin/, so that it can avoid some restrictions.
 # Turn on tracing of internal errors and deprecated options  
 trace enable tracepoints "error deprecated"  
 # Enable TCP/IP connections  
 interface ssl library STAFTCP option Secure=Yes option Port=6550  
 interface tcp library STAFTCP option Secure=No option Port=6500  
 # Set default local trust  
 #trust machine local://local level 5  
 trust default level 5  
 # Add default service loader  
 serviceloader library STAFDSLS  
 SERVICE STAX LIBRARY JSTAF EXECUTE \  
 D:/STAF/services/stax/STAX.jar OPTION J2=-Xmx384m  
 SERVICE EVENT LIBRARY JSTAF EXECUTE \  
 D:/STAF/services/stax/STAFEvent.jar  
 SET MAXQUEUESIZE 10000  
 SERVICE Cron LIBRARY JSTAF EXECUTE D:\STAF\services\cron\STAFCron.jar  

WebDriver Wait is easier after using implicitlyWait()

"WebDriver will wait until the page has fully loaded (that is, the "onload" event has fired) before returning control to your test or script."
The classic example is Javascript starting to run after the page has loaded (onload); while if the website is very "rich", it is always hard for WebDriver to identify some elements by its own blocking API.

Previous solution mentioned/suggested by experts are : Use the Wait class to wait for a specific element to appear:http://groups.google.com/group/webdriver/browse_thread/thread/50c963cd25fb416c/61aae67b8a2560fd?#61aae67b8a2560fd

Here is one example, which i wrapped the presenceOfElementLocated():

 import org.openqa.selenium.By;  
 import org.openqa.selenium.WebDriver;  
 import org.openqa.selenium.WebElement;  
 import org.openqa.selenium.support.ui.WebDriverWait;  
 import com.google.common.base.Function;  
 public class MyWaiter {  
      private WebDriver driver;  
      public MyWaiter(WebDriver driver){  
           this.driver = driver;  
      }  
      public WebElement waitForMe(By locatorname, int timeout){  
           WebDriverWait wait = new WebDriverWait(driver, timeout);  
           return wait.until(MyWaiter.presenceOfElementLocated(locatorname));  
      }  
      public static Function<WebDriver, WebElement> presenceOfElementLocated(final By locator) {  
           // TODO Auto-generated method stub  
           return new Function<WebDriver, WebElement>() {  
                @Override  
                public WebElement apply(WebDriver driver) {  
                     return driver.findElement(locator);  
                }  
           };  
      }  
 }  

My test code:
 public static void main(String[] args) {  
           WebDriver driver = new FirefoxDriver();  
           driver.get("http://www.google.com/");  
           MyWaiter myWaiter = new MyWaiter(driver);  
           WebElement search = myWaiter.waitForMe(By.name("btnG"), 10);  
           search.click();  
      }  

This way looks a little bit fussy to me, although it works well.

Now, implicitlyWait() gives tests a "KISS" and most importantly, it did solve the problem.

driver.findElement(By.id("signIn")).click();  
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);  
driver.switchTo().frame("canvas_frame");  
driver.findElement(By.partialLinkText("Buzz")).click();  
driver.findElement(By.linkText("Cheng Chi")).click();  


You may notice that the first sample code using wait.until(), you have to two wait.until() for each link element, otherwise the code will fail; While the second one, you just add one implicitlyWait() in front of the "Buzz" link.
I do not know why, but it shows that implicitlyWait() is a better choice for locating (ajax) elements.

PS: I marked "in front of" as bold above, which means implicitlyWait() is a kind of registration method, you need to tell Webdriver in advance.

Thursday, September 02, 2010

Check and Set Isolation level in SQL Server

Check isolation level:
DBCC useroptions


Default Isolation level in SQL Server is "READ COMMITTED", Once you want to change it:

For example, Set isolation level to READ UNCOMMITTED
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

Wednesday, September 01, 2010

Switching Frame and Windows in WebDriver sample code

1> Switch to different Frames:
 List<WebElement> frameset = driver.findElements(By.tagName("frame"));  
 if(frameset.size()>0) {  
 for (WebElement framename : frameset){  
 System.out.println("frameid: " + framename.getAttribute("name"));  
 }  
 }  
 else System.out.println("can not find any frame in HTML");  

Notice when you set the frame index, it starts with 0 for the first frame:
 driver.switchTo().frame(0);  


2> Switch to different Windows:
 Set<string> handlers = driver.getWindowHandles();  
 if (driver.getWindowHandles().size()>= 1){  
 for(String handler : handlers){  
 driver.switchTo().window(handler);  
 if (driver.getCurrentUrl().contains("Popup")){  
 System.out.println("Get focus on Popup window");  
 break;  
 }  
 }  
 }  
 else System.out.println("No windows founded!");  


Writing a common function based you own app for switching will be more helpful for your Code Clean!

Thursday, August 12, 2010

The image makes me feel exciting to fight against!

Typical memory leak pattern taking from VisualVM:

Wednesday, August 11, 2010

Deal with Javascript alert( ) and confrim( ) in WebDriver, as workaround

I posted one blog about Deal with "JS div DialogPane in WebDriver":
http://joychester.blogspot.com/2010/07/js-alert-handling-with-webdriver.html

Currently I met more dialog on different kind of page to deal with

<1> Javascript alert(msg)::
Most of time, I meet this situation when there is a validation on Front end. If you have to deal with this situation, one way to disable alert, before you click on a button which trigger the alert(), so that alert will not be triggered, while the validation always be there:

JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("window.alert = function(msg){};");

button.click();



<2> Javascript confrim(msg):
Here I will post an example how to simulate pressing "OK" button to deal with confirmation dialog.
First, find out the JavaScript which is sending a confirmation message by sniffer tool:

confirmed = confirm("Warning! This account is currently in use. Would you like to continue to login?");
if (confirmed)
{
this.document.forms[0].submitAction.value = "TERMINATE";
this.document.forms[0].submit();
}
else
{
this.document.forms[0].submitAction.value = "ABORT";
this.document.forms[0].submit();
}

function doSubmit(url, submitAction){

document.forms[0].action = url;
document.forms[0].target = "_self";
document.forms[0].submitAction.value = submitAction;
document.forms[0].submit();
}


Then, you may need to execute such bellowing javascript to simulate pressing "OK" button:

JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("javascript:doSubmit('/app/login.do','TERMINATE');");


instead of simple click() which will trigger the confirmation dialog:

login.click();


<3> The easiest Walkround:
I like the post on the Watir Wiki, which introduce "the Simplest way to stop JavaScript Pop Ups", the idea can be borrowed from that:
http://wiki.openqa.org/display/WTR/JavaScript+Pop+Ups
Hope this helpful!

Update:
WebDriver-Beta1 will have an implementation of the Alerts and Prompts API for the FirefoxDriver :)

Monday, August 02, 2010

calculate performance by datediff() and aggregation() functions

As we store Raw data in our Database, which just history record without any calculation, just log start timestamp and end timestamp of each action/method. So here is a sample on how to calculate performance by datediff() and aggregation() functions:

declare @starttime varchar(50)

declare @endtime varchar(50)

set @starttime='03/08/2010 10:25:00.000'
set @endtime='03/08/2010 12:00:00.000'

select count(*), avg(datediff(millisecond,starttime, endtime)), min(datediff(millisecond,starttime, endtime)), max(datediff(millisecond,starttime, endtime))
from statistics_log
where event_type='send' and starttime > @starttime and starttime < @endtime

Thursday, July 22, 2010

Deal with JS div DialogPane in WebDriver

Updated: Another related post on js alert and confirmation stuff on http://joychester.blogspot.com/2010/08/deal-with-javascript-alert-and-confrim.html

During my testing, I met one "Send" link which will trigger js DialogPane to get your confirmation, such "OK" or "Cancel", onclick will execute JavaScript function like this for "OK" :

onclick="confirmSend()"

Here is a sample code how I simulate clicking "OK" within the js DialogPane:

driver.switchTo().activeElement();
JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("confirmSend()");


Thanks for Shawn's Demo code, she gives me really great suggestion and inspiration!! :)

“Raft” Logo

Friday, July 16, 2010

Take screen shot on WebDriver

Simple code on take screen shot on WebDriver(InternetExplorerDriver,FirefoxDriver and ChromeDriver):

FirefoxDriver driver = new FirefoxDriver();
....
File pngFile = new File("D:\\test\\", "shot.png");
File tmpFile = driver.getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(tmpFile, pngFile);

Thursday, July 15, 2010

Get WebElement within PageFactory class by refelction

I just wandering how to get PageFactory's private WebElements in another test method?
Here is the code which Vance Zhao provided a way to find WebElements by reflection:


......
FSILoginPage landingpage =
PageFactory.initElements(driver, LoginPage.class);

Field f = landingpage.getClass().getDeclaredField("username");

WebElement e = new DefaultElementLocator(driver, f).findElement();

e.sendKeys("123456");
.....


Here is the PageObjects code:

public class LoginPage {

private WebElement username;
......


I will not consider to do this kind of coding in our test code , it is really a “Demo code”.

I will try to Abstract public methods in PageObjects Class which is a really worth stuff to do and make testing more fun and challenging! And also consider make WebElement to public as a workaround :)

The fast way to locate page elements on PageFactory

From PageFactory.java source code, it has a demo that without @FindBy annotation, it will find page element using Xpath by default, which is a Clean and Smart way, however not a fast way:

public class Page {
private WebElement submit;
}

there will be an element that can be located using the xpath expression
"//*[@id='submit']" or "//*[@name='submit']"


So from performance perspective, I will be glad to use @FindBy annotation way:

public class Page {
@FindBy(How.ID_OR_NAME, using="submit")
private WebElement loginbutton;
}

Tuesday, July 13, 2010

Emulating selenium APIs within WebDriver

Emulating selenium APIs within WebDriver which allows for the WebDriver and Selenium APIs to live side-by-side, a simple sample FYI:


WebDriver driver = new FirefoxDriver();

driver.get("http://www.google.com.hk/");

//PageObjects design pattern
GoogleSearchPage googlesearchpage =
PageFactory.initElements(driver, GoogleSearchPage.class);

String baseUrl = driver.getCurrentUrl();

//Change into Selenium instance
Selenium selenium = new WebDriverBackedSelenium(driver, baseUrl);

//Using selenium method
selenium.type("q", "Gmail");
selenium.click("btnG");

//using PageObjects and driver
googlesearchpage.clearsearchtext(driver);

googlesearchpage.searchfor(driver, "joychester");

//change back to Webdriver instance
WebDriver driverInstance =
((WebDriverBackedSelenium)selenium).getUnderlyingWebDriver();

googlesearchpage.clearsearchtext(driver);

googlesearchpage.searchfor(driverInstance, "Gmail");

//close browser and stop selenium
driverInstance.close();
selenium.stop();

Quit completely when emulating Selenium API in WebDriver

Once you Emulating Selenium API in WebDriver code, you need to quit your service like this separately:
driverInstance.close();
selenium.stop();


......
String baseUrl = driver.getCurrentUrl();

Selenium selenium = new WebDriverBackedSelenium(driver, baseUrl);

selenium.open("http://www.google.com.hk/");


WebDriver driverInstance =
((WebDriverBackedSelenium)selenium).getUnderlyingWebDriver();

googlesearchpage.searchfor(driverInstance, "Gmail");

driverInstance.close();
selenium.stop();
......

"Make it Small, Do it Clean"

Currently, I am going to work on an automation framework design based on Webdirver and TestNG with my intern James, here is one screen shot on project folder structure where QA can organize their tests:



Try to "Make it Small, Do it Clean".

Saturday, June 12, 2010

String match using Regular expression in both Java and Ruby

sometimes, I will looking for some dynamic data/elements from HTMLsource to make my automation scripts more robust and reliable, so using regular expression to do string match is very useful for me to get them:

Example in Java( for selenium usage):

import java.util.regex.Pattern;
import java.util.regex.Matcher;

public class RegexTestHarness {

public static void main(String[] args){


Pattern pattern =
Pattern.compile("information-abc-([0-9]+)");

Matcher matcher =
pattern.matcher("information-abc-9887234-information-abc-223333889-information-abc-12233");

while (matcher.find()) {
for (int i=1; i<=matcher.groupCount(); i++) {
String groupStr = matcher.group(i);
System.out.println(groupStr);
}
}

}
}


Example in Ruby( for Watir usage):

def regex_match(search_pattern)
source_string = "information-abc-9887234-information-abc-223333889-information-abc-12233";

source_string.scan(/#{search_pattern}/) { |match| puts match;}

end

regex_match("information-abc-([0-9]+)");


Love Ruby, it is simple :)

Friday, June 11, 2010

World CUP 2010 , Right here, Right now!

Two talented team i will support:

- Argentina
- Mexico

Let's enjoy the Game from Today!:)

Wednesday, June 09, 2010

SoapUI MockOperation Dispatching in Groovy script

SoapUI provides several ways to dispatch mock service response, you can find the detail information from: http://www.soapui.org/Service-Mocking/simulating-complex-behaviour.html.

If you want to manage your mock response dispatch more flexible, then the Script way is your choice.

Here is the sample script you can select your mock response according to the different requests:

def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context);
def holder = groovyUtils.getXmlHolder(mockRequest.getRequestContent());

//Get node value from Xpath
def OID1 = holder.getNodeValue("//v2:getRequest[1]/v2:OIDs[1]");
def OID2 = holder.getNodeValue("//v2:getRequest[1]/v2:OIDs[2]");

//if condition 1 is matched, return mockresopnse1
if (OID1 == "C1308C7C-097F"&& OID2 == "") {

mockOperation.setDefaultResponse("mockresopnse1");

}
//else if condition 2 is matched, return mockresopnse2
else if (OID1 == "A097B4E5-0159" && OID2 == "17001212-AABF") {

mockOperation.setDefaultResponse("mockresopnse2");

}


PS: Due to lack of documentation and sample scripts on this, I spent me several hours to do some investigation on it, the only thing to do is "reading the APIs and have a try".
Hope it helps,And Thanks for Lynn and Vance's help on this as well! :)

Tuesday, May 25, 2010

Bad response on Firefox with enabling Gzip

While functional testing with enabling gzip(We are using Apache/2.0.49) bring me a bad news, there is randomly returns bad response instead of 200 or 304 response code with Firefox/3.6.3:



Apache access.log has indicated the .css file return 304 Not modified, however, FF seems neither receive 304 response code successfully nor retrieve it from local cache. So the page looks wired.

Another issue is that the page will display the irrecognizable characters in its response, something like this:
���������HTTP/1.1 200 OK Date: Tue, 25 May 2010 07:30:02 GMT Server: Apache/2.0.49 (Win32) DAV/2 mod_ssl/2.0.55 OpenSSL/0.9.8e mod_jk/1.2.15 X-Powered-By: Servlet 2.4; JBoss-4.0.4.GA (build: CVSTag=JBoss_4_0_4_GA date=200605151000)/Tomcat-5.5 Vary: Accept-Encoding Content-Encoding: gzip Keep-Alive: timeout=15, max=88 Connection: Keep-Alive Transfer-Encoding: chunked Content-Type: text/html;charset=UTF-8 a ¬� ������2d3 �VQo...


Meanwhile, IE seems works fine. Does it mean its a Firefox issue? Or it is a Apache 2.0.* gzip issue? (As i did not see such issues reported by Apache 2.2 version)

After taking simple search, there are other people met the same problem as well:
http://forums.mozillazine.org/viewtopic.php?f=38&t=1121915&p=9413795#p9413795

I tried Apache2.2.10, but it seems preventing 304 Not modified response, which is a bug of apache:
https://issues.apache.org/bugzilla/show_bug.cgi?id=45023

Anyone met the similar issues? Any better solutions?

Friday, May 21, 2010

"Firewall(?)" blocks "Accept-Encoding" header

recently, I did Gzip testing with loadrunner with "Accept-Encoding: gzip, deflate" request headers, so want to make sure that Gzip rocks as i told to my teammates.

My test scenario is running from remote server which located in US DC, it sent requests to China servers.

However, i noticed the test result is not changed, then i doubt about Gzip impact, keep look at apache access logs by adding \"%{Accept-Encoding}i\" to LogFormat in httpd.conf. There is no "gzip, deflate" header in access log found:
"GET / HTTP/1.1" 200 2788 0 "-" "Mozilla/5.0 (compatible; MSIE 6.0; Windows NT 5.1)" "-"


Then I did another round of test from local desktop, then everything is fine:
"GET / HTTP/1.1" 200 2788 0 "-" "Mozilla/5.0 (compatible; MSIE 6.0; Windows NT 5.1)" "gzip, deflate"


So "Firewall" blocks "Accept-Encoding" header is just my assumption, but not quite sure... At least notice your test result is making sense, otherwise, you are wasting of time, do not let such test result misleading!

Update:
Find a person with similar issue as mine: http://forums.mozillazine.org/viewtopic.php?f=9&t=1483185
There is Outpost Firewall which disable gzip...

Another latest post on "pushing beyond Gzipping" by Yahoo! talking about forcing Gzip when Accept-Encoding request header is mangled or stripped.

Wednesday, May 12, 2010

Simple Performance profiling samples by Btrace

If you want to dig into why certain method runs slow than you expected, then you can do performance profiling by Btrace, Here is some simple example i used before, hope this helps, BTW, I used JVisualVM Btrace plugin, it is really easy of use, but Be caution with bugs :)

Calculate method execution time:

 /* BTrace Script Template */
 package com.sun.btrace.samples;  

 import com.sun.btrace.annotations.*;  
 import static com.sun.btrace.BTraceUtils.*;  
 import com.sun.btrace.aggregation.*;  
 @BTrace  
 public class MethodResponseTime {  
      /* put your code here */  
   @TLS  
   private static long starttime;  
   private static Aggregation average = newAggregation(AggregationFunction.AVERAGE);  
   private static Aggregation globalcount = newAggregation(AggregationFunction.COUNT);  
      @OnMethod(  
      clazz = "com.myapp.service.pipeline.PipelineServlet",  
      method = "execute"  
      )  
      public static void onCall(){  
        println("enter this method!");  
     starttime = timeNanos();  
      }  
   @OnMethod(  
      clazz = "com.myapp.operations.PrePopulateResponse",  
      method = "execute",  
   location = @Location(Kind.RETURN)  
      )  
      public static void onReturn(){  
        println("Method End!");  
     int duration = (int)(timeNanos()- starttime)/1000000;  
     println(duration);  
     addToAggregation(average,duration);  
     addToAggregation(globalcount,duration);  
      }  
   @OnTimer(20000)  
   public static void onEvent(){  
     println("----------------");  
     printAggregation("Average", average);  
     printAggregation("Global Count", globalcount);  
     println("----------------");  
   }  
 }  


------------------------------------------------------------

Execution time between code lines:

 /* BTrace Script Template */  
 package com.sun.btrace.samples;  

 import com.sun.btrace.annotations.*;  
 import static com.sun.btrace.BTraceUtils.*;  
 import com.sun.btrace.aggregation.*;  
 @BTrace  
 public class CodeLines {  
 @TLS private static long startTime;  
 @OnMethod(  
 clazz="com.myapp.impl.CommonResourceChangePollingImpl",  
 location=@Location(value=Kind.LINE, line=97)  
 )  
 public static void onEnter(){  
 //println("enter this method");  
 startTime= timeNanos();  
 }  
 @OnMethod(  
 clazz="com.myapp.impl.CommonResourceChangePollingImpl",  
 location=@Location(value=Kind.LINE, line=100)  
 )  
 public static void onReturn(){  
 //println("method end!");  
 int duration = (int)(timeNanos()-startTime)/1000000;  
 println(duration);  
 addToAggregation(average,duration);  
 addToAggregation(globalCount, duration);  
 }  
 @OnTimer(30000)  
 public static void onEvent() {  
 println("---------------------------------------------");  
 printAggregation("Average", average);  
 printAggregation("Global Count", globalCount);  
 println("---------------------------------------------");  
 }  
 }  

PS: it calculates from code line 97 to 100 , but not including 100 execution time itself

--------------------------------------------------------------
Call dump trace:

 /* BTrace Script Template */  
 package com.sun.btrace.samples;  

 import com.sun.btrace.annotations.*;  
 import static com.sun.btrace.BTraceUtils.*;  
 import com.sun.btrace.aggregation.*;  
 @BTrace  
 public class CallTree {  
 @OnMethod(  
 clazz="com.myapp.impl.CommonResourceChangePollingImpl",  
 method="getResourceChanges",  
 location=@Location(value=Kind.LINE, line=-1)  
 )  
 public static void online(@ProbeClassName String pcn, @ProbeMethodName String pmn, int line) {  
 print(strcat(pcn, "."));  
 print(strcat(pmn, ":"));  
 println(line);  
 }  
 }  


-----------------------------------------------------------
Regular expression match on Clazz:

 /* BTrace Script Template */  
 package com.sun.btrace.samples;  

 import com.sun.btrace.annotations.*;  
 import static com.sun.btrace.BTraceUtils.*;  
 import com.sun.btrace.aggregation.*;  
 @BTrace  
 public class RegexMatch {  
 @OnMethod(  
 clazz="/com\\.mycompany\\.service\\..*/",  
 location=@Location(value=Kind.LINE, line=-1)  
 )  
 public static void online(@ProbeClassName String pcn, @ProbeMethodName String pmn, int line) {  
 print(strcat(pcn, "."));  
 print(strcat(pmn, ":"));  
 println(line);  
 }  
 }  

Monday, May 10, 2010

Create short URL by tinyurl service

Google charts usually create a long url which is really meaningless for us to read. and I want to convert by some external services instead of creating a new one, I tried Shorturl but failed due to network service problem or something.

here is one simple way to generate using http://tinyurl.com/ service, easy of use :)


require 'open-uri'
require 'uri'

retrun_URL = 'http://chart.apis.google.com/chart?chxl=0:|load+google+landing+page|click_suggestion|1:|0|1|2|3|4|5&chxt=x,y&chco=76A4FB&chd=s:94&chtt=Detail+Page+Response+Time+for+GoogleSuggest.html&cht=lc&chs=900x300&chxr=1,5.977,5.401'

encoding_url = URI.escape(retrun_URL, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))

tiny_url = open("http://tinyurl.com/api-create.php?url=#{encoding_url}").read

puts tiny_url


Sample OUTPUT:
>>http://tinyurl.com/1c2

Note: If you did not encoding/escape the URL, then the service will call it "bad URI(is not URI?)...URI::InvalidURIError"

Friday, May 07, 2010

Class loading issue by using Jaxb

After Monitoring our JVM by Jconsole/JvisalVM, I found the Perm Gen is not that stable and will perform Full GC after a short interval, meanwhile, the trend of total class loaded number is going up almost linear...

However Jconsole/JvisalVM only give me a rough idea that we got a problem on class loading or we just guess we may "dynamically creating new classes on the fly".




The simplest way to retrieve class loading information is to check the Verbose output check-box on Jconsole, so that you can trace the class loading event on command window where you started your service.


Here is some of loaded class information which caused the trouble we identify, Each new JAXB context would dynamically generate new classes instead of reusing the existing one:

[Loaded com.whatever.v2_0.RespondedDateRangeSummary$JaxbAccessor
F_responseOID from __JVM_DefineClass__]
[Loaded com.whatever.v2_0.RespondedDateRangeSummary$JaxbAccessor
F_respondDate from __JVM_DefineClass__]
[Loaded com.whatever.v2_0.RespondedDateRangeSummary$JaxbAccessor
F_respondDate from __JVM_DefineClass__]
[Loaded com.whatever.v2_0.RespondedDateRangeSummary$JaxbAccessor
F_statusRespondDate from __JVM_DefineClass__]
[Loaded com.whatever.v2_0.RespondedDateRangeSummary$JaxbAccessor
F_statusRespondDate from __JVM_DefineClass__]
[Loaded com.whatever.v2_0.RespondedDateRangeSummary$JaxbAccessor
F_statusOID from __JVM_DefineClass__]
[Loaded com.whatever.v2_0.RespondedDateRangeSummary$JaxbAccessor
F_statusOID from __JVM_DefineClass__]
[Loaded com.whatever.v2_0.RespondedDateRangeSummary$JaxbAccessor
F_statusCode from __JVM_DefineClass__]
[Loaded com.whatever.v2_0.RespondedDateRangeSummary$JaxbAccessor
F_statusCode from __JVM_DefineClass__]


Here is one potential solution on this similar problem, you can try that:
http://fleets.wordpress.com/2010/01/08/jaxb-memory-leak-with-metro-too-many-loaded-classes/

And also in HornetQ performance tuning " Avoiding Anti-Patterns", the first item is that "Re-use connections / sessions / consumers / producers", so we should Always re-use them instead of create new one. And also you could add -XX:+TraceClassLoading and -XX:+TraceClassUnloading for your running production to trace those information instead.

Sunday, April 25, 2010

Class Relation diagram in ExperT automation suite

I have posted "ExperT" automation for end-end performance test with Watir and Httpwatch for a while: http://joychester.blogspot.com/2010/02/preview-of-expert-end-to-end.html,
Here I made one simple class diagram for their relationship between these main classes:


And created Github account already, hope to open source the code soon...