CVS: Packages/FunctionalTests/FunctionalTests - Request.py:1.8 ScenarioGenerator.py:1.9 interfaces.py:1.4

Tres Seaver <[email protected]>
Newsgroups gmane.comp.web.zope.public-cvs
Message-ID <[email protected]>
Update of /cvs-repository/Packages/FunctionalTests/FunctionalTests
In directory cvs.zope.org:/tmp/cvs-serv26739/FunctionalTests

Modified Files:
	Request.py ScenarioGenerator.py interfaces.py 
Log Message:
 - Lay in last set of updates (not yet finished).


=== Packages/FunctionalTests/FunctionalTests/Request.py 1.7 => 1.8 ===
--- Packages/FunctionalTests/FunctionalTests/Request.py:1.7	Sun Jun 15 06:01:25 2003
+++ Packages/FunctionalTests/FunctionalTests/Request.py	Sat Apr  2 18:21:53 2005
@@ -1,13 +1,16 @@
 """ Classes:  RequestError, HTTPRequest, ZEORequest
     Functions: buildRequest
 
-$Id
+$Id $
 """
 import re
 import httplib
 import urllib
 import urlparse
 import base64
+import MimeWriter
+import StringIO
+import rfc822
 
 from interfaces import IPluginFunction
 from interfaces import IRequest
@@ -183,6 +186,8 @@
         self._expected_redirect = None
         self._expected_cookies = ()
         self._payload_checker = None
+        self._contentTypeCache = None
+        self._dataCache = None
     
     def _initURLParts( self, URL ):
 
@@ -198,7 +203,7 @@
             self._host, self._port = hp[0], int( hp[1] )
         else:
             self._host = self._netloc
-            self._port = 80 # XXX s.b. default for scheme?
+            self._port = None
 
         self._path, self._params, self._query, self._fragment = parts[2:]
     #
@@ -316,12 +321,23 @@
         """ See IHTTPRequest.
         """
         return self._expected_cookies is not None
+
+    def setMultipart( self ):
+
+        """ See IHTTPRequest.
+        """
+        self._has_file_field = 1
     
     def getContentType( self ):
 
         """ See IHTTPRequest.
         """
-        return self._has_file_field and _MULTIPART or _URLENCODED
+        if self._has_file_field:
+            self._setMultipartDataAndType()
+            return self._contentTypeCache
+
+        return _URLENCODED
+
 
     def getData( self ):
 
@@ -329,7 +345,8 @@
         """
         if self._has_file_field:
 
-            return _mime_encode( self._fields )
+            self._setMultipartDataAndType()
+            return self._dataCache
 
         else:
 
@@ -368,8 +385,11 @@
 
         """ See IHTTPRequest.
         """
+        if self._dataCache:
+            # adding could change boundary which may have been gotten
+            raise RequestError, "cannot add field after getting data or type"
         field = _buildField( field_desc )
-        if field[2] == 'file':
+        if field[1] == 'file':
             self._has_file_field = 1
         self._fields.append( field )
 
@@ -414,6 +434,21 @@
         """
         self._payload_checker = PluginFunction( filename, function )
 
+    # we need to cache because content-type and data must both state
+    # the same boundary
+    def _setMultipartDataAndType( self ):
+        """ See IHTTPRequest.
+        """
+        if not self._has_file_field:
+            raise RuntimeError, \
+                "attempt to cache data and type of non-multipart type request"
+        if not self._contentTypeCache:
+            mess = _mime_encode( self._fields )
+            type = mess['content-type']
+            type = type.replace('\n ', ' ')
+            self._dataCache = mess.fp.read()[1:]
+            self._contentTypeCache = type
+
     #
     #   Request invocation
     #
@@ -428,14 +463,28 @@
             invocation.beginRequest()
 
         host, port = self.getHost(), self.getPort()
+        if port:
+            conport = port
+        else:
+            conport = 80                # XXX s.b. default for scheme
         try:
-            connection = httplib.HTTP( host, port )
+            connection = httplib.HTTP( host, conport )
             connection.putrequest( self.getMethod(), self.getURI() )
-            connection.putheader( 'Host', '%s:%d' % ( host, port ) )
 
