2008年6月19日

阶段三 容错性测试

你的新内核已经通过了测试的前两个阶段了吗?现在,可以开始实验了。那就是,做一些任何一个正常人在普通工作中都不会做的蠢事,因为没人会知道那些蠢事会导致内核崩溃。那到底需要做些什么事呢?好的,如果存在一个“标准的”程序的话,那它当然早就被包含在一些测试套件中了。

例如,第三阶段可以从拔掉和重插USB设备开始。从理论上讲,重插一个USB设置不应该会产生任何事情,至少从用户的角度看来。如果USB子系统存在bug,连续进行很多次这样的测试就可能会导致内核崩溃(这个测试如果出现问题,说明了之前未曾有人在类似系统配置下尝试这个测试)。
接下来,你可以编写一个脚本,连续读取/proc目录中文件的内容,等等。一句话,在这阶段,你应该做些普通用户永远不会做的事(或者极常少做:为什么有人会不断重复地挂载和卸载某个文件系统呢?)

posted @ 2008-06-19 13:23 测试时代-谷峰| 编辑 收藏

阶段二(自动测试)

测试的下一阶段是,使用专门设计的程序来检查特定的内核子系统能否正常工作。我们还执行内核的退化以及性能测试。后者对于内核开发员(还有我们)来说,相当地重要,因为它使得我们能识别出损害性能的改变。比如,如果我们把内核从2.6.x-rc1升级到2.6.x-rc2后,有种文件系统的性能却降低了10%,查找导致这种情况发生的补丁无疑是个好主意。

