unittest.h
Go to the documentation of this file.
1 //
2 // Bareflank Hypervisor
3 //
4 // Copyright (C) 2015 Assured Information Security, Inc.
5 // Author: Rian Quinn <quinnr@ainfosec.com>
6 // Author: Brendan Kerrigan <kerriganb@ainfosec.com>
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Lesser General Public
10 // License as published by the Free Software Foundation; either
11 // version 2.1 of the License, or (at your option) any later version.
12 //
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Lesser General Public License for more details.
17 //
18 // You should have received a copy of the GNU Lesser General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 
22 #ifndef UNITTEST_H
23 #define UNITTEST_H
24 
25 #ifdef OS_LINUX
26 #define LINUX_TARGET
27 #endif
28 
29 #include <gsl/gsl>
30 
31 #include <memory>
32 #include <cstdlib>
33 #include <stdlib.h>
34 #include <string>
35 #include <iostream>
36 
37 #include <exception.h>
38 #include <view_as_pointer.h>
39 
40 #define NO_HIPPOMOCKS_NAMESPACE
41 
42 #pragma GCC system_header
43 #include <hippomocks.h>
44 
46 {
47  bool caught;
53 };
54 
55 inline auto operator"" _ut_ree(const char *str, std::size_t len)
56 { (void)len; return std::make_shared<std::runtime_error>(str); }
57 
58 inline auto operator"" _ut_lee(const char *str, std::size_t len)
59 { (void)len; return std::make_shared<std::logic_error>(str); }
60 
61 inline auto operator"" _ut_iae(const char *str, std::size_t len)
62 { (void)len; return std::make_shared<std::invalid_argument>(str); }
63 
64 inline auto operator"" _ut_ore(const char *str, std::size_t len)
65 { (void)len; return std::make_shared<std::out_of_range>(str); }
66 
67 inline auto operator"" _ut_dme(const char *str, std::size_t len)
68 { (void)len; return std::make_shared<std::domain_error>(str); }
69 
70 inline auto operator"" _ut_ffe(const char *str, std::size_t len)
71 { (void)len; return std::make_shared<gsl::fail_fast>(str); }
72 
73 inline auto operator"" _ut_bae(const char *str, std::size_t len)
74 { (void)str; (void)len; return std::make_shared<std::bad_alloc>(); }
75 
76 template<class T, class = typename std::enable_if<std::is_integral<T>::value>::type>
77 auto make_ptr(const T ptr)
78 { return reinterpret_cast<void *>(ptr); }
79 
80 template<class P, class T, class = typename std::enable_if<std::is_integral<T>::value>::type>
81 auto make_ptr(const T ptr)
82 { return reinterpret_cast<P *>(ptr); }
83 
84 template<class T, class = typename std::enable_if<std::is_pointer<T>::value>::type>
85 auto make_uintptr(const T ptr)
86 { return reinterpret_cast<uintptr_t>(ptr); }
87 
100 #define expect_true(c) expect_true_with_args(c, gsl::cstring_span<>(#c), gsl::cstring_span<>(__PRETTY_FUNCTION__), __LINE__)
101 
114 #define expect_false(c) expect_false_with_args(c, gsl::cstring_span<>(#c), gsl::cstring_span<>(__PRETTY_FUNCTION__), __LINE__)
115 
162 #define expect_exception(f, e) expect_exception_with_args(f, e, __PRETTY_FUNCTION__, __LINE__)
163 
198 #define expect_no_exception(f) expect_no_exception_with_args(f, __PRETTY_FUNCTION__, __LINE__)
199 
229 #define RUN_UNITTEST_WITH_MOCKS(a,b) \
230  this->run_unittest_with_mocks(a,b, static_cast<const char *>(__PRETTY_FUNCTION__), __LINE__);
231 
246 #define RUN_ALL_TESTS(ut) [&]() -> decltype(auto) { (void) argc; (void) argv; ut _ut; return _ut.run(); }()
247 
254 template<class T> void
256 { }
257 
258 namespace bfn
259 {
260 
271 template<class T> auto
272 mock_no_delete(MockRepository &mocks)
273 {
274  auto &&ptr = mocks.Mock<T>();
275  mocks.OnCallDestructor(ptr);
276 
277  return ptr;
278 }
279 
289 template<class T> auto
290 mock_shared(MockRepository &mocks)
291 { return std::shared_ptr<T>(mocks.Mock<T>(), no_delete<T>); }
292 
302 template<class T> auto
303 mock_unique(MockRepository &mocks)
304 { return std::unique_ptr<T>(mock_no_delete<T>(mocks)); }
305 
306 }
307 
315 static void
316 check_exception_type(struct exception_state &state, const std::exception &caught, const std::exception &expected)
317 {
318  state.caught = true;
319  state.caught_type = std::string(typeid(caught).name());
320  state.caught_what = caught.what();
321 
322  if (typeid(caught) != typeid(expected))
323  {
324  state.wrong_exception = true;
325  return;
326  }
327 
328  if (state.expecting_what.size() > 0 && state.caught_what != state.expecting_what)
329  state.wrong_exception = true;
330 }
331 
355 class unittest
356 {
357 
358  // -------------------------------------------------------------------------
359  // Public Interface
360  // -------------------------------------------------------------------------
361 
362 protected:
363 
382  virtual bool
383  list() { return true; };
384 
404  virtual bool
405  init() { return true; };
406 
426  virtual bool
427  fini() { return true; };
428 
429  template<typename T>
430  void run_unittest_with_mocks(MockRepository &mocks, T lamda, const char *func, int line)
431  {
432  // TODO: Would be great if we could get a printout of the functions
433  // that are called that we were not expecting. Would also be great
434  // to get a list of calls that we were expecting that were not called.
435  //
436  // This will require some mods to HippoMocks to store a list of both
437  // types of problems, and then get that list when an exception
438  // occurs.
439  //
440  // There also seems to be a lot of logic in HippMocks for the
441  // NotImplementedException that doesn't ever seem to be filled in
442  // correctly. e.what() always returns "std::exception" but there
443  // appears to be logic to fill in e.what() with something else. Would
444  // be great to clean that up.
445 
446  std::cout << std::uppercase;
447 
448  try
449  {
450  lamda();
451  }
452  catch (std::exception &e)
453  {
454  this->expect_failed(e.what(), func, line);
455  }
456 
457  try
458  {
459  m_pass += mocks.VerifyAll();
460  }
461  catch (std::exception &e)
462  {
463  this->expect_failed(e.what(), func, line);
464  }
465 
466  mocks.reset();
467 
468  // There is an issue with clang-tidy were is basically doesn't see the
469  // call to this from hippomocks, so we run it here to silence the
470  // warnings. Hippomocks currently doesn't support the reuse of the
471  // mocking engine after a call to reset, so it's safe to run this as
472  // any code that tries to use the mocking engine will fail regardless.
473  MockRepoInstanceHolder<0>::instance = 0;
474  }
475 
476  void
477  compare_exceptions(const struct exception_state &state, gsl::cstring_span<> func, int line, int path_id = -1)
478  {
479  std::string not_caught = "no exception was caught";
480  std::string wrong_caught = "wrong exception caught";
481 
482  if (path_id >= 0)
483  {
484  not_caught.append(std::string(" on path number ") + std::to_string(path_id));
485  wrong_caught.append(std::string(" on path number ") + std::to_string(path_id));
486  }
487 
488  if (!state.caught)
489  {
490  this->expect_failed(not_caught.c_str(), func.data(), line);
491  std::cerr << " - expecting: " << '\n';
492  std::cout << " - type: " << state.expecting_type << '\n';
493  std::cout << " - what: " << state.expecting_what << '\n';
494  }
495  else
496  {
497  if (state.wrong_exception)
498  {
499  this->expect_failed(wrong_caught.c_str(), func.data(), line);
500  std::cerr << " - caught: " << '\n';
501  std::cout << " - type: " << state.caught_type << '\n';
502  std::cout << " - what: " << state.caught_what << '\n';
503  std::cerr << " - expecting: " << '\n';
504  std::cout << " - type: " << state.expecting_type << '\n';
505  std::cout << " - what: " << state.expecting_what << '\n';
506  }
507  else
508  {
509  this->inc_pass();
510  }
511  }
512  }
513 
514 public:
515 
569  template <typename F> void
570  expect_exception_with_args(F &&f, std::shared_ptr<const std::exception> expected,
571  gsl::cstring_span<> func, int line, int path_id = -1)
572  {
573  struct exception_state state = {false, false, "", "", "", ""};
574 
575  state.expecting_type = std::string(typeid(*expected).name());
576  state.expecting_what = expected->what();
577 
578  try { f(); }
579  catch (BaseException &be) { throw; }
580  catch (bfn::general_exception &ge) { check_exception_type(state, ge, *expected); }
581  catch (std::exception &e) { check_exception_type(state, e, *expected); }
582  catch (...)
583  {
584  state.wrong_exception = true;
585  state.caught = true;
586  state.caught_type = "unknown exception";
587  std::cerr << "unknown exception caught" << '\n';
588  }
589 
590  compare_exceptions(state, func, line, path_id);
591  }
592 
631  template <typename F> void
632  expect_no_exception_with_args(F &&f, gsl::cstring_span<> func, int line, int path_id = -1)
633  {
634  struct exception_state state = {false, false, "", "", "", ""};
635 
636  try { f(); }
637  catch (BaseException &be) { throw; }
638  catch (bfn::general_exception &ge)
639  {
640  state.caught = true;
641  state.caught_type = std::string("expected not to catch ") + typeid(ge).name();
642  }
643  catch (std::exception &e)
644  {
645  state.caught = true;
646  state.caught_type = std::string("expected not to catch ") + typeid(e).name() + ": "_s + e.what();
647  }
648  catch (...)
649  {
650  state.caught = true;
651  state.caught_type = "caught unkown exception";
652  }
653 
654  if (path_id >= 0)
655  state.caught_type.append(std::string(" on path number ") + std::to_string(path_id));
656 
657  this->expect_false_with_args(state.caught, gsl::string_span<>(state.caught_type), func, line);
658  }
659 
660 
674  void
675  expect_true_with_args(bool condition, gsl::cstring_span<> condition_text, gsl::cstring_span<> func, int line)
676  {
677  if (condition)
678  {
679  this->inc_pass();
680  return;
681  }
682 
683  std::string reason = std::string(condition_text.data()) + " is false";
684  this->expect_failed(reason.c_str(), func.data(), line);
685  }
686 
692  // this function.
701  void
702  expect_false_with_args(bool condition, gsl::cstring_span<> condition_text, gsl::cstring_span<> func, int line)
703  {
704  if (!condition)
705  {
706  this->inc_pass();
707  return;
708  }
709 
710  std::string reason = std::string(condition_text.data());
711  this->expect_failed(reason.c_str(), func.data(), line);
712  }
713 
714 protected:
715 
716  void inc_pass() { m_pass++; }
717  void inc_fail() { m_fail++; }
718 
719  // -------------------------------------------------------------------------
720  // Private Interface
721  // -------------------------------------------------------------------------
722 
723 private:
724 
725  decltype(auto)
726  internal_init()
727  {
728  m_pass = 0;
729  m_fail = 0;
730 
731  return true;
732  }
733 
734  decltype(auto)
735  internal_fini()
736  {
737  if (m_fail > 0)
738  {
739  std::cout << '\n';
740  std::cout << "totals: ";
741  std::cout << std::dec << m_pass << " passed, ";
742  std::cout << "\033[1;31m";
743  std::cout << std::dec << m_fail << " failed";
744  std::cout << "\033[0m";
745  std::cout << '\n';
746 
747  return false;
748  }
749  else
750  {
751  std::cout << "totals: ";
752  std::cout << "\033[1;32m";
753  std::cout << std::dec << m_pass << " passed, ";
754  std::cout << "\033[0m";
755  std::cout << std::dec << m_fail << " failed";
756  std::cout << '\n';
757 
758  return true;
759  }
760  }
761 
762 public:
763 
765  m_pass(0),
766  m_fail(0)
767  { }
768 
769  virtual ~unittest() {}
770 
771  decltype(auto)
772  run()
773  {
774  if (this->internal_init() == false)
775  return EXIT_FAILURE;
776 
777  if (this->init() == false)
778  {
779  std::cout << "\033[1;31mFAILED\033[0m: init" << '\n';
780  return EXIT_FAILURE;
781  }
782 
783  try
784  {
785  if (this->list() == false)
786  {
787  std::cout << "\033[1;31mFAILED\033[0m: list" << '\n';
788  return EXIT_FAILURE;
789  }
790  }
791  catch (std::exception &e)
792  {
793  std::cout << "unexpected exception was caught: " << e.what() << '\n';
794  std::cout << "\033[1;31mFAILED\033[0m: list" << '\n';
795  return EXIT_FAILURE;
796  }
797  catch (...)
798  {
799  std::cout << "unexpected exception was caught: unknown" << '\n';
800  std::cout << "\033[1;31mFAILED\033[0m: list" << '\n';
801  return EXIT_FAILURE;
802  }
803 
804  if (this->fini() == false)
805  {
806  std::cout << "\033[1;31mFAILED\033[0m: fini" << '\n';
807  return EXIT_FAILURE;
808  }
809 
810  if (this->internal_fini() == false)
811  return EXIT_FAILURE;
812 
813  return EXIT_SUCCESS;
814  }
815 
816  void
817  expect_failed(const char *condition, const char *func, int line)
818  {
819  std::cout << "\033[1;31mFAILED\033[0m: [" << std::dec << line << "]: " << func << '\n';
820  std::cout << " - reason: " << condition << '\n';
821  this->inc_fail();
822  }
823 
824  void
825  assert_failed(const char *condition, const char *func, int line)
826  {
827  this->expect_failed(condition, func, line);
828  throw (0);
829  }
830 
831 private:
832 
833  int m_pass;
834  int m_fail;
835 };
836 
837 #endif
virtual bool init()
Definition: unittest.h:405
std::string expecting_type
Definition: unittest.h:50
auto mock_unique(MockRepository &mocks)
Definition: unittest.h:303
auto mock_no_delete(MockRepository &mocks)
Definition: unittest.h:272
virtual ~unittest()
Definition: unittest.h:769
void expect_exception_with_args(F &&f, std::shared_ptr< const std::exception > expected, gsl::cstring_span<> func, int line, int path_id=-1)
Definition: unittest.h:570
void inc_fail()
Definition: unittest.h:717
void expect_true_with_args(bool condition, gsl::cstring_span<> condition_text, gsl::cstring_span<> func, int line)
Definition: unittest.h:675
decltype(auto) run()
Definition: unittest.h:772
void expect_no_exception_with_args(F &&f, gsl::cstring_span<> func, int line, int path_id=-1)
Definition: unittest.h:632
unittest()
Definition: unittest.h:764
virtual bool fini()
Definition: unittest.h:427
void expect_false_with_args(bool condition, gsl::cstring_span<> condition_text, gsl::cstring_span<> func, int line)
Definition: unittest.h:702
void no_delete(T *)
Definition: unittest.h:255
auto make_ptr(const T ptr)
Definition: unittest.h:77
std::string caught_type
Definition: unittest.h:49
auto make_uintptr(const T ptr)
Definition: unittest.h:85
void assert_failed(const char *condition, const char *func, int line)
Definition: unittest.h:825
std::string to_string(const T val, const int base)
Definition: to_string.h:42
void expect_failed(const char *condition, const char *func, int line)
Definition: unittest.h:817
void inc_pass()
Definition: unittest.h:716
void run_unittest_with_mocks(MockRepository &mocks, T lamda, const char *func, int line)
Definition: unittest.h:430
constexpr const auto name
Definition: cpuid_x64.h:81
std::string expecting_what
Definition: unittest.h:52
std::string caught_what
Definition: unittest.h:51
auto mock_shared(MockRepository &mocks)
Definition: unittest.h:290
void compare_exceptions(const struct exception_state &state, gsl::cstring_span<> func, int line, int path_id=-1)
Definition: unittest.h:477
bool wrong_exception
Definition: unittest.h:48
virtual bool list()
Definition: unittest.h:383