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 Test8848-谷峰 阅读(466) | 评论 (0)编辑 收藏

这种情况大多数是因为你的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 Test8848-谷峰 阅读(23122) | 评论 (4)编辑 收藏

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



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 Test8848-谷峰| 编辑 收藏


大多数测试工程师再做性能测试的时候,都接触到基于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 Test8848-谷峰 阅读(297) | 评论 (0)编辑 收藏

在网上闲逛的是后看到关于单元测试的东西,觉得非常不错,拿过来一同分享一下,测试中有一个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 Test8848-谷峰 阅读(700) | 评论 (0)编辑 收藏

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 Test8848-谷峰 阅读(320) | 评论 (0)编辑 收藏

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

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

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

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

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

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

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

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


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

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

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

posted @ 2007-12-25 12:12 Test8848-谷峰 阅读(1693) | 评论 (0)编辑 收藏


搭建Jboss测试环境

Jboss结构说明:

$JBOSS-HOME/server/下有3个目录,all/default/minimal,它们是表示3种配置,全部的配置、默认配置、最小配置,我们在启动JBOSS服务时,可以指定

run –c all 表示是启动all配置(将会加载所有服务)run 表示是以默认配置启动;

run –c mimimal 表示是启动mimimal配置。这三者所加载的服务数量不同,具体区别可查阅JBOSS相关文档,你还可以自己定义一个配置,如test,属于高手去为了性能上的考虑了,那这份文档对你的帮助不会太大了。

 如果你确定了JBOSS服务启动的配置,那么你的应用及相关配置就需要部署在对应的目录下的相关的路径下,如all/default/minimal/下。

JBOSS常用配置文件的路径

为了描述方便,以下描述将以JBOSS3.2.6版本,default配置为例,如果你以别的配置启动,则在对应的目录下寻找或配置相关文件。

1、相关DTD定义文件位于$JBOSS-HOME/docs/dtd,在其下的文件中可以查阅某个XML配置文件中某个元素或属性的意义。

2、数据源配置文件的路径,$JBOSS-HOME/server/default/deploy,文件格式必须是*-ds.xml的样式。不同的数据库配置参数可参考$JBOSS-HOME/docs/examples/jca/下的示例文档。有关参数的细节在下面会有一定的描述。

3、公共的jar包我们可以放在$JBOSS-HOME/server/default/lib下,如数据库的JDBC jar,log4j.jar等,如果这些JAR已经在该lib下放置了,那么我们的应用WEB-INF/lib下不应该再放置,否则JBOSS也会将他们再一次加载,白白占用内存。

4、配置日志的文件,log4j.xml位于$JBOSS-HOME/server/default/conf下,具体配置下文还会有一点介绍

5$JBOSS-HOME/server/default/work下存放的是JSP编译后的.java.class文件,如果调试JSP时出错了,可以到该目录下(一级级去翻吧)去找对应的文件,调试问题。还有个小TIPS,有时我们明明把一个JSP更新到对应的目录里了,可是去刷新页面时,内容还是旧的,为了解决该问题,我们可以到work目录下,将对应的.java.class文件删了,再刷新即可,这个原因不好推测,有时可以自动刷新,有时不行。单个的TOMCAT都能自动刷新,不知为何集成到JBOSS里却有时不行。还需注意的事,你在清除旧的javaclass文件时,不能为了省事,而直接删除目录,否则刷新时会报错的,等着你的就是要重启JBOSS服务了。

6$JBOSS-HOME/server/default/log下存放的是日志文件,默认的log4j.xml配置是将日志输出到该文件夹下的server.log文件,可以去查看信息进行调试。

7、我们的应用一般部署在$JBOSS-HOME/server/default/deploy/jbossweb-tomcat50.sar/下,建个TestCrm.war目录,把我们的整个应用部署在该.war目录下,这样JSPServlet将由tomcat引擎来解析处理。

8、配置应用的WEB服务的端口号及参数。