关于内核的自动测试,我们推荐你使用AutoTest 套件(http://test.kernel.org/autotest/)。该套件包含了很多测试应用程序以及统计工具,而且相当简单的用户界面。

为了安装AutoTest你以root帐户可以进入到/usr/local 目录并运行

# svn checkout svn://test.kernel.org/autotest/trunk autotest

虽然通常不推荐以root帐户运行类似命令,但这个命令应该是安全的,除非你不信任你的DNS服务器,因为它只是下载一些文件,并保存到/usr/local下。除此之外,你还得以root帐户运行AutoTest,因为它的一些测试要求超级用户权限才能完成。所以,你不应该在生产系统上使用AutoTest:在极端情况下,保存在进行特权级测试的系统中的数据可能会受到损坏甚至毁坏,我们相信你不喜欢你的产品数据会发生这种情况。
根据设计,AutoTest是非交互式性的,所以一旦启动,你就不需要再关心它了(当然了,如果有坏事真的发生了,你就要不得不恢复系统了,但这是另外一个话题)。要启动测试,你可以到/usr/local/autotest/client目录下 (我们假设AutoTest被安装到了/usr/local下),然后以root身份执行:

# bin/autotest tests/test_name/control

这里,test_name是位于目录/usr/local/autotest/client/tests中的目录名称,该目录中含有你想要进行的测试。控制文件tests/test_name/control包含了为AutoTest准备的指令。最简单的情况下,只需要一个这样的指令,也就是:

job.run_test(’test_name’)

这里,test_name是包含控制文件的目录名称。更加复杂的控制文件的内容可能类似下面:

job.run_test(’pktgen’, ’eth0’, 50000, 0, tag=’clone_skb_off’)
job.run_test(’pktgen’, ’eth0’, 50000, 1, tag=’clone_skb_on’)

这里,测试名称后面的字符串表示的是应当要传递到测试程序的参数。你可以修改这些参数,但是首先要阅读测试程序的文档以及脚本tests/test_name/test_name.py(比如tests/pktgen/pktgen.py)。实际上AutoTest就是使用该脚本来启动测试的(可能你已经注意到,
AutoTest脚本是用Python语言写的)。脚本sts/test_name/test_name.py的执行结果保存在目录results/default/test_name/中,该目录中的status文件包含着标识测试是否已经成功完成的信息。想取消正在进行的测试,按Ctrl+C即可。
如果你想连续运行一串测试,最好的做法是准备单独一个文件,该文件中包含着多个AutoTest的指令。这个文件中的指令和上面提到的控制文件中的指令类似。比如,文件samples/all_tests包含有运行所有可以测试指令,其中前五行如下

job.run_test(’aiostress’)
job.run_test(’bonnie’)
job.run_test(’dbench’)
job.run_test(’fio’)
job.run_test(’fsx’)

要运行这个文件中的指令所请求的全部测试,你可以做使用命令bin/autotest samples/all_tests。但是要记得这要花很长的时间才能完成。类似地,要运行定制的一串测试,得把为AutoTest准备的指令们放到一个文件里面,然后给这个文件起个名称,这个名称将作为命令行的一个参数传递给autotest.要并行地运行几个测试,你得准备一个特殊的控制文件,这个控制文件包含的指令如下:

def kernbench():
job.run_test(’kernbench’, 2, 5)
def dbench():
job.run_test(’dbench’)
job.parallel([kernbench], [dbench])

在测试执行的过程序中,在任何时间里,你都可以通过按Ctrl+C来停止它们。
为了造福那些不喜欢命令行和配置文件的朋友,ATCC (AutoTest Control Center )已经被发明出来了。如果你运行它,比如通过命令ui/menu,将会得到一个用菜单驱动的简单界面,你可以通过界面选择测试和profiling工具,浏览测试结果,还可以通过它来限制范围,配置它们。
如果你厌烦了AutoTest套件中包含的一系列工具,可以访问站点http://ltp.sourceforge.net/tooltable.php 。该站点有可以用于linux内核测试的全面的工具清单。

posted @ 2008-06-19 13:20 测试时代-谷峰| 编辑 收藏

通常,有多种测试linux内核的方法,但我们只关注下面这4种:

1. 在平时的工作中使用内核的测试版本.

2. 在新内核上运行专用的测试套件,比如LTP.

3. 新内核安装后,做些非常规的事情.

4. 新内核安装后,测试系统的性能.

当然,测试过程它们可以混合运用,所以可以认为它们是测试过程的不同阶段。


阶段一:内核的冒烟测试

内核测试的第一阶段很简单:我们尝试着启动内核,然后把它用在平时的工作中。

• 在以全功能配置启动系统之前,推荐先用init=/bin/bash命令行参数来启动内核。它使得内核只启动一个bash进程。这样,你可以检查在最小配置下是否能正常挂载和卸载文件系统,然后,你可以测试一些更加复杂的内核功能,比如挂起到disk或RAM的缓冲。这种情况下,唯一被挂载了的模块是位于initrd映象中的那些模块。提到 了initrd映象。通常,你可以参考你的boot loader文档来获取更多关于手工向内核传递命令行的信息(我们的观点是,如果使用的是GRUB,会更容易操作些。)

• 接着,建议以runlevel 2来启动系统(通常,是通过把数字2作为命令行参数传递给内核),这时,网络服务器和X服务器还没启动(你的系统有可能为这个用途使用的是另一个runlevel,尽管不大可能,所以你应该查看/etc/inittab文件确认一下)。在这个配置下,你可以检查网络接口是否能工作,你还可以尝试手工启动X服务器来确认它不会崩溃掉。

• 最后,根据需要,你可以把系统启动到runlevel 5(也就是全功能)或者3(也就是除了X,全功能开启)
现在,你已经可以按平时的样子使用系统一段时间了。但是,如果想快点测试内核,你可以执行一些典型的操作,比如下载一些文件,读取邮件,浏览一些站点,抓取一些音轨(我们假设,使用的是合法买来的音频CD),刻录一张CD或DVD等等操作来连续地检查是否有任何一项会出错。出错的话,就意味着内核有问题存在了

posted @ 2008-06-19 13:17 测试时代-谷峰| 编辑 收藏


2008年5月15日

 

1.监控IIS的时候,counter并没有出现在add measurements里面


解决方案:

Some Win 2000 counters cannot be monitored from an NT machine.

Run the Controller or Tuning Console on a Win 2000 machine.

 

2. add measurements的时候出现了如下画面


解决方案:

Server上面创建一个用户,用户名和密码和监控机上面的要一样

posted @ 2008-05-15 12:12 测试时代-谷峰 阅读(126) | 评论 (0)编辑 收藏


2008年3月12日

这种情况大多数是因为你的mysql是使用rpm方式安装的,它会自动寻找 /var/lib/mysql/mysql.sock 这个文件,
通过unix socket登录mysql。
常见解决办法如下:
1、
创建/修改文件 /etc/my.cnf,至少增加/修改一行
[mysql]
[client]
socket = /tmp/mysql.sock
#在这里写上你的mysql.sock的正确位置,通常不是在 /tmp/ 下就是在 /var/lib/mysql/ 下

2、
指定IP地址,使用tcp方式连接mysql,而不使用本地sock方式
#mysql -h127.0.0.1 -uuser -ppassword

3、
为 mysql.sock 加个连接,比如说实际的mysql.sock在 /tmp/ 下,则
#ln -s /tmp/mysql.sock /var/lib/mysql/mysql.sock即可

posted @ 2008-03-12 14:44 测试时代-谷峰 阅读(360) | 评论 (0)编辑 收藏


2008年1月28日

使用测试优先方法开发用户界面



1、概述

  测试优先是测试驱动开发(Test-Driven Development, TDD)的核心思想,它要求在编写产品代码前先编写基于产品代码的测试代码。在测试驱动开发的单元测试中,对GUI应用实施自动测试应该是测试驱动开发的软肋之一。由于界面的操作是有由人来完成的,所以要想在GUI中完成单元自动测试是有一定难度的。Kent Beck在它的《测试驱动开发》中就曾提到过这个问题。
  本文将通过一个例子来讲解在测试驱动开发中如何针对GUI进行单元测试。这个例子是 David Astels著的《测试驱动开发实用指南(影印版)》中一个关于影片列表管理的例子。该书中文版即将在国内出版。书中讨论并介绍了开发这个例子的多种方法。笔者将介绍其中的一种,并且为了方便使用C  的朋友的学习,书中的代码我用C  写了一遍,类名和变量名尽量和原书保持一致,以方便阅读该书的C   读者。在此也要感谢David Astels给我们带来如此精彩的一本书。
  本文叙述背景为:CppUnit1.9.0, Visual C   6.0, Windows2000 pro。文中叙述有误之处,敬请批评指正。如果读者对CppUnit还没有一定的了解,可以先参考笔者的另一篇文章《CppUnit测试框架入门》。

2、需求分析

  对于这个影片管理的应用,我们主要实现增加、删除和显示影片列表的功能。基于这些需求,我们可以画一张GUI草图。

  界面的控件主要有:一个显示所有影片的列表listbox控件,一个填写新的影片名的edit控件,一个增加button控件,一个删除button控件。由此,我们的开发目标就十分的明确了。

3、编写UI测试代码

  这部分的UI测试代码主要是测试各个控件是否正确生成并且是可见的,以及测试一些控件的label文字是否正确。
  我们从TestCase继承一个类TestWidgets用于测试窗口,并添加四个测试,分别测试listbox、edit、add button、delete button。






class TestWidgets : public CppUnit::TestCase
{
     CPPUNIT_TEST_SUITE(TestWidgets);
     CPPUNIT_TEST(testList);
     CPPUNIT_TEST(testField);
     CPPUNIT_TEST(testAddButton);
     CPPUNIT_TEST(testDeleteButton);
     CPPUNIT_TEST_SUITE_END();
public:
     TestWidgets();
     virtual ~TestWidgets();
public:
     virtual void setUp();
     virtual void tearDown();
     void testList();
     void testField();
     void testAddButton();
     void testDeleteButton();
private:
     MovieListWindow* m_pWindow;
};


其中,MovieListWindow是一个窗口类。我们来看看其中的一个测试,请看代码中的注释。






void TestWidgets::testAddButton()
{
       //得到btn指针
       CButton* pAddButton = m_pWindow->GetAddButton();
       //检查是否生成btn
       CPPUNIT_ASSERT(pAddButton->m_hWnd);
       //检查btn是否可见
       CPPUNIT_ASSERT_EQUAL(TRUE, ::IsWindowVisible(pAddButton->m_hWnd));
       CString strText;
       pAddButton->GetWindowText(strText);
       CString strExpect = "Add";
       //检查btn的Label文字是否正确
       CPPUNIT_ASSERT_EQUAL(strExpect, strText);
}


  编译测试代码,编译器会给我们一些出错信息。这要求我们必须马上编写产品代码以让编译通过。首先第一个要实现的产品代码就是MovieListWindow窗口类。






class AFX_EXT_CLASS MovieListWindow : public CDialog
{
public:
     MovieListWindow(CWnd* pParent = NULL); // standard constructor
     CListBox* GetMovieListBox(){return &m_MovieListBox;};
     CEdit* GetMovieField(){return &m_MovieField;};
     CButton* GetAddButton(){return &m_AddBtn;};
     CButton* GetDeleteButton(){return &m_DeleteBtn;};
     void Init();
     // Dialog Data
     //{{AFX_DATA(MovieListWindow)
     enum { IDD = IDD_MOVIELISTDLG };
     CButton m_AddBtn;
     CButton m_DeleteBtn;
     CEdit m_MovieField;
     CListBox m_MovieListBox;
     //}}AFX_DATA
     // Overrides
     // ClassWizard generated virtual function overrides
     //{{AFX_VIRTUAL(MovieListWindow)
  protected:
     virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
     //}}AFX_VIRTUAL
     // Implementation
  protected:
    // Generated message map functions
    //{{AFX_MSG(MovieListWindow)
    //}}AFX_MSG
  DECLARE_MESSAGE_MAP()
};


  在MovieListWindow窗口类中我们实现了需要的控件以及针对这些控件的一些方法,如GetMovieListBox()等,本文在此不做详述。编译测试代码和产品代码,检查是否通过。如未通过则继续检查产品代码以使编译和测试通过。

4、编写控件行为测试代码

  接下来应该是编写点击add button和delete button的测试代码了。同样,我们从TestCase继承出TestOperation:






class TestOperation : public CppUnit::TestCase
{
     CPPUNIT_TEST_SUITE(TestOperation);
     CPPUNIT_TEST(testMovieList);
     CPPUNIT_TEST(testAdd);
     CPPUNIT_TEST(testDelete);
     CPPUNIT_TEST_SUITE_END();
public:
     void testMovieList();
     void testAdd();
     void testDelete();
public:
     void setUp();
     void tearDown();
     TestOperation();
     virtual ~TestOperation();
private:
     static CString LOST_IN_SPACE;
     CStringArray m_MovieNames;
     MovieListWindow* m_pWindow;
     MovieListEditor* m_pEditor;
};


  你会发现,在TestOperation类中出现了一个成员变量MovieListEditor* m_pEditor。类MovieListEditor是一个用来保存影片数据以及对影片数据进行增加,删除操作的管理类。后面我们会给出它的实现。看看setUp()做了什么:






void TestOperation::setUp()
{
     //创建一个MovieListEditor实例
     m_pEditor = new MovieListEditor();
     m_MovieNames.RemoveAll();
     //将MovieListEditor中的影片列表拷贝到m_MovieNames,为后面测试作准备
     for(int n=0; n<m_pEditor->GetMovies()->GetSize(); n  )
     {
        m_MovieNames.Add(m_pEditor->GetMovies()->GetAt(n));
     }
}


我们来看看添加影片的测试,请看代码注释:






void TestOperation::testAdd()
{
     //拷贝一份movie list
     CStringArray MovieNamesWithAddition;
     for(int n=0; n<m_MovieNames.GetSize(); n  )
     {
        MovieNamesWithAddition.Add(m_MovieNames.GetAt(n));
     }
     MovieNamesWithAddition.Add(LOST_IN_SPACE);
     //生成窗口
     MovieListWindow *pWindow = new MovieListWindow(m_pEditor);
     pWindow->Init();
     //填写新的影片的名称
     CEdit* pEdit = pWindow->GetMovieField();
     pEdit->SetWindowText(LOST_IN_SPACE);
     //点击add btn
     CButton* pBtn = pWindow->GetAddButton();
     ::SendMessage(pBtn->m_hWnd, BM_CLICK, 0, 0);
     //检查列表控件中是否已加入新的影片
     CListBox* pListBox = pWindow->GetMovieListBox();
     CPPUNIT_ASSERT_EQUAL(MovieNamesWithAddition.GetSize(), pListBox->GetCount());
     //检查列表控件中影片名是否正确
     CString strNewMovieName;
     pListBox->GetText(pListBox->GetCount()-1, strNewMovieName);
     CPPUNIT_ASSERT_EQUAL(LOST_IN_SPACE, strNewMovieName);
     //销毁窗口
     pWindow->DestroyWindow();
     delete pWindow;
     pWindow = NULL;
}


编译后会有出错信息,主要的错误有:
a)、我们把m_pEditor保存在MovieListWindow中了,这需要我们修改原来的MovieListWindow的构造函数。
b)、没有MovieListEditor类。

