手机版
你好,游客 登录 注册
背景:
阅读新闻

用 C 语言对 Gtk+ 应用进行功能测试

[日期:2017-10-13] 来源:Linux中国  作者:Linux [字体: ]

用 C 语言对 Gtk+ 应用进行功能测试

这个简单教程教你如何测试你应用的功能。自动化测试用来保证你程序的质量以及让它以预想的运行。单元测试只是检测你算法的某一部分,而并不注重各组件间的适应性。这就是为什么会有功能测试,它有时也称为集成测试。

功能测试简单地与你的用户界面进行交互,无论它是网站还是桌面应用。为了展示功能测试如何工作,我们以测试一个 Gtk+ 应用为例。为了简单起见,这个教程里,我们使用 Gtk+ 2.0 教程的示例。

 

基础设置

对于每一个功能测试,你通常需要定义一些全局变量,比如 “用户交互时延” 或者 “失败的超时时间”(也就是说,如果在指定的时间内一个事件没有发生,程序就要中断)。

  1. #define TTT_FUNCTIONAL_TEST_UTIL_IDLE_CONDITION(f)((TttFunctionalTestUtilIdleCondition)(f))
  2. #define TTT_FUNCTIONAL_TEST_UTIL_REACTION_TIME (125000)
  3. #define TTT_FUNCTIONAL_TEST_UTIL_REACTION_TIME_LONG (500000)
  4. typedef gboolean (*TttFunctionalTestUtilIdleCondition)(gpointer data);
  5. struct timespec ttt_functional_test_util_default_timeout ={
  6.   20,
  7.   0,
  8. };

现在我们可以实现我们自己的超时函数。这里,为了能够得到期望的延迟,我们采用 usleep 函数。

  1. void
  2. ttt_functional_test_util_reaction_time()
  3. {
  4.   usleep(TTT_FUNCTIONAL_TEST_UTIL_REACTION_TIME);
  5. }
  6. void
  7. ttt_functional_test_util_reaction_time_long()
  8. {
  9.   usleep(TTT_FUNCTIONAL_TEST_UTIL_REACTION_TIME_LONG);
  10. }

直到获得控制状态,超时函数才会推迟执行。这对于一个异步执行的动作很有帮助,这也是为什么采用这么长的时延。

  1. void
  2. ttt_functional_test_util_idle_condition_and_timeout(
  3.      TttFunctionalTestUtilIdleCondition idle_condition,
  4.      struct timespec *timeout,
  5.      pointer data)
  6. {
  7.   struct timespec start_time, current_time;
  8.   clock_gettime(CLOCK_MONOTONIC,
  9.                 &start_time);
  10.   while(TTT_FUNCTIONAL_TEST_UTIL_IDLE_CONDITION(idle_condition)(data)){
  11.     ttt_functional_test_util_reaction_time();
  12.     clock_gettime(CLOCK_MONOTONIC,
  13.                   &current_time);
  14.     if(start_time.tv_sec +timeout->tv_sec < current_time.tv_sec){
  15.       break;
  16.     }
  17.   }
  18.   ttt_functional_test_util_reaction_time();
  19. }

 

与图形化用户界面交互

为了模拟用户交互的操作, Gdk 库 为我们提供了一些需要的函数。要完成我们的工作,我们只需要如下 3 个函数:

  • gdk_display_warp_pointer()
  • gdk_test_simulate_button()
  • gdk_test_simulate_key()

举个例子,为了测试按钮点击,我们可以这么做:

  1. gboolean
  2. ttt_functional_test_util_button_click(GtkButton*button)
  3. {
  4.   GtkWidget*widget;
  5.   GdkWindow*window;
  6.   gint x, y;
  7.   gint origin_x, origin_y;
  8.   if(button == NULL ||
  9.      !GTK_IS_BUTTON(button)){
  10.     return(FALSE);
  11.   }
  12.   widget = button;
  13.   if(!GTK_WIDGET_REALIZED(widget)){
  14.     ttt_functional_test_util_reaction_time_long();
  15.   }
  16.   /* retrieve window and pointer position */
  17.   gdk_threads_enter();
  18.   window = gtk_widget_get_window(widget);
  19.   x = widget->allocation.x + widget->allocation.width /2.0;
  20.   y = widget->allocation.y + widget->allocation.height /2.0;
  21.   gdk_window_get_origin(window,&origin_x,&origin_y);
  22.   gdk_display_warp_pointer(gtk_widget_get_display(widget),
  23.                            gtk_widget_get_screen(widget),
  24.                            origin_x + x, origin_y + y);
  25.   gdk_threads_leave();
  26.   /* click the button */
  27.   ttt_functional_test_util_reaction_time();
  28.   gdk_test_simulate_button(window,
  29.                            x,
  30.                            y,
  31.                            1,
  32.                            GDK_BUTTON1_MASK,
  33.                            GDK_BUTTON_PRESS);
  34.   ttt_functional_test_util_reaction_time();
  35.   gdk_test_simulate_button(window,
  36.                            x,
  37.                            y,
  38.                            1,
  39.                            GDK_BUTTON1_MASK,
  40.                            GDK_BUTTON_RELEASE);
  41.   ttt_functional_test_util_reaction_time();
  42.   ttt_functional_test_util_reaction_time_long();
  43.   return(TRUE);
  44. }