JBOSS3.2.6是在…./ deploy/jbossweb-tomcat50.sar/server.xml中,自己找8080,修改为你想设定的端口即可。

JBOSS3.2.3是在…/default/deploy/jbossweb-tomcat41.sar/META-INF/jboss-service.xml中配置。

 

JBOSS 应用程序的配置调整

配置负载均衡

需要修改配置文件以适应apache中配置的“session sticky配置”。如下:

Jboss3.2.3修改:

%JBOSS_HOME%/server/default/deploy/jbossweb-tomcat41.sar/META-INF/ jboss-service.xml

 

修改<Engine name="MainEngine" defaultHost="localhost">

<Engine name="MainEngine" jvmRoute=" server106"  defaultHost="localhost">

与上述workers2.properties定义的内容一致。

 

Jboss3.2.6修改:

%JBOSS_HOME%/server/default/deploy/jbossweb-tomcat50.sar/server.xml

修改<Engine name="jboss.web" defaultHost="localhost">为:

<Engine name="jboss.web" jvmRoute=" server106" defaultHost="localhost">

与上述workers2.properties定义的内容一致。

Jboss4.0的修改和Jboss3.2.6的一致。

 

如上配置后,就可以实现apache+jboss的负载均衡。

配置集群(Clustering),调整参数

如果我们要想使得几台JBOSS应用服务器互为备份(仅限于SESSION),在群内一台JBOSS服务器down掉的时候,用户不受影响,继续以登陆用户身份进行工作,则我们需要配置Clustering,并启动相关服务。下面将描述相关配置项:

1)、修改应用APP的web.xml:

Jboss3.2.6:

../default/deploy/jbossweb-tomcat50.sar/Test.war/WEB-INF/web.xml

 

Jboss3.2.3

../default/deploy/jbossweb-tomcat41.sar/Test.war/WEB-INF/web.xml

修改内容如下:

 

<?xml version="1.0"?>

<!DOCTYPE web-app PUBLIC

"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"

"http://java.sun.com/dtd/web-app_2_3.dtd ">

<web-app>

…….

…..

….

<distributable/>

</web-app>

2)、需要启动一个服务cluster-service.xml

如果是以run -c all 来启动jboss服务,则默认是将cluster服务启动,无需做什么配置工作,只需确认相关文件是否存在即可。

确认…/server/all/deploy/下存在cluster-service.xml文件;

确认…/server/all/lib/下存在jgroups.jar文件,如果没有则重新安装jboss。

如果应用程序部署在default配置下,则需将上述的两个文件分别拷入

…/server/default/deploy/、…/server/default/lib/目录下。

 

配置完毕,如有异常,请查看日志。更详细的信息请参阅JBOSS.com上提供的admin.pdf。

配置数据源,调整参数

各种类型的数据库,都可以根据%JBOSS_HOME%/docs/examples/jca/下的示例文档配置,更详细的参数配置可参阅JBOSS.com上的文档或…/docs/dtd/下的dtd定义来配置。

下面以oracle为例,提供我们在生产机环境的配置。

xml文件需要被部署在%JBOSS_HOME%/server/all(or default)/deploy/下,对应的数据库驱动程序jarcopy至部署的配置的lib下,如…all/lib/…default/lib/下。

 

<datasources>

 <local-tx-datasource>

    <jndi-name>jdbc/TestDataSource</jndi-name>

    <connection-url>jdbc:oracle:thin:@192.168.1.2:1521:test</connection-url>

    <!--<connection-url>jdbc:oracle:oci:@youroracle-tns-name</connection-url>-->

    <driver-class>oracle.jdbc.driver.OracleDriver</driver-class>

    <user-name>test</user-name>

<password>test</password>

    <!-- Uses the pingDatabase method to check a connection is still valid before handing it out from the pool -->

    <valid-connection-checker-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleValidConnectionChecker</valid-connection-checker-class-name>

    <!-- Checks the Oracle error codes and messages for fatal errors -->