MovieListEditor的实现如下:






class AFX_EXT_CLASS MovieListEditor
{
public:
     MovieListEditor();
     virtual ~MovieListEditor();
public:
     virtual CStringArray* GetMovies(){return &m_arMovieList;};
     virtual void Add(CString strMovie){m_arMovieList.Add(strMovie);};
     virtual void Delete(int nIndex){m_arMovieList.RemoveAt(nIndex);};
private:
     CStringArray m_arMovieList;
};


再次编译,已经通过.运行测试,发现在:






CPPUNIT_ASSERT_EQUAL(MovieNamesWithAddition.GetSize(), pListBox->GetCount());


测试通不过。检查后知道原因是,我们在测试代码里:






::SendMessage(pBtn->m_hWnd, BM_CLICK, 0, 0);


给add button发送了点击按钮的消息,但是在MovieListWindow 窗口中我们没有加入消息的响应函数,因此测试没有通过。赶紧添加消息响应函数。






void MovieListWindow::OnClickAddButton()
{
     UpdateData();
     CString strNewMovieName;
     m_MovieField.GetWindowText(strNewMovieName);
     if("" != strNewMovieName)
     {
        m_pEditor->Add(strNewMovieName);
        m_MovieListBox.AddString(strNewMovieName);
     }
}


