Przeglądaj źródła

Add proper support for ensures(...) clause to be evaluated on return.

Sam Jaffe 6 lat temu
rodzic
commit
e9777117f4
2 zmienionych plików z 55 dodań i 3 usunięć
  1. 19 3
      include/expect/expect.hpp
  2. 36 0
      test/expect_test.cxx

+ 19 - 3
include/expect/expect.hpp

@@ -26,8 +26,23 @@
 #define NUM(...) SELECT_5TH(__VA_ARGS__, FOURPLUS, THREE, TWO, ONE, throwaway)
 #define SELECT_5TH(a1, a2, a3, a4, a5, ...) a5
 
-
 namespace contract {
+  template <typename expect>
+  class ensure_t {
+  private:
+    std::function<bool()> passes_;
+    std::string message_;
+  public:
+    template <typename F>
+    ensure_t(F && passes, std::string const & msg, char const * = "")
+      : passes_(passes), message_(msg) {}
+    ~ensure_t() noexcept(false) {
+      if (!std::uncaught_exception() && !passes_()) {
+        throw expect{message_};
+      }
+    }
+  };
+
   template <typename except>
   void _contract_impl(bool expr, std::string const & msg, char const * = "") {
     if ( ! expr ) throw except{ msg };
@@ -101,8 +116,9 @@ namespace contract {
  *  ensures( bool-expr, error_type, custom_msg )
  */
 #define ensures( ... ) \
-  contract::_contract_impl<EXCEPT_T(__VA_ARGS__, std::runtime_error)>( \
-    FIRST(__VA_ARGS__), \
+  contract::ensure_t<EXCEPT_T(__VA_ARGS__, std::runtime_error)>\
+    CONCAT(contract_ensures_, __LINE__)( \
+    [&]() { return FIRST(__VA_ARGS__); }, \
     EXCEPT_MSG(__VA_ARGS__ , \
                DEF_MSG("postcondition", FIRST(__VA_ARGS__))) \
   )

+ 36 - 0
test/expect_test.cxx

@@ -67,6 +67,42 @@ TEST(ExpectGracefulTest, ReturnValueCanBeControlled) {
   EXPECT_THAT(get_gracefully(false), Eq(7));
 }
 
+TEST(EnsureTest, EnsureFiresOffWhenScopeExit) {
+  int i = 7;
+  ensures(i == 6);
+  i = 6;
+}
+
+TEST(EnsureTest, ThrowsIfFailed) {
+  int i = 7;
+  EXPECT_THROW(ensures(i == 6), std::runtime_error);
+}
+
+TEST(EnsureTest, FailsWithErrorMessageContainingExpr) {
+  int i = 1;
+  try {
+    { ensures(i == 2); }
+    FAIL() << "expected exception";
+  } catch (std::exception const & e) {
+    EXPECT_THAT(e.what(), HasSubstr("i == 2"));
+  }
+}
+
+TEST(EnsureTest, CanProvideCustomErrorType) {
+  int i = 1;
+  EXPECT_THROW((expects(i == 2, my_error, "error")), my_error);
+}
+
+TEST(EnsureTest, CanProvideCustomErrorMessage) {
+  int i = 1;
+  try {
+    { ensures(i == 2, "example error message"); }
+    FAIL() << "expected exception";
+  } catch (std::exception const & e) {
+    EXPECT_THAT(e.what(), StrEq("example error message"));
+  }
+}
+
 TEST(ReturnEnsureTest, ReturnsFirstExprIfPassesLambda) {
   int i = 6;
   EXPECT_THAT(return_ensures(i + 1, _ > 5), Eq(7));