我们想要保证按钮处于激活状态,因此我们提供一个空闲条件函数:

  1. gboolean
  2. ttt_functional_test_util_idle_test_toggle_active(
  3.      GtkToggleButton**toggle_button)
  4. {
  5.   gboolean do_idle;
  6.   do_idle = TRUE;
  7.   gdk_threads_enter();
  8.   if(*toggle_button != NULL &&
  9.      GTK_IS_TOGGLE_BUTTON(*toggle_button)&&
  10.      gtk_toggle_button_get_active(*toggle_button)){
  11.     do_idle = FALSE;
  12.   }
  13.   gdk_threads_leave();
  14.   return(do_idle);
  15. }

 

测试场景

因为这个 Tictactoe 程序非常简单,我们只需要确保点击了一个 GtkToggleButton 按钮即可。一旦该按钮肯定进入了激活状态,功能测试就可以执行。为了点击按钮,我们使用上面提到的很方便的 util 函数。

如图所示,我们假设,填满第一行,玩家 A 就赢,因为玩家 B 没有注意,只填充了第二行。

  1. GtkWindow*window;
  2. Tictactoe*ttt;
  3. void*
  4. ttt_functional_test_gtk_main(void*)
  5. {
  6.   gtk_main();
  7.   pthread_exit(NULL);
  8. }
  9. void
  10. ttt_functional_test_dumb_player_b()
  11. {
  12.   GtkButton*buttons[3][3];
  13.   guint i;
  14.   /* to avoid race-conditions copy the buttons */
  15.   gdk_threads_enter();
  16.   memcpy(buttons, ttt->buttons,9*sizeof(GtkButton*));
  17.   gdk_threads_leave();
  18.   /* TEST 1 - the dumb player B */
  19.   for(i =0; i <3; i++){
  20.     /* assert player A clicks the button successfully */
  21.     if(!ttt_functional_test_util_button_click(buttons[0][i])){
  22.       exit(-1);
  23.     }
  24.     functional_test_util_idle_condition_and_timeout(
  25.          ttt_functional_test_util_idle_test_toggle_active,
  26.          ttt_functional_test_util_default_timeout,
  27.          &buttons[0][i]);
  28.     /* assert player B clicks the button successfully */
  29.     if(!ttt_functional_test_util_button_click(buttons[1][i])){
  30.       exit(-1);
  31.     }
  32.     functional_test_util_idle_condition_and_timeout(
  33.          ttt_functional_test_util_idle_test_toggle_active,
  34.          ttt_functional_test_util_default_timeout,
  35.          &buttons[1][i]);
  36.   }
  37. }
  38. int
  39. main(int argc,char**argv)
  40. {
  41.   pthread_t thread;
  42.   gtk_init(&argc,&argv);
  43.   /* start the tictactoe application */
  44.   window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  45.   ttt = tictactoe_new();
  46.   gtk_container_add(window, ttt);
  47.   gtk_widget_show_all(window);
  48.   /* start the Gtk+ dispatcher */
  49.   pthread_create(&thread, NULL,
  50.                  ttt_functional_test_gtk_main, NULL);
  51.   /* launch test routines */
  52.   ttt_functional_test_dumb_player_b();
  53.   /* terminate the application */
  54.   gdk_threads_enter();
  55.   gtk_main_quit();
  56.   gdk_threads_leave();
  57.   return(0);
  58. }

(题图:opensource.com)


作者简介:

Joël Krähemann - 精通 C 语言编程的自由软件爱好者。不管代码多复杂,它也是一点点写成的。作为高级的 Gtk+ 程序开发者,我知道多线程编程有多大的挑战性,有了多线程编程,我们就有了未来需求的良好基础。

摘自: https://opensource.com/article/17/7/functional-testing

作者:Joël Krähemann 译者:sugarfillet 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

本文永久更新链接地址http://www.linuxidc.com/Linux/2017-10/147545.htm

linux
相关资讯       GTK+ 
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数

       

评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款