编译、测试、通过。

5、Mock Objects

  在删除操作的单元测试中,我们遇到的一个问题是,影片列表的数据应该是保存在一个文本文件或者数据库当中的,如果我们编写的测试依赖于这些实际的文件或数据库,那么我们的测试就会受制于这些外部的资源。一旦文件或者数据库里的数据发生变化,必然会波及到我们的测试代码,从而产生错误的测试信息。前面的MovieListEditor中我们没有加入一些初始化的数据,在测试删除操作时会遇到一些问题 。
  这里,我们引入Mock Objects。Mock Objects用来模拟外部复杂的资源(如数据库,网络连接等),使UI可以测试那些依赖于这些复杂外界资源的模块。例如在测试一个跟数据库有关系的模块时,我们并不一定要建立一个真实的数据库连接,而只需建立一个Mock Objects就可以了。测试所需的数据都存在于这个Mock Objects。可以说,Mock Objects为我们提供了一个轻量级的、可控制的、高效的模型。
  在本例中,影片的增加、删除都会跟文件或数据库操作发生关系。这时我们就可以利用Mock Objects来隔离测试代码与文件或数据库。使用Mock Objects一般有以下几个步骤:
a)、定义一个外部资源的接口.(这个接口一般是可以在重构过程中提炼出来的)。
b)、定义一个Mock Objects,从外部资源的接口继承下来,实现外部资源的接口。
c)、创建一个Mock Objects,并设置它的内部期望值。
d)、把创建的这个Mock Objects传递给需要测试的模块进行操作。
e)、操作完毕后将Mock Objects内部的状态与期待状态比较。 现在我们就根据这个步骤来实现本例子中的Mock Objects.通过对前面的代码进行重构,我们可以提炼出一个接口MovieListEditor:






class AFX_EXT_CLASS MovieListEditor
{
public:
    MovieListEditor();
    virtual ~MovieListEditor();
public:
    virtual CStringArray* GetMovies()=0;
    virtual void Add(CString strMovie)=0;
    virtual void Delete(int nIndex)=0;
};


  请注意它和前面我们定义的MovieListEditor的不同。接下来,我们应该定义一个Mock Objects,当然它是从MovieListEditor继承下来的:






