发布新日志

  • 用户体验量化方法

    2013-02-22 11:27:07


    • 有用性(useful:它表示设计的网站产品应当是有用的,而不应当局限于上级的条条框框去设计一些对用户来说根本毫无用处的东西;
    • 可找到性(findable:网站应当提供良好的导航和定位元素,使用户能很快的找到所需信息,并且知道自身所在的位置,不至于迷航;
    • 可获得性(accessible:它要求网站信息应当能为所有用户所获得,这个是专门针对于残疾人而言的,比如盲人,网站也要支持这种功能。
    • 满意度(desirable:是指网站元素应当满足用户的各种情感体验,这个是来源于情感设计的;
    • 可靠性(credible:是指网站的元素要是能够让用户所信赖的,要尽量设计和提供使用户充分信赖的组件;
    • 价值性(valuable:它是指网站要能盈利,而对于非赢利性网站,也要能促使实现预期目标。
  • svn分支的创建与合并

    2012-06-25 13:44:20

    转自http://blog.csdn.net/jixiuffff/archive/2010/05/13/5586858.aspx

    假如主干是这样的

    a---->b------>c----->d------------>e

    a b c d e 分别代表版本号

    突然发现项目在版本e时引入了一个bug ,要修改这个bug,可能需要一段时间才能完成  即如果在e处直接对代码进行修改,可能会影响其他人继续进行开发,一种办法就是copy e 的代码到另一个地方(创建一个分支) ,在这个分支上修正bug 待修正好bug 后,将其合并到主干中,继续进行开发

    a---->b------>c----->d------------>e-------->f ------------>g(主干继续进行正常开发)

                                                      |

                                                      |创建一个分支

                                                      V

                                                      e1 -------->f1-------->g1

    分支到达g1 时,修正了bug ,这时需要将分支合并(merge)到主干中,变成如下这种状态

    a---->b------>c----->d------------>e-------->f ------------>g  ------->   h-------->

                                                      |                                                  ^

                                                      |创建一个分支                                |

                                                      V                                                 |

                                                      e1 -------->f1--------->g1--------->

    g 和g1两个代码进行合并(可能需要解决冲突才能进行合并) 成为h 然后继续进行开发

    演示代码如下

    :假如所有的项目都放在svn://localhost/java中

    新建一个drp 项目 svn://localhost/java/drp

    1 在本地创建目

    jixiuf@jf /tmp/svnTmp $ mkdir drp/{trunk,branches,tag} 创建目录

     jixiuf@jf /tmp/svnTmp $touch drp/trunk/{hello.java,hello2.java,hello3.java} 创建几个文件

    jixiuf@jf /tmp/svnTmp $ tree 目录结构如下 jixiuf@jf /tmp/svnTmp $ tree
    .
    └── drp
        ├── branches
        ├── tag
        └── trunk
            ├── hello2.java
            ├── hello3.java
            └── hello.java
    4 directories, 3 files

    trunk :项目的主干目录,branches 为分支目录,

    然后 将项目提交到svn://localhost/java/drp中,也就是创建 svn://localhost/java/drp 项目

    jixiuf@jf /tmp/svnTmp $ svn import -m "create the project drp " drp/ svn://localhost/java/drp

    删除本的的drp 目录,然后从远程下载

    jixiuf@jf /tmp/svnTmp $ rm drp/ -rf

    jixiuf@jf /tmp/svnTmp $ svn co svn://localhost/java/drp  

    jixiuf@jf /tmp/svnTmp $ cd drp

    jixiuf@jf /tmp/svnTmp/drp $ svn log  看一看日志, 在版本84时创建 了项目drp
    ------------------------------------------------------------------------
    r84 | jixiuf | 2010-05-13 14:44:21 +0800 (四, 2010-05-13) | 1 行

    create the project drp             ------------------------------这个是运行
    svn import -m "create the project drp " drp/ svn://localhost/java/drp  时添加的日志信息
    ------------------------------------------------------------------------

    此时的r84 就是上文说的e状态(此时发现了一个bug ,决定创建一个分支以解决这个bug )

    jixiuf@jf /tmp/svnTmp/drp $ svn cp  -m "创建一个分支" svn://localhost/java/drp/trunk/     svn://localhost/java/drp/branches/debugDrp1.0

    提交后的版本为 85。

    创建一个分支并命名为debugDrp1.0

    因为本地的drp 是r84 版本的,而服务器端的是r85版的,需要更新一下

    jixiuf@jf /tmp/svnTmp/drp $ svn up   up 是update 的简写(svn update 命令也是一样)

    jixiuf@jf /tmp/svnTmp/drp $ tree 现在看一下目录结构(会发现 branches 目录下多了个debugDrp1.0目录 ,且其目录下的文件 就是trunk 目录下的文件,这个过程叫做创建分支)
    .
    ├── branches
    │   └── debugDrp1.0
    │       ├── hello2.java
    │       ├── hello3.java
    │       └── hello.java
    ├── tag
    └── trunk
        ├── hello2.java
        ├── hello3.java
        └── hello.java
    4 directories, 6 files

    jixiuf@jf /tmp/svnTmp/drp $ svn log   看一下日志记录
    ------------------------------------------------------------------------
    r85 | jixiuf | 2010-05-13 14:51:35 +0800 (四, 2010-05-13) | 2 行

    创建一个分支

    ------------------------------------------------------------------------
    r84 | jixiuf | 2010-05-13 14:44:21 +0800 (四, 2010-05-13) | 1 行

    create the project drp
    ------------------------------------------------------------------------

    假如出现bug 的文件 是hello.java

    在branches/debugDrp1.0 目录下修改hello.java

    假如 这样就修改了bug

    jixiuf@jf /tmp/svnTmp/drp $ echo "bug已修正" >>branches/debugDrp1.0/hello.java

    就是向文件 hello.java 的末尾添加了  bug已修正 这几个字

    为了显示如果解决冲空,在主干上也同时向hello.java 中添加一些信息,以制造冲突


    jixiuf@jf /tmp/svnTmp/drp $ echo "在主干中向hello.java 添加的信息,以便产生冲突" >>  trunk/hello.java

    现在向服务器提交

    jixiuf@jf /tmp/svnTmp/drp $ svn ci -m "modify the  hello.java in branches/debugDrp1.0 and trunk"
    正在发送       branches/debugDrp1.0/hello.java
    正在发送       trunk/hello.java
    传输文件数据..
    提交后的版本为 86。

    jixiuf@jf /tmp/svnTmp/drp $ svn up   更新到最新版本86

    现在 bug 已经在 branches/debugDrp1.0 中进行了修正,并且在这个过程中主干trunk 中也对文件 hello.java 进行了修改,

    修正了bug 的人发现,主干中已经有了有些新变化,需要将这些新变化添加进来,

    也就是将主干中的东西合并到分支 中来,

    =============================================================

    jixiuf@jf /tmp/svnTmp/drp $ cd branches/debugDrp1.0/    到分支debugDrp1.0的根目录

    然后看一下日志

    jixiuf@jf /tmp/svnTmp/drp/branches/debugDrp1.0 $ svn log   查看全部日志,(注意 分支 的日志包括创建分支之前的trunk 的日志,和创建分支后在分支上发展的独立的日志 ,现在查看全部目录 )
    ------------------------------------------------------------------------
    r86 | jixiuf | 2010-05-13 15:13:23 +0800 (四, 2010-05-13) | 1 行
    modify the  hello.java in branches/debugDrp1.0 and trunk
    ------------------------------------------------------------------------
    r85 | jixiuf | 2010-05-13 14:51:35 +0800 (四, 2010-05-13) | 2 行
    创建一个分支
    ------------------------------------------------------------------------
    r84 | jixiuf | 2010-05-13 14:44:21 +0800 (四, 2010-05-13) | 1 行
    create the project drp
    ------------------------------------------------------------------------

    jixiuf@jf /tmp/svnTmp/drp/branches/debugDrp1.0 $ svn log --stop-on-copy (这里查看他建 分支之后,在分支上独立发展的日志,不包括主干上的日志 )
    ------------------------------------------------------------------------
    r86 | jixiuf | 2010-05-13 15:13:23 +0800 (四, 2010-05-13) | 1 行
    modify the  hello.java in branches/debugDrp1.0 and trunk
    ------------------------------------------------------------------------
    r85 | jixiuf | 2010-05-13 14:51:35 +0800 (四, 2010-05-13) | 2 行
    创建一个分支
    ------------------------------------------------------------------------

    jixiuf@jf /tmp/svnTmp/drp/branches/debugDrp1.0 $ svn merge svn://localhost/java/drp/trunk     将trunk 目录合并到当前目录
    在 “hello.java” 中发现冲突。
    选择: (p) 推迟,(df) 显示全部差异,(e) 编辑,
            (mc) 我的版本, (tc) 他人的版本,
            (s) 显示全部选项:
    结果出现以下提示说hello.java 出现冲突,然后它给提供了几种解决方案,

            p 推迟,(会在hello.java 同一目录产生多个helllo.java 版本的文件 ,有r84  版的,这个是发生冲突前的版本,以及分支上的版本,trunk 上的版本,及一个进行了diff 比较后生成的) 需要手动修改hello.java 然后删除其他几个版本,然后svn ci 重新提交

            df  只是显示文件 的差异,e 编辑文件 直接解决冲突

    此时我选e

    会出现一个编辑器,其中的内容如下

        <<<<<<< .working
      bug已修正
       =======
       在主干中向hello.java 添加的信息,以便产生冲突
       >>>>>>> .merge-right.r86
    ~                                                                                                                           
    ~

    意思是说当前工作( .working)的目录中hello.java 有  "bug已修正" 这一行

    .merge-right.r86文件(也就是主干中的hello.java 中有这一行)  "   在主干中向hello.java 添加的信息,以便产生冲突"

    合并文件为

       bug已修正
       在主干中向hello.java 添加的信息,以便产生冲突

    冲突解决 后选r                                   

    于是就将trunk 合并到了当前分支 ,查看一个

    jixiuf@jf /tmp/svnTmp/drp/branches/debugDrp1.0 $ cat hello.java
    bug已修正
    在主干中向hello.java 添加的信息,以便产生冲突

    ====================================================

    另外,虽然分支中的代码已经修正了bug 并且拥有了trunk 中最新添加 的代码

    还要将它合并到trunk 中去

    jixiuf@jf /tmp/svnTmp/drp/branches/debugDrp1.0 $ cd /tmp/svnTmp/drp/trunk/

    jixiuf@jf /tmp/svnTmp/drp/trunk $ svn merge svn://localhost/java/drp/branches/debugDrp1.0

    同样,解决冲突后就可以在trunk 中继续进行代码开发

    a---->b------>c----->d------------>e-------->f ------------>g  ------->   h-------->

                                                      |                                                  ^

                                                      |创建一个分支                                |

                                                      V                                                 |

                                                      e1 -------->f1--------->g1--------->

  • [转]什么是好的团队合作精神

    2012-02-23 11:47:29

    1.  不挑剔责难、只解决困难;

    2.  重目标、轻过程;重结果、轻形式;

    3.  尊重个体差异、成就群体优异;

    4.  职位分工是混凝土、团队文化是粘合剂;

    5.  谨记:尺有所短、寸有所长;

    6.  不为自己失职找借口、只为别人工作留接口;

    7.  搞小团体的唯一作用就是毁了大团队。

  • vim 编码方式(encoding、fileencoding、fileencodings、termencoding介绍)

    2012-02-21 13:35:29

    vim 编码方式的设置

    和所有的流行文本编辑器一样,Vim 可以很好的编辑各种字符编码的文件,这当然包括UCS-2、UTF-8 等流行的 Unicode 编码方式。然而不幸的是,和很多来自 Linux 世界的软件一样,这需要你自己动手设置。

    Vim 有四个跟字符编码方式有关的选项,encoding、fileencoding、fileencodings、termencoding (这些选项可能的取值请参考 Vim 在线帮助 :help encoding-names),它们的意义如下:

    * encoding: Vim 内部使用的字符编码方式,包括 Vim 的 buffer (缓冲区)、菜单文本、消息文本等。默认是根据你的locale选择.用户手册上建议只在 .vimrc 中改变它的值,事实上似乎也只有在.vimrc 中改变它的值才有意义。你可以用另外一种编码来编辑和保存文件,如你的vim的encoding为utf-8,所编辑的文件采用cp936编码,vim会 自动将读入的文件转成utf-8(vim的能读懂的方式),而当你写入文件时,又会自动转回成cp936(文件的保存编码).

    * fileencoding: Vim 中当前编辑的文件的字符编码方式,Vim 保存文件时也会将文件保存为这种字符编码方式 (不管是否新文件都如此)。

    * fileencodings: Vim自动探测fileencoding的顺序列表,启动时会按照它所列出的字符编码方式逐一探测即将打开的文件的字符编码方式,并且将 fileencoding 设置为最终探测到的字符编码方式。因此最好将Unicode 编码方式放到这个列表的最前面,将拉丁语系编码方式 latin1 放到最后面。

    * termencoding: Vim 所工作的终端 (或者 Windows 的 Console 窗口) 的字符编码方式。如果vim所在的term与vim编码相同,则无需设置。如其不然,你可以用vim的termencoding选项将自动转换成term 的编码.这个选项在 Windows 下对我们常用的 GUI 模式的 gVim 无效,而对 Console 模式的Vim 而言就是 Windows 控制台的代码页,并且通常我们不需要改变它。

    好了,解释完了这一堆容易让新手犯糊涂的参数,我们来看看 Vim 的多字符编码方式支持是如何工作的。

    1. Vim 启动,根据 .vimrc 中设置的 encoding 的值来设置 buffer、菜单文本、消息文的字符编码方式。

    2. 读取需要编辑的文件,根据 fileencodings 中列出的字符编码方式逐一探测该文件编码方式。并设置 fileencoding 为探测到的,看起来是正确的 (注1) 字符编码方式。

    3. 对比 fileencoding 和 encoding 的值,若不同则调用 iconv 将文件内容转换为encoding 所描述的字符编码方式,并且把转换后的内容放到为此文件开辟的 buffer 里,此时我们就可以开始编辑这个文件了。注意,完成这一步动作需要调用外部的 iconv.dll(注2),你需要保证这个文件存在于 $VIMRUNTIME 或者其他列在 PATH 环境变量中的目录里。

    4. 编辑完成后保存文件时,再次对比 fileencoding 和 encoding 的值。若不同,再次调用 iconv 将即将保存的 buffer 中的文本转换为 fileencoding 所描述的字符编码方式,并保存到指定的文件中。同样,这需要调用 iconv.dll由于 Unicode 能够包含几乎所有的语言的字符,而且 Unicode 的 UTF-8 编码方式又是非常具有性价比的编码方式 (空间消耗比 UCS-2 小),因此建议 encoding 的值设置为utf-8。这么做的另一个理由是 encoding 设置为 utf-8 时,Vim 自动探测文件的编码方式会更准确 (或许这个理由才是主要的 ;)。我们在中文 Windows 里编辑的文件,为了兼顾与其他软件的兼容性,文件编码还是设置为 GB2312/GBK 比较合适,因此 fileencoding 建议设置为 chinese (chinese 是个别名,在 Unix 里表示 gb2312,在 Windows 里表示cp936,也就是 GBK 的代码页)。


  • runkit的使用(转)

    2012-02-06 16:05:07

    转自:http://shendongming.blog.51cto.com/458802/759403

    函数的重定义

    runkit_function_redefine

    <?php
    function testme
    () {
      echo 
    "Original Testme Implementation\n"
    ;
    }
    testme
    ();
    runkit_function_redefine('testme','','echo "New Testme Implementation\n";'
    );
    testme
    ();
    ?>

     

    方法的重定义

     

    runkit_method_redefine

     

    <?php
    class Example {
        function 
    foo() {
            return 
    "foo!\n";
        }
    }

    // create an Example object
    $e = new Example();

    // output Example::foo() (before redefine)
    echo "Before: " $e->foo();

    // Redefine the 'foo' method
    runkit_method_redefine(
        
    'Example',
        
    'foo',
        
    '',
        
    'return "bar!\n";',
        
    RUNKIT_ACC_PUBLIC
    );

    // output Example::foo() (after redefine)
    echo "After: " $e->foo();
    ?>

     

    方法的重命名

     

    runkit_method_rename

     

    <?php
    class Example {
        function 
    foo() {
            return 
    "foo!\n";
        }
    }

    // Rename the 'foo' method to 'bar'
    runkit_method_rename(
        
    'Example',
        
    'foo',
        
    'bar'
    );

    // output renamed function
    echo Example::bar();
    ?>
  • 利用PHPUnit单元测试对PHP代码进行检查(转)

    2012-02-06 15:42:41

    这篇文写的很不错,首先是文笔,文章结构前呼后应,再者是内容,比较精辟,推荐
    ———————————————————————————
     
    测试驱动的开发和单元测试是确保代码在经过修改和重大调整之后依然能如我们期望的一样工作的最新方法。在本文中,您将学习到如何在模块、数据库和用户界面(UI)层对自己的 PHP 代码进行单元测试。

    现在是凌晨 3 点。我们怎样才能知道自己的代码依然在工作呢?

        Web 应用程序是 24x7 不间断运行的,因此我的程序是否还在运行这个问题会在晚上一直困扰我。单元测试已经帮我对自己的代码建立了足够的信心 —— 这样我就可以安稳地睡个好觉了。

        单元测试 是一个为代码编写测试用例并自动运行这些测试的框架。测试驱动的开发 是一种单元测试方法,其思想是应该首先编写测试程序,并验证这些测试可以发现错误,然后才开始编写需要通过这些测试的代码。当所有测试都通过时,我们开发 的特性也就完成了。这些单元测试的价值是我们可以随时运行它们 —— 在签入代码之前,重大修改之后,或者部署到正在运行的系统之后都可以。

    PHP 单元测试

        对于 PHP 来说,单元测试框架是 PHPUnit2。可以使用 PEAR 命令行作为一个 PEAR 模块来安装这个系统:% pear install PHPUnit2

        在安装这个框架之后,可以通过创建派生于 PHPUnit2_Framework_TestCase 的测试类来编写单元测试。

    模块单元测试

        我发现开始单元测试最好的地方是在应用程序的业务逻辑模块中。我使用了一个简单的例子:这是一个对两个数字进行求和的函数。为了开始测试,我们首先编写测试用例,如下所示。


    清单 1. TestAdd.php

    <?php
    require_once 'Add.php';
    require_once 'PHPUnit2/Framework/TestCase.php';

    class TestAdd extends PHPUnit2_Framework_TestCase
    {
    function test1() { $this->assertTrue( add( 1, 2 ) == 3 ); }
    function test2() { $this->assertTrue( add( 1, 1 ) == 2 ); }
    }
    ?>

        这个 TestAdd 类有两个方法,都使用了 test 前缀。每个方法都定义了一个测试,这个测试可以与清单 1 一样简单,也可以十分复杂。在本例中,我们在第一个测试中只是简单地断定 1 加 2 等于 3,在第二个测试中是 1 加 1 等于 2。

        PHPUnit2 系统定义了 assertTrue() 方法,它用来测试参数中包含的条件值是否为真。然后,我们又编写了 Add.php 模块,最初让它产生错误的结果。


    清单 2. Add.php

    <?php
    function add( $a, $b ) { return 0; }
    ?>

    现在运行单元测试时,这两个测试都会失败。


    清单 3. 测试失败

    % phpunit TestAdd.php
    PHPUnit 2.2.1 by Sebastian Bergmann.

    FF

    Time: 0.0031270980834961
    There were 2 failures:
    1) test1(TestAdd)

    2) test2(TestAdd)


    FAILURES!!!
    Tests run: 2, Failures: 2, Errors: 0, Incomplete Tests: 0.

    现在我知道这两个测试都可以正常工作了。因此,可以修改 add() 函数来真正地做实际的事情了。

    <?php
    function add( $a, $b ) { return $a+$b; }
    ?>

    现在这两个测试都可以通过了。


    清单 4. 测试通过

    % phpunit TestAdd.php
    PHPUnit 2.2.1 by Sebastian Bergmann.

    ..

    Time: 0.0023679733276367

    OK (2 tests)
    %

        尽管这个测试驱动开发的例子非常简单,但是我们可以从中体会到它的思想。我们首先创建了测试用例,并且有足够多的代码让这个测试运行起来,不过结果是错误的。然后我们验证测试的确是失败的,接着实现了实际的代码使这个测试能够通过。

        我发现在实现代码时我会一直不断地添加代码,直到拥有一个覆盖所有代码路径的完整测试为止。在本文的最后,您会看到有关编写什么测试和如何编写这些测试的一些建议。

    数据库测试

        在进行模块测试之后,就可以进行数据库访问测试了。数据库访问测试 带来了两个有趣的问题。首先,我们必须在每次测试之前将数据库恢复到某个已知点。其次,要注意这种恢复可能会对现有数据库造成破坏,因此我们必须对非生产数据库进行测试,或者在编写测试用例时注意不能影响现有数据库的内容。

    数据库的单元测试是从数据库开始的。为了阐述这个问题,我们需要使用下面的简单模式。


    清单 5. Schema.sql

    DROP TABLE IF EXISTS authors;
    CREATE TABLE authors (
    id MEDIUMINT NOT NULL AUTO_INCREMENT,
    name TEXT NOT NULL,
    PRIMARY KEY ( id )
    );

    清单 5 是一个 authors 表,每条记录都有一个相关的 ID。

    接下来,就可以编写测试用例了。


    清单 6. TestAuthors.php

    <?php
    require_once 'dblib.php';
    require_once 'PHPUnit2/Framework/TestCase.php';

    class TestAuthors extends PHPUnit2_Framework_TestCase
    {
    function test_delete_all() {
    $this->assertTrue( Authors::delete_all() );
    }
    function test_insert() {
    $this->assertTrue( Authors::delete_all() );
    $this->assertTrue( Authors::insert( 'Jack' ) );
    }
    function test_insert_and_get() {
    $this->assertTrue( Authors::delete_all() );
    $this->assertTrue( Authors::insert( 'Jack' ) );
    $this->assertTrue( Authors::insert( 'Joe' ) );
    $found = Authors::get_all();
    $this->assertTrue( $found != null );
    $this->assertTrue( count( $found ) == 2 );
    }
    }
    ?>

        这组测试覆盖了从表中删除作者、向表中插入作者以及在验证作者是否存在的同时插入作者等功能。这是一个累加的测试,我发现对于寻找错误来说这非常有用。观 察一下哪些测试可以正常工作,而哪些测试不能正常工作,就可以快速地找出哪些地方出错了,然后就可以进一步理解它们之间的区别。

    最初产生失败的 dblib.php PHP 数据库访问代码版本如下所示。

    清单 7. dblib.php

    <?php
    require_once('DB.php');

    class Authors
    {
    public static function get_db()
    {
    $dsn = 'mysql://root:password@localhost/unitdb';
    $db =& DB::Connect( $dsn, array() );
    if (PEAR::isError($db)) { die($db->getMessage()); }
    return $db;
    }
    public static function delete_all()
    {
    return false;
    }
    public static function insert( $name )
    {
    return false;
    }
    public static function get_all()
    {
    return null;
    }
    }
    ?>

    对清单 8 中的代码执行单元测试会显示这 3 个测试全部失败了:


    清单 8. dblib.php

    % phpunit TestAuthors.php
    PHPUnit 2.2.1 by Sebastian Bergmann.

    FFF

    Time: 0.007500171661377
    There were 3 failures:
    1) test_delete_all(TestAuthors)

    2) test_insert(TestAuthors)

    3) test_insert_and_get(TestAuthors)


    FAILURES!!!
    Tests run: 3, Failures: 3, Errors: 0, Incomplete Tests: 0.
    %

        现在我们可以开始添加正确访问数据库的代码 —— 一个方法一个方法地添加 —— 直到所有这 3 个测试都可以通过。最终版本的 dblib.php 代码如下所示。


    清单 9. 完整的 dblib.php

    <?php
    require_once('DB.php');

    class Authors
    {
    public static function get_db()
    {
    $dsn = 'mysql://root:password@localhost/unitdb';
    $db =& DB::Connect( $dsn, array() );
    if (PEAR::isError($db)) { die($db->getMessage()); }
    return $db;
    }
    public static function delete_all()
    {
    $db = Authors::get_db();
    $sth = $db->prepare( 'DELETE FROM authors' );
    $db->execute( $sth );
    return true;
    }
    public static function insert( $name )
    {
    $db = Authors::get_db();
    $sth = $db->prepare( 'INSERT INTO authors VALUES (null,?)' );
    $db->execute( $sth, array( $name ) );
    return true;
    }
    public static function get_all()
    {
    $db = Authors::get_db();
    $res = $db->query( "SELECT * FROM authors" );
    $rows = array();
    while( $res->fetchInto( $row ) ) { $rows []= $row; }
    return $rows;
    }
    }
    ?>

    HTML 测试

        对整个 PHP 应用程序进行测试的下一个步骤是对前端的超文本标记语言(HTML)界面进行测试。要进行这种测试,我们需要一个如下所示的 Web 页面。


    图 1. 测试 Web 页面
    测试 Web 页面

    这个页面对两个数字进行求和。为了对这个页面进行测试,我们首先从单元测试代码开始入手。


    清单 10. TestPage.php

    <?php
    require_once 'HTTP/Client.php';
    require_once 'PHPUnit2/Framework/TestCase.php';

    class TestPage extends PHPUnit2_Framework_TestCase
    {
    function get_page( $url )
    {
    $client = new HTTP_Client();
    $client->get( $url );
    $resp = $client->currentResponse();
    return $resp['body'];
    }
    function test_get()
    {
    $page = TestPage::get_page( 'http://localhost/unit/add.php' );
    $this->assertTrue( strlen( $page ) > 0 );
    $this->assertTrue( preg_match( '/<html>/', $page ) == 1 );
    }
    function test_add()
    {
    $page = TestPage::get_page( 'http://localhost/unit/add.php?a=10&b=20' );
    $this->assertTrue( strlen( $page ) > 0 );
    $this->assertTrue( preg_match( '/<html>/', $page ) == 1 );
    preg_match( '/<span id="result">(.*?)<\/span>/', $page, $out );
    $this->assertTrue( $out[1]=='30' );
    }
    }
    ?>

        这个测试使用了 PEAR 提供的 HTTP Client 模块。我发现它比内嵌的 PHP Client URL Library(CURL)更简单一点儿,不过也可以使用后者。

        有一个测试会检查所返回的页面,并判断这个页面是否包含 HTML。第二个测试会通过将值放到请求的 URL 中来请求计算 10 和 20 的和,然后检查返回的页面中的结果。

    这个页面的代码如下所示。


    清单 11. TestPage.php

    <html><body><form>
    <input type="text" name="a" value="<?php echo($_REQUEST['a']); ?>" /> +
    <input type="text" name="b" value="<?php echo($_REQUEST['b']); ?>" /> =
    <span id="result"><?php echo($_REQUEST['a']+$_REQUEST['b']); ?></span>
    <br/>
    <input type="submit" value="Add" />
    </form></body></html>

        这个页面相当简单。两个输入域显示了请求中提供的当前值。结果 span 显示了这两个值的和。<span> 标记标出了所有区别:它对于用户来说是不可见的,但是对于单元测试来说却是可见的。因此单元测试并不需要复杂的逻辑来找到这个值。相反,它会检索一个特定 <span> 标记的值。这样当界面发生变化时,只要 span 存在,测试就可以通过。

        与前面一样,首先编写测试用例,然后创建一个失败版本的页面。我们对失败情况进行测试,然后修改页面的内容使其可以工作。结果如下:


    清单 12. 测试失败情况,然后修改页面

    % phpunit TestPage.php
    PHPUnit 2.2.1 by Sebastian Bergmann.

    ..

    Time: 0.25711488723755

    OK (2 tests)
    %

    这两个测试都可以通过,这就意味着测试代码可以正常工作。

        在对这段代码运行测试时,所有的测试都可以没有问题地运行,这样我们就可以知道自己的代码可以正确工作了。

    不过对 HTML 前端的测试有一个缺陷:JavaScript。超文本传输协议(HTTP)客户机代码对页面进行检索,但是却没有执行 JavaScript。因此如果我们在 JavaScript. 中有很多代码,就必须创建用户代理级的单元测试。我发现实现这种功能的最佳方法是使用 Microsoft® Internet Explorer® 内嵌的自动化层功能。通过使用 PHP 编写的 Microsoft Windows® 脚本,可以使用组件对象模型(COM)接口来控制 Internet Explorer,让它在页面之间进行导航,然后使用文档对象模型(DOM)方法在执行特定用户操作之后查找页面中的元素。

        这是我了解的对前端 JavaScript. 代码进行单元测试的惟一一种方法。我承认它并不容易编写和维护,这些测试即使在对页面稍微进行改动时也很容易遭到破坏。

    编写哪些测试以及如何编写这些测试

    在编写测试时,我喜欢覆盖以下情况:

    所有正面测试
    这组测试可以确保所有的东西都如我们期望的一样工作。
    所有负面测试
    逐一使用这些测试,从而确保每个失效或异常情况都被测试到了。
    正面序列测试
    这组测试可以确保按照正确顺序的调用可以像我们期望的一样工作。
    负面序列测试
    这组测试可以确保当不按正确顺序进行调用时就会失败。
    负载测试
    在适当情况下,可以执行一小组测试来确定这些测试的性能在我们期望的范围之内。例如,2,000 次调用应该在 2 秒之内完成。
    资源测试
    这些测试确保应用编程接口(API)可以正确地分配并释放资源 —— 例如,连续几次调用打开、写入以及关闭基于文件的 API,从而确保没有文件依然是被打开的。
    回调测试
    对于具有回调方法的 API 来说,这些测试可以确保如果没有定义回调函数,代码可以正常运行。另外,这些测试还可以确保在定义了回调函数但是这些回调函数操作有误或产生异常时,代码依然可以正常运行。

    这是有关单元测试的几点想法。有关如何编写单元测试,我也有几点建议:

    不要使用随机数据
    尽管在一个界面中产生随机数据看起来貌似一个好主意,但是我们要避免这样做,因为这些数据会变得非常难以调试。如果数据是在每次调用时 随机生成的,那么就可能产生一次测试时出现了错误而另外一次测试却没有出现错误的情况。如果测试需要随机数据,可以在一个文件中生成这些数据,然后每次运 行时都使用这个文件。采用这种方法,我们就获得了一些 “噪音” 数据,但是仍然可以对错误进行调试。
    分组测试
    我们很容易累积起数千个测试,需要几个小时才能执行完。这没什么问题,但是对这些测试进行分组使我们可以快速运行某组测试并对主要关注的问题进行检查,然后晚上运行完整的测试。
    编写稳健的 API 和稳健的测试
    编写 API 和测试时要注意它们不能在增加新功能或修改现有功能时很容易就会崩溃,这一点非常重要。这里没有通用的绝招,但是有一条准则是那些 “振荡的” 测试(一会儿失败,一会儿成功,反复不停的测试)应该很快地丢弃。

    结束语

    单元测试对于工程师来说意义重大。它们是敏捷开发过程(这个过程非常强调编码的作用,因为文档需要一些证据证明代码是按照规范进行工作的)的一个基础。单元测试就提供了这种证据。这个过程从单元测试开始入手,这定义了代码应该 实现但目前尚未 实现的功能。因此,所有的测试最初都会失败。然后当代码接近完成时,测试就通过了。当所有测试全部通过时,代码也就变得非常完善了。

    我从来没有在不使用单元测试的情况下编写大型代码或修改大型或复杂的代码块。我通常都是在修改代码之前就为现有代码编写了单元测试,这样可以确保自 己清楚在修改代码时破坏了什么(或者没有破坏什么)。这为我对自己提供给客户的代码提供了很大的信心,相信它们正在正确运行 —— 即便是在凌晨 3 点。


  • nosql数据库和redis

    2011-06-21 20:23:32

    原文:http://www.iteye.com/topic/524977

    我的摘要:

    1.什么是关系型数据库

    关系型数据库是支持采用了关系模型来组织数据的数据库,简单来说,关系模型就是指二维表模型,以下是关系模型中的常用概念:

    关系:可以理解为一张二维表,每个关系都具有一个关系名,就是通常说的表名。

    元组:可以理解为二维表中的一行,在数据库中经常被称为记录。

    属性:可以理解为二维表中的一列,在数据库中经常被称为字段。

    域:属性的取值范围,也就是数据库中某一列的取值限制。

    关键字:一组可以唯一标识元组的属性。数据库中常称为主键,由一个或多个列组成。

    关系模式:指对关系的描述,其格式为:关系名(属性1,属性2,…,属性N)。在数据库中通常称为表结构

    优点:容易理解、易于维护、使用方便;

    2.什么是NoSql数据库,为什么使用NoSql数据库

    相对于关系型数据库(sqlserver、oracle、mysql……),nosql就是非关系数据库

    随着web2.0的发展,SNS等大型高并发应用对超大量数据、高发量、可扩展性要求越来越高,关系型数据库已经无法应付,并且关系型数据库的一些特性和优点(比如事物一致性、读写实时性、多表关联查询等),在这类应用中也得不到展现。

    关系数据库在这些应用场景下显得不那么合适了,为了解决这类问题的非关系数据库应运而生,现在这两年,各种各样非关系数据库,特别是键值数据库(Key-Value Store DB)风起云涌: Redis,Tokyo Cabinet,Cassandra,Voldemort,MongoDB,Dynomite,HBase,CouchDB,Hypertable, Riak,Tin, Flare, Lightcloud, KiokuDB,Scalaris, Kai, ThruDB,  ......

    3.Redis简介
    Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过10万次读写操作,是我知道的性能最快的Key-Value DB。

    Redis的出色之处不仅仅是性能,Redis最大的魅力是支持保存List链表和Set集合的数据结构,而且还支持对List进行各种操作,例如从List两端push和pop数据,取List区间,排序等等,对Set支持各种集合的并集交集操作,此外单个value的最大限制是1GB,不像memcached只能保存1MB的数据,因此Redis可以用来实现很多有用的功能,比方说用他的List来做FIFO双向链表,实现一个轻量级的高性能消息队列服务,用他的Set可以做高性能的tag系统等等。另外Redis也可以对存入的Key-Value设置expire时间,因此也可以被当作一个功能加强版的memcached来用。

    Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,并且它没有原生的可扩展机制,不具有scale(可扩展)能力,要依赖客户端来实现分布式读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。目前使用Redis的网站有github,Engine Yard。

  • redis-cli的使用

    2011-06-15 10:16:13

    TYPE key — 用来获取某key的类型 
    KEYS pattern — 匹配所有符合模式的key,比如KEYS * 就列出所有的key了,当然,复杂度O(n) 
    RANDOMKEY - 返回随机的一个key 
    RENAME oldkey newkey — 改变key的名字 
    SELECT: ./redis-cli select 6 
    # 表示切换到数据库6  

    SET: ./redis-cli -n 0 set nid 123456789 
    # 表示向数据库0中插入字符串key为nid,value值为123456789  

    GET: ./redis-cli -n 0 get nid 或者 ./redis-cli get nid 
    # 表示获取数据库为0,key为nid的字符串,在不指定数据编号的情况下,默认连接的是0数据库,所以可以省略-n参数 

    GETSET: ./redis-cli getset nid 987654321 
    # 表示返回指定key的原始值,并指定一个新值给他 

    MGET: ./redis-cli mget nid uid … 
    # 表示获取多个key的值 
       
    SETNX: ./redis-cli setnx nnid 888888 
    # 表示当一个指定的key不存在时,设置这个key指定的value,如果存在,则设置不成功 

    SETEX: ./redis-cli setex nid 5 666666 
    # 表示设置一个key指定的value保存5秒后失效,设置key/value的有效期 

    MSET: ./redis-cli mset nid0001 "0001" nid0002 "0002" nid0003 "0003"
    # 表示多键值对的数据保存 

    INCR: ./redis-cli incr count 
    # 表示对给定key的value进行递增(+1)的操作,当然value必须是一个integer 

    INCRBY: ./redis-cli incrby count 5 
    # 表示对给定key的value进行指定步长的递增操作 

    DECR: ./redis-cli decr count 
    # 表示对给定key的value进行递减(-1)的操作 

    DECRBY: ./redis-cli decrby count 7 
    # 表示对给定key的value进行指定步长的递减操作 

    APPEND: ./redis-cli append content "bad"   或者  ./redis-cli append content "good"
    # 表示追加一个value到指定的key中,如果key不存在,则新建key 

    SUBSTR: ./redis-cli substr content 0 4 
    # 表示返回指定key的value的部分字符串

    # 列表操作,精华 
    RPUSH key string — 将某个值加入到一个key列表末尾 
    LPUSH key string — 将某个值加入到一个key列表头部 
    LLEN key — 列表长度 
    LRANGE key start end — 返回列表中某个范围的值,相当于mysql里面的分页查询那样 
    LTRIM key start end — 只保留列表中某个范围的值 
    LINDEX key index — 获取列表中特定索引号的值,要注意是O(n)复杂度 
    LSET key index value — 设置列表中某个位置的值 
    RPOP key 

    # 集合操作 
    SADD key member — 增加元素 
    SREM key member — 删除元素 
    SCARD key — 返回集合大小 
    SISMEMBER key member — 判断某个值是否在集合中 
    SINTER key1 key2 ... keyN — 获取多个集合的交集元素 
    SMEMBERS key — 列出集合的所有元素 

     

  • Google Protocol Buffer

    2011-03-14 21:36:38

    Protocol Buffer是 Google 公司針對結構化的數據制定的一個比 XML 傳輸速度更快的一種標記文本格式, 它能使用 Google 已經公布出來的一些源程式及 API 進行快速處理封裝, 并且進行二進制序列化後內容會變得非常少,解封包的速度也非常快. 在應用程序的參數設定/配置 ,簡單數據或者固定格式數據的封裝傳送上,它會比XML方便應用. 當然, 畢竟XML已經成為了多種行業標準的編寫工具, Protocol Buffer 還只是google 公司內部使用的一個比較優秀的數據處理辦法, 在通用性上肯定會有天差地別. Protocol Buffer 在處理大量未知結構的內容, 或者對指定結構的數據進行 顯示格式化時會比較吃力. 因為我目前致少還沒看到它在這方面提供的功能.下面是一段  Protocol Buffer 對指定結構數據的定義示例:

    message Person {  

        required string name = 1; 

        required int32 id = 2; 

        optional string email = 3; 

     

        enum PhoneType { 

            MOBILE = 0; 

            HOME = 1; 

            WORK = 2; 

        } 

     

        message PhoneNumber { 

           required string number = 1; 

           optional PhoneType type = 2 [default = HOME]; 

        } 

        repeated PhoneNumber phone = 4; 

    }

    下面是對上述數據結構進行調用/輸出/序列化 的C++程序示例:

    Person person; 

    person.set_name("John Doe"); 

    person.set_id(1234); 

    person.set_email("jdoe@example.com"); 

    fstream output("myfile", ios::out | ios::binary); 

    person.SerializeToOstream(&output);

    輸入/以字符串導入的C++程序示例:

    Person person; 

    person.set_name("John Doe"); 

    person.set_id(1234); 

    person.set_email("jdoe@example.com"); 

    fstream output("myfile", ios::out | ios::binary); 

    person.SerializeToOstream(&output);

    從上面的例子我們可以看到, Protocol Buffer實際上就是 C++裡面的一個結構型, 所以它的運行速度比起 XML文件會快上非常大的倍數.  因為XML 需要從文件中讀取出字符串,再轉譯成 XML 文檔對象結構模型, 然後再從XML 文檔對象結構模型中讀取出指定節點的字符串, 最後再將這個字符串轉換成指定類型的變量, 這樣繁複的處理, 將大大消耗 CPU 資源. 而Protocol Buffer只需要簡單地將一個二進制序列, 按照指定的格式讀取到 C++ 結構類型中去就行了.


  • 网络传输时间

    2011-03-10 14:12:51

    长1Km。10Mbps的基带总线LAN,信号传播速度为200M/us,计算一个1000比特的帧从发送到接收结束的最大时间是多少

    发送和接收需要时间,速度是10Mbps;传播也需要时间,速度200M/us;不过第一个比特到达接收端时,1000比特还没发送完,所以不是 (帧发送时间+传播时间+帧接收时间),因为中间有重叠。

    开始之后1000/10Mbps = 100us,帧的最后一个比特刚好完成发送,再过1km/(200m/us)=5us最后一个比特传播到接收端,接收端再用1/10Mbps = 0.1us收完这最后一个比特。就接收结束,整个费时100us+5us+0.1us = 105.1us.

    ps.这是在没考虑冲突的情况下得最大时间。

    我觉得1000b在发送前要处理时间和接受完的时间应该相等(不考试其他因素),所以100应该*2吧????
    1000b/(10*10^6)b/s*10^6*2+5US=205us????

    2个相等没错,但不是一个做完,另一个才开始做。
    当1帧在发送端还没发完的时候,就已经有部分传播到接收端开始接收了。有重叠。

    这也是设计二层协议的时候要考虑的地方,有一个最小长度限制,一桢没完全发完就有部分到达,便于检测到冲突做处理:)

    - 千里孤行

  • 【shell】uniq的用法

    2011-03-09 11:57:43

    [root@stu100 ~]# cat test

      boy took bat home

      boy took bat home

      girl took bat home

      dog brought hat home

      dog brought hat home

      dog brought hat home

      看test文件的内容,可以看到其中的连续重复行

      [root@stu100 ~]# uniq test

      boy took bat home

      girl took bat home

      dog brought hat home

      uniq命令不加任何参数,仅显示连续重复的行一次

      [root@stu100 ~]# uniq -c test

      2 boy took bat home

      1 girl took bat home

      3 dog brought hat home

      -c 参数显示文件中每行连续出现的次数。

      [root@stu100 ~]# uniq -d test

      boy took bat home

      dog brought hat home

      -d选项仅显示文件中连续重复出现的行。

      [root@stu100 ~]# uniq -u test

      girl took bat home

      -u选项显示文件中没有连续出现的行。

      [root@stu100 ~]# uniq -f 2 -s 2 test

      boy took bat home

      忽略每行的前2个字段,忽略第二个空白字符和第三个字段的首字符,结果at home

      [root@stu100 ~]# uniq -f 1 test

      boy took bat home

      dog brought hat home

      忽略每行的第一个字段,这样boy ,girl开头的行看起来是连续重复的行。

  • sed常用命令实例

    2011-03-06 23:04:23

    sed常用命令实例


    删除:d命令
    *
    $ sed '2d' example-----删除example文件的第二行。
    *
    $ sed '2,$d' example-----删除example文件的第二行到末尾所有行。
    *
    $ sed '$d' example-----删除example文件的最后一行。
    *
    $ sed '/test/'d example-----删除example文件所有包含test的行。
    替换:s命令
    *
    $ sed 's/test/mytest/g' example-----在整行范围内把test替换为mytest。如果没有g标记,则只有每行第一个匹配的test被替换成mytest。
    *
    $ sed -n 's/^test/mytest/p' example-----(-n)选项和p标志一起使用表示只打印那些发生替换的行。也就是说,如果某一行开头的test被替换成mytest,就打印它。
    *
    $ sed 's/^192.168.0.1/&localhost/' example-----&符号表示替换换字符串中被找到的部份。所有以192.168.0.1开头的行都会被替换成它自已加 localhost,变成192.168.0.1localhost。
    *
    $ sed -n 's/\(love\)able/\1rs/p' example-----love被标记为1,所有loveable会被替换成lovers,而且替换的行会被打印出来。
    *
    $ sed 's#10#100#g' example-----不论什么字符,紧跟着s命令的都被认为是新的分隔符,所以,“#”在这里是分隔符,代替了默认的“/”分隔符。表示把所有10替换成100。
    选定行的范围:逗号
    *
    $ sed -n '/test/,/check/p' example-----所有在模板test和check所确定的范围内的行都被打印。
    *
    $ sed -n '5,/^test/p' example-----打印从第五行开始到第一个包含以test开始的行之间的所有行。
    *
    $ sed '/test/,/check/s/$/sed test/' example-----对于模板test和west之间的行,每行的末尾用字符串sed test替换。
    多点编辑:e命令
    *
    $ sed -e '1,5d' -e 's/test/check/' example-----(-e)选项允许在同一行里执行多条命令。如例子所示,第一条命令删除1至5行,第二条命令用check替换test。命令的执 行顺序对结果有影响。如果两个命令都是替换命令,那么第一个替换命令将影响第二个替换命令的结果。
    *
    $ sed --expression='s/test/check/' --expression='/love/d' example-----一个比-e更好的命令是--expression。它能给sed表达式赋值。
    从文件读入:r命令
    *
    $ sed '/test/r file' example-----file里的内容被读进来,显示在与test匹配的行后面,如果匹配多行,则file的内容将显示在所有匹配行的下面。
    写入文件:w命令
    *
    $ sed -n '/test/w file' example-----在example中所有包含test的行都被写入file里。
    追加命令:a命令
    *
    $ sed '/^test/a\\--->this is a example' example<-----'this is a example'被追加到以test开头的行后面,sed要求命令a后面有一个反斜杠。
    插入:i命令
    $ sed '/test/i\\
    new line
    -------------------------' example
    如果test被匹配,则把反斜杠后面的文本插入到匹配行的前面。
    下一个:n命令
    *
    $ sed '/test/{ n; s/aa/bb/; }' example-----如果test被匹配,则移动到匹配行的下一行,替换这一行的aa,变为bb,并打印该行,然后继续。
    变形:y命令
    *
    $ sed '1,10y/abcde/ABCDE/' example-----把1--10行内所有abcde转变为大写,注意,正则表达式元字符不能使用这个命令。
    退出:q命令
    *
    $ sed '10q' example-----打印完第10行后,退出sed。
    保持和获取:h命令和G命令
    *
    $ sed -e '/test/h' -e '$G example-----在sed处理文件的时候,每一行都被保存在一个叫模式空间的临时缓冲区中,除非行被删除或者输出被取消,否则所有被处理的行都将 打印在屏幕上。接着模式空间被清空,并存入新的一行等待处理。在这个例子里,匹配test的行被找到后,将存入模式空间,h命令将其复制并存入一个称为保 持缓存区的特殊缓冲区内。第二条语句的意思是,当到达最后一行后,G命令取出保持缓冲区的行,然后把它放回模式空间中,且追加到现在已经存在于模式空间中 的行的末尾。在这个例子中就是追加到最后一行。简单来说,任何包含test的行都被复制并追加到该文件的末尾。
    保持和互换:h命令和x命令
    *
    $ sed -e '/test/h' -e '/check/x' example -----互换模式空间和保持缓冲区的内容。也就是把包含test与check的行互换。
    7. 脚本
    Sed脚本是一个sed的命令清单,启动Sed时以-f选项引导脚本文件名。Sed对于脚本中输入的命令非常挑剔,在命令的末尾不能有任何空白或文本,如果在一行中有多个命令,要用分号分隔。以#开头的行为注释行,且不能跨行。
  • 【C++】前置声明

    2011-01-13 15:52:30

    类或结构体的前向声明只能用来定义指针对象或引用,因为编译到这里时还没有发现定义,不知道该类或者结构的内部成员,没有办法具体的构造一个对象,所以会报错。
    将类成员改成指针就好了。

    相关的编译错误:

    编译错误一:XX does not name a type
    编译错误二:field `XX' has incomplete type

    编译错误一:
    XX does not name a type, 中文意思为“XX没有命名一个类型“
    拿个例子来讲,如果两个类定义如下:
    class B{
    public:
    B(){}
    ~B(){}

    private:
    A a;
    };

    class A{
    public:
    A(){}
    ~A(){}

    private:
    int a;
    };

    编译成则将报一个error:"A does not name a type"
    报错的位置为红色那一行。

    即使clase A和class B分别在两个文件定义,并且在定义B的文件头中#include了class A的头文件也同样会报这个错(这是因为编译和链接之间的先后关系造成的)。

    解决该错误的办法:
    在class B定义声明之前先声明一下class A, 如下:
    class A;
    class B{
    public:
    B(){}
    ~B(){}

    private:
    A a;
    };

    class A{
    public:
    A(){}
    ~A(){}

    private:
    int a;
    };

    参考:http://hi.baidu.com/funrole/blog/item/325e1825967f7b6934a80f9c.html

  • Linux下头文件和库文件搜索路径

    2011-01-13 15:33:04

    Include的header文件、库文件,总共有下列来源指定gcc去哪里找:

    1.在编译时指定的(在~gcc/gcc/collect2.c:locatelib()

    2.写在specs内的

    3.后来用-D -I -L指定的

    4.gcc环境变量设定(编译的时候)

    5.ld.so的环境变量(这是run time的时候)

     

    下面详细说明gcc环境变量指定:

    一、头文件

    gcc 在编译时如何去寻找所需要的头文件 :

    ※所以header file的搜寻会从-I开始

    ※然后找gcc的环境变量 C_INCLUDE_PATH,CPLUS_INCLUDE_PATH,OBJC_INCLUDE_PATH

    ※再找内定目录

    /usr/include

    /usr/local/include

    /usr/lib/gcc-lib/i386-linux/2.95.2/include

    /usr/lib/gcc-lib/i386-linux/2.95.2/../../../../include/g -3

    /usr/lib/gcc-lib/i386-linux/2.95.2/../../../../i386-linux/include

    库文件但是假如装gcc的时候,是有给定的prefix的话,那么就是

    /usr/include

    prefix/include

    prefix/xxx-xxx-xxx-gnulibc/include

    prefix/lib/gcc-lib/xxxx-xxx-xxx-gnulibc/2.8.1/include

    二、库文件

    cos()等函式库的选项要多加 -lm

    编译的时候:

    ※gcc会去找-L

    ※再找gcc的环境变量LIBRARY_PATH

    ※再找内定目录 /lib /usr/lib /usr/local/lib 这是当初compile gcc时写在程序内的

    三、运行时动态库的搜索路径

    1、在配置文件/etc/ld.so.conf中指定动态库搜索路径

    2、通过环境变量LD_LIBRARY_PATH指定动态库搜索路径(当通过该环境变量指定多个动态库搜索路径时,路径之间用冒号":"分隔)

    3、在编译目标代码时指定该程序的动态库搜索路径(还可以在编译目标代码时指定程序的动态库搜索路径。

    这是通过gcc 的参数"-Wl,-rpath,"指定(如例3所示)。当指定多个动态库搜索路径时,路径之间用冒号":"分隔)

    4、默认的动态库搜索路径/lib

    5、默认的动态库搜索路径/usr/lib

    可以通过执行可执行文件pos得到的结果不同获知其搜索到了哪个动态库,从而获得第1个动态库搜索顺序,然后删除该动态库,

    再执行程序pos,获得第2个动态库搜索路径,再删除第2个被搜索到的动态库,

    如此往复,将可得到Linux搜索动态库的先后顺序。

    程序pos执行的输出结果和搜索到的动态库的对应关系如表1所示

    程序pos输出结果 使用的动态库 对应的动态库搜索路径指定方式

    ./ ./libpos.so 编译目标代码时指定的动态库搜索路径

    /root/test/env/lib /root/test/env/lib/libpos.so 环境变量LD_LIBRARY_PATH指定的动态库搜索路径

    /root/test/conf/lib /root/test/conf/lib/libpos.so 配置文件/etc/ld.so.conf中指定的动态库搜索路径

    /lib /lib/libpos.so 默认的动态库搜索路径/lib

    /usr/lib /usr/lib/libpos.so 默认的动态库搜索路径/usr/lib

    综合以上结果可知,动态库的搜索路径搜索的先后顺序是:

    1.编译目标代码时指定的动态库搜索路径;

    2.环境变量LD_LIBRARY_PATH指定的动态库搜索路径;

    3.配置文件/etc/ld.so.conf中指定的动态库搜索路径;

    4.默认的动态库搜索路径/lib;

    5.默认的动态库搜索路径/usr/lib。

    参考文件:http://www.xker.com/page/e2008/0220/49692.html

  • MAC OS CiscoVPN connection_entry文件位置

    2011-01-03 16:25:26

    MAC OS CiscoVPN connection_entry文件位置:
    /etc/CiscoSystemsVPNClient/Profiles

    注:这个pcf文件支持不同机器上的vpn相互拷贝,也支持不同OS下的客户端使用,比如从windows拷贝到MacOS。
  • cppunit linux 配置与使用

    2010-12-30 21:41:51

    OS:linux
    CppUnit:cppunit-1.11.6
    1、下载、解压
         到http://sourceforge.net/projects/cppunit下载,然后复制cppunit-1.11.6.tar.gz到/usr/src;  
        运行:tar -xf cppunit-1.10.2.tar.gz 解压缩;或者在WIN下直接解压
    2、安装
    进入cppunit-1.11.6目录下。依次运行下列命令
          A :./configure;  
    B :make;
          C :make check;
          D:make install
    3、copy *.h文件
        .o, .a文件已经安装到/usr/local/lib中去了,但头文件没安装到/usr/include中去
    把cppunit-1.10.2的cppunit目录复制到/usr/include下
    4、导入lib
    运行时要先设置环境变量LD_LIBRARY_PATH到cppunit的安装目录,也就是/usr/local/lib,命令如下:
           export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
    5、编写测试程序
    testApp.cpp
    #include <iostream>
     
    #include <cppunit/TestRunner.h>
    #include <cppunit/TestResult.h>
    #include <cppunit/TestResultCollector.h>
    #include <cppunit/extensions/HelperMacros.h>
    #include <cppunit/BriefTestProgressListener.h>
    #include <cppunit/extensions/TestFactoryRegistry.h>
     
     
    class Test : public CPPUNIT_NS::TestCase
    {
     CPPUNIT_TEST_SUITE(Test);
     CPPUNIT_TEST(testHelloWorld);
       CPPUNIT_TEST_SUITE_END();
     
     public:
       void setUp(void) {}
       void tearDown(void) {}
     
     protected:
       void testHelloWorld(void) { std::cout << "Hello, world!" << std::endl; }
     };
     
     CPPUNIT_TEST_SUITE_REGISTRATION(Test);
     
     int main( int argc, char **argv )
     {
       // Create the event manager and test controller
       CPPUNIT_NS::TestResult controller;
     
       // Add a listener that colllects test result
       CPPUNIT_NS::TestResultCollector result;
     controller.addListener( &result );       
     
       // Add a listener that print dots as test run.
       CPPUNIT_NS::BriefTestProgressListener progress;
       controller.addListener( &progress );     
     
       // Add the top suite to the test runner
       CPPUNIT_NS::TestRunner runner;
       runner.addTest( CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest() );
       runner.run( controller );
     
       return result.wasSuccessful() ? 0 : 1;
     }
    6、编译,运行
       有两种方法
        (a) 链接静态库。编译命令:
          g++ -L/usr/local/lib/libcppunit.a testApp.cpp -lcppunit -ldl -o testApp
        运行:
          ./ testApp
        结果:
         
          : OK
        (b) 链接动态库。编译命令:
           g++ testApp.cpp -lcppunit -ldl -o testApp
        然后运行:
           ./ testApp
        结果:
           Test::
             : OK
    7、其他例子
    在cppunit-docs-1.11.6中有文档money_example.html,该文档详细的介绍了测试、开发的过程;

    本文转自:http://blog.csdn.net/lengxingfei/archive/2006/04/19/669659.aspx
  • 【C++】vs2008设置include与lib的方法

    2010-12-28 15:51:49

    tools-->options--->projects and solutions-->VC++ directoris 然后在show directoris for 中选择 :

    include files,并添加路径

    Library files,并添加路径

  • 【php】php中的一些常量以及获取当前类名函数名的方法

    2010-12-01 11:16:17

    1.获取行号、文件路径文件名、类名、方法名的常量
    __LINE__     文件中的当前行号。
    __FILE__    文件的完整路径和文件名。如果用在包含文件中,则返回包含文件名。自 PHP 4.0.2 起,__FILE__ 总是包含一个绝对路径,而在此之前的版本有时会包含一个相对路径。
    __FUNCTION__    函数名称(PHP 4.3.0 新加)。自 PHP 5 起本常量返回该函数被定义时的名字(区分大小写)。在 PHP 4 中该值总是小写字母的。
    __CLASS__    类的名称(PHP 4.3.0 新加)。自 PHP 5 起本常量返回该类被定义时的名字(区分大小写)。在 PHP 4 中该值总是小写字母的。
    __METHOD__    类的方法名(PHP 5.0.0 新加)。返回该方法被定义时的名字(区分大小写)。

    注:这些常量前后均是两个下划线。

    2.获取类名、方法名、变量名的方法
    发现PHP强大之处之一,就是经常有意想不到函数或者是这些常量帮助你完成方便的完成某些功能。越来越体会到为什么PHP的比试题目里面会出现那么多考查具体的PHP函数的题目了。
    再一口气找了其他的一些常量或函数,持续添加。
    get_class(class name);        //取得当前语句所在类的类名
    get_class_methods(class name);    //取得class name 类的所有的方法名,并且组成一个数组
    get_class_vars(class name);    //取得class name 类的所有的变亮名,并组成一个数组

  • 【php】php Fatal error: Cannot redeclare class

    2010-11-28 15:45:35

    通常是文件重复包含问题引起的,比如应该使用include_once来包含文件,而不要只使用include包含,就可以避免了
  • .rpmnew和.rpmsave的问题

    2010-11-26 11:35:46

    原文转自:http://tech.idv2.com/2007/10/16/rpm-config-noreplace/

    RPM spec文件有个名为 %config 的宏,它可以标识配置文件,这样在升级时用户对配置文件做过的修改就不会丢失。没有它,用户千辛万苦修改过的配置文件会在升级过程中被覆盖。

    %config也可以写成%config(noreplace),不过网上关于它的说明却屈指可数。下面是关于这两者的一些实验,都是在RedHat 9的RPM(rpm-4.2-0.69)上进行的,其他版本有可能不同。

    RPM中的文件的制约条件有三个:1. 该文件在spec中如何标识(默认,%config或者%config(noreplace)); 2. 在rpm升级包中该文件是否被更新;3. 该文件是否被用户编辑过。

    下表就是各个条件的组合结果。

     文件标识 在RPM升级包中是否更新了?  旧版本文件未被用户编辑过   旧版本文件被用户编辑过
     默认  No  用新文件覆盖  用新文件覆盖
       Yes  用新文件覆盖  用新文件覆盖
     %config  No  用新文件覆盖  保持旧文件
       Yes  用新文件覆盖  旧文件改名为.rpmsave并复制新文件
     %config(noreplace)  No  用新文件覆盖  保持旧文件
       Yes  用新文件覆盖  保持旧文件,新文件安装为.rpmnew

     

    其中(noreplace)有效的两种情况上存在以下的问题:当spec文件中的定义改变时会发生什么?结论如下:

     文件标识 在RPM包中是否更新了?   旧版本文件被用户编辑过?
     由%config(noreplace)修改为%config  yes  旧文件改名为.rpmsave并安装新文件
     由%config修改为%config(noreplace)  yes  保持旧文件,新文件安装为.rpmnew

    结论:非配置文件或是上次安装后没有被修改的文件会被RPM包内的新文件覆盖。如果配置文件被修改过,但在新的RPM包中未作更新,则修改过的文件保留。仅当配置文件被修改并且在新的RPM包中被更新时,具体动作取决于(noreplace)。 %config使得旧文件被改名为.rpmsave并安装新文件,而%config(noreplace)将保持旧文件并将新文件安装为.rpmnew。

    建议一般配置文件都标识为(noreplace),除非升级所带来的配置文件变动非常重要,使用上一版本的配置文件无法正常工作时才使用%config。

571/3123>
Open Toolbar