<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter</exception-sorter-class-name>

        <!-- sql to call when connection is created

        <new-connection-sql>some arbitrary sql</new-connection-sql>

        -->

    <min-pool-size>5</min-pool-size><!—最小连接池数目-->

    <max-pool-size>800</max-pool-size><!—最大连接池数目-->

    <idle-timeout-minutes>5</idle-timeout-minutes><!—数据库连接空闲时间,单位为分钟,如果负载较大,可以设为5,如果一般,可以设为3-->

<!—在从连接池里获得一个连接之前先通过执行一个简单的SQL来校验connection的有效性-->

    <check-valid-connection-sql>select 1 from dual</check-valid-connection-sql>

 </local-tx-datasource>

</datasources>

 

如有多个数据源,则根据数据库的类型,配置完成,部署在上述路径下即可。

 

在代码中可通过JNDI来从连接池中获取连接。

……

Context ctx = new javax.naming.InitialContext();

DataSource ds = (DataSource)ctx.lookup("java:jdbc/TestDataSource ");

Connection con = ds.getConnection();

posted @ 2007-12-25 11:42 Test8848-谷峰 阅读(403) | 评论 (0)编辑 收藏

第4次TestAge测试时代测试专家沙龙于12月23日在北京举行

第四次TestAge测试时代测试专家沙龙


议题:功能自动测试框架探讨

主持嘉宾:王凯

时间:2007-12-23(周日),下午14001700

地点:(确认后通知)

细节话题:
1、什么是自动测试框架
2、自动测试框架的类型
3、功能自动测试框架的具体技术
4、功能自动测试框架如何设计
5、。。。。。。

活动要求:
人数要求:人数有限
人员要求:测试技术经理、测试技术主管、高级测试工程师、自动化测试工程师,有至少4年以上软件测试的实际工作经验或者有2年专职的自动化测试开发经验。

报名方式:
请下载Testage测试专家沙龙申请表,并将申请表发送给我们,我们评估后会及时的和您联系。下载地址
 

posted @ 2007-11-30 14:36 Test8848-谷峰 阅读(189) | 评论 (0)编辑 收藏

对于开源工具测试管理工具testlink,想必大家都不陌生,这个工具非常小巧,安装起来也比较简单,是基于WAMP/LAMP的一个B/S结构的软件,至于什么是WAMP/LAMP,我就不用解释了,开源中应用非常广泛的架构。
testlink的下载地址http://bbs.testage.net/thread-10037-1-1.html
testlink的安装视频过几天也会发布,欢迎广大网友随时关注测试时代的论坛开源工具板块http://bbs.testage.net/forum-11-1.html


1.关于使用的过程,我这里不在重复,网上的教程也很多,关于需求我先贴副图出来

这里可以并不能看出如何去自动导入需求,在这里我们只能先自己创建需求的类型“功能需求”、“性能需求”、“安全性需求”、“其他需求”等等。自动导入需求只能针对下一级模块


2.Testlink “Import”目前可以支持三种倒入模式CVS、CVS(doors)、XML三种模式,我们就重点来看一下XML模式,要想使用XML导入测试需求,首先要对XML有一些了解才行

XML介绍http://baike.baidu.com/view/63.htm,另外大家可以下载一个XML的语法看看,和html非常类似,假如你没有这方面经验的话可以利用我已经写好的XML模板,我一般会使用我一般会使用Ultraedit来编辑我的测试需求

大家可以看到我把基本的语法都标示出来了,不过更高级的应用还需要多多了解一下XML的语法。最后贴图来看一下效果

建议大家把每一个需求都分门别类,如xxx项目-----测试需求-----功能需求、性能需求、安全性需求等等,如果有想要xml语法的朋友,请留下邮件地址,我会统一发给大家。

posted @ 2007-11-28 15:54 Test8848-谷峰 阅读(1471) | 评论 (3)编辑 收藏

仅列出标题
共3页: 1 2 3 

posts - 28, comments - 17, trackbacks - 0, articles - 0

Copyright © Test8848-谷峰