class mockEditor : public MovieListEditor
{
public:
     mockEditor();
     virtual ~mockEditor();
public:
     virtual CStringArray* GetMovies(){return &m_arMovieList;};
     virtual void Add(CString strMovie){m_arMovieList.Add(strMovie);};
     virtual void Delete(int nIndex){m_arMovieList.RemoveAt(nIndex);};
private:
     CStringArray m_arMovieList;
};


然后给这个Mock Objects设置初识值,我们选择在它的构造函数里进行。






mockEditor::mockEditor()
{
     m_arMovieList.Add("Star Wars");
     m_arMovieList.Add("Star Trek");
     m_arMovieList.Add("Stargate");
}


  我们添加了三个影片用于测试。接着,应该把这个MockObjects的一个实例传递给需要测试的模块。这里就是我们要测试的UI(MovieListWindow)。






m_pEditor = new mockEditor();
   MovieListWindow *pWindow = new MovieListWindow(m_pEditor);


最后我们来看看经过修改后的新的测试添加影片的方法:






void TestOperation::testAdd()
{
     //拷贝一份movie list
      CStringArray MovieNamesWithAddition;
     for(int n=0; n<m_MovieNames.GetSize(); n  )
     {
        MovieNamesWithAddition.Add(m_MovieNames.GetAt(n));
     }
     MovieNamesWithAddition.Add(LOST_IN_SPACE);
     //生成窗口
     MovieListWindow *pWindow = new MovieListWindow(m_pEditor);
     pWindow->Init();
     //填写新的影片的名称
     CEdit* pEdit = pWindow->GetMovieField();
     pEdit->SetWindowText(LOST_IN_SPACE);
     //点击add btn
     CButton* pBtn = pWindow->GetAddButton();
     ::SendMessage(pBtn->m_hWnd, BM_CLICK, 0, 0);
     //检查列表控件中是否已加入新的影片
     CListBox* pListBox = pWindow->GetMovieListBox();
     CPPUNIT_ASSERT_EQUAL(MovieNamesWithAddition.GetSize(), pListBox->GetCount());
     //将Mock Objects的内部数据和期望值进行比较
     CPPUNIT_ASSERT_EQUAL(MovieNamesWithAddition.GetSize(),
     m_pEditor->GetMovies()->GetSize());
     //检查列表控件中影片名是否正确
     CString strNewMovieName;
     pListBox->GetText(pListBox->GetCount()-1, strNewMovieName);
     CPPUNIT_ASSERT_EQUAL(LOST_IN_SPACE, strNewMovieName);
     //将Mock Objects的内部数据和期望值进行比较
     int nIndex = m_pEditor->GetMovies()->GetSize();
     CPPUNIT_ASSERT_EQUAL(LOST_IN_SPACE, m_pEditor->GetMovies()->GetAt(nIndex-1));
     //销毁窗口
     pWindow->DestroyWindow();
     delete pWindow;
     pWindow = NULL;
}


  请注意,这里测试的数据都是mockEditor里的,而且在UI进行添加操作后,还将mockEditor内部的状态与期待状态做了比较。






CPPUNIT_ASSERT_EQUAL(MovieNamesWithAddition.GetSize(), m_pEditor->GetMovies()->GetSize());
   CPPUNIT_ASSERT_EQUAL(LOST_IN_SPACE, m_pEditor->GetMovies()->GetAt(nIndex-1));


  其他删除操作的测试跟添加类似,在此不做详述。至此,我们就完成了这个GUI应用程序的开发。



6、源码说明

  本文附带的代码包括三个Project,分别是Movie、 GuiTestFirst、AppMovieList.Movie是产品代码.GuiTestFirst是测试代码。AppMovieList是使用Movie输出的产品代码而写的应用程序,它从MovieListEditor继承出一个新的影片管理类 MyEditor。它主要是演示如何使用我们提炼出来的MovieListEditor接口。例如你可以实现CXmlMovieListEditor,CAccessMovieListEditor等等。进入GuiTestFirst打开所有这些工程。



7、总结

a)、对GUI应用实施测试优先开发方法,这在测试驱动开发中并不是必须的,可根据开发的实际情况来选择。
b)、我们通过引入Mock Objects,我们使测试代码和外部复杂的资源隔离开来,同时也使我们能够从中既有代码中提炼出清晰的接口,使代码整洁可用。

8、参考资料



  • 《测试驱动开发实用指南(影印版)》David Astels
  • 《测试驱动开发(中文版)》Kent Beck
  • 《Endo-Testing: Unit Testing with Mock Objects》Tim Mackinnon, Steve Freeman, Philip Craig

posted @ 2008-01-28 17:21 测试时代-谷峰| 编辑 收藏


大多数测试工程师再做性能测试的时候,都接触到基于unix平台的Apache服务器,我一般都用Apachect1 restart来启动
可是有时候效果并不好!所以这里推荐下列启动方式

1.起动Apache有很多种方法-但实质都是调用httpd--fei一般只用Apachectl好用简单apachectl是控制Apache的脚本-start,stop,restart就不用说了