+            if port:
+                connection.putheader( 'Host', '%s:%d' % ( host, port ) )
+            else:
+                # Port could be set to 80, or the default for the scheme,
+                # RFC2616 says that giving no port is equivalent to giving the
+                # default.  However, in practice (Apache multihosting), a port
+                # 80 URI is sometimes redirected to the portless URI, so we
+                # give what the config file asked.
+                connection.putheader( 'Host', '%s' % host )
             token = self.getAuthenticationToken()
             if token:
                 scrambled = base64.encodestring( token )
+                if scrambled[-1] == '\n':
+                    scrambled = scrambled[:-1]
                 connection.putheader( 'Authorization', 'Basic %s' % scrambled )
 
             for key, value in self.getHeaders():
@@ -489,6 +538,12 @@
 
         self._plugin = PluginFunction( filename, function )
 
+    def setExpectedResult( self, result_code ):
+
+        """ See IZEORequest.
+        """
+        self._expected_result = result_code
+
     #
     #   Request invocation
     #
@@ -565,14 +620,29 @@
     raise RequestError, 'Invalid field_desc: %s' % field_desc
 
 _URLENCODED = 'application/x-www-form-urlencoded'
-_MULTIPART  = 'multipart/form-data'
+#_MULTIPART  = 'multipart/form-data'
 
 def _mime_encode( fields ):
 
     """ Build a 'multipart/form-data' representation of the
         (name,type,value) items in fields.
     """
-    raise NotImplementedError
+    # MimeWriter has subtle differences from the spec, will ususally be OK
+    # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306    
+    fp = StringIO.StringIO()
+    writer = MimeWriter.MimeWriter( fp )
+    writer.startmultipartbody( 'form-data' )
+    for field in fields:
+        if field[1] == 'file':
+            raise NotImplementedError
+        subwriter = writer.nextpart()
+        subwriter.addheader( 'content-disposition'
+                           , 'form-data; name="%s"' % field[0] )
+        f = subwriter.startbody( 'text/plain' )
+        f.write( field[2] )
+    writer.lastpart()
+    fp.seek(0)
+    return rfc822.Message( fp )
 
 def _buildSleepRequest( cp, section ):
 


=== Packages/FunctionalTests/FunctionalTests/ScenarioGenerator.py 1.8 => 1.9 ===
--- Packages/FunctionalTests/FunctionalTests/ScenarioGenerator.py:1.8	Thu Mar  4 08:51:30 2004
+++ Packages/FunctionalTests/FunctionalTests/ScenarioGenerator.py	Sat Apr  2 18:21:53 2005
@@ -13,6 +13,8 @@
 import mimetools
 import urllib
 import urlparse
+import multifile
+import StringIO
 
 _SCENARIO_SKELETON = """\
 [Scenario]
@@ -64,17 +66,18 @@
         LoggingProxy) into a scenario file (to be consumed by
         FT_Runner).
     """
-    _verbosity          = 1
-    _capture_cookies    = 0
-    _logfile_directory  = '/tmp'
-    _logfile_prefix     = 'watch'
-    _logfile_extension  = 'request'
-    _output_file        = None
-    _exclude_patterns   = []
-    _exclude_file       = None
-    _exclude_regex      = None
-    _site_host         = None
-    _site_path          = None
+    _verbosity              = 1
+    _capture_cookies        = 0
+    _logfile_directory      = '/tmp'
+    _logfile_prefix         = 'watch'
+    _logfile_extension_req  = 'request'
+    _logfile_extension_resp = 'response'
+    _output_file            = None
+    _exclude_patterns       = []
+    _exclude_file           = None
+    _exclude_regex          = None
+    _site_host              = None
+    _site_path              = None
 
     def __init__( self, args ):
 
@@ -106,8 +109,11 @@
     -f, --logfile-prefix            Prefix for log file names
                                     (default '%(LOGFILE_PREFIX)s')
 
-    -e, --logfile-extension         Extension for log file names
-                                    (default '%(LOGFILE_EXTENSION)s')
+    -e, --logfile-extension         Extension for log file request names
+                                    (default '%(LOGFILE_EXTENSION_REQ)s')
+
+    -E, --logfile-response          Extension for log file response names
+                                    (default '%(LOGFILE_EXTENSION_RESP)s')
 
     -o, --output-file               Write to 'file', instead of default
                                     (%(LOGFILE_PREFIX)s.zft). 
