testing.h 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name: wx/testing.h
  3. // Purpose: helpers for GUI testing
  4. // Author: Vaclav Slavik
  5. // Created: 2012-08-28
  6. // Copyright: (c) 2012 Vaclav Slavik
  7. // Licence: wxWindows Licence
  8. /////////////////////////////////////////////////////////////////////////////
  9. #ifndef _WX_TESTING_H_
  10. #define _WX_TESTING_H_
  11. #include "wx/debug.h"
  12. #include "wx/string.h"
  13. #include "wx/modalhook.h"
  14. class WXDLLIMPEXP_FWD_CORE wxMessageDialogBase;
  15. class WXDLLIMPEXP_FWD_CORE wxFileDialogBase;
  16. // ----------------------------------------------------------------------------
  17. // testing API
  18. // ----------------------------------------------------------------------------
  19. // Don't include this code when building the library itself
  20. #ifndef WXBUILDING
  21. #include "wx/beforestd.h"
  22. #include <algorithm>
  23. #include <iterator>
  24. #include <queue>
  25. #include "wx/afterstd.h"
  26. #include "wx/cpp.h"
  27. #include "wx/dialog.h"
  28. #include "wx/msgdlg.h"
  29. #include "wx/filedlg.h"
  30. class wxTestingModalHook;
  31. // Non-template base class for wxExpectModal<T> (via wxExpectModalBase).
  32. // Only used internally.
  33. class wxModalExpectation
  34. {
  35. public:
  36. wxModalExpectation() : m_isOptional(false) {}
  37. virtual ~wxModalExpectation() {}
  38. bool IsOptional() const { return m_isOptional; }
  39. virtual int Invoke(wxDialog *dlg) const = 0;
  40. virtual wxString GetDescription() const = 0;
  41. protected:
  42. // Is this dialog optional, i.e. not required to be shown?
  43. bool m_isOptional;
  44. };
  45. // This must be specialized for each type. The specialization MUST be derived
  46. // from wxExpectModalBase<T>.
  47. template<class T> class wxExpectModal {};
  48. /**
  49. Base class for wxExpectModal<T> specializations.
  50. Every such specialization must be derived from wxExpectModalBase; there's
  51. no other use for this class than to serve as wxExpectModal<T>'s base class.
  52. T must be a class derived from wxDialog.
  53. */
  54. template<class T>
  55. class wxExpectModalBase : public wxModalExpectation
  56. {
  57. public:
  58. typedef T DialogType;
  59. typedef wxExpectModal<DialogType> ExpectationType;
  60. /**
  61. Returns a copy of the expectation where the expected dialog is marked
  62. as optional.
  63. Optional dialogs aren't required to appear, it's not an error if they
  64. don't.
  65. */
  66. ExpectationType Optional() const
  67. {
  68. ExpectationType e(*static_cast<const ExpectationType*>(this));
  69. e.m_isOptional = true;
  70. return e;
  71. }
  72. protected:
  73. virtual int Invoke(wxDialog *dlg) const
  74. {
  75. DialogType *t = dynamic_cast<DialogType*>(dlg);
  76. if ( t )
  77. return OnInvoked(t);
  78. else
  79. return wxID_NONE; // not handled
  80. }
  81. /// Returns description of the expected dialog (by default, its class).
  82. virtual wxString GetDescription() const
  83. {
  84. return wxCLASSINFO(T)->GetClassName();
  85. }
  86. /**
  87. This method is called when ShowModal() was invoked on a dialog of type T.
  88. @return Return value is used as ShowModal()'s return value.
  89. */
  90. virtual int OnInvoked(DialogType *dlg) const = 0;
  91. };
  92. // wxExpectModal<T> specializations for common dialogs:
  93. template<>
  94. class wxExpectModal<wxMessageDialog> : public wxExpectModalBase<wxMessageDialog>
  95. {
  96. public:
  97. wxExpectModal(int id)
  98. {
  99. switch ( id )
  100. {
  101. case wxYES:
  102. m_id = wxID_YES;
  103. break;
  104. case wxNO:
  105. m_id = wxID_NO;
  106. break;
  107. case wxCANCEL:
  108. m_id = wxID_CANCEL;
  109. break;
  110. case wxOK:
  111. m_id = wxID_OK;
  112. break;
  113. case wxHELP:
  114. m_id = wxID_HELP;
  115. break;
  116. default:
  117. m_id = id;
  118. break;
  119. }
  120. }
  121. protected:
  122. virtual int OnInvoked(wxMessageDialog *WXUNUSED(dlg)) const
  123. {
  124. return m_id;
  125. }
  126. int m_id;
  127. };
  128. #if wxUSE_FILEDLG
  129. template<>
  130. class wxExpectModal<wxFileDialog> : public wxExpectModalBase<wxFileDialog>
  131. {
  132. public:
  133. wxExpectModal(const wxString& path, int id = wxID_OK)
  134. : m_path(path), m_id(id)
  135. {
  136. }
  137. protected:
  138. virtual int OnInvoked(wxFileDialog *dlg) const
  139. {
  140. dlg->SetPath(m_path);
  141. return m_id;
  142. }
  143. wxString m_path;
  144. int m_id;
  145. };
  146. #endif
  147. // Implementation of wxModalDialogHook for use in testing, with
  148. // wxExpectModal<T> and the wxTEST_DIALOG() macro. It is not intended for
  149. // direct use, use the macro instead.
  150. class wxTestingModalHook : public wxModalDialogHook
  151. {
  152. public:
  153. wxTestingModalHook()
  154. {
  155. Register();
  156. }
  157. // Called to verify that all expectations were met. This cannot be done in
  158. // the destructor, because ReportFailure() may throw (either because it's
  159. // overriden or because wx's assertions handling is, globally). And
  160. // throwing from the destructor would introduce all sort of problems,
  161. // including messing up the order of errors in some cases.
  162. void CheckUnmetExpectations()
  163. {
  164. while ( !m_expectations.empty() )
  165. {
  166. const wxModalExpectation *expect = m_expectations.front();
  167. m_expectations.pop();
  168. if ( expect->IsOptional() )
  169. continue;
  170. ReportFailure
  171. (
  172. wxString::Format
  173. (
  174. "Expected %s dialog was not shown.",
  175. expect->GetDescription()
  176. )
  177. );
  178. break;
  179. }
  180. }
  181. void AddExpectation(const wxModalExpectation& e)
  182. {
  183. m_expectations.push(&e);
  184. }
  185. protected:
  186. virtual int Enter(wxDialog *dlg)
  187. {
  188. while ( !m_expectations.empty() )
  189. {
  190. const wxModalExpectation *expect = m_expectations.front();
  191. m_expectations.pop();
  192. int ret = expect->Invoke(dlg);
  193. if ( ret != wxID_NONE )
  194. return ret; // dialog shown as expected
  195. // not showing an optional dialog is OK, but showing an unexpected
  196. // one definitely isn't:
  197. if ( !expect->IsOptional() )
  198. {
  199. ReportFailure
  200. (
  201. wxString::Format
  202. (
  203. "A %s dialog was shown unexpectedly, expected %s.",
  204. dlg->GetClassInfo()->GetClassName(),
  205. expect->GetDescription()
  206. )
  207. );
  208. return wxID_NONE;
  209. }
  210. // else: try the next expectation in the chain
  211. }
  212. ReportFailure
  213. (
  214. wxString::Format
  215. (
  216. "A dialog (%s) was shown unexpectedly.",
  217. dlg->GetClassInfo()->GetClassName()
  218. )
  219. );
  220. return wxID_NONE;
  221. }
  222. protected:
  223. virtual void ReportFailure(const wxString& msg)
  224. {
  225. wxFAIL_MSG( msg );
  226. }
  227. private:
  228. std::queue<const wxModalExpectation*> m_expectations;
  229. wxDECLARE_NO_COPY_CLASS(wxTestingModalHook);
  230. };
  231. // Redefining this value makes it possible to customize the hook class,
  232. // including e.g. its error reporting.
  233. #define wxTEST_DIALOG_HOOK_CLASS wxTestingModalHook
  234. #define WX_TEST_IMPL_ADD_EXPECTATION(pos, expect) \
  235. const wxModalExpectation& wx_exp##pos = expect; \
  236. wx_hook.AddExpectation(wx_exp##pos);
  237. /**
  238. Runs given code with all modal dialogs redirected to wxExpectModal<T>
  239. hooks, instead of being shown to the user.
  240. The first argument is any valid expression, typically a function call. The
  241. remaining arguments are wxExpectModal<T> instances defining the dialogs
  242. that are expected to be shown, in order of appearance.
  243. Some typical examples:
  244. @code
  245. wxTEST_DIALOG
  246. (
  247. rc = dlg.ShowModal(),
  248. wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt")
  249. );
  250. @endcode
  251. Sometimes, the code may show more than one dialog:
  252. @code
  253. wxTEST_DIALOG
  254. (
  255. RunSomeFunction(),
  256. wxExpectModal<wxMessageDialog>(wxNO),
  257. wxExpectModal<MyConfirmationDialog>(wxYES),
  258. wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt")
  259. );
  260. @endcode
  261. Notice that wxExpectModal<T> has some convenience methods for further
  262. tweaking the expectations. For example, it's possible to mark an expected
  263. dialog as @em optional for situations when a dialog may be shown, but isn't
  264. required to, by calling the Optional() method:
  265. @code
  266. wxTEST_DIALOG
  267. (
  268. RunSomeFunction(),
  269. wxExpectModal<wxMessageDialog>(wxNO),
  270. wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt").Optional()
  271. );
  272. @endcode
  273. @note By default, errors are reported with wxFAIL_MSG(). You may customize this by
  274. implementing a class derived from wxTestingModalHook, overriding its
  275. ReportFailure() method and redefining the wxTEST_DIALOG_HOOK_CLASS
  276. macro to be the name of this class.
  277. @note Custom dialogs are supported too. All you have to do is to specialize
  278. wxExpectModal<> for your dialog type and implement its OnInvoked()
  279. method.
  280. */
  281. #ifdef HAVE_VARIADIC_MACROS
  282. #define wxTEST_DIALOG(codeToRun, ...) \
  283. { \
  284. wxTEST_DIALOG_HOOK_CLASS wx_hook; \
  285. wxCALL_FOR_EACH(WX_TEST_IMPL_ADD_EXPECTATION, __VA_ARGS__) \
  286. codeToRun; \
  287. wx_hook.CheckUnmetExpectations(); \
  288. }
  289. #endif /* HAVE_VARIADIC_MACROS */
  290. #endif // !WXBUILDING
  291. #endif // _WX_TESTING_H_