2.参数---configtest是看配置文件的语法有没有错误-有就会指出哪行-什么错--主要是调试用.status是如果你加载了mod_status模块后调用lynx显示apache的工作状态.

3.主要是graceful---完美的重起--我推荐每次重起都用它不用restart--那么它与restart的不同---可以写一篇文章啦--概括的说restart向apache主进程发送一个SIGNUP信号--而graceful发的是SIGUSER1信号--发送HUP信号给主进程将导致结束它的子进程, 就想TERM(stop)信号一样,但是主进程将不会终止. 它将重新读取配置文件, 并且重新打开所有的日志文件. 然后重新产生所有子进程并继续为点击操作提供服务.而USR1 信号引起主进程处理并警告收到当前请求之后的子进程结束(或在它们不做任何服务时立刻结束). 主进程将重新读取它的配置文件并打开它的日志文件. 当每一个子进程结束后,主进程将使用配置信息产生一个新的子进程来立刻响应请求并为之服务. --好象挺复杂的是吧--主要是知道graceful要比restart 好就行了

posted @ 2008-01-28 17:09 测试时代-谷峰 阅读(84) | 评论 (0)编辑 收藏


2008年1月25日

在网上闲逛的是后看到关于单元测试的东西,觉得非常不错,拿过来一同分享一下,测试中有一个pareto的原则,想必大家都知道,就是在需求和设计的静态测试中,可以发现80%的bug,但这是一个理想的状态。又有多少公司能够做到全面的需求和设计的静态测试呢,所以有很多公司会把一部分的精力放在单元测试里面,来进一步的靠近pareto的原则,一般来说,单元测试会详细描述,单元模块的划分,以及该模块的接口描述和说明。下面我来贴出来一段详细设计里面所设计到的模块的说明,我想对于有的看不到详细设计的测试工程师会有所帮助!

 ——————————————————————————————————————————————————————————————

6、系统参数程序设计说明

6.1程序描述

显示传输系统参数配置界面

6.2功能

提供传输子系统系统正常运行所必需的参数。

6.3性能

6.4输入项

输入数据:

1.    写参数配置文件:参数配置属性页;

2.读参数配置文件:参数配置文件。

输入说明:

参数文件与系统互动,通过参数配置属性页建立/更新参数配置文件;

各模块通过调用函数从参数配置文件中取得相应的参数值。

6.5输出项

输出数据:

1.    写参数配置文件:参数配置文件;

2.读参数配置文件:参数配置属性页、各模块所需参数。

输出说明:

参数文件与系统互动,将参数配置文件中的参数值回显到参数配置属性页;各模块通过调用函数从参数配置文件中取得相应的参数值;

6.6算法

参数配置文件位于系统程序的运行目录,名称:Octdts.Properties。参数配置文件涉及到整个系统的正常运行,故参数配置文件的管理异常重要,一般情况下,不允许对参数配置文件直接进行手工修改,须通过程序实现对参数配置文件的更新;为了最大程度上的实现系统的通用性,参数配置文件中所包含的参数很详细,基本函概了系统所需的动态参数;由于设计到安全问题,数据库用户密码不以明文方式记录在参数配置文件中,调用一个加密算法对密码进行加密,在取密码时,需调用一个解密算法。

6.7流程逻辑

 

 

6.8接口

序号

调用者

被调用者

1.       

类名称FrameMain

类说明:程序主界面

调用说明:按钮、菜单显示参数配置界面

名称:本类的实例化对象 ParameterPage

调用说明:show()toFront()方法

2.       

各个服务模块

调用说明:直接调用getParam()方法

——————————————————————————————————————————————————————————

那么对于这一个单元模块来讲,在开发时是再涉及到单元测试时,往往show(),toFront()方法还没有开发出来,所以测试工程师需要独立开发驱动模块或桩模块。,驱动模块和桩模块的概念如下,网上有很多概念,不好理解,我认为用这种说法更方便理解

驱动模块在大多数场合称为"主程序",它接收测试数据并将这些数据传递到被测试模块,相反桩模块就是那些被测试模块所调用的“程序”,它们之间的区别是调用与被调用的关系

往往被测试单元的上层程序还没有开发出来我们就需要实施单元测试,上层程序有时要包含多个调用界面,函数,方法等,而我们测试的单元模块的期望结果有可能只有返回一个值或是传递一个参数,这时候就需要编写驱动模块或者是桩模块(如果是需要调用其它函数返回的值)。

如何编写桩模块?举一个简单的例子

/*被测程序*/
int Fun(int in)
{
    if (in >= 0)
    {
        return 1;
    }
    else
    {
        return -1;
    }
}



那么通过TCL进行扩展指令编写时,针对该被测函数,驱动如下:

