php测试工具:phpt

2012年5月13日 没有评论

说起php测试,通常会想起PHPUnit,而phpt却很少有人提及。其实在php源码中绝大多数测试均是由phpt完成。

什么是phpt?
phpt是一个小测试脚本,php内核组和质量保证团队用其做php功能性测试。phpt可以保障新版本php不会出现功能缺失、和一些已知bug。编写phpt测试脚本有助于使php自身更加稳定。

编写phpt需要哪些技能?
很简单,了解php语言即可。当然,还需要一个文本编辑器。

phpt可以做哪些测试?
函数测试、基本语法测试、扩张测试等。通常可以到目录php-src/trunk/ext/standard/tests/找到很多新手上路的例子。

一个简单的示例:ext/standard/tests/strings/strtr.phpt

1
2
3
4
5
6
7
8
9
10
--TEST--
strtr() function - basic test for strstr()
--FILE--
<?php
/* Do not change this test it is a README.TESTING example. */
$trans = array("hello"=>"hi", "hi"=>"hello", "a"=>"A", "world"=>"planet");
var_dump(strtr("# hi all, I said hello world! #", $trans));
?>
--EXPECT--
string(32) "# hello All, I sAid hi planet! #"

一个phpt脚本通常至少由三部分组成:TEST, FILE, EXPECT。

  • TEST是此测试用例的标题和说明
  • FILE是测试代码
  • EXPECT是预期值
  • 运行之

    1
    2
    3
    4
    5
    6
    7
    8
    
    #
    # run-tests.php依赖环境变量TEST_PHP_EXECUTABLE,先手动导入之
    #
    export TEST_PHP_EXECUTABLE=/usr/bin/php
     
    cd tests
    cp /usr/local/lib/php/build/run-tests.php ./
    php run-tests.php strtr.phpt

    结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    
    # php run-tests.php strtr.phpt 
     
    =====================================================================
    PHP         : /usr/bin/php 
    PHP_SAPI    : cli
    PHP_VERSION : 5.4.0
    ZEND_VERSION: 2.4.0
    PHP_OS      : Darwin - Darwin mac 11.0.0 Darwin Kernel Version 11.0.0: Mon Jun 13 11:33:28 PDT 2011; root:xnu-1699.22.70~3/RELEASE_X86_64 x86_64
    INI actual  : /usr/local/lib/php.ini
    More .INIs  :  
    CWD         : /Volumes/d2/dev/dangdang/reco_server/src/tests
    Extra dirs  : 
    VALGRIND    : Not used
    =====================================================================
    Running selected tests.
    PASS strtr() function - basic test for strstr() [strtr.phpt] 
    =====================================================================
    Number of tests :    1                 1
    Tests skipped   :    0 (  0.0%) --------
    Tests warned    :    0 (  0.0%) (  0.0%)
    Tests failed    :    0 (  0.0%) (  0.0%)
    Expected fail   :    0 (  0.0%) (  0.0%)
    Tests passed    :    1 (100.0%) (100.0%)
    ---------------------------------------------------------------------
    Time taken      :    0 seconds
    =====================================================================

    嗯哼,测试通过~

    很显然,EXPECT标签是无法满足变化的需求的,于是有了EXPECTF

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    /ext/standard/tests/strings/str_shuffle.phpt
    --TEST--
    Testing str_shuffle.
    --FILE--
    <?php
    /* Do not change this test it is a README.TESTING example. */
    $s = '123';
    var_dump(str_shuffle($s));
    var_dump($s);
    ?>
    --EXPECTF--
    string(3) "%s"
    string(3) "123"

    常见的几个:%s->字符串,%i->INT, %d->numbers, %c->char, %x等。。。和printf的语法是接近的,很容易上手。更多格式

    还有EXPECTREGEX,更强大一些,支持正则表达。
    /ext/standard/tests/strings/strings001.phpt

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    --TEST--
    Test whether strstr() and strrchr() are binary safe.
    --FILE--
    <?php
    /* Do not change this test it is a README.TESTING example. */
    $s = "alabala nica".chr(0)."turska panica";
    var_dump(strstr($s, "nic"));
    var_dump(strrchr($s," nic"));
    ?>
    --EXPECTREGEX--
    string\(18\) \"nica\x00turska panica\"
    string\(19\) \" nica\x00turska panica\"

    另外一个很重要的标签:SKIPIF。通常用于监测测试环境,不符合则跳出中断之。
    /ext/exif/tests/exif005.phpt

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    --TEST--
    Check for exif_read_data, unusual IFD start
    --SKIPIF--
    <?php 
    	if (!extension_loaded('exif')) print 'skip exif extension not available';
    ?>
    --FILE--
    <?php
    /* Do not change this test it is a README.TESTING example.
     * test5.jpg is a 1*1 image that contains an Exif section with ifd = 00000009h
     */
    $image  = exif_read_data('./ext/exif/tests/test5.jpg','',true,false);
    var_dump($image['IFD0']);
    ?>
    --EXPECT--
    array(2) {
      ["ImageDescription"]=>
      string(11) "Ifd00000009"
      ["DateTime"]=>
      string(19) "2002:10:18 20:06:00"
    }

    有时候,测试会留下一些垃圾,CLEAN标签会帮上忙。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    --TEST--
    Will fail to clean up
    --FILE--
    <?php
          $temp_filename = "fred.tmp";
          $fp = fopen($temp_filename, "w");
          fwrite($fp, "Hello Boys!");
          fclose($fp);
    ?>
    --CLEAN--
    <?php
          unlink($temp_filename);
    ?>
    --EXPECT--

    有了这些基本语法,可以应付80%的测试了,更复杂一些的可以参考官方链接:http://qa.php.net/write-test.php

    分类: php, 技术 标签: , ,

    freetds dblib的使用

    2012年3月18日 没有评论

    freetds dblib的使用 http://wiki.panzhibiao.com/db/freetds

    ps: dblib的API不咋好用,和mysql C API比起来还是有点距离。

    分类: c++ 标签: , ,

    php pdo_dblib坑爹的地方

    2012年3月14日 没有评论

    先看pdo构造函数PDO::__construct:

    1
    
    PDO::__construct() ( string $dsn [, string $username [, string $password [, array $driver_options ]]] )

    第四个参数是$driver_options,但当pdo的驱动为dblib时,根本是不支持的。我们来看一下pdo_dblib的构造函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    
    /*
     * 第二参数是driver_options, 但只是保持接口统一,并未实际使用
     */
    static int pdo_dblib_handle_factory(pdo_dbh_t *dbh,
                                        zval *driver_options TSRMLS_DC)
    {
        pdo_dblib_db_handle *H;
        int i, ret = 0;
        struct pdo_data_src_parser vars[] = {
            { "charset",    NULL,   0 },
            { "appname",    "PHP " PDO_DBLIB_FLAVOUR,   0 },
            { "host",       "127.0.0.1", 0 },
            { "dbname",     NULL,   0 },
            { "secure",     NULL,   0 }, /* DBSETLSECURE */
            /* TODO: DBSETLVERSION ? */
        };
     
        php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 5);
     
        H = pecalloc(1, sizeof(*H), dbh->is_persistent);
        H->login = dblogin();
        H->err.sqlstate = dbh->error_code;
     
        if (!H->login) {
            goto cleanup;
        }
     
        if (dbh->username) {
            DBSETLUSER(H->login, dbh->username);
        }
        if (dbh->password) {
            DBSETLPWD(H->login, dbh->password);
        }
     
    #if !PHP_DBLIB_IS_MSSQL
        if (vars[0].optval) {
            DBSETLCHARSET(H->login, vars[0].optval);
        }
    #endif
     
        DBSETLAPP(H->login, vars[1].optval);
     
    #if PHP_DBLIB_IS_MSSQL
        dbprocerrhandle(H->login, (EHANDLEFUNC) error_handler);
        dbprocmsghandle(H->login, (MHANDLEFUNC) msg_handler);
    #endif
     
        H->link = dbopen(H->login, vars[2].optval);
     
        if (H->link == NULL) {
            goto cleanup;
        }
     
        /* dblib do not return more than this length from text/image */
        DBSETOPT(H->link, DBTEXTLIMIT, "2147483647");
     
        /* limit text/image from network */
        DBSETOPT(H->link, DBTEXTSIZE, "2147483647");
     
        /* allow double quoted indentifiers */
        DBSETOPT(H->link, DBQUOTEDIDENT, 1);
     
        if (vars[3].optval && FAIL == dbuse(H->link, vars[3].optval)) {
            goto cleanup;
        }
     
        ret = 1;
        dbh->max_escaped_char_length = 2;
        dbh->alloc_own_columns = 1;
     
    cleanup:
        for (i = 0; i < sizeof(vars)/sizeof(vars[0]); i++) {
            if (vars[i].freeme) {
                efree(vars[i].optval);
            }
        }
     
        dbh->methods = &dblib_methods;
        dbh->driver_data = H;
     
        if (!ret) {
            zend_throw_exception_ex(php_pdo_get_exception(), DBLIB_G(err).dberr TSRMLS_CC,
                "SQLSTATE[%s] %s (severity %d)",
                DBLIB_G(err).sqlstate,
                DBLIB_G(err).dberrstr,
                DBLIB_G(err).severity);
        }
     
        return ret;
    }

    第二参数是driver_options, 但只是保持接口统一,并未实际使用。

    而dblib里设置超时等选项的函数在这里是这样的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    PHP_MINIT_FUNCTION(pdo_dblib)
    {
        if (FAIL == dbinit()) {
            return FAILURE;
        }
     
        if (FAILURE == php_pdo_register_driver(&pdo_dblib_driver)) {
            return FAILURE;
        }
     
        /* TODO: 
        dbsetifile()
        dbsetmaxprocs()
        dbsetlogintime()
        dbsettime()
         */
     
    #if !PHP_DBLIB_IS_MSSQL
        dberrhandle(error_handler);
        dbmsghandle(msg_handler);
    #endif
     
        return SUCCESS;
    }

    看到TODO没有?哈哈,其实pdo_dblib不支持设置driver_options,更不支持超时设置,写程序需要小心。

    分类: c++, php, 技术 标签: , , ,

    libevhtp,一个比libevent官方的evhttp更好使的httpd库(一)

    2012年2月23日 没有评论

    libevhtp,嗯,没拼写错误,是一个在libevent基础开发的高性能、高易用的http server框架。而libevent官方也出了一个类似框架,出身更纯正一些的evhttp。

    但从个人使用来看易用性、稳定性、性能均超过evhttp。libevhtp在libevent的介绍是:A fast and flexible replacement for libevent’s httpd API。

    安装依赖库

    1
    
    apt-get install cmake openssl

    安装libevent

    1
    2
    3
    4
    5
    6
    
    wget --no-check-certificate "https://github.com/downloads/libevent/libevent/libevent-2.0.17-stable.tar.gz"
    tar zxvf libevent-2.0.17-stable.tar.gz
    cd libevent-2.0.17-stable
    ./configure
    make
    make install

    安装libevhtp。

    1
    2
    3
    4
    5
    6
    
    wget --no-check-certificate "https://github.com/ellzey/libevhtp/zipball/master" -O libevhtp.zip
    unzip libevhtp.zip
    cd ellzey-libevhtp-81ec5e2
    cmake .
    make 
    make install

    一个简单的示例,1分钟搭建多线程高性能HTTP服务器。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    
    //
    //  main.c
    //  test-evhtp
    //
    //  Created by Zhibiao Pan on 12-2-21.
    //  Copyright 2012. All rights reserved.
    //
    //  build:
    //    gcc -o evhtpd  -I/usr/include -I/usr/local/include -L/usr/lib -L/usr/local/lib 
    //      -levhtp -levent -levent_openssl -levent_pthreads -lssl -lcrypto main.c
    //
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdint.h>
    #include <errno.h>
     
    #include <evhtp.h>
     
    void
    testcb(evhtp_request_t * req, void * a);
     
    void
    testcb(evhtp_request_t * req, void * a) {
      evbuffer_add_reference(req->buffer_out, "foobar", 6, NULL, NULL);
      evhtp_send_reply(req, EVHTP_RES_OK);
    }
     
    int main (int argc, const char * argv[])
    {
      evbase_t *evbase = event_base_new();
      evhtp_t  *htp    = evhtp_new(evbase, NULL);
     
      evhtp_set_cb(htp, "/", testcb, NULL);   /* 设置回调函数 */
      evhtp_use_threads(htp, NULL, 4, NULL);  /* 设置4个线程 */
     
      /* 监听本地所有IP的8080端口, backlog为1024 */
      evhtp_bind_socket(htp, "0.0.0.0", 8080, 1024);
     
      /*  进入循环、监听连接,http server开始工作 */
      event_base_loop(evbase, 0);
     
      return 0;
    }

    买了两本书《编程原本》、《研究之美》

    2012年2月11日 没有评论

    原来高德纳还写过《研究之美》这样的小说体呢
    编程原本、研究之美

    分类: 阅读 标签:

    printf常用示例

    2011年12月24日 没有评论

    格式:
    %[flags] [width] [.precision] [{h | l | ll}]type

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    
    //  默认
    printf("|%f|\n", 34.12345);
    printf("|%d|\n", 654321);
    /*
     |34.123450|
     |654321|
     */
     
    // 右对齐,不足长度填空格
    printf("|%14.4f|\n", 34.12345);
    printf("|%14d|\n", 654321);
    /*
     |       34.1234|
     |        654321|
     */
     
     
    // 右对齐,不足长度填0
    printf("|%014.4f|\n", 34.12345);
    printf("|%014d|\n", 654321);
    /*
     |000000034.1234|
     |00000000654321|
     */
     
     
    // 左对齐,不足长度填空格
    printf("|%-14.4f|\n", 34.12345);
    printf("|%-14d|\n", 654321);
    /*
     |34.1234       |
     |654321        |
     */
     
     
    // size前缀
    // 注意:不同平台实现有一些差异,比如I32, I64不是通用的。h, l, ll是没有任何问题的
    printf("|%14hd|\n", (short int)654321);
    printf("|%14ld|\n", (long int)654321);
    printf("|%14ld|\n", (long unsigned int)654321);
    printf("|%14lld|\n", (long long)654321);
    /*
     |         -1039|
     |        654321|
     |        654321|
     |        654321|
     */
     
     
     
    const char *s = "---------------";  // s.length = 15
    int width = 10, len = 10;
    // 左对齐输出,width表示字符输出总宽度,len表示输出s的长度、不足填空格。
    // 若width = len < s.length 则可以实现输且只输width个字符
    printf("|%-*.*s|\n", width, len, s);
    // output:
    // |----------|
    分类: c++, tips 标签: ,

    C调用lua_pcall后的出栈问题

    2011年12月20日 没有评论

    Like lua_call, lua_pcall always removes the function and its arguments from the stack.

    在压入结果前,lua_pcall会删除栈中函数及其参数。故lua_pcall后调用lua_pop恢复栈时,只需弹出函数返回结果数即可。
    否则会引起栈不够用,或破环先前数据。

    分类: lua, tips, 技术 标签: , ,

    Alfred 扩展之查看本机IP

    2011年12月18日 没有评论

    新做了一个Alfred扩展,查看本机IP

    您可以直接下载安装:Locate IP Address.alfredextension

    Command

    1
    2
    3
    4
    
    # 执行代码如下
    IP=$(ifconfig en1 | grep -v inet6|grep inet | cut -d : -f 3 | cut -d " " -f 2)
    echo $IP | pbcopy
    echo $IP

    See More Extensions:

    http://jdfwarrior.tumblr.com/post/7380798414

    分类: tips 标签: , ,

    快速log2()的实现

    2011年12月13日 没有评论

    先在lua源码里看到:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    int luaO_log2 (unsigned int x) {
      static const lu_byte log_2[256] = {
        0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
        6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
        7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
        7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
        8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
        8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
        8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
        8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
      };
      int l = -1;
      while (x >= 256) { l += 8; x >>= 8; }
      return l + log_2[x];
    }

    后有人贴出链接,其int->float的思路确实很赞,里面涉及了float在内存如何分布。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    //
    // 这段代码可能也有移植问题,long在64位机是8字节的,
    // 移植性受到float内存分布的限制,但目前来说都是IEEE标准的
    typedef unsigned long uint32;
    typedef long int32;
     
    static inline int32 ilog2(uint32 x) {
    	return ilog2((float)x);
    }
     
    // integer log2 of a float
    static inline int32 ilog2(float x)
    {
    	uint32 ix = (uint32&)x;
    	uint32 exp = (ix >> 23) & 0xFF;
    	int32 log2 = int32(exp) - 127;
     
    	return log2;
    }
     
    // 汇编实现,简单直接,移植性欠缺一些
    static inline int32 ilog2_x86(uint32 x) 
    {
    	int32 retval;
    	__asm {
    		bsr eax, x
    		mov retval, eax
    	}
    	return retval;
    }
    分类: c++, 技术 标签: ,

    如果POST成功,要在POST方法后重定向网址,这样可以阻止用户通过刷新页面重复提交。

    2011年12月11日 没有评论

    http://en.wikipedia.org/wiki/Post/Redirect/Get

    这个细节确实很重要,一般可以防止无意重复数据提交

    分类: tips 标签: , ,