@@ -128,7 +134,8 @@
 %(MESSAGE)s\n""" % { 'GENERATOR_EXE'        : sys.argv[0]
                    , 'LOGFILE_DIRECTORY'    : self._logfile_directory
                    , 'LOGFILE_PREFIX'       : self._logfile_prefix
-                   , 'LOGFILE_EXTENSION'    : self._logfile_extension
+                   , 'LOGFILE_EXTENSION_REQ'    : self._logfile_extension_req
+                   , 'LOGFILE_EXTENSION_RESP'    : self._logfile_extension_resp
                    , 'MESSAGE'              : msg or ''
                    } )
         sys.exit( 1 )
@@ -139,12 +146,13 @@
         """
         verbosity = self._verbosity
         capture_cookies = self._capture_cookies
-        logfile_directory = logfile_prefix = logfile_extension = None
+        logfile_directory = logfile_prefix = None
+        logfile_extension_req = logfile_extension_resp = None
         output_file = exclude_file = site_host = site_path = None
 
         try:
             opts, ignored = getopt.getopt( args
-                                         , "?vqcCl:f:e:o:x:X:h:p:"
+                                         , "?vqcCl:f:e:E:o:x:X:h:p:"
                                          , [ 'help'
                                            , 'verbose'
                                            , 'quiet'
@@ -153,6 +161,7 @@
                                            , 'logfile-directory='
                                            , 'logfile-prefix='
                                            , 'logfile-extension='
+                                           , 'logfile-extension-response='
                                            , 'output-file'
                                            , 'exclude-pattern'
                                            , 'exclude-file'
@@ -187,7 +196,10 @@
                 logfile_prefix = v
 
             if o == '-e' or o == '--logfile-extension':
-                logfile_extension = v
+                logfile_extension_req = v
+
+            if o == '-E' or o == '--logfile-extension-response':
+                logfile_extension_resp = v
 
             if o == '-o' or o == '--output-file':
                 output_file = v
@@ -213,8 +225,11 @@
         if logfile_prefix is not None:
             self._logfile_prefix = logfile_prefix
 
-        if logfile_extension is not None:
-            self._logfile_extension = logfile_extension
+        if logfile_extension_req is not None:
+            self._logfile_extension_req = logfile_extension_req
+
+        if logfile_extension_resp is not None:
+            self._logfile_extension_resp = logfile_extension_resp
 
         if site_host is not None:
             self._site_host = site_host
@@ -298,24 +313,28 @@
         return self._exclude_regex
 
     def processFile( self
-                   , filename
+                   , infilename
+                   , outfilename
                    , request_num
                    , parms={}
                    , REQUEST_LINE=re.compile( r'^([^\s]+)\s+'
                                               r'([^\s]+)\s+'
                                               r'([^\s]+)$' )
+                   , RESPONSE_LINE=re.compile( r'^([^\s]+)\s+'
+                                               r'([0-9][0-9][0-9])\s+'
+                                               r'(.*)$' )
                    ):
         """
             Process a single request file;  record global context
             in parms.
         """
-        self._log( 'Scanning file: %s' % filename, 1 )
+        self._log( 'Scanning request file: %s' % infilename, 1 )
 
         parms[ 'content_type' ] = None
         parms[ 'header_count' ] = 0
         parms[ 'cookie_count' ] = 0
 
-        f = open( filename )
+        f = open( infilename )
         all_text = f.read()
         body_end = f.tell()
         f.seek( 0 )
@@ -422,8 +441,19 @@
         payload = f.read()
         f.close()
 
+        if outfilename:
+            self._log( 'Scanning response file: %s' % outfilename, 1 )
+            f = open( outfilename )
+            # could exclude here as well
+            status = f.readline().rstrip()
+            match = RESPONSE_LINE.match( status )
+            http_verb, code, reason = match.groups()        
+            f.close()
+        else:
+            code = 200
+
         self._print( _RESULT_SKELETON
-                   , RESULT_CODE=200    # TODO:  Read from response?
+                   , RESULT_CODE=int( code )
                    )
 
         return request_num
@@ -431,16 +461,22 @@
     def processScenario( self ):
         """
             Read all files in '_logfile_directory' whose prefix
-            matches '_logfile_prefix' and '_logfile_extension;
+            matches '_logfile_prefix', '_logfile_extension_req', and
+            '_logfile_extension_resp';
             create a scenario file with one section per request file,
             dumping to specified output file.
         """
         self._print( _SCENARIO_SKELETON )
 
-        glob_pattern = '%s/%s*.%s' % ( self._logfile_directory
-                                     , self._logfile_prefix
-                                     , self._logfile_extension
-                                     )
+        glob__in_pattern = '%s/%s*.%s' % ( self._logfile_directory
+                                         , self._logfile_prefix
+                                         , self._logfile_extension_req
+                                         )
+
+        glob__out_pattern = '%s/%s*.%s' % ( self._logfile_directory
+                                          , self._logfile_prefix
+                                          , self._logfile_extension_resp
+                                          )
 
         request_num = 0
         parms = { 'saw_authentication' : 0
@@ -448,11 +484,30 @@
                 , 'site_path' : self._site_path
                 }
 
-        filenames = glob.glob( glob_pattern )
-        filenames.sort()
-        for filename in filenames:
-            request_num = self.processFile( filename, request_num, parms )
-
+        infilenames = glob.glob( glob__in_pattern )
+        infilenames.sort()
+        outfilenames = glob.glob( glob__out_pattern )
+        outfilenames.sort()
+        for infilename in infilenames:
+            # find the response file name that matches this request file
+            outfilename = re.sub( self._logfile_extension_req + '$'
+                                , self._logfile_extension_resp
+                                , infilename
+                                )
+            # XXX error if missing?  Optional outfile processing?
+            if outfilename in outfilenames:
+                request_num = self.processFile( infilename
+                                              , outfilename
+                                              , request_num
+                                              , parms
+                                              )
+            else:
+                request_num = self.processFile( infilename
+                                              , None
+                                              , request_num
+                                              , parms
+                                              )
+                
         self._print( _DEFAULT_SKELETON % parms )
 
 if __name__ == '__main__':


=== Packages/FunctionalTests/FunctionalTests/interfaces.py 1.3 => 1.4 ===
--- Packages/FunctionalTests/FunctionalTests/interfaces.py:1.3	Fri Jun 13 23:22:55 2003
+++ Packages/FunctionalTests/FunctionalTests/interfaces.py	Sat Apr  2 18:21:53 2005
@@ -1,6 +1,6 @@
 """ Interfaces:  IRequestInvocation
 
-$Id$
+$Id $
 """
 
 try:
@@ -204,6 +204,10 @@
         """ Should invocations of this request retain cookies?
         """
 
+    def setMultipart():
+        """ Set content-type to multipart/form-data.
+        """
+
     def getContentType():
 
         """ Return the HTTP 'Content-type' to use for this request.
@@ -222,6 +226,13 @@
         o Return None if no function is assigned.
         """
 
+    def getHook():
+
+        """ Return a function used to change configuration variables.
+
+        o Return None if no function is assigned.
+        """
+
     def addHeader( header_desc ):
 
         """ Construct a header tuple and add it to our list.
@@ -267,6 +278,15 @@
         """ Create a checker function as a plugin function.
         """
 
+    def _setMultipartDataAndType():
+        """ Encode and cache the data and content-type.
+        """
+
+    def setHook( filename, function ):
+
+        """ Create a hook function as a plugin function.
+        """
+
 
 class IResult( Interface ):
 
@@ -327,6 +347,11 @@
         """ Should we validate elapsed times?
         """
 
+    def checkHook():
+
+        """ Should we call any hooks?
+        """
+
     def listInvocations( roll_up=1 ):
 
         """ Return the list of invocations we manage.
@@ -366,6 +391,11 @@
         """ Setup our parent request.
         """
 
+    def updateDefaults( dict ):
+
+        """ Update our defaults dict with dict.
+        """
+
     def setCookies( cookies ):
 
         """ Store our cookies.
@@ -443,3 +473,8 @@
     
     o Executed via ZEO, rather than via HTTP.
     """
+
+    def setExpectedResult( result_code ):
+
+        """ Override the default expected result code.
+        """

_______________________________________________
Zope-CVS maillist  -  [email protected]
http://mail.zope.org/mailman/listinfo/zope-cvs

Zope CVS instructions: http://dev.zope.org/CVS
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.