/*用户自己扩展的用户指令,用来驱动被测函数*/
int Ex_TestFun(ClientData clientData,Tcl_Interp * interp,int argc,  char* argv[])
{
     int i;
     int ret,iExceptedRet;

    //打开测试结果记录文件
    FILE * out;
    out = fopen("D:\\result.txt","a");
                     
    //第一步:检查用户输入参数个数是否正确
    if (3 != argc)
    {
        fputs("Parameters error",out);
        fflush(out);
                return TCL_ERROR;
    }
   
   //第二步:取出用户输入参数
    if (TCL_OK != Tcl_GetInt(interp,argv[1],&i))
   {
              return TCL_ERROR;            
   }

   if (TCL_OK != Tcl_GetInt(interp,argv[2],&iExceptedRet))
   {
        return TCL_ERROR;
   }


   //第三步:将参数传递给被测函数
   ret = Fun(i);

   //第四步:将被测函数执行结果和输入的期望结果进行比较,根据比较结果作为用例执行结果输出到测试报告中
  if (ret != iExceptedRet)
   {
        fputs("test fail",out);
        fflush(out);
   }
   else
   {
        fputs("test success",out);
        fflush(out);
   }

   return TCL_OK;
}
——————————————————————————

驱动模块编写完毕后,就可以直接实施单元测试而不必等待上层的程序开发出来才能进行测试,提高工作效率

posted @ 2008-01-25 11:23 测试时代-谷峰 阅读(190) | 评论 (0)编辑 收藏


2008年1月14日

SmartHttpd 1.0.0.2

1.基于完整端口的静态Web服务器的服务器套件,可支持各种测试工具.
2.适合用户高并发高负荷的场合的静态页面场合特别适用于图片服务器.
3.为了满足高性能的要求以及保持灵活性,动态内容采用预留接口实现.
4.配置说明
1)配置文件为当前目录下的config.ini
2)ListenPort:指定绑定端口
3)WWWRoot:静态页面内容所在目录
4)AcceptExNum:可承受的突然并发连接(建议采用默认值)
5)MaxWorkThread:工作线程数目(0代表根据CPU数目指定最佳值)
6)ServiceName:安装后服务的名称

较1.0.0.1有多处改进,在17000个并发持续连接的情况下仍有良好的性能,改天放Linux下基于epoll模型的
——————————————————————————————————————————————————

//完成端口的部分代码

//主线程

//创建原始完成端口句柄
hIOCP=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);
if(NULL==hIOCP)
{
//错误处理
}

//创建监听套节字 ListenPort()封装了bind listen等函数
SocketListen=ListenPort();

//绑定原始完成端口句柄到监听套节字SocketListen
if(NULL==CreateIoCompletionPort((HANDLE)SocketListen,hIOCP,0,0))
{
//错误处理
}

//投递AcceptEx
{
unsigned long Num;
unsigned long AcceptExNum;

//GetConfigInt封装了读取配置文件
AcceptExNum=GetConfigInt("Httpd","AcceptExNum",2000);

//投递多个AccpetEx,PostAccpetEx封装了AccpetEx
for(Num=0;Num<AcceptExNum;Num++)
{
PostAccpetEx(NULL);
}
}

{ //启动工作线程

unsigned long MaxWorkThread;
unsigned long threadcount;
DWORD dwThreadId;
HANDLE* hThread;

//读取配置文件中开启线程的数目
MaxWorkThread=GetConfigInt("Httpd","MaxWorkThread",2);

//0代表根据CPU数目设置线程数目 CPU数目*2+2
if(MaxWorkThread<1)
{
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
MaxWorkThread=systemInfo.dwNumberOfProcessors*2+2;
}

hThread=HeapAlloc(GetProcessHeap(),0,sizeof(HANDLE)*MaxWorkThread);

for(threadcount=0;threadcount<MaxWorkThread;threadcount++)
{
hThread[threadcount]=CreateThread(NULL,0,WorkerThread,hIOCP,0,&dwThreadId);
}

//等待工作线程退出,主线程进入阻塞状态
WaitForMultipleObjects(MaxWorkThread,hThread,TRUE,INFINITE);

for(threadcount=0;threadcount<MaxWorkThread;threadcount++)
{
CloseHandle(hThread);
}

HeapFree(GetProcessHeap(),0,hThread);

}



//工作线程

//LOOP循环条件,根据情况设定相应的条件
while(LOOP)
{
BOOL Succes;
P_IOCPDATA IOCPData;//自己定义的数据结构
unsigned long TransByte;
LPWSAOVERLAPPED lpWsaOverlapped;

//获取完成通知
//完成端口的精华就在这里
//通过调用GetQueuedCompletionStatus,线程可以获得原子性的时间段,避免了线程切换的开销
Succes=
GetQueuedCompletionStatus(
hIOCP,
&TransByte,
(unsigned long*)&IOCPData,
&lpWsaOverlapped,
INFINITE);

if(Succes&&NULL!=lpWsaOverlapped&&TransByte)//完全正常
{
IOCPData=(P_IOCPDATA)lpWsaOverlapped;

//根据不同的I/O类型进行不同的处理
switch(IOCPData->IOType)
{
//AcceptEx具有连接和接受的功能
case AcptDone:
{
//有一个AccpetEx完成,再投递一个AccpetEx
PostAccpetEx(NULL);
{
//此段代码可以不调用,未发生错误,不知道为什么,
//如果不调用的话,句柄不会很多,不会超出限制
/*
//更行AccpetEx返回的套节字状态
int ErrorCode;
ErrorCode=
setsockopt(
IOCPData->Socket,
SOL_SOCKET,
SO_UPDATE_ACCEPT_CONTEXT,
(char*)&SocketListen,
sizeof(SocketListen)
);
if(SOCKET_ERROR==ErrorCode)
{
Test();
FreeIOCPData(IOCPData);
continue;
}
*/
}
//绑定AccpetEx返回的套节字到hIOCP
if(hIOCP!=CreateIoCompletionPort((HANDLE)IOCPData->Socket,hIOCP,(unsigned long)IOCPData,0))
{
Test();
FreeIOCPData(IOCPData);
continue;
}
//我写的是一个Web服务器,下面的是Http消息头的分析处理
if(HttpParseHead(IOCPData))
{
Respons(IOCPData);
}
break;

}
//...... 其他处理情况
}
}
//.... 出现错误情况下的处理
}

