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

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

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

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

 

基础设置

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

  1. <span class="com">#</span><span class="kwd">define</span><span class="pln"> TTT_FUNCTIONAL_TEST_UTIL_IDLE_CONDITION</span><span class="pun">(</span><span class="pln">f</span><span class="pun">)</span><span class="pun">((</span><span class="typ">TttFunctionalTestUtilIdleCondition</span><span class="pun">)(</span><span class="pln">f</span><span class="pun">))</span>
  2. <span class="com">#</span><span class="kwd">define</span><span class="pln"> TTT_FUNCTIONAL_TEST_UTIL_REACTION_TIME </span><span class="pun">(</span><span class="lit">125000</span><span class="pun">)</span>
  3. <span class="com">#</span><span class="kwd">define</span><span class="pln"> TTT_FUNCTIONAL_TEST_UTIL_REACTION_TIME_LONG </span><span class="pun">(</span><span class="lit">500000</span><span class="pun">)</span>
  4. <span class="kwd">typedef</span><span class="pln"> gboolean </span><span class="pun">(*</span><span class="typ">TttFunctionalTestUtilIdleCondition</span><span class="pun">)(</span><span class="pln">gpointer data</span><span class="pun">);</span>
  5. <span class="kwd">struct</span><span class="pln"> timespec ttt_functional_test_util_default_timeout </span><span class="pun">=</span><span class="pun">{</span>
  6. <span class="pln">  </span><span class="lit">20</span><span class="pun">,</span>
  7. <span class="pln">  </span><span class="lit">0</span><span class="pun">,</span>
  8. <span class="pun">};</span>

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

  1. <span class="kwd">void</span>
  2. <span class="pln">ttt_functional_test_util_reaction_time</span><span class="pun">()</span>
  3. <span class="pun">{</span>
  4. <span class="pln">  usleep</span><span class="pun">(</span><span class="pln">TTT_FUNCTIONAL_TEST_UTIL_REACTION_TIME</span><span class="pun">);</span>
  5. <span class="pun">}</span>
  6. <span class="kwd">void</span>
  7. <span class="pln">ttt_functional_test_util_reaction_time_long</span><span class="pun">()</span>
  8. <span class="pun">{</span>
  9. <span class="pln">  usleep</span><span class="pun">(</span><span class="pln">TTT_FUNCTIONAL_TEST_UTIL_REACTION_TIME_LONG</span><span class="pun">);</span>
  10. <span class="pun">}</span>

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

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

 

与图形化用户界面交互

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

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

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

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

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

  1. <span class="pln">gboolean</span>
  2. <span class="pln">ttt_functional_test_util_idle_test_toggle_active</span><span class="pun">(</span>
  3. <span class="pln">     </span><span class="typ">GtkToggleButton</span><span class="pun">**</span><span class="pln">toggle_button</span><span class="pun">)</span>
  4. <span class="pun">{</span>
  5. <span class="pln">  gboolean do_idle</span><span class="pun">;</span>
  6. <span class="pln">  do_idle </span><span class="pun">=</span><span class="pln"> TRUE</span><span class="pun">;</span>
  7. <span class="pln">  gdk_threads_enter</span><span class="pun">();</span>
  8. <span class="pln">  </span><span class="kwd">if</span><span class="pun">(*</span><span class="pln">toggle_button </span><span class="pun">!=</span><span class="pln"> NULL </span><span class="pun">&&</span>
  9. <span class="pln">     GTK_IS_TOGGLE_BUTTON</span><span class="pun">(*</span><span class="pln">toggle_button</span><span class="pun">)</span><span class="pun">&&</span>
  10. <span class="pln">     gtk_toggle_button_get_active</span><span class="pun">(*</span><span class="pln">toggle_button</span><span class="pun">)){</span>
  11. <span class="pln">    do_idle </span><span class="pun">=</span><span class="pln"> FALSE</span><span class="pun">;</span>
  12. <span class="pln">  </span><span class="pun">}</span>
  13. <span class="pln">  gdk_threads_leave</span><span class="pun">();</span>
  14. <span class="pln">  </span><span class="kwd">return</span><span class="pun">(</span><span class="pln">do_idle</span><span class="pun">);</span>
  15. <span class="pun">}</span>

 

测试场景

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

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

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

(题图:opensource.com)


作者简介:

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

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

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

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

相关推荐