6 Replies Latest reply on Sep 28, 2010 5:46 PM by Borislav Andruschuk

    Load testing with Grinder

    Peter Johnson Master

      I am playing around with Grinder to do load testing. For a practice application I am using seambay.I have a script that registers new items to be auctioned off. This script works great if I run only a single thread in Grinder (1 simulated user). However, if I run multiple threads or multiple process within Grinder to simulate multiple simultaneous users, I get conversation-related errors. Lots of them. Here is the first one:



      2008-03-20 13:34:33,511 ERROR [STDERR] Mar 20, 2008 1:34:33 PM com.sun.facelets.FaceletViewHandler handleRenderException
      SEVERE: Error Rendering View[/sell.xhtml]
      javax.faces.FacesException: javax.el.ELException: /sell.xhtml @36,124 value="#{auctionAction.auction.title}": Error reading 'auction' on type org.jboss.seam.example.seambay.AuctionAction_$$_javassist_5
           at javax.faces.component.UIOutput.getValue(UIOutput.java:176)
           at com.sun.faces.renderkit.html_basic.HtmlBasicInputRenderer.getValue(HtmlBasicInputRenderer.java:189)
           at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getCurrentValue(HtmlBasicRenderer.java:320)
           at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeEnd(HtmlBasicRenderer.java:200)
      . . .
      Caused by: javax.el.ELException: /sell.xhtml @36,124 value="#{auctionAction.auction.title}": Error reading 'auction' on type org.jboss.seam.example.seambay.AuctionAction_$$_javassist_5
           at com.sun.facelets.el.TagValueExpression.getValue(TagValueExpression.java:76)
           at javax.faces.component.UIOutput.getValue(UIOutput.java:173)
           ... 57 more
      Caused by: org.jboss.seam.NoConversationException: no long-running conversation for @Conversational bean: auctionAction
           at org.jboss.seam.core.ConversationalInterceptor.aroundInvoke(ConversationalInterceptor.java:40)
           at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
      



      I saw the JBossWorld presentation by Jaffe and Yuan on Seam performance and they used Grinder, so I know it is possible. But there must be some configuration setting that I need to make to ensure that the processes/threads in Grinder are each treated as completely separate users/conversations. Any insights are appreciated.


       

        • 1. Re: Load testing with Grinder
          Shane Bryzak Master

          Which requests are you sending?  Can you post your grinder script?

          • 2. Re: Load testing with Grinder
            Peter Johnson Master

            I just knew that someone would ask for the script. Sigh. I'm at home, on vacation, today and next week and the script is at work.


            Let me describe what I did. I first ran a script populating the database with 1000 users.


            Then I recorded a script where I:



            1. login in as a user (modified the script to use a random user)

            2. clicked the Sell link

            3. filled in the forms about the item to be sold (modified the script to randomly generate the title and description)

            4. logged out



            So the script is mostly what was recorded using the grinder proxy, with the only modifications being to randomly select users and generate item info.


            I will either reproduce the script or sneak into work to get it and then post it later.

            • 3. Re: Load testing with Grinder
              Shane Bryzak Master

              Is it using the same conversation ID through each iteration of the sell wizard?

              • 4. Re: Load testing with Grinder
                Peter Johnson Master

                I had considered this possibility. When the Grinder proxy generates the script, it recorded almost all of the URLs as http://...?cid=9999. But later, when it sent the requests, it also looked like it was setting the cid from the prior response. (Sorry for being so vague, if I had the script I would post the exact code.) I tried removing the ?cid=9999 from the recorded URLs, but that did not help.


                From what I know about conversations, I would suspect that leaving the ?cid=9999 on the URLs would prevent even the single user run from working (because the cid for the response would be an unknown cid), but running with a single user works fine.


                I am still wondering why Jaffe and Yuan would present on using Grinder with Seam yet not point out any problems - the only change to the script that they mentioned was to change the think time between requests. And yes, I tried playing with that.

                • 5. Re: Load testing with Grinder
                  Peter Johnson Master

                  Here are the grinder properties file and the script. As I mentioned, the database is populated with 1000 users before this script is run. If I play around with the wait time (current 500 milliseconds) and the number of processors (currently 8), I can get a clean run (for example, running only 4 users with 5000 millisecond wait time). But with the current settings, I get the exception I posted earlier thrown many times, and not all auction items are created (last time I ran it I got 136 of the expected 160 items).


                  grinder.processes=8
                  grinder.threads=1
                  grinder.runs=20
                  grinder.processIncrementInterval=200
                  grinder.processIncrement=2
                  grinder.useConsole=true
                  grinder.jvm.arguments=-Xms128m -Xmx128m -Dpython.home=C:/apps/Java/jython2.2.1
                  grinder.logDirectory=d:/opt/cmg/grinder/log
                  grinder.numberOfOldLogs=30
                  grinder.script=d:/opt/cmg/grinder/createItem2.py




                  # The Grinder 3.0.1
                  # HTTP script recorded by TCPProxy at Mar 14, 2008 11:53:17 AM
                  
                  import random
                  from net.grinder.script import Test
                  from net.grinder.script.Grinder import grinder
                  from net.grinder.plugin.http import HTTPPluginControl, HTTPRequest
                  from HTTPClient import NVPair
                  connectionDefaults = HTTPPluginControl.getConnectionDefaults()
                  httpUtilities = HTTPPluginControl.getHTTPUtilities()
                  
                  # To use a proxy server, uncomment the next line and set the host and port.
                  # connectionDefaults.setProxyServer("localhost", 8001)
                  
                  # These definitions at the top level of the file are evaluated once,
                  # when the worker process is started.
                  
                  connectionDefaults.defaultHeaders = \
                    ( NVPair('Accept-Language', 'en-us,en;q=0.7,de;q=0.3'),
                      NVPair('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.7'),
                      NVPair('Accept-Encoding', 'gzip,deflate'),
                      NVPair('User-Agent', 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.12) Gecko/20080201 Firefox/2.0.0.12'), )
                  
                  headers0= \
                    ( NVPair('Accept', 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'),
                      NVPair('Referer', 'http://localhost:8080/seam-bay/home.seam'), )
                  
                  headers1= \
                    ( NVPair('Accept', 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'),
                      NVPair('Referer', 'http://localhost:8080/seam-bay/login.seam'), )
                  
                  headers2= \
                    ( NVPair('Accept', 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'),
                      NVPair('Referer', 'http://localhost:8080/seam-bay/home.seam'), )
                  
                  headers3= \
                    ( NVPair('Accept', 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'),
                      NVPair('Referer', 'http://localhost:8080/seam-bay/sell.seam'), )
                  
                  headers4= \
                    ( NVPair('Accept', '*/*'),
                      NVPair('Referer', 'http://localhost:8080/seam-bay/sell2.seam'), )
                  
                  headers5= \
                    ( NVPair('Accept', 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'),
                      NVPair('Referer', 'http://localhost:8080/seam-bay/sell2.seam'),
                      NVPair('Cache-Control', 'no-cache'), )
                  
                  headers6= \
                    ( NVPair('Accept', 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'),
                      NVPair('Referer', 'http://localhost:8080/seam-bay/sell2.seam'), )
                  
                  headers7= \
                    ( NVPair('Accept', 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'),
                      NVPair('Referer', 'http://localhost:8080/seam-bay/sell3.seam'), )
                  
                  headers8= \
                    ( NVPair('Accept', 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'),
                      NVPair('Referer', 'http://localhost:8080/seam-bay/sell4.seam'), )
                  
                  headers9= \
                    ( NVPair('Accept', 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'),
                      NVPair('Referer', 'http://localhost:8080/seam-bay/sell5.seam'), )
                  
                  headers10= \
                    ( NVPair('Accept', 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'),
                      NVPair('Referer', 'http://localhost:8080/seam-bay/preview.seam'), )
                  
                  headers11= \
                    ( NVPair('Accept', 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'),
                      NVPair('Referer', 'http://localhost:8080/seam-bay/home.seam'), )
                  
                  url0 = 'http://localhost:8080'
                  
                  #PETER BEGIN
                  
                  # Init the random number generator:
                  random.seed()
                  
                  # A 6-digit sequential serial number suffix is added to each item name
                  itemNumber = 100000
                  
                  # The SeamBay category ids:
                  CATEGORY_ID = [
                         '1001' ,
                         '1002',
                         '2001',
                         '2002',
                         '2003',
                         '3001',
                         '3002',
                         '3003',
                         '3004',
                         '4001',
                         '4002',
                         '4003',
                         '4004',
                         '5001',
                         '5002',
                         '5003',
                         '6001',
                         '6002',
                         '6003',
                         '7001',
                         '7002',
                         '7003',
                         '7004',
                         '7005',
                         '8001',
                         '8002',
                         '8003',
                         '8004',
                         '8005',
                         '9001',
                         '9002',
                        '10001',
                        '10002',
                        '10003',
                        '10004',
                        '11001',
                        '11002',
                        '11003',
                        '12001',
                        '12002',
                        '12003',
                        '12004',
                        '13001',
                        '13002',
                        '13003',
                        '14001',
                        '14002',
                        '14003',
                        '14004',
                        '15001',
                        '15002',
                        '15003',
                        '15004']
                  
                  #PETER END
                  
                  
                  # Create an HTTPRequest for each request, then replace the
                  # reference to the HTTPRequest with an instrumented version.
                  # You can access the unadorned instance using request101.__target__.
                  request101 = HTTPRequest(url=url0, headers=headers0)
                  request101 = Test(101, 'GET login.seam').wrap(request101)
                  
                  request201 = HTTPRequest(url=url0, headers=headers1)
                  request201 = Test(201, 'POST login.seam').wrap(request201)
                  
                  request202 = HTTPRequest(url=url0, headers=headers1)
                  request202 = Test(202, 'GET home.seam').wrap(request202)
                  
                  request301 = HTTPRequest(url=url0, headers=headers2)
                  request301 = Test(301, 'GET sell.seam').wrap(request301)
                  
                  request401 = HTTPRequest(url=url0, headers=headers3)
                  request401 = Test(401, 'POST sell.seam').wrap(request401)
                  
                  request402 = HTTPRequest(url=url0, headers=headers3)
                  request402 = Test(402, 'GET sell2.seam').wrap(request402)
                  
                  request403 = HTTPRequest(url=url0, headers=headers4)
                  request403 = Test(403, 'GET remote.js').wrap(request403)
                  
                  request404 = HTTPRequest(url=url0, headers=headers4)
                  request404 = Test(404, 'GET interface.js').wrap(request404)
                  
                  request501 = HTTPRequest(url=url0, headers=headers5)
                  request501 = Test(501, 'POST execute').wrap(request501)
                  
                  request601 = HTTPRequest(url=url0, headers=headers6)
                  request601 = Test(601, 'POST sell2.seam').wrap(request601)
                  
                  request602 = HTTPRequest(url=url0, headers=headers6)
                  request602 = Test(602, 'GET sell3.seam').wrap(request602)
                  
                  request701 = HTTPRequest(url=url0, headers=headers7)
                  request701 = Test(701, 'POST sell3.seam').wrap(request701)
                  
                  request702 = HTTPRequest(url=url0, headers=headers7)
                  request702 = Test(702, 'GET sell4.seam').wrap(request702)
                  
                  request801 = HTTPRequest(url=url0, headers=headers8)
                  request801 = Test(801, 'POST sell4.seam').wrap(request801)
                  
                  request802 = HTTPRequest(url=url0, headers=headers8)
                  request802 = Test(802, 'GET sell5.seam').wrap(request802)
                  
                  request901 = HTTPRequest(url=url0, headers=headers9)
                  request901 = Test(901, 'POST sell5.seam').wrap(request901)
                  
                  request902 = HTTPRequest(url=url0, headers=headers9)
                  request902 = Test(902, 'GET preview.seam').wrap(request902)
                  
                  request1001 = HTTPRequest(url=url0, headers=headers10)
                  request1001 = Test(1001, 'POST preview.seam').wrap(request1001)
                  
                  request1002 = HTTPRequest(url=url0, headers=headers10)
                  request1002 = Test(1002, 'GET home.seam').wrap(request1002)
                  
                  request1101 = HTTPRequest(url=url0, headers=headers11)
                  request1101 = Test(1101, 'GET home.seam').wrap(request1101)
                  
                  request1102 = HTTPRequest(url=url0, headers=headers11)
                  request1102 = Test(1102, 'GET home.seam').wrap(request1102)
                  
                  
                  class TestRunner:
                    """A TestRunner instance is created for each worker thread."""
                  
                    # A method for each recorded page.
                    def page1(self):
                      """GET login.seam (request 101)."""
                      result = request101.GET('/seam-bay/login.seam')
                      self.token_cid = \
                        httpUtilities.valueFromBodyURI('cid') # '3406'
                      self.token_j_id9 = \
                        httpUtilities.valueFromHiddenInput('j_id9') # 'j_id9'
                      self.token_javaxfacesViewState = \
                        httpUtilities.valueFromHiddenInput('javax.faces.ViewState') # 'H4sIAAAAAAAAAL1XXWwUVRS+XfpHaaClpJQYyEKR...'
                  
                      return result
                  
                    def page2(self):
                      """POST login.seam (requests 201-202)."""
                  
                      # PETER BEGIN
                      numb = random.randint(100,999)
                      userName = "user00" + str(numb)
                      userPwd = "password00" + str(numb)
                      # PETER END
                  
                      # Expecting 302 'Moved Temporarily'
                      result = request201.POST('/seam-bay/login.seam',
                        ( NVPair('j_id9', self.token_j_id9),
                          NVPair('j_id9:username', userName),
                          NVPair('j_id9:password', userPwd),
                          NVPair('j_id9:j_id13', 'Sign In > '),
                          NVPair('javax.faces.ViewState', self.token_javaxfacesViewState), ),
                        ( NVPair('Content-Type', 'application/x-www-form-urlencoded'), ))
                      self.token_cid = \
                        httpUtilities.valueFromLocationURI('cid') # '3407'
                  
                      grinder.sleep(79)
                      request202.GET('/seam-bay/home.seam' +
                        '?cid=' +
                        self.token_cid)
                      # 16 different values for token_actionMethod found in response, using the first one.
                      self.token_actionMethod = \
                        httpUtilities.valueFromBodyURI('actionMethod') # 'home.xhtml:identity.logout'
                      # 15 different values for token_categoryId found in response, using the first one.
                      self.token_categoryId = \
                        httpUtilities.valueFromBodyURI('categoryId') # '1'
                      self.token_j_id8 = \
                        httpUtilities.valueFromHiddenInput('j_id8') # 'j_id8'
                      self.token_javaxfacesViewState = \
                        httpUtilities.valueFromHiddenInput('javax.faces.ViewState') # 'H4sIAAAAAAAAAL1YW2wcVxk+3tSxY8dp4kDqCpqs...'
                  
                      return result
                  
                    def page3(self):
                      """GET sell.seam (request 301)."""
                      result = request301.GET('/seam-bay/sell.seam')
                      self.token_actionMethod = \
                        httpUtilities.valueFromBodyURI('actionMethod') # 'sell.xhtml:identity.logout'
                      # 2 different values for token_javaxfacesViewState found in response, using the first one.
                      self.token_javaxfacesViewState = \
                        httpUtilities.valueFromHiddenInput('javax.faces.ViewState') # 'H4sIAAAAAAAAAL1YW2wcVxk+Xsex4ySOk5TEBSVs...'
                      self.token_j_id26 = \
                        httpUtilities.valueFromHiddenInput('j_id26') # 'j_id26'
                  
                      return result
                  
                    def page4(self):
                      global itemNumber
                      """POST sell.seam (requests 401-404)."""
                  
                      #PETER BEGIN
                      itemNumber = itemNumber + 1
                      rndNumber = random.randint(100000,999999);
                      itemTitle = 'item' + str(rndNumber) + '_' + str(itemNumber)
                      print "itemTitle=" + itemTitle
                      #PETER END
                  
                      # Expecting 302 'Moved Temporarily'
                      result = request401.POST('/seam-bay/sell.seam',
                        ( NVPair('j_id26', self.token_j_id26),
                          NVPair('j_id26:title', itemTitle),
                          NVPair('j_id26:j_id35', 'Next >'),
                          NVPair('javax.faces.ViewState', self.token_javaxfacesViewState), ),
                        ( NVPair('Content-Type', 'application/x-www-form-urlencoded'), ))
                      self.token_cid = \
                        httpUtilities.valueFromLocationURI('cid') # '3408'
                  
                      grinder.sleep(31)
                      request402.GET('/seam-bay/sell2.seam' +
                        '?cid=' +
                        self.token_cid)
                      self.token_actionMethod = \
                        httpUtilities.valueFromBodyURI('actionMethod') # 'sell2.xhtml:identity.logout'
                      # 2 different values for token_javaxfacesViewState found in response, using the first one.
                      self.token_javaxfacesViewState = \
                        httpUtilities.valueFromHiddenInput('javax.faces.ViewState') # 'H4sIAAAAAAAAAL1Yb2wbZxl/4zRN6mRZ26A2FXQ4...'
                      self.token_catSelection = \
                        httpUtilities.valueFromHiddenInput('catSelection') # 'catSelection'
                  
                      grinder.sleep(109)
                      request403.GET('/seam-bay/seam/resource/remoting/resource/remote.js')
                  
                      grinder.sleep(47)
                      request404.GET('/seam-bay/seam/resource/remoting/interface.js' +
                        '?categoryAction')
                  
                      return result
                  
                    def page5(self):
                      """POST execute (request 501)."""
                      result = request501.POST('/seam-bay/seam/resource/remoting/execute',
                        '<envelope><header><context></context></header><body><eval expr=\"#{allCategories}\" id=\"0\"/></body></envelope>',
                        ( NVPair('Content-Type', 'application/xml'), ))
                  
                      return result
                  
                    def page6(self):
                      """POST sell2.seam (requests 601-602)."""
                  
                      # PETER BEGIN
                      cat = random.choice(CATEGORY_ID)
                      # PETER END
                  
                      # Expecting 302 'Moved Temporarily'
                      result = request601.POST('/seam-bay/sell2.seam',
                        ( NVPair('catSelection', self.token_catSelection),
                          NVPair('catSelection:categoryId', cat),
                          NVPair('catSelection:j_id30', 'Next >'),
                          NVPair('javax.faces.ViewState', self.token_javaxfacesViewState), ),
                        ( NVPair('Content-Type', 'application/x-www-form-urlencoded'), ))
                  
                      grinder.sleep(15)
                      request602.GET('/seam-bay/sell3.seam' +
                        '?cid=' +
                        self.token_cid)
                      self.token_actionMethod = \
                        httpUtilities.valueFromBodyURI('actionMethod') # 'sell3.xhtml:identity.logout'
                      # 2 different values for token_javaxfacesViewState found in response, using the first one.
                      self.token_javaxfacesViewState = \
                        httpUtilities.valueFromHiddenInput('javax.faces.ViewState') # 'H4sIAAAAAAAAAM1ZW2wbxxUdUZYlS7Is24Uto7FD...'
                  
                      return result
                  
                    def page7(self):
                      """POST sell3.seam (requests 701-702)."""
                  
                      # PETER BEGIN
                      duration = str(random.randint(7,31))
                      price = str(random.randint(0,1000)) + ".00"
                      # PETER END
                  
                      # Expecting 302 'Moved Temporarily'
                      result = request701.POST('/seam-bay/sell3.seam',
                        ( NVPair('j_id26', self.token_j_id26),
                          NVPair('j_id26:duration', duration),
                          NVPair('j_id26:price', price),
                          NVPair('j_id26:j_id42', 'Next >'),
                          NVPair('javax.faces.ViewState', self.token_javaxfacesViewState), ),
                        ( NVPair('Content-Type', 'application/x-www-form-urlencoded'), ))
                  
                      grinder.sleep(125)
                      request702.GET('/seam-bay/sell4.seam' +
                        '?cid=' +
                        self.token_cid)
                      self.token_actionMethod = \
                        httpUtilities.valueFromBodyURI('actionMethod') # 'sell4.xhtml:identity.logout'
                      # 2 different values for token_javaxfacesViewState found in response, using the first one.
                      self.token_javaxfacesViewState = \
                        httpUtilities.valueFromHiddenInput('javax.faces.ViewState') # 'H4sIAAAAAAAAAL1ZaYwbVx1/9ibZzW7uhCQtPZzd...'
                      self.token_j_id27 = \
                        httpUtilities.valueFromHiddenInput('j_id27') # 'j_id27'
                      self.token_j_id42 = \
                        httpUtilities.valueFromHiddenInput('j_id42') # 'j_id42'
                  
                      return result
                  
                    def page8(self):
                      """POST sell4.seam (requests 801-802)."""
                  
                      # Expecting 302 'Moved Temporarily'
                      result = request801.POST('/seam-bay/sell4.seam',
                        ( NVPair('j_id42', self.token_j_id42),
                          NVPair('j_id42:j_id46', 'Next >'),
                          NVPair('javax.faces.ViewState', self.token_javaxfacesViewState), ),
                        ( NVPair('Content-Type', 'application/x-www-form-urlencoded'), ))
                  
                      grinder.sleep(31)
                      request802.GET('/seam-bay/sell5.seam' +
                        '?cid=' +
                        self.token_cid)
                      self.token_actionMethod = \
                        httpUtilities.valueFromBodyURI('actionMethod') # 'sell5.xhtml:identity.logout'
                      # 2 different values for token_javaxfacesViewState found in response, using the first one.
                      self.token_javaxfacesViewState = \
                        httpUtilities.valueFromHiddenInput('javax.faces.ViewState') # 'H4sIAAAAAAAAAL1YXWwcVxW+XsexYyfBsavUUUm7...'
                  
                      return result
                  
                    def page9(self):
                      """POST sell5.seam (requests 901-902)."""
                  
                      # Expecting 302 'Moved Temporarily'
                      result = request901.POST('/seam-bay/sell5.seam',
                        ( NVPair('j_id26', self.token_j_id26),
                          NVPair('j_id26:description', 'interesting description'),
                          NVPair('j_id26:j_id33', 'Next >'),
                          NVPair('javax.faces.ViewState', self.token_javaxfacesViewState), ),
                        ( NVPair('Content-Type', 'application/x-www-form-urlencoded'), ))
                  
                      grinder.sleep(31)
                      request902.GET('/seam-bay/preview.seam' +
                        '?cid=' +
                        self.token_cid)
                      self.token_actionMethod = \
                        httpUtilities.valueFromBodyURI('actionMethod') # 'preview.xhtml:identity.logout'
                      self.token_javaxfacesViewState = \
                        httpUtilities.valueFromHiddenInput('javax.faces.ViewState') # 'H4sIAAAAAAAAAM1aW4wb1Rk+dkh2s7kQEpSEQqiz...'
                      self.token_j_id45 = \
                        httpUtilities.valueFromHiddenInput('j_id45') # 'j_id45'
                  
                      return result
                  
                    def page10(self):
                      """POST preview.seam (requests 1001-1002)."""
                  
                      # Expecting 302 'Moved Temporarily'
                      result = request1001.POST('/seam-bay/preview.seam',
                        ( NVPair('j_id45', self.token_j_id45),
                          NVPair('j_id45:j_id49', 'Confirm Listing'),
                          NVPair('javax.faces.ViewState', self.token_javaxfacesViewState), ),
                        ( NVPair('Content-Type', 'application/x-www-form-urlencoded'), ))
                  
                      request1002.GET('/seam-bay/home.seam' +
                        '?cid=' +
                        self.token_cid)
                      # 16 different values for token_actionMethod found in response, using the first one.
                      self.token_actionMethod = \
                        httpUtilities.valueFromBodyURI('actionMethod') # 'home.xhtml:identity.logout'
                      # 14 different values for token_categoryId found in response; the first matched
                      # the last known value of token_categoryId - don't update the variable.
                      self.token_javaxfacesViewState = \
                        httpUtilities.valueFromHiddenInput('javax.faces.ViewState') # 'H4sIAAAAAAAAAL1YW2wcVxk+3tSxY8dp4kDqCpqs...'
                  
                      return result
                  
                    def page11(self):
                      """GET home.seam (requests 1101-1102)."""
                  
                      # Expecting 302 'Moved Temporarily'
                      result = request1101.GET('/seam-bay/home.seam' +
                        '?actionMethod=' +
                        self.token_actionMethod)
                  
                      grinder.sleep(31)
                      request1102.GET('/seam-bay/home.seam')
                      # 14 different values for token_categoryId found in response; the first matched
                      # the last known value of token_categoryId - don't update the variable.
                      self.token_actionMethod = \
                        httpUtilities.valueFromBodyURI('actionMethod') # 'home.xhtml:auctionSearch.queryAuctions'
                      self.token_javaxfacesViewState = \
                        httpUtilities.valueFromHiddenInput('javax.faces.ViewState') # 'H4sIAAAAAAAAAL1YW2wcVxk+3tSxY8dp4kDqCpqs...'
                  
                      return result
                  
                    def __call__(self):
                      """This method is called for every run performed by the worker thread."""
                      self.page1()      # GET login.seam (request 101)
                  
                      grinder.sleep(500)
                      self.page2()      # POST login.seam (requests 201-202)
                  
                      grinder.sleep(500)
                      self.page3()      # GET sell.seam (request 301)
                  
                      grinder.sleep(500)
                      self.page4()      # POST sell.seam (requests 401-404)
                  
                      grinder.sleep(500)
                      self.page5()      # POST execute (request 501)
                  
                      grinder.sleep(500)
                      self.page6()      # POST sell2.seam (requests 601-602)
                  
                      grinder.sleep(500)
                      self.page7()      # POST sell3.seam (requests 701-702)
                  
                      grinder.sleep(500)
                      self.page8()      # POST sell4.seam (requests 801-802)
                  
                      grinder.sleep(500)
                      self.page9()      # POST sell5.seam (requests 901-902)
                  
                      grinder.sleep(500)
                      self.page10()     # POST preview.seam (requests 1001-1002)
                  
                      grinder.sleep(500)
                      self.page11()     # GET home.seam (requests 1101-1102)
                  
                  
                  def instrumentMethod(test, method_name, c=TestRunner):
                    """Instrument a method with the given Test."""
                    unadorned = getattr(c, method_name)
                    import new
                    method = new.instancemethod(test.wrap(unadorned), None, c)
                    setattr(c, method_name, method)
                  
                  # Replace each method with an instrumented version.
                  # You can call the unadorned method using self.page1.__target__().
                  instrumentMethod(Test(100, 'Page 1'), 'page1')
                  instrumentMethod(Test(200, 'Page 2'), 'page2')
                  instrumentMethod(Test(300, 'Page 3'), 'page3')
                  instrumentMethod(Test(400, 'Page 4'), 'page4')
                  instrumentMethod(Test(500, 'Page 5'), 'page5')
                  instrumentMethod(Test(600, 'Page 6'), 'page6')
                  instrumentMethod(Test(700, 'Page 7'), 'page7')
                  instrumentMethod(Test(800, 'Page 8'), 'page8')
                  instrumentMethod(Test(900, 'Page 9'), 'page9')
                  instrumentMethod(Test(1000, 'Page 10'), 'page10')
                  instrumentMethod(Test(1100, 'Page 11'), 'page11')
                  



                  • 6. Re: Load testing with Grinder
                    Borislav Andruschuk Newbie

                    The Grinder is the best tool for performance testing. I've working on GrinderStone - IDE for Grinder scripts which allows debug scripts using Eclipse and provides some interesting features for development like modularity and pretty useful logging in debug mode. This project you can download from official project site:


                    http://code.google.com/p/grinderstone


                    We also have Eclipse Update site for simple plugin installation into Eclipse platform. All details you can obtain on our site and support group. The GrinderStone gives you more power to develope Grinder scripts.