You are viewing a plain text version of this content. The canonical link for it is here.
Posted to mod_python-commits@quetz.apache.org by gr...@apache.org on 2002/09/24 18:01:28 UTC

cvs commit: httpd-python/test/htdocs tests.py

grisha      2002/09/24 09:01:28

  Modified:    Doc      modpython4.tex
               src      filterobject.c mod_python.c requestobject.c util.c
               test     test.py
               test/conf test.conf.tmpl
               test/htdocs tests.py
  Log:
  More thorough tests, especially request object, also a few fixes.
  
  Revision  Changes    Path
  1.25      +16 -9     httpd-python/Doc/modpython4.tex
  
  Index: modpython4.tex
  ===================================================================
  RCS file: /home/cvs/httpd-python/Doc/modpython4.tex,v
  retrieving revision 1.24
  retrieving revision 1.25
  diff -u -r1.24 -r1.25
  --- modpython4.tex	19 Sep 2002 20:11:35 -0000	1.24
  +++ modpython4.tex	24 Sep 2002 16:01:28 -0000	1.25
  @@ -318,7 +318,9 @@
   \var{server} is a reference to a \member{req.server} object. If
   \var{server} is not specified, then the error will be logged to the
   default error log, otherwise it will be written to the error log for
  -the appropriate virtual server.
  +the appropriate virtual server. When \var{server} is not specified,
  +the setting of LogLevel does not apply, the LogLevel is dictated by
  +an httpd compile-time default, usually \code{warn}.
   
   If you have a reference to a request object available, consider using
   \method{req.log_error} intead, it will prepend request-specific
  @@ -366,7 +368,8 @@
   
   \begin{funcdesc}{config_tree}{}
   Returns the server-level configuration tree. This tree does not
  -include directives from .htaccess files.
  +include directives from .htaccess files. This is a \emph{copy} of
  +the tree, modifying it has no effect on the actual configuration.
   \end{funcdesc}
   
   \begin{funcdesc}{server_root}{}
  @@ -483,6 +486,10 @@
   the list of methods is first cleared.
   \end{methoddesc}
   
  +\begin{methoddesc}[request]{document_root}{}
  +Returns DocumentRoot setting.
  +\end{methoddesc}
  +
   \begin{methoddesc}[request]{get_basic_auth_pw}{}
   Returns a string containing the password when Basic authentication is
   used.
  @@ -614,6 +621,13 @@
   Writes \var{string} directly to the client, then flushes the buffer. 
   \end{methoddesc}
   
  +\begin{methoddesc}[request]{set_content_length}{len}
  +Sets the value of \member{req.clength} and the "Conent-Length" header
  +to len. Note that after the headers have been sent out (which happens
  +just before the first byte of the body is written, i.e. first call to
  +\member{req.write()}), calling the method is meaningless.
  +\end{methoddesc}
  +
   \subsubsection{Request Members\label{pyapi-mprequest-mem}}
   
   \begin{memberdesc}[request]{connection}
  @@ -821,13 +835,6 @@
   \begin{memberdesc}[request]{interpreter}
   The name of the subinterpreter under which we're running.
   \emph{(Read-Only)}
  -\end{memberdesc}
  -
  -\begin{memberdesc}[request]{notes}
  -A \code{table} object that could be used to store miscellaneous
  -general purpose info that lives for as long as the request lives. If
  -you need to pass data between handlers, it's better to simply add
  -members to the request object than to use \member{notes}.
   \end{memberdesc}
   
   \begin{memberdesc}[request]{content_type}
  
  
  
  1.14      +14 -19    httpd-python/src/filterobject.c
  
  Index: filterobject.c
  ===================================================================
  RCS file: /home/cvs/httpd-python/src/filterobject.c,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- filterobject.c	17 Sep 2002 03:37:23 -0000	1.13
  +++ filterobject.c	24 Sep 2002 16:01:28 -0000	1.14
  @@ -104,7 +104,6 @@
   
       result->closed = 0;
       result->softspace = 0;
  -    result->bytes_written = 0;
   
       result->handler = handler;
       result->dir = dir;
  @@ -346,7 +345,6 @@
   					      c->bucket_alloc);
   	APR_BRIGADE_INSERT_TAIL(self->bb_out, b);
   	
  -	self->bytes_written += len;
       }
   
       Py_INCREF(Py_None);
  @@ -401,24 +399,21 @@
   
       if (! self->closed) {
   
  -	if (self->bytes_written) {
  -
  -	    /* does the output brigade exist? */
  -	    if (!self->bb_out) {
  -		self->bb_out = apr_brigade_create(self->f->r->pool,
  -						  c->bucket_alloc);
  -	    }
  +        /* does the output brigade exist? */
  +        if (!self->bb_out) {
  +            self->bb_out = apr_brigade_create(self->f->r->pool,
  +                                              c->bucket_alloc);
  +        }
   
  -	    APR_BRIGADE_INSERT_TAIL(self->bb_out, 
  -				    apr_bucket_eos_create(c->bucket_alloc));
  +        APR_BRIGADE_INSERT_TAIL(self->bb_out, 
  +                                apr_bucket_eos_create(c->bucket_alloc));
   	
  -	    if (! self->is_input) {
  -                Py_BEGIN_ALLOW_THREADS;
  -		ap_pass_brigade(self->f->next, self->bb_out);
  -                Py_END_ALLOW_THREADS;
  -		self->bb_out = NULL;
  -	    }
  -	}
  +        if (! self->is_input) {
  +            Py_BEGIN_ALLOW_THREADS;
  +            ap_pass_brigade(self->f->next, self->bb_out);
  +            Py_END_ALLOW_THREADS;
  +            self->bb_out = NULL;
  +        }
   
   	self->closed = 1;
       }
  
  
  
  1.78      +10 -8     httpd-python/src/mod_python.c
  
  Index: mod_python.c
  ===================================================================
  RCS file: /home/cvs/httpd-python/src/mod_python.c,v
  retrieving revision 1.77
  retrieving revision 1.78
  diff -u -r1.77 -r1.78
  --- mod_python.c	18 Sep 2002 20:13:37 -0000	1.77
  +++ mod_python.c	24 Sep 2002 16:01:28 -0000	1.78
  @@ -1135,6 +1135,7 @@
       /* create filter */
       filter = (filterobject *)MpFilter_FromFilter(f, bb, is_input, mode, readbytes,
                                                    fh->handler, fh->dir);
  +
       Py_INCREF(request_obj);
       filter->request_obj = request_obj;
   
  @@ -1501,7 +1502,7 @@
       /* register the filter NOTE - this only works so long as the
          directive is only allowed in the main config. For .htaccess we
          would have to make sure not to duplicate this */
  -    ap_register_input_filter(name, python_input_filter, NULL, AP_FTYPE_CONNECTION);
  +    frec = ap_register_input_filter(name, python_input_filter, NULL, AP_FTYPE_CONNECTION);
    
       conf = (py_config *) mconfig;
   
  @@ -1518,23 +1519,24 @@
                                                   const char *handler, const char *name) {
       py_config *conf;
       py_handler *fh;
  +    ap_filter_rec_t *frec;
    
       if (!name)
           name = apr_pstrdup(cmd->pool, handler);
   
  +    /* register the filter NOTE - this only works so long as the
  +       directive is only allowed in the main config. For .htaccess we
  +       would have to make sure not to duplicate this */
  +    frec = ap_register_output_filter(name, python_output_filter, NULL, AP_FTYPE_RESOURCE);
  + 
       conf = (py_config *) mconfig;
       
       fh = (py_handler *) apr_pcalloc(cmd->pool, sizeof(py_handler));
       fh->handler = (char *)handler;
       fh->dir = conf->config_dir;
   
  -    apr_hash_set(conf->out_filters, name, APR_HASH_KEY_STRING, fh);
  +    apr_hash_set(conf->out_filters, frec->name, APR_HASH_KEY_STRING, fh);
   
  -    /* register the filter NOTE - this only works so long as the
  -       directive is only allowed in the main config. For .htaccess we
  -       would have to make sure not to duplicate this */
  -    ap_register_output_filter(name, python_output_filter, NULL, AP_FTYPE_RESOURCE);
  - 
       return NULL;
   }
   
  
  
  
  1.32      +52 -4     httpd-python/src/requestobject.c
  
  Index: requestobject.c
  ===================================================================
  RCS file: /home/cvs/httpd-python/src/requestobject.c,v
  retrieving revision 1.31
  retrieving revision 1.32
  diff -u -r1.31 -r1.32
  --- requestobject.c	15 Sep 2002 23:45:35 -0000	1.31
  +++ requestobject.c	24 Sep 2002 16:01:28 -0000	1.32
  @@ -756,6 +756,26 @@
   
   static PyObject * req_send_http_header(requestobject *self)
   {
  +    Py_INCREF(Py_None);
  +    return Py_None;
  +}
  +
  +/**
  + ** request.set_content_length(request self, long content_length)
  + **
  + *      write output to the client
  + */
  +
  +static PyObject * req_set_content_length(requestobject *self, PyObject *args)
  +{
  +    long len;
  +
  +    if (! PyArg_ParseTuple(args, "l", &len))
  +        return NULL;  /* bad args */
  +
  +    ap_set_content_length(self->request_rec, len);
  +
  +    Py_INCREF(Py_None);
       return Py_None;
   }
   
  @@ -788,7 +808,20 @@
   
   }
   
  +//XXX segfault generator
  +static char* req_segfault(requestobject *self)
  +{
  +
  +    char *x = 1;
  +
  +    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, self->request_rec, "about to segfault...");
  +
  +    *x = 'x';
  +    return x;
  +}
  +
   static PyMethodDef request_methods[] = {
  +    {"segfault",       (PyCFunction) req_segfault,       METH_NOARGS},
       {"add_common_vars",       (PyCFunction) req_add_common_vars,       METH_NOARGS},
       {"add_handler",           (PyCFunction) req_add_handler,           METH_VARARGS},
       {"allow_methods",         (PyCFunction) req_allow_methods,         METH_VARARGS},
  @@ -805,6 +838,7 @@
       {"readlines",             (PyCFunction) req_readlines,             METH_VARARGS},
       {"register_cleanup",      (PyCFunction) req_register_cleanup,      METH_VARARGS},
       {"send_http_header",      (PyCFunction) req_send_http_header,      METH_NOARGS},
  +    {"set_content_length",    (PyCFunction) req_set_content_length,    METH_VARARGS},
       {"write",                 (PyCFunction) req_write,                 METH_VARARGS},
       { NULL, NULL } /* sentinel */
   };
  @@ -967,6 +1001,20 @@
   }
   
   /**
  + ** getreq_recmbr_time
  + **
  + *    Retrieves apr_time_t request_rec members
  + */
  +
  +static PyObject *getreq_recmbr_time(requestobject *self, void *name) 
  +{
  +    PyMemberDef *md = find_memberdef(request_rec_mbrs, name);
  +    char *addr = (char *)self->request_rec + md->offset;
  +    apr_time_t time = *(apr_time_t*)addr;
  +    return PyFloat_FromDouble(time*0.000001);
  +}
  +
  +/**
    ** getreq_rec_ah
    **
    *    For array headers that will get converted to tuple
  @@ -974,7 +1022,7 @@
   
   static PyObject *getreq_rec_ah(requestobject *self, void *name) 
   {
  -    const PyMemberDef *md = find_memberdef(request_rec_mbrs, (char*)name);
  +    const PyMemberDef *md = find_memberdef(request_rec_mbrs, name);
       apr_array_header_t *ah = 
           (apr_array_header_t *)((void *)self->request_rec + md->offset);
   
  @@ -1038,7 +1086,7 @@
       {"protocol",     (getter)getreq_recmbr, NULL, "Protocol as given to us, or HTTP/0.9", "protocol"},
       {"proto_num",    (getter)getreq_recmbr, NULL, "Protocol version. 1.1 = 1001", "proto_num"},
       {"hostname",     (getter)getreq_recmbr, NULL, "Host, as set by full URI or Host:", "hostname"},
  -    {"request_time", (getter)getreq_recmbr, NULL, "When request started", "request_time"},
  +    {"request_time", (getter)getreq_recmbr_time, NULL, "When request started", "request_time"},
       {"status_line",  (getter)getreq_recmbr, NULL, "Status line, if set by script", "status_line"},
       {"status",       (getter)getreq_recmbr, (setter)setreq_recmbr, "Status", "status"},
       {"method",       (getter)getreq_recmbr, NULL, "Request method", "method"},
  @@ -1048,7 +1096,7 @@
       {"allowed_methods", (getter)getreq_rec_ml, NULL, "Allowed methods", "allowed_methods"},
       {"sent_bodyct",  (getter)getreq_recmbr, NULL, "Byte count in stream for body", "sent_boduct"},
       {"bytes_sent",   (getter)getreq_recmbr, NULL, "Bytes sent", "bytes_sent"},
  -    {"mtime",        (getter)getreq_recmbr, NULL, "Time resource was last modified", "mtime"},
  +    {"mtime",        (getter)getreq_recmbr_time, NULL, "Time resource was last modified", "mtime"},
       {"chunked",      (getter)getreq_recmbr, NULL, "Sending chunked transfer-coding", "chunked"},
       {"boundary",     (getter)getreq_recmbr, NULL, "Multipart/byteranges boundary", "boundary"},
       {"range",        (getter)getreq_recmbr, NULL, "The Range: header", "range"},
  
  
  
  1.12      +4 -4      httpd-python/src/util.c
  
  Index: util.c
  ===================================================================
  RCS file: /home/cvs/httpd-python/src/util.c,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- util.c	19 Sep 2002 20:11:35 -0000	1.11
  +++ util.c	24 Sep 2002 16:01:28 -0000	1.12
  @@ -186,19 +186,19 @@
           PyTuple_SET_ITEM(t, 6, Py_None);
       }
       if (f->valid & APR_FINFO_ATIME) {
  -        PyTuple_SET_ITEM(t, 7, PyInt_FromLong(f->atime/1000000));
  +        PyTuple_SET_ITEM(t, 7, PyInt_FromLong(f->atime*0.000001));
       } else {
           Py_INCREF(Py_None);
           PyTuple_SET_ITEM(t, 7, Py_None);
       }
       if (f->valid & APR_FINFO_MTIME) {
  -        PyTuple_SET_ITEM(t, 8, PyInt_FromLong(f->mtime/1000000));
  +        PyTuple_SET_ITEM(t, 8, PyInt_FromLong(f->mtime*0.000001));
       } else {
           Py_INCREF(Py_None);
           PyTuple_SET_ITEM(t, 8, Py_None);
       }
       if (f->valid & APR_FINFO_CTIME) {
  -        PyTuple_SET_ITEM(t, 9, PyInt_FromLong(f->ctime/10000000));
  +        PyTuple_SET_ITEM(t, 9, PyInt_FromLong(f->ctime*0.000001));
       } else {
           Py_INCREF(Py_None);
           PyTuple_SET_ITEM(t, 9, Py_None);
  
  
  
  1.11      +46 -60    httpd-python/test/test.py
  
  Index: test.py
  ===================================================================
  RCS file: /home/cvs/httpd-python/test/test.py,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- test.py	6 Sep 2002 22:06:29 -0000	1.10
  +++ test.py	24 Sep 2002 16:01:28 -0000	1.11
  @@ -16,6 +16,13 @@
   import commands
   import urllib
   
  +
  +# need to incorporate the gdb into the testing process....
  +# httpd needs to be invoked under gdb [make optional], and
  +# the url fetching should be forked.
  +# what's the deal with gdb's being different?
  +
  +
   def findUnusedPort():
   
       # bind to port 0 which makes the OS find the next
  @@ -53,6 +60,11 @@
           f.write("\n# --APPENDED-- \n\n"+append)
           f.close()
   
  +    def makeGdbFile(file):
  +        f = open(file, "w")
  +        f.write("run -f %s -X" % PARAMS("config"))
  +        f.close()
  +
       def startApache(self):
   
           print "  Starting Apache...."
  @@ -60,12 +72,17 @@
           cmd = '%s -f %s' % (HTTPD, PARAMS["config"])
           print "  ", cmd
           print commands.getoutput(cmd)
  +        self.apache_running = 1
   
  -    def tearDown(self):
  -
  +    def stopApache(self):
           print "  Stopping Apache..."
           pid = commands.getoutput("cat %s/logs/httpd.pid" % PARAMS["server_root"])
           commands.getoutput("kill "+pid)
  +        self.apache_running = 0
  +
  +    def tearDown(self):
  +        if self.apache_running:
  +            self.stopApache()
   
       def testLoadModule(self):
   
  @@ -237,7 +254,6 @@
                 "  SetHandler python-program\n" + \
                 "  PythonHandler tests::req_document_root\n" + \
                 "  PythonDebug On\n" + \
  -              "  PythonOption secret sauce\n" + \
                 "</Directory>\n"
   
           self.makeConfig(cfg)
  @@ -254,56 +270,6 @@
           if (rsp != PARAMS["document_root"]):
               self.fail("test failed")
   
  -    def test_req_get_config(self):
  -
  -        print "\n* Testing req.get_config() and get_options()"
  -
  -        cfg = "<Directory %s/htdocs>\n" % PARAMS["server_root"]+ \
  -              "  SetHandler python-program\n" + \
  -              "  PythonHandler tests::req_get_config\n" + \
  -              "  PythonDebug On\n" + \
  -              "  PythonOption secret sauce\n" + \
  -              "</Directory>\n"
  -
  -        self.makeConfig(cfg)
  -        self.startApache()
  -
  -        url = "http://127.0.0.1:%s/tests.py" % PARAMS["port"]
  -        print "    url: "+url
  -        
  -        f = urllib.urlopen(url)
  -        rsp = f.read()
  -        f.close()
  -        print "    response: "+rsp
  -
  -        if (rsp != "test ok"):
  -            self.fail("test failed")
  -
  -    def test_req_get_remote_host(self):
  -
  -        print "\n* Testing req.get_remote_host()"
  -
  -        cfg = "<Directory %s/htdocs>\n" % PARAMS["server_root"]+ \
  -              "  SetHandler python-program\n" + \
  -              "  PythonHandler tests::req_get_remote_host\n" + \
  -              "  PythonDebug On\n" + \
  -              "</Directory>\n" + \
  -              "HostNameLookups Off\n"
  -
  -        self.makeConfig(cfg)
  -        self.startApache()
  -
  -        url = "http://127.0.0.1:%s/tests.py" % PARAMS["port"]
  -        print "    url: "+url
  -        
  -        f = urllib.urlopen(url)
  -        rsp = f.read()
  -        f.close()
  -        print "    response: "+rsp
  -
  -        if (rsp != "test ok"):
  -            self.fail("test failed")
  -
       def test_req_internal_redirect(self):
   
           print "\n* Testing req.internal_redirect()"
  @@ -535,9 +501,9 @@
           cfg = "  SetHandler python-program\n" + \
                 "  PythonPath ['%s']+sys.path\n" % PARAMS["document_root"] + \
                 "  PythonHandler tests::simplehandler\n" + \
  -              "  PythonOutputFilter tests::outputfilter myfilter\n" + \
  +              "  PythonOutputFilter tests::outputfilter MP_TEST_FILTER\n" + \
                 "  PythonDebug On\n" + \
  -              "  AddOutputFilter myfilter .py\n"
  +              "  AddOutputFilter MP_TEST_FILTER .py\n"
   
           self.makeConfig(cfg)
           self.startApache()
  @@ -575,19 +541,39 @@
           if (rsp != "test ok"):
               self.fail("test failed")
   
  +    def test_internal(self):
  +
  +        print "\n* Testing internally"
  +
  +        cfg = "  SetHandler python-program\n" + \
  +              "  PythonPath ['%s']+sys.path\n" % PARAMS["document_root"] + \
  +              "  PythonHandler tests\n" + \
  +              "  PythonOption testing 123\n" + \
  +              "  PythonDebug On\n"
  +
  +        self.makeConfig(cfg)
  +        self.startApache()
  +
  +        url = "http://127.0.0.1:%s/tests.py" % PARAMS["port"]
  +        print "    url: "+url
  +
  +        f = urllib.urlopen(url)
  +        rsp = f.read()
  +        f.close()
  +        print "    response: ", rsp
  +
  +        if (rsp[-7:] != "test ok"):
  +            self.fail("Some tests failed, see error_log")
  +
   def suite():
   
       mpTestSuite = unittest.TestSuite()
       mpTestSuite.addTest(ModPythonTestCase("testLoadModule"))
  -    mpTestSuite.addTest(ModPythonTestCase("test_apache_log_error"))
  -    mpTestSuite.addTest(ModPythonTestCase("test_apache_table"))
  -    mpTestSuite.addTest(ModPythonTestCase("test_req_add_common_vars"))
  +    mpTestSuite.addTest(ModPythonTestCase("test_internal"))
       mpTestSuite.addTest(ModPythonTestCase("test_req_add_handler"))
       mpTestSuite.addTest(ModPythonTestCase("test_req_allow_methods"))
       mpTestSuite.addTest(ModPythonTestCase("test_req_document_root"))
       mpTestSuite.addTest(ModPythonTestCase("test_req_get_basic_auth_pw"))
  -    mpTestSuite.addTest(ModPythonTestCase("test_req_get_config"))
  -    mpTestSuite.addTest(ModPythonTestCase("test_req_get_remote_host"))
       mpTestSuite.addTest(ModPythonTestCase("test_req_internal_redirect"))
       mpTestSuite.addTest(ModPythonTestCase("test_req_read"))
       mpTestSuite.addTest(ModPythonTestCase("test_req_readline"))
  
  
  
  1.2       +1 -0      httpd-python/test/conf/test.conf.tmpl
  
  Index: test.conf.tmpl
  ===================================================================
  RCS file: /home/cvs/httpd-python/test/conf/test.conf.tmpl,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- test.conf.tmpl	18 Aug 2002 18:43:36 -0000	1.1
  +++ test.conf.tmpl	24 Sep 2002 16:01:28 -0000	1.2
  @@ -25,6 +25,7 @@
   
   ServerRoot "%(server_root)s"
   ErrorLog "logs/error_log"
  +LogLevel debug
   TypesConfig "conf/mime.types"
   PidFile "logs/httpd.pid"
   
  
  
  
  1.9       +352 -51   httpd-python/test/htdocs/tests.py
  
  Index: tests.py
  ===================================================================
  RCS file: /home/cvs/httpd-python/test/htdocs/tests.py,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- tests.py	6 Sep 2002 22:06:29 -0000	1.8
  +++ tests.py	24 Sep 2002 16:01:28 -0000	1.9
  @@ -2,43 +2,355 @@
   # mod_python tests
   
   from mod_python import apache
  +import unittest
  +import re
  +import time
  +import os
  +
  +class InternalTestCase(unittest.TestCase):
  +
  +    def __init__(self, methodName, req):
  +        unittest.TestCase.__init__(self, methodName)
  +        self.req = req
  +
  +    def test_apache_log_error(self):
  +
  +        s = self.req.server
  +        apache.log_error("Testing apache.log_error():", apache.APLOG_INFO, s)
  +        apache.log_error("xEMERGx", apache.APLOG_EMERG, s)
  +        apache.log_error("xALERTx", apache.APLOG_ALERT, s)
  +        apache.log_error("xCRITx", apache.APLOG_CRIT, s)
  +        apache.log_error("xERRx", apache.APLOG_ERR, s)
  +        apache.log_error("xWARNINGx", apache.APLOG_WARNING, s)
  +        apache.log_error("xNOTICEx", apache.APLOG_NOTICE, s)
  +        apache.log_error("xINFOx", apache.APLOG_INFO, s)
  +        apache.log_error("xDEBUGx", apache.APLOG_DEBUG, s)
  +
  +        # see what's in the log now
  +        f = open("%s/logs/error_log" % apache.server_root())
  +        # for some reason re doesn't like \n, why?
  +        import string
  +        log = "".join(map(string.strip, f.readlines()))
  +        f.close()
  +
  +        if not re.search("xEMERGx.*xALERTx.*xCRITx.*xERRx.*xWARNINGx.*xNOTICEx.*xINFOx.*xDEBUGx", log):
  +            self.fail("Could not find test messages in error_log")
  +            
  +
  +    def test_apache_table(self):
  +
  +        self.req.log_error("Testing table object.")
  +
  +        # tests borrowed from Python test quite for dict
  +        _test_table()
  +
  +        # inheritance
  +        class mytable(apache.table):
  +            def __str__(self):
  +                return "str() from mytable"
  +        mt = mytable({'a':'b'})
  +
  +        # add()
  +        a = apache.table({'a':'b'})
  +        a.add('a', 'c')
  +        if a['a'] != ['b', 'c']:
  +            self.fail('table.add() broken: a["a"] is %s' % `a["a"]`)
  +
  +    def test_req_add_common_vars(self):
  +
  +        self.req.log_error("Testing req.add_common_vars().")
  +
  +        a = len(self.req.subprocess_env)
  +        self.req.add_common_vars()
  +        b = len(self.req.subprocess_env)
  +        if a >= b: 
  +            self.fail("req.subprocess_env() is same size before and after")
  +
  +    def test_req_members(self):
  +
  +        # just run through request members making sure
  +        # they make sense
  +
  +        req = self.req
  +        log = req.log_error
  +        log("Examining request memebers:")
  +
  +        log("    req.connection: %s" % `req.connection`)
  +        s = str(type(req.connection))
  +        if s != "<type 'mp_conn'>":
  +            self.fail("strange req.connection type %s" % `s`)
  +
  +        log("    req.server: '%s'" % `req.server`)
  +        s = str(type(req.server))
  +        if s != "<type 'mp_server'>":
  +            self.fail("strange req.server type %s" % `s`)
  +
  +        for x in ((req.next, "next"),
  +                  (req.prev, "prev"),
  +                  (req.main, "main")):
  +            val, name = x
  +            log("    req.%s: '%s'" % (name, `val`))
  +            if val:
  +                self.fail("strange, req.%s should be None, not %s" % (name, `val`))
  +        
  +        log("    req.the_request: '%s'" % req.the_request)
  +        if not re.match(r"GET /.* HTTP/1\.", req.the_request):
  +            self.fail("strange req.the_request %s" % `req.the_request`)
  +
  +        for x in ((req.assbackwards, "assbackwards"),
  +                  (req.proxyreq, "proxyreq"),
  +                  (req.header_only, "header_only")):
  +            val, name = x
  +            log("    req.%s: %s" % (name, `val`))
  +            if val:
  +                self.fail("%s should be 0" % name)
  +
  +        log("    req.protocol: %s" % `req.protocol`)
  +        if not req.protocol == req.the_request.split()[-1]:
  +            self.fail("req.protocol doesn't match req.the_request")
  +
  +        log("    req.proto_num: %s" % `req.proto_num`)
  +        if req.proto_num != 1000 + int(req.protocol[-1]):
  +            self.fail("req.proto_num doesn't match req.protocol")
  +
  +        log("    req.hostname: %s" % `req.hostname`)
  +        if req.hostname != "127.0.0.1":
  +            self.fail("req.hostname isn't '127.0.0.1'")
  +
  +        log("    req.request_time: %s" % `req.request_time`)
  +        if (time.time() - req.request_time) > 2:
  +            self.fail("req.request_time suggests request started more than 2 secs ago")
  +
  +        log("    req.status_line: %s" % `req.status_line`)
  +        if req.status_line:
  +            self.fail("req.status_line should be None at this point")
  +
  +        log("    req.status: %s" % `req.status`)
  +        if req.status != 200:
  +            self.fail("req.status should be 200")
  +        req.status = req.status # make sure its writable
  +
  +        log("    req.method: %s" % `req.method`)
  +        if req.method != "GET":
  +            self.fail("req.method should be 'GET'")
  +
  +        log("    req.method_number: %s" % `req.method_number`)        
  +        if req.method_number != 0:
  +            self.fail("req.method_number should be 0")
  +
  +        log("    req.allowed: %s" % `req.allowed`)
  +        if req.allowed != 0:
  +            self.fail("req.allowed should be 0")
  +            
  +        log("    req.allowed_xmethods: %s" % `req.allowed_xmethods`)
  +        if req.allowed_xmethods != ():
  +            self.fail("req.allowed_xmethods should be an empty tuple")
  +            
  +        log("    req.allowed_methods: %s" % `req.allowed_methods`)
  +        if req.allowed_methods:
  +            self.fail("req.allowed_methods should be None")
  +            
  +        log("    req.sent_bodyct: %s" % `req.sent_bodyct`)
  +        if req.sent_bodyct != 0:
  +            self.fail("req.sent_bodyct should be 0")
  +            
  +        log("    req.bytes_sent: %s" % `req.bytes_sent`)
  +        save = req.bytes_sent
  +        log("       writing 4 bytes...")
  +        req.write("1234")
  +        log("       req.bytes_sent: %s" % `req.bytes_sent`)
  +        if req.bytes_sent - save != 4:
  +            self.fail("req.bytes_sent should have incremented by 4, but didn't")
  +
  +        log("    req.mtime: %s" % `req.mtime`)
  +        if req.mtime != 0:
  +            self.fail("req.mtime should be 0")
  +        
  +        log("    req.chunked: %s" % `req.chunked`)
  +        if req.chunked != 0:
  +            self.fail("req.chunked should be 0")
  +            
  +        log("    req.range: %s" % `req.range`)
  +        if req.range:
  +            self.fail("req.range should be None")
  +            
  +        log("    req.clength: %s" % `req.clength`)
  +        log("        calling req.set_content_length(15)...")
  +        req.set_content_length(15)
  +        log("        req.clength: %s" % `req.clength`)
  +        if req.clength != 15:
  +            self.fail("req.clength should be 15")
  +        
  +        log("    req.remaining: %s" % `req.remaining`)
  +        if req.remaining != 0:
  +            self.fail("req.remaining should be 0")
  +            
  +        log("    req.read_length: %s" % `req.read_length`)
  +        if req.read_length != 0:
  +            self.fail("req.read_length should be 0")
  +        
  +        log("    req.read_body: %s" % `req.read_body`)
  +        if req.read_body != 0:
  +            self.fail("req.read_body should be 0")
  +            
  +        log("    req.read_chunked: %s" % `req.read_chunked`)
  +        if req.read_chunked != 0:
  +            self.fail("req.read_chunked should be 0")
  +            
  +        log("    req.expecting_100: %s" % `req.expecting_100`)
  +        if req.expecting_100 != 0:
  +            self.fail("req.expecting_100 should be 0")
  +
  +        log("    req.headers_int: %s" % `req.headers_in`)
  +        if req.headers_in["User-agent"][:13].lower() != "python-urllib":
  +            self.fail("The 'user-agnet' header should begin with 'Python-urllib'")
  +            
  +        log("    req.headers_out: %s" % `req.headers_out`)
  +        if ((not req.headers_out.has_key("content-length")) or
  +            req.headers_out["content-length"] != "15"):
  +            self.fail("req.headers_out['content-length'] should be 15")
  +            
  +        log("    req.subprocess_env: %s" % `req.subprocess_env`)
  +        if req.subprocess_env["SERVER_SOFTWARE"].find("Python") == -1:
  +            self.fail("req.subprocess_env['SERVER_SOFTWARE'] should contain 'Python'")
  +            
  +        log("    req.notes: %s" % `req.notes`)
  +        log("        doing req.notes['testing'] = '123' ...")
  +        req.notes['testing'] = '123'
  +        log("    req.notes: %s" % `req.notes`)
  +        if req.notes["testing"] != '123':
  +            self.fail("req.notes['testing'] should be '123'")
  +        
  +        log("    req.phase: %s" % `req.phase`)
  +        if req.phase != "PythonHandler":
  +            self.fail("req.phase should be 'PythonHandler'")
  +            
  +        log("    req.interpreter: %s" % `req.interpreter`)
  +        if req.interpreter != req.server.server_hostname:
  +            self.fail("req.interpreter should be same as req.server_hostname: %s" % `req.server_hostname`)
  +            
  +        log("    req.content_type: %s" % `req.content_type`)
  +        log("        doing req.content_type = 'test/123' ...")
  +        req.content_type = 'test/123'
  +        log("        req.content_type: %s" % `req.content_type`)
  +        if req.content_type != 'test/123' or not req._content_type_set:
  +            self.fail("req.content_type should be 'test/123' and req._content_type_set 1")
  +        
  +        log("    req.handler: %s" % `req.handler`)
  +        if req.handler != "python-program":
  +            self.fail("req.handler should be 'python-program'")
  +            
  +        log("    req.content_encoding: %s" % `req.content_encoding`)
  +        if req.content_encoding:
  +            self.fail("req.content_encoding should be None")
  +            
  +        log("    req.vlist_validator: %s" % `req.vlist_validator`)
  +        if req.vlist_validator:
  +            self.fail("req.vlist_validator should be None")
  +            
  +        log("    req.user: %s" % `req.user`)
  +        if req.user:
  +            self.fail("req.user should be None")
  +            
  +        log("    req.ap_auth_type: %s" % `req.ap_auth_type`)
  +        if req.ap_auth_type:
  +            self.fail("req.ap_auth_type should be None")
  +            
  +        log("    req.no_cache: %s" % `req.no_cache`)
  +        if req.no_cache != 0:
  +            self.fail("req.no_cache should be 0")
  +            
  +        log("    req.no_local_copy: %s" % `req.no_local_copy`)
  +        if req.no_local_copy != 0:
  +            self.fail("req.no_local_copy should be 0")
  +            
  +        log("    req.unparsed_uri: %s" % `req.unparsed_uri`)
  +        if req.unparsed_uri != "/tests.py":
  +            self.fail("req.unparse_uri should be '/tests.py'")
  +            
  +        log("    req.uri: %s" % `req.uri`)
  +        if req.uri != "/tests.py":
  +            self.fail("req.uri should be '/tests.py'")
  +            
  +        log("    req.filename: %s" % `req.filename`)
  +        if req.filename != req.document_root() + req.uri:
  +            self.fail("req.filename should be req.document_root() + req.uri, but it isn't")
  +            
  +        log("    req.canonical_filename: %s" % `req.canonical_filename`)
  +        if not req.canonical_filename:
  +            self.fail("req.canonical_filename should not be blank")
  +        
  +        log("    req.path_info: %s" % `req.path_info`)
  +        if req.path_info != '':
  +            self.fail("req.path_info should be ''")
  +        
  +        log("    req.args: %s" % `req.args`)
  +        if req.args:
  +            self.fail("req.args should be None")
  +            
  +        log("    req.finfo: %s" % `req.finfo`)
  +        if req.finfo[10] != req.canonical_filename:
  +            self.fail("req.finfo[10] should be the (canonical) filename")
  +        
  +        log("    req.parsed_uri: %s" % `req.parsed_uri`)
  +        if req.parsed_uri[6] != '/tests.py':
  +            self.fail("req.parsed_uri[6] should be '/tests.py'")
  +            
  +        log("    req.used_path_info: %s" % `req.used_path_info`)
  +        if req.used_path_info != 2:
  +            self.fail("req.used_path_info should be 2") # XXX really? :-)
  +            
  +        log("    req.eos_sent: %s" % `req.eos_sent`)
  +        if req.eos_sent:
  +            self.fail("req.eos_sent says we sent EOS, but we didn't")
  +
  +    def test_req_get_config(self):
  +
  +        req = self.req
  +        log = req.log_error
  +
  +        log("req.get_config(): %s" % `req.get_config()`)
  +        if req.get_config()["PythonDebug"] != "1":
  +            self.fail("get_config return should show PythonDebug 1")
  +
  +        log("req.get_options(): %s" % `req.get_options()`)
  +        if req.get_options() != apache.table({"testing":"123"}):
  +            self.fail("get_options() should contain 'testing':'123'")
  +
  +    def test_req_get_remote_host(self):
  +
  +        # simulating this test for real is too complex...
  +        req = self.req
  +        log = req.log_error
  +        log("req.get_get_remote_host(): %s" % `req.get_remote_host(apache.REMOTE_HOST)`)
  +        log("req.get_get_remote_host(): %s" % `req.get_remote_host()`)
  +        if (req.get_remote_host(apache.REMOTE_HOST) != None) or \
  +           (req.get_remote_host() != "127.0.0.1"):
  +            self.fail("remote host test failed")
  +
  +
  +def make_suite(req):
  +
  +    mpTestSuite = unittest.TestSuite()
  +    mpTestSuite.addTest(InternalTestCase("test_apache_log_error", req))
  +    mpTestSuite.addTest(InternalTestCase("test_apache_table", req))
  +    mpTestSuite.addTest(InternalTestCase("test_req_add_common_vars", req))
  +    mpTestSuite.addTest(InternalTestCase("test_req_members", req))
  +    mpTestSuite.addTest(InternalTestCase("test_req_get_config", req))
  +    mpTestSuite.addTest(InternalTestCase("test_req_get_remote_host", req))
  +    return mpTestSuite
   
  -TestFailed = "TestFailed"
  -
  -def apache_log_error(req):
  -
  -    apache.log_error("This is a test message")
  -    req.write("Just wrote something to log\n")
  -
  -    return apache.OK
  -
  -def apache_table(req):
  -
  -    # tests borrowed from Python test quite for dict
  -    _test_table()
  -
  -    # inheritance
  -    class mytable(apache.table):
  -        def __str__(self):
  -            return "str() from mytable"
  -    mt = mytable({'a':'b'})
   
  -    # add()
  -    a = apache.table({'a':'b'})
  -    a.add('a', 'c')
  -    if a['a'] != ['b', 'c']: raise TestFailed, 'table.add() broken: a["a"] is %s' % `a["a"]`
  +def handler(req):
   
  -    req.write("test ok")
  -    return apache.OK
  +    tr = unittest.TextTestRunner()
  +    result = tr.run(make_suite(req))
   
  -def req_add_common_vars(req):
  +    if result.wasSuccessful():
  +        req.write("test ok")
  +    else:
  +        req.write("test failed")
   
  -    a = len(req.subprocess_env)
  -    req.add_common_vars()
  -    b = len(req.subprocess_env)
  -    if a >= b: raise TestFailed, 'req.subprocess_env() is same size before and after'
  -    
  -    req.write("test ok")
       return apache.OK
   
   def req_add_handler(req):
  @@ -75,24 +387,6 @@
       req.write(req.document_root())
       return apache.OK
   
  -def req_get_config(req):
  -
  -    if req.get_config() == apache.table({"PythonDebug":"1"}) and \
  -       req.get_options() == apache.table({"secret":"sauce"}):
  -        req.write("test ok")
  -
  -    return apache.OK
  -
  -def req_get_remote_host(req):
  -
  -    # simulating this test for real is too complex...
  -
  -    if (req.get_remote_host(apache.REMOTE_HOST) == None) and \
  -       (req.get_remote_host() != ""):
  -        req.write("test ok")
  -
  -    return apache.OK
  -
   def req_internal_redirect(req):
   
       req.internal_redirect("/test.int")
  @@ -158,7 +452,12 @@
   def outputfilter(filter):
   
       s = filter.read()
  -    filter.write(s.upper())
  +    while s:
  +        filter.write(s.upper())
  +        s = filter.read()
  +
  +    if s is None:
  +        filter.close()
   
       return apache.OK
   
  @@ -178,6 +477,8 @@
       return apache.OK
   
   def _test_table():
  +
  +    log = apache.log_error
   
       d = apache.table()
       if d.keys() != []: raise TestFailed, '{}.keys()'