posted @ 2008-01-14 16:07 测试时代-谷峰 阅读(90) | 评论 (0)编辑 收藏


2007年12月25日

上周测试时代举办了一个测试沙龙,会议议题为《功能自动测试框架探讨》,来得都是行业内有着多年测试和开发经验的朋友,同样沙龙也重点突出了测试框架的议题内容,议题内容为:
1、什么是自动测试框架
2、自动测试框架的类型
3功能自动测试框架的具体技术
4、功能自动测试框架如何设计
其中测试站在测试的角度的上阐述了如何去做好自动化测试框架,开发也从开发的角度上叙述了如何做好自动化测试框架,听了以后,觉得收获非常大,会议后续报道http://bbs.testage.net/thread-10612-1-1.html

目前测试工作大多数以手动为主,并不是各个软件公司不想做自动化测试,无奈再没有成熟单位应用的情况下,但靠每个公司自己的摸索,显然比手动测试代价更大,且项目变化频度过快,也对测试框架提出了挑战,到底公司能够下多大的人力,物力来做测试框架的搭建,想必也是困扰了大家许久。框架这个概念并不是只有在测试里面有,开发同样也有
 框架的概念
     
————————————————————————————————————————————————————

框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种定义认为,框架是可被应用开发者定制的应用骨架。前者是从应用方面而后者是从目的方面给出的定义。

      可以说,一个框架是一个可复用的设计构件,它规定了应用的体系结构,阐明了整个设计、协作构件之间的依赖关系、责任分配和控制流程,表现为一组抽象类以及其实例之间协作的方法,它为构件复用提供了上下文(Context)关系。因此构件库的大规模重用也需要框架。

      构件领域框架方法在很大程度上借鉴了硬件技术发展的成就,它是构件技术、软件体系结构研究和应用软件开发三者发展结合的产物。在很多情况下,框架通常以构件库的形式出现,但构件库只是框架的一个重要部分。框架的关键还在于框架内对象间的交互模式和控制流模式。

       框架比构件可定制性强。在某种程度上,将构件和框架看成两个不同但彼此协作的技术或许更好。框架为构件提供重用的环境,为构件处理错误、交换数据及激活操作提供了标准的方法。

      应用框架的概念也很简单。它并不是包含构件应用程序的小片程序,而是实现了某应用领域通用完备功能(除去特殊应用的部分)的底层服务。使用这种框架的编程人员可以在一个通用功能已经实现的基础上开始具体的系统开发。框架提供了所有应用期望的默认行为的类集合。具体的应用通过重写子类(该子类属于框架的默认行为)或组装对象来支持应用专用的行为。

      应用框架强调的是软件的设计重用性和系统的可扩充性,以缩短大型应用软件系统的开发周期,提高开发质量。与传统的基于类库的面向对象重用技术比较,应用框架更注重于面向专业领域的软件重用。应用框架具有领域相关性,构件根据框架进行复合而生成可运行的系统。框架的粒度越大,其中包含的领域知识就更加完整。


      框架,即framework。其实就是某种应用的半成品,就是一组组件,供你选用完成你自己的系统。简单说就是使用别人搭好的舞台,你来做表演。而且,框架一般是成熟的,不断升级的软件。
————————————————————————————————————————————————————

同样,测试框架也是如此,每个公司力求的最终结果,就是花少量的资源来尽可能多的完成测试任务,所以测试框架的建立以及框架的重用性方面是最值得探讨的地方,沙龙里面“自动化测试的框架要讲究粒度”和“建立测试框架需要一定的开发能力”这2句话说的非常有道理,你不能苛求测试人员完成所有测试应用框架的建立,这是不现实的,时间、资源都不允许。所以被测系统的主营业务,核心应用理当成为框架的首选。

沙龙中提出如何想起来要搭建测试框架这个问题,发言人给了很好也是很出乎意料的答案。并不是所谓的为了提高企业竞争力或是完善公司标准化,完全是为了更快和更高效的解决问题来组织和搭建的。

posted @ 2007-12-25 12:12 测试时代-谷峰 阅读(366) | 评论 (0)编辑 收藏


仅列出标题  下一页

posts - 21, comments - 12, trackbacks - 0, articles - 0

Copyright © 测